From 7f7d7ea1fa515157162d0d21245925551ed103a1 Mon Sep 17 00:00:00 2001 From: Aurabindo Pillai Date: Wed, 22 Jul 2026 14:45:53 -0400 Subject: [PATCH] drm/amd/display: Add new sources for DCN6 Add DCN6 code to DC, DML2, and DMUB Signed-off-by: Aurabindo Pillai Signed-off-by: Roman Li Reviewed-by: Ivan Lipski Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h | 389 ++ .../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 1673 +++++ .../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h | 150 + .../dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.c | 369 ++ .../dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.h | 35 + .../amd/display/dc/dccg/dcn60/dcn60_dccg.c | 187 + .../amd/display/dc/dccg/dcn60/dcn60_dccg.h | 182 + .../dc/dio/dcn60/dcn60_dio_link_encoder.c | 372 ++ .../dc/dio/dcn60/dcn60_dio_link_encoder.h | 43 + .../dc/dio/dcn60/dcn60_dio_stream_encoder.c | 538 ++ .../dc/dio/dcn60/dcn60_dio_stream_encoder.h | 181 + .../dml21/inc/bounding_boxes/dcn5_soc_bb.h | 36 + .../dml21/inc/bounding_boxes/dcn6_soc_bb.h | 229 + .../bounding_boxes/utm_qos_model_dchub_v1.h | 109 + .../bounding_boxes/utm_qos_model_dchub_v2.h | 122 + .../bounding_boxes/utm_qos_model_dchub_v3.h | 53 + .../inc/bounding_boxes/utm_qos_model_types.h | 51 + .../dc/dml2_0/dml21/inc/dml_dmub_cmd.h | 502 ++ .../dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.c | 152 + .../dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.h | 9 + .../dml21/src/dml2_cga/dml2_cga_factory.c | 38 + .../dml21/src/dml2_cga/dml2_cga_factory.h | 9 + .../dml2_core_calcs_dsc_shared_types.h | 32 + .../dml2_core/dml2_core_dcn5_calcs_dchub.c | 5893 +++++++++++++++++ .../dml2_core/dml2_core_dcn5_calcs_dchub.h | 426 ++ .../dml2_core_dcn5_calcs_display_pipe.c | 1029 +++ .../dml2_core_dcn5_calcs_display_pipe.h | 151 + .../dml2_core_dcn5_calcs_dsc_latency.c | 501 ++ .../dml2_core_dcn5_calcs_dsc_latency.h | 23 + .../dml2_core_dcn5_funcs_initialize.c | 78 + .../dml2_core_dcn5_funcs_initialize.h | 12 + .../dml2_core_dcn5_funcs_mode_programming.c | 1791 +++++ .../dml2_core_dcn5_funcs_mode_programming.h | 11 + .../dml2_core_dcn5_funcs_mode_support.c | 2957 +++++++++ .../dml2_core_dcn5_funcs_mode_support.h | 11 + .../src/dml2_core/dml2_core_dcn6_calcs.c | 11 + .../src/dml2_core/dml2_core_dcn6_calcs.h | 8 + .../dml2_core/dml2_core_dcn6_calcs_dchub.c | 1951 ++++++ .../dml2_core/dml2_core_dcn6_calcs_dchub.h | 101 + .../dml2_core_dcn6_funcs_initialize.c | 165 + .../dml2_core_dcn6_funcs_initialize.h | 12 + .../dml2_core_dcn6_funcs_mode_programming.c | 1389 ++++ .../dml2_core_dcn6_funcs_mode_programming.h | 11 + .../dml2_core_dcn6_funcs_mode_support.c | 4277 ++++++++++++ .../dml2_core_dcn6_funcs_mode_support.h | 11 + .../dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c | 1 - .../dml21/src/dml2_dpmm/dml2_dpmm_dcn5.c | 449 ++ .../dml21/src/dml2_dpmm/dml2_dpmm_dcn5.h | 19 + .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.c | 522 ++ .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h | 21 + .../dml2_pmo/dml2_pmo_dcn5_stage_optimizers.c | 2228 +++++++ .../dml2_pmo/dml2_pmo_dcn5_stage_optimizers.h | 97 + .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.c | 506 ++ .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h | 22 + .../dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c | 1789 +++++ .../dml2_pmo/dml2_pmo_dcn6_stage_optimizers.h | 17 + .../alternate_pstate_shared_lib.c | 1078 +++ .../alternate_pstate_shared_lib.h | 109 + .../dml2_0/dml21/src/dml2_top/dml2_top_utm.c | 394 ++ .../dml2_0/dml21/src/dml2_top/dml2_top_utm.h | 9 + .../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.c | 144 + .../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.h | 11 + .../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c | 405 ++ .../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.h | 12 + .../dml2_utm_soc_bb/dml2_utm_soc_bb_factory.c | 29 + .../dml2_utm_soc_bb/dml2_utm_soc_bb_factory.h | 11 + .../drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c | 391 ++ .../drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h | 86 + .../drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c | 346 + .../drm/amd/display/dc/dpp/dcn60/dcn60_dpp.h | 525 ++ .../amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c | 315 + .../drm/amd/display/dc/dsc/dcn60/dcn60_dsc.c | 447 ++ .../drm/amd/display/dc/dsc/dcn60/dcn60_dsc.h | 248 + .../display/dc/gpio/dcn60/hw_factory_dcn60.c | 228 + .../display/dc/gpio/dcn60/hw_factory_dcn60.h | 11 + .../dc/gpio/dcn60/hw_translate_dcn60.c | 168 + .../dc/gpio/dcn60/hw_translate_dcn60.h | 13 + .../dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.c | 114 + .../dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.h | 18 + .../hpo/dcn60/dcn60_hpo_frl_stream_encoder.c | 358 + .../hpo/dcn60/dcn60_hpo_frl_stream_encoder.h | 32 + .../display/dc/hubbub/dcn60/dcn60_hubbub.c | 1816 +++++ .../display/dc/hubbub/dcn60/dcn60_hubbub.h | 207 + .../amd/display/dc/hubp/dcn50/dcn50_hubp.c | 536 ++ .../amd/display/dc/hubp/dcn50/dcn50_hubp.h | 48 + .../amd/display/dc/hubp/dcn60/dcn60_hubp.c | 477 ++ .../amd/display/dc/hubp/dcn60/dcn60_hubp.h | 272 + .../amd/display/dc/hwss/dcn50/dcn50_hwseq.c | 759 +++ .../amd/display/dc/hwss/dcn50/dcn50_hwseq.h | 32 + .../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 1168 ++++ .../amd/display/dc/hwss/dcn60/dcn60_hwseq.h | 32 + .../amd/display/dc/hwss/dcn60/dcn60_init.c | 183 + .../amd/display/dc/hwss/dcn60/dcn60_init.h | 12 + .../display/dc/irq/dcn60/irq_service_dcn60.c | 417 ++ .../display/dc/irq/dcn60/irq_service_dcn60.h | 12 + .../drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c | 379 ++ .../drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h | 487 ++ .../drm/amd/display/dc/opp/dcn60/dcn60_opp.c | 25 + .../drm/amd/display/dc/opp/dcn60/dcn60_opp.h | 64 + .../amd/display/dc/optc/dcn60/dcn60_optc.c | 107 + .../amd/display/dc/optc/dcn60/dcn60_optc.h | 178 + .../dc/resource/dcn60/dcn60_resource.c | 2375 +++++++ .../dc/resource/dcn60/dcn60_resource.h | 676 ++ .../dcn60/dcn60_soc_and_ip_translator.c | 117 + .../dcn60/dcn60_soc_and_ip_translator.h | 16 + .../gpu/drm/amd/display/dmub/src/dmub_dcn60.c | 732 ++ .../gpu/drm/amd/display/dmub/src/dmub_dcn60.h | 303 + 107 files changed, 48882 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.c create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.h create mode 100644 drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c create mode 100644 drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.h create mode 100644 drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn5_soc_bb.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn6_soc_bb.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v1.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v2.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v3.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_types.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_dmub_cmd.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_calcs_dsc_shared_types.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.h create mode 100644 drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c create mode 100644 drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h create mode 100644 drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c create mode 100644 drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.h create mode 100644 drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c create mode 100644 drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.c create mode 100644 drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.h create mode 100644 drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.c create mode 100644 drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.h create mode 100644 drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.c create mode 100644 drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.h create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c create mode 100644 drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.h create mode 100644 drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c create mode 100644 drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h create mode 100644 drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.c create mode 100644 drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.h create mode 100644 drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c create mode 100644 drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.h create mode 100644 drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c create mode 100644 drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h create mode 100644 drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c create mode 100644 drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.h create mode 100644 drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c create mode 100644 drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.h create mode 100644 drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c create mode 100644 drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h create mode 100644 drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.c create mode 100644 drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.h create mode 100644 drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.c create mode 100644 drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.h create mode 100644 drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c create mode 100644 drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.h create mode 100644 drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c create mode 100644 drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.h create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.c create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.h diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h new file mode 100644 index 000000000000..e10621b5eeff --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#ifndef DALSMC_H +#define DALSMC_H + +/** + * @file dalsmc.h + * + * @brief VBIOS and DAL to PMFW Interface + * + * Clients: VBIOS and DAL + * Protocols: dalsmc + * + * @date 2016 - 2026 + */ + +/** + * @mainpage PMFW-DAL Message Interface + * + * The protocol uses six registers: + * + * - MSG_REG — write the message ID (DALSMC_MSG_*) to trigger the transaction + * - ARG_REG_0 — input argument Reg0; also carries response data on completion + * - ARG_REG_1 — input argument Reg1 + * - ARG_REG_2 — input argument Reg2 + * - ARG_REG_3 — input argument Reg3 + * - RESP_REG — poll until non-zero; value is a DALSMC_Result_* response code + * + * Programming sequence: + * 1. Clear RESP_REG to 0 + * 2. Write input arguments to ARG_REG_0..3 + * 3. Write message ID to MSG_REG (triggers PMFW interrupt) + * 4. Poll RESP_REG until non-zero — value is the result code + * 5. Read response data from ARG_REG_0 (message-specific) + * + * For payloads too large for the four argument registers, the protocol supports + * DRAM table transfers where DAL allocates a DRAM buffer and exchanges bulk data + * with PMFW through system memory. + * + * This documentation contains the subsections:\n\n + * @ref ResponseCodes\n + * @ref Messages\n + * @ref DramTables\n + */ + +#define DALSMC_VERSION 0x1 + +/** @defgroup ResponseCodes PMFW Response Codes + * @{ + */ +// SMU Response Codes: +#define DALSMC_Result_OK 0x01 +#define DALSMC_Result_Failed 0xFF +#define DALSMC_Result_UnknownCmd 0xFE +#define DALSMC_Result_CmdRejectedPrereq 0xFD +#define DALSMC_Result_CmdRejectedBusy 0xFC +/** @} */ + +/** @defgroup Messages Message definitions + * @{ + */ + +/** Generic register overlay — four 32-bit C2PMSG argument registers. */ +typedef struct { + uint32_t Reg0; + uint32_t Reg1; + uint32_t Reg2; + uint32_t Reg3; +} DALSMC_args_t; + +/** + * DALSMC_MSG_TestMessage - Test interface connectivity. + * + * Echos back the argument value incremented by 1. Use to verify the mailbox + * is functional before sending real messages. + * + * Request: TestValue — arbitrary test integer + * Response: Reg0 — TestValue + 1 + */ +#define DALSMC_MSG_TestMessage 0x01 +typedef union { + struct { + uint32_t TestValue; + uint32_t Reserved[3]; + }; + DALSMC_args_t Args; +} DALSMC_TestMessage_arg_t; + +/** + * DALSMC_MSG_GetMsgHeaderVersion - Query the DALSMC header version running on PMFW. + * + * DAL uses the returned version to determine which messages are supported in + * environments that require backwards compatibility. + * + * Request: (none) + * Response: Reg0 — DALSMC_VERSION value compiled into PMFW + */ +#define DALSMC_MSG_GetMsgHeaderVersion 0x02 + +/** + * DALSMC_MSG_TransferTableSmu2Dram - Transfer a PMFW table into DRAM. + * DALSMC_MSG_TransferTableDram2Smu - Transfer a DRAM buffer into PMFW. + * + * Both directions use the same argument layout. The DRAM address must be set + * beforehand (AddrLow / AddrHigh are the GPU MC address bits [31:0] / [63:32]). + * + * Smu2Dram supported tables: TABLE_DAL_INIT (DPM clocks + UTM QoS + memory config) + * Dram2Smu supported tables: TABLE_SOC_UTM (debug override of UTM QoS parameters) + * + * Request: TableId — table identifier (TABLE_* defines below) + * AddrLow — GPU MC address bits [31:0] of destination/source buffer + * AddrHigh — GPU MC address bits [63:32] of destination/source buffer + * Response: (none beyond result code) + */ +#define DALSMC_MSG_TransferTableSmu2Dram 0x03 +#define DALSMC_MSG_TransferTableDram2Smu 0x04 +typedef union { + struct { + uint32_t TableId; + uint32_t AddrLow; + uint32_t AddrHigh; + uint32_t Reserved; + }; + DALSMC_args_t Args; +} DALSMC_TransferTable_arg_t; + +/** + * DALSMC_MSG_SetHardMinByFreq - Set a lower bound frequency constraint on a PPCLK. + * + * Response does not indicate that the effective clock has already been raised to + * meet the minimum requirement; poll DALSMC_MSG_ReturnHardMinStatus for the status + * of the request. + * + * Supported clocks: SOCCLK, DISPCLK, DPPCLK, DCFCLK, DTBCLK. + * + * Request: FreqKhz[23:0] — target minimum frequency in kHz (0 to ~16.7 GHz) + * Ppclk[31:24] — PPCLK_e clock identifier + * Response: (none beyond result code) + */ +#define DALSMC_MSG_SetHardMinByFreq 0x05 +typedef union { + struct { + uint32_t FreqKhz : 24; + uint32_t Ppclk : 8; + uint32_t Reserved[3]; + }; + DALSMC_args_t Args; +} DALSMC_SetHardMinByFreq_arg_t; + +/** + * DALSMC_MSG_SetMinDeepSleepDcfclk - Set the minimum DCFCLK frequency in deep sleep. + * + * Request: MinDcfclkMhz — minimum DCFCLK frequency in MHz during deep sleep + * Response: (none beyond result code) + */ +#define DALSMC_MSG_SetMinDeepSleepDcfclk 0x06 +typedef union { + struct { + uint32_t MinDcfclkMhz; + uint32_t Reserved[3]; + }; + DALSMC_args_t Args; +} DALSMC_SetMinDeepSleepDcfclk_arg_t; + +/** + * DALSMC_MSG_BacoAudioD3PME - Wake the audio block from D3/BACO. + * + * Triggers PMFW to bring the AZ (audio) block out of its D3 power state. + * No arguments or response data; result code indicates success. + */ +#define DALSMC_MSG_BacoAudioD3PME 0x07 + +/** + * DALSMC_MSG_ReturnHardMinStatus - Query outstanding hard-min request status. + * + * Returns a bitmask reporting which PPCLK hard-min requests have been satisfied + * by the arbiter. Each bit position corresponds to the matching PPCLK_e value. + * A set bit means the arbiter has reached or exceeded the requested minimum. + * + * Request: (none) + * Response: Reg0 — bitmask of satisfied PPCLKs (bit N set ↔ PPCLK_e N is satisfied) + */ +#define DALSMC_MSG_ReturnHardMinStatus 0x08 + +/** + * DALSMC_MSG_IndicatePstateStatus - Indicate to PMFW various DMU behaviors required + * to support UCLK P-state, for example whether or not DMU needs to modulate refresh + * rate to perform UCLK switches. + * + * Request: WaitResp[0] — DAL requires a synchronous response before proceeding + * DrrEnable[1] — DRR (dynamic refresh rate modulation) is active + * AltCh[2] — alternate-channel mode is active + * AllowUclk[16] — DCN can tolerate UCLK P-state switches + * AllowFclk[17] — DCN can tolerate FCLK P-state switches + * Response: (none beyond result code) + */ +#define DALSMC_MSG_IndicatePstateStatus 0x09 +typedef union { + struct { + uint32_t WaitResp : 1; + uint32_t DrrEnable : 1; + uint32_t AltCh : 1; + uint32_t Reserved : 13; + uint32_t AllowUclk : 1; + uint32_t AllowFclk : 1; + uint32_t Reserved1 : 14; + uint32_t Reserved2[3]; + }; + DALSMC_args_t Args; +} DALSMC_IndicatePstateStatus_arg_t; + +/** + * DALSMC_MSG_UpdateUTMQoSRequest - Update the active UTM QoS bandwidth/latency request. + * + * Passes the current display bandwidth and latency requirements to PMFW so it + * can select the appropriate SoC operating point (UCLK/FCLK level) from the + * UTM table. Called whenever the display configuration changes. + * + * The QoS requirement must take effect before PMFW sends its response. + * + * Request: LatencySopIndex — index into the UTM SOP table that satisfies latency + * NominalBandwidthKBps — required nominal (average) bandwidth in KB/s + * UrgentBandwidthKBps — required urgent bandwidth in KB/s + * LsdmaBandwidthKBps — required LSDMA bandwidth in KB/s + * Response: (none beyond result code) + */ +#define DALSMC_MSG_UpdateUTMQoSRequest 0x0A +typedef union { + struct { + uint32_t LatencySopIndex; + uint32_t NominalBandwidthKBps; + uint32_t UrgentBandwidthKBps; + uint32_t LsdmaBandwidthKBps; + }; + DALSMC_args_t Args; +} DALSMC_UpdateUTMQoSRequest_arg_t; + +/** + * DALSMC_MSG_SetDisplayIdleOptimizations - Notify PMFW of DCN idle-state conditions. + * + * Indicates which display-side power optimizations are currently safe to apply. + * PMFW uses these flags to gate deeper SoC power states such as S0i2. + * + * Request: DfRequestDisabled[0] — DF (data fabric) requests from DCN are disabled + * PhyRefClkOff[1] — PHY reference clock has been gated off + * S0i2Rdy[2] — DCN is ready for the system to enter S0i2 + * Response: (none beyond result code) + */ +#define DALSMC_MSG_SetDisplayIdleOptimizations 0x0B +typedef union { + struct { + uint32_t DfRequestDisabled : 1; + uint32_t PhyRefClkOff : 1; + uint32_t S0i2Rdy : 1; + uint32_t Reserved : 29; + uint32_t Reserved1[3]; + }; + DALSMC_args_t Args; +} DALSMC_SetDisplayIdleOptimizations_arg_t; + +/** + * DALSMC_MSG_SetStutterEfficiency - Report DCN stutter efficiency to PMFW. + * + * Informs PMFW of the current stutter utilisation for base and low-power stutter + * modes so PMFW can adjust memory power policy accordingly. + * + * Base mode — lower enter+exit latency (PHY LP1, no UCIE LP). + * Low-power mode — higher enter+exit latency (PHY LP2, UCIE LP1). + * + * Request: BaseEfficiencyPct[7:0] — stutter efficiency % in base mode + * LowPowerEfficiencyPct[15:8] — stutter efficiency % in low-power mode + * Response: (none beyond result code) + */ +#define DALSMC_MSG_SetStutterEfficiency 0x0C +typedef union { + struct { + uint32_t BaseEfficiencyPct : 8; + uint32_t LowPowerEfficiencyPct : 8; + uint32_t Reserved : 16; + uint32_t Reserved1[3]; + }; + DALSMC_args_t Args; +} DALSMC_SetStutterEfficiency_arg_t; + +#define DALSMC_Message_Count 0x0D ///< Total number of messages + +/** @} */ + +/** @defgroup DramTables DRAM Tables + * @brief Bulk data structures exchanged between DAL and PMFW via system DRAM. + * + * Used when the payload exceeds the four 32-bit C2PMSG argument registers + * (DALSMC_args_t). DAL allocates a DRAM buffer, passes its address through + * DALSMC_TransferTable_arg_t, and issues either a + * DALSMC_MSG_TransferTableSmu2Dram or DALSMC_MSG_TransferTableDram2Smu message. + * @{ + */ +typedef struct { + uint32_t LoadLevelCount : 4; + uint32_t SopCount : 4; + uint32_t Reserved : 24; +} SocUtmTableHeader_t; + +typedef struct { + uint32_t UrgentRampPs; + uint32_t TripPs; + uint32_t MetaTripToMemPs; + uint32_t MaxReqLatencyUrgPs; + uint32_t AvgReqLatencyUrgPs; + uint32_t MaxReqLatencyNonUrgPs; + uint32_t AvgReqLatencyNonUrgPs; + uint32_t DfResponseTimePs; + uint32_t UrgentBandwidthKBps; + uint32_t NominalBandwidthKBps; + uint32_t LsdmaBandwidthKBps; + uint32_t Reserved[1]; +} SocUtmSopEntry_t; + +typedef struct { + uint32_t SmuVersion; + uint32_t SmuDriverIfVersion; + uint32_t Reserved[2]; +} DalInitHeader_t; + +#define NUM_CLOCK_LEVELS 8 +typedef struct { + uint32_t Clocks[NUM_CLOCK_LEVELS]; + uint32_t DcMaxClock; + uint32_t NumClocks; + uint32_t Reserved[2]; +} DpmClock_t; + +typedef struct { + uint32_t NumUmcChannels; + uint32_t Reserved[3]; +} MemoryConfig_t; + +/** + * TABLE_SOC_UTM - SoC UTM QoS table. + * + * Provides per-load-level bandwidth and latency bounds used by the display + * engine to meet memory access requirements. + * + * Normal path: embedded in DalInitTable_t, fetched once via + * DALSMC_MSG_TransferTableSmu2Dram(TABLE_DAL_INIT). + * + * Override path (debug only): TABLE_SOC_UTM via + * DALSMC_MSG_TransferTableDram2Smu to override custom QoS parameters into PMFW. + */ +#define TABLE_SOC_UTM 0xC +/* TODO: rename back to MAX_UTM_SOP_COUNT once utm_qos_model_types.h conflict is resolved */ +#define DALSMC_MAX_UTM_SOP_COUNT 16 +#define MAX_UTM_LOAD_LEVEL_COUNT 16 +#define UTM_LOAD_LEVEL_INDEX_IDLE 0 +#define UTM_LOAD_LEVEL_INDEX_ACTIVE_ALTERNATE_PSTATE 1 +#define UTM_LOAD_LEVEL_INDEX_ACTIVE 2 +#define UTM_SOP_ENTRIES_OFFSET(LoadLevel, SopIndex) \ + (sizeof(SocUtmTableHeader_t) \ + + ((LoadLevel) * DALSMC_MAX_UTM_SOP_COUNT \ + + (SopIndex)) * sizeof(SocUtmSopEntry_t)) +typedef struct { + SocUtmTableHeader_t Header; + SocUtmSopEntry_t Sops[MAX_UTM_LOAD_LEVEL_COUNT][DALSMC_MAX_UTM_SOP_COUNT]; +} SocUtmTable_t; + +/** + * TABLE_DAL_INIT - Full TABLE_DAL_INIT payload transferred from SMU to DRAM. + */ +#define TABLE_DAL_INIT 0xD +#define MAX_PPCLK_COUNT 12 +#define DPM_CLOCK_OFFSET(Ppclk) \ + (sizeof(DalInitHeader_t) + (Ppclk) * sizeof(DpmClock_t)) +#define UTM_TABLE_OFFSET \ + (sizeof(DalInitHeader_t) + MAX_PPCLK_COUNT * sizeof(DpmClock_t)) +#define MEMORY_CONFIG_OFFSET \ + (sizeof(DalInitHeader_t) + MAX_PPCLK_COUNT * sizeof(DpmClock_t) \ + + sizeof(SocUtmTable_t)) +typedef struct { + DalInitHeader_t Header; + DpmClock_t PPClocks[MAX_PPCLK_COUNT]; + SocUtmTable_t UtmTable; + MemoryConfig_t MemoryConfig; +} DalInitTable_t; + +/** @} */ + +#endif /* DALSMC_H */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c new file mode 100644 index 000000000000..d71b0aed90c9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c @@ -0,0 +1,1673 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dccg.h" +#include "clk_mgr_internal.h" +#include "dcn60/dcn60_clk_mgr_smu_msg.h" +#include "hw_sequencer.h" +#include "dce100/dce_clk_mgr.h" +#include "dcn20/dcn20_clk_mgr.h" +#include "dcn401/dcn401_clk_mgr.h" +#include "dcn60/dcn60_clk_mgr.h" +#include "soc_and_ip_translator.h" +#include "bounding_boxes/utm_qos_model_types.h" +#include "bounding_boxes/utm_qos_model_dchub_v3.h" +#include "reg_helper.h" +#include "core_types.h" +#include "dm_helpers.h" +#include "link_service.h" +#include "dc_state_priv.h" +#include "atomfirmware.h" + +#include "dcn401/dcn401_smu14_driver_if.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" + +#undef DC_LOGGER +#define DC_LOGGER \ + clk_mgr_base->ctx->logger + +#define mmCLK08_CLK8_CLK0_CURRENT_CNT 0x1B83F +#define mmCLK08_CLK8_CLK1_CURRENT_CNT 0x1B840 +#define mmCLK08_CLK8_CLK2_CURRENT_CNT 0x1B841 +#define mmCLK08_CLK8_CLK3_CURRENT_CNT 0x1B842 +#define mmCLK08_CLK8_CLK4_CURRENT_CNT 0x1B843 + +#define mmCLK08_CLK8_CLK0_BYPASS_CNTL 0x1B816 +#define mmCLK08_CLK8_CLK1_BYPASS_CNTL 0x1B81E +#define mmCLK08_CLK8_CLK2_BYPASS_CNTL 0x1B826 +#define mmCLK08_CLK8_CLK3_BYPASS_CNTL 0x1B82E +#define mmCLK08_CLK8_CLK4_BYPASS_CNTL 0x1B836 + +#define mmCLK08_CLK8_CLK0_DS_CNTL 0x1B810 +#define mmCLK08_CLK8_CLK1_DS_CNTL 0x1B818 +#define mmCLK08_CLK8_CLK2_DS_CNTL 0x1B820 +#define mmCLK08_CLK8_CLK3_DS_CNTL 0x1B828 +#define mmCLK08_CLK8_CLK4_DS_CNTL 0x1B830 + +#define mmCLK08_CLK8_CLK_TICK_CNT_CONFIG_REG 0x1B83D + +#define DCN_BASE__INST0_SEG1 0x000000C0 + +#define CLK8_CLK_TICK_CNT_CONFIG_REG__TIMER_THRESHOLD__SHIFT 0x0 +#define CLK8_CLK_TICK_CNT_CONFIG_REG__TIMER_THRESHOLD_MASK 0x00FFFFFFL +#define CLK8_CLK0_BYPASS_CNTL__CLK0_BYPASS_SEL__SHIFT 0x0 +#define CLK8_CLK0_BYPASS_CNTL__CLK0_BYPASS_SEL_MASK 0x00000007L +#define CLK8_CLK1_BYPASS_CNTL__CLK1_BYPASS_SEL__SHIFT 0x0 +#define CLK8_CLK1_BYPASS_CNTL__CLK1_BYPASS_SEL_MASK 0x00000007L +#define CLK8_CLK2_BYPASS_CNTL__CLK2_BYPASS_SEL__SHIFT 0x0 +#define CLK8_CLK2_BYPASS_CNTL__CLK2_BYPASS_SEL_MASK 0x00000007L +#define CLK8_CLK3_BYPASS_CNTL__CLK3_BYPASS_SEL__SHIFT 0x0 +#define CLK8_CLK3_BYPASS_CNTL__CLK3_BYPASS_SEL_MASK 0x00000007L +#define CLK8_CLK4_BYPASS_CNTL__CLK4_BYPASS_SEL__SHIFT 0x0 +#define CLK8_CLK4_BYPASS_CNTL__CLK4_BYPASS_SEL_MASK 0x00000007L +#define CLK8_CLK0_DS_CNTL__CLK0_DS_DIV_ID__SHIFT 0x0 +#define CLK8_CLK0_DS_CNTL__CLK0_ALLOW_DS__SHIFT 0x4 +#define CLK8_CLK0_DS_CNTL__CLK0_DS_DIV_ID_MASK 0x0000000FL +#define CLK8_CLK0_DS_CNTL__CLK0_ALLOW_DS_MASK 0x00000010L +#define CLK8_CLK1_DS_CNTL__CLK1_DS_DIV_ID__SHIFT 0x0 +#define CLK8_CLK1_DS_CNTL__CLK1_ALLOW_DS__SHIFT 0x4 +#define CLK8_CLK1_DS_CNTL__CLK1_DS_DIV_ID_MASK 0x0000000FL +#define CLK8_CLK1_DS_CNTL__CLK1_ALLOW_DS_MASK 0x00000010L +#define CLK8_CLK2_DS_CNTL__CLK2_DS_DIV_ID__SHIFT 0x0 +#define CLK8_CLK2_DS_CNTL__CLK2_ALLOW_DS__SHIFT 0x4 +#define CLK8_CLK2_DS_CNTL__CLK2_DS_DIV_ID_MASK 0x0000000FL +#define CLK8_CLK2_DS_CNTL__CLK2_ALLOW_DS_MASK 0x00000010L +#define CLK8_CLK3_DS_CNTL__CLK3_DS_DIV_ID__SHIFT 0x0 +#define CLK8_CLK3_DS_CNTL__CLK3_ALLOW_DS__SHIFT 0x4 +#define CLK8_CLK3_DS_CNTL__CLK3_DS_DIV_ID_MASK 0x0000000FL +#define CLK8_CLK3_DS_CNTL__CLK3_ALLOW_DS_MASK 0x00000010L +#define CLK8_CLK4_DS_CNTL__CLK4_DS_DIV_ID__SHIFT 0x0 +#define CLK8_CLK4_DS_CNTL__CLK4_ALLOW_DS__SHIFT 0x4 +#define CLK8_CLK4_DS_CNTL__CLK4_DS_DIV_ID_MASK 0x0000000FL +#define CLK8_CLK4_DS_CNTL__CLK4_ALLOW_DS_MASK 0x00000010L + +#define DCN60_CLKIP_REFCLK_KHZ 48000 /* 48 MHz */ + +#undef FN +#define FN(reg_name, field_name) \ + clk_mgr->clk_mgr_shift->field_name, clk_mgr->clk_mgr_mask->field_name + +#define REG(reg) \ + (clk_mgr->regs->reg) + +#define BASE_INNER(seg) DCN_BASE__INST0_SEG ## seg + +#define BASE(seg) BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(reg ## reg_name ## _BASE_IDX) + \ + reg ## reg_name + +#define CLK_SR_DCN60(reg_name, block, inst)\ + .reg_name = mm ## block ## _ ## reg_name + +static const struct clk_mgr_registers clk_mgr_regs_dcn60 = { + CLK_REG_LIST_DCN60() +}; + +static const struct clk_mgr_shift clk_mgr_shift_dcn60 = { + CLK_COMMON_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct clk_mgr_mask clk_mgr_mask_dcn60 = { + CLK_COMMON_MASK_SH_LIST_DCN60(_MASK) +}; + +#define TO_DCN60_CLK_MGR(clk_mgr)\ + container_of(clk_mgr, struct dcn60_clk_mgr, base) + +static bool dcn60_is_ppclk_dpm_enabled(struct clk_mgr_internal *clk_mgr, PPCLK_e clk) +{ + bool ppclk_dpm_enabled = false; + + switch (clk) { + case PPCLK_SOCCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_socclk_levels > 1; + break; + case PPCLK_UCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_memclk_levels > 1; + break; + case PPCLK_FCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_fclk_levels > 1; + break; + case PPCLK_DISPCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dispclk_levels > 1; + break; + case PPCLK_DPPCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dppclk_levels > 1; + break; + case PPCLK_DPREFCLK: + ppclk_dpm_enabled = false; + break; + case PPCLK_DCFCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels > 1; + break; + case PPCLK_DTBCLK: + ppclk_dpm_enabled = + clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels > 1; + break; + default: + ppclk_dpm_enabled = false; + } + + ppclk_dpm_enabled &= clk_mgr->smu_present; + + return ppclk_dpm_enabled; +} + +static bool dcn60_check_native_scaling(struct pipe_ctx *pipe) +{ + bool is_native_scaling = false; + int width = pipe->plane_state->src_rect.width; + int height = pipe->plane_state->src_rect.height; + + if (pipe->stream->timing.h_addressable == width && + pipe->stream->timing.v_addressable == height && + pipe->plane_state->dst_rect.width == width && + pipe->plane_state->dst_rect.height == height) + is_native_scaling = true; + + return is_native_scaling; +} + +static void dcn60_update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr, + struct dc_state *context, bool safe_to_lower, int ref_dppclk_khz) +{ + uint32_t i; + + clk_mgr->dccg->ref_dppclk = ref_dppclk_khz; + for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) { + int dpp_inst = 0, dppclk_khz, prev_dppclk_khz; + + dppclk_khz = context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz; + + if (context->res_ctx.pipe_ctx[i].plane_res.dpp) + dpp_inst = context->res_ctx.pipe_ctx[i].plane_res.dpp->inst; + else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz == 0) { + /* dpp == NULL && dppclk_khz == 0 is valid because of pipe harvesting. + * In this case just continue in loop + */ + continue; + } else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz > 0) { + /* The software state is not valid if dpp resource is NULL and + * dppclk_khz > 0. + */ + ASSERT(false); + continue; + } + + prev_dppclk_khz = clk_mgr->dccg->pipe_dppclk_khz[i]; + + if (safe_to_lower || prev_dppclk_khz < dppclk_khz) + clk_mgr->dccg->funcs->update_dpp_dto( + clk_mgr->dccg, dpp_inst, dppclk_khz); + } +} + +static int dcn60_set_hard_min_by_freq_optimized(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, int requested_clk_khz) +{ + if (!clk_mgr->smu_present || !dcn60_is_ppclk_dpm_enabled(clk_mgr, clk)) + return 0; + + /* + * SMU set hard min interface takes requested clock in mhz and return + * actual clock configured in khz. If we floor requested clk to mhz, + * there is a chance that the actual clock configured in khz is less + * than requested. If we ceil it to mhz, there is a chance that it + * unnecessarily dumps up to a higher dpm level, which burns more power. + * The solution is to set by flooring it to mhz first. If the actual + * clock returned is less than requested, then we will ceil the + * requested value to mhz and call it again. + */ + int actual_clk_khz = dcn60_smu_set_hard_min_by_freq(clk_mgr, clk, + (uint16_t)khz_to_mhz_floor(requested_clk_khz)); + + if (actual_clk_khz < requested_clk_khz) + actual_clk_khz = dcn60_smu_set_hard_min_by_freq(clk_mgr, clk, + (uint16_t)khz_to_mhz_ceil(requested_clk_khz)); + + return actual_clk_khz; +} + +static void dcn60_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, + struct dc_state *context, + int ref_dtbclk_khz) +{ + (void)ref_dtbclk_khz; + int i; + struct dccg *dccg = clk_mgr->dccg; + struct pipe_ctx *otg_master; + bool use_hpo_encoder; + + for (i = 0; i < context->stream_count; i++) { + otg_master = resource_get_otg_master_for_stream( + &context->res_ctx, context->streams[i]); + + ASSERT(otg_master); + if (!otg_master) + continue; + + ASSERT(otg_master->clock_source); + ASSERT(otg_master->clock_source->funcs->program_pix_clk); + 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; + + if (otg_master->stream_res.pix_clk_params.controller_id > CONTROLLER_ID_UNDEFINED) + otg_master->clock_source->funcs->program_pix_clk( + otg_master->clock_source, + &otg_master->stream_res.pix_clk_params, + dccg->ctx->dc->link_srv->dp_get_encoding_format( + &otg_master->link_config.dp_link_settings), + &otg_master->pll_settings); + } +} + +static unsigned int dcn60_build_update_display_clocks_sequence( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + struct dc_clocks *new_clocks, + bool safe_to_lower, + unsigned int num_steps_start) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn60_clk_mgr *clk_mgr60 = TO_DCN60_CLK_MGR(clk_mgr_internal); + struct dc *dc = clk_mgr_base->ctx->dc; + struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu; + struct dcn60_clk_mgr_block_sequence *block_sequence = clk_mgr60->block_sequence; + bool force_reset = false; + 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 = num_steps_start; + + /* CLK_MGR60_READ_CLOCKS_FROM_DENTIST */ + if (clk_mgr_base->clks.dispclk_khz == 0 || + (dc->debug.force_clock_mode & 0x1)) { + /* This is from resume or boot up, if forced_clock cfg option used, + * we bypass program dispclk and DPPCLK, but need set them for S3. + * Force_clock_mode 0x1: force reset the clock even it is the same clock + * as long as it is in Passive level. + */ + force_reset = true; + + clk_mgr_base->clks.dispclk_khz = clk_mgr_base->boot_snapshot.dispclk; + clk_mgr_base->clks.actual_dispclk_khz = clk_mgr_base->clks.dispclk_khz; + + clk_mgr_base->clks.dppclk_khz = clk_mgr_base->boot_snapshot.dppclk; + clk_mgr_base->clks.actual_dppclk_khz = clk_mgr_base->clks.dppclk_khz; + } + + /* DTBCLK */ + if (!new_clocks->dtbclk_en && dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DTBCLK)) { + new_clocks->ref_dtbclk_khz = clk_mgr_base->bw_params->clk_table.entries[0].dtbclk_mhz * 1000; + } + + /* clock limits are received with MHz precision, divide by 1000 to prevent setting clocks at every call */ + if (!dc->debug.disable_dtb_ref_clk_switch && + should_set_clock(safe_to_lower, new_clocks->ref_dtbclk_khz / 1000, clk_mgr_base->clks.ref_dtbclk_khz / 1000) && //TODO these should be ceiled + dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DTBCLK)) { + /* DCCG requires KHz precision for DTBCLK */ + block_sequence[num_steps].params.update_hardmin_params.ppclk = PPCLK_DTBCLK; + block_sequence[num_steps].params.update_hardmin_params.freq_mhz = + (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_MGR60_UPDATE_HARDMIN_PPCLK; + num_steps++; + + /* Update DTO in DCCG */ + block_sequence[num_steps].params.update_dtbclk_dto_params.context = context; + block_sequence[num_steps].params.update_dtbclk_dto_params.ref_dtbclk_khz = &clk_mgr_base->clks.ref_dtbclk_khz; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_DTBCLK_DTO; + num_steps++; + } + + if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr_base->clks.dppclk_khz)) { + if (clk_mgr_base->clks.dppclk_khz > new_clocks->dppclk_khz) + dppclk_lowered = true; + + clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz; + clk_mgr_base->clks.actual_dppclk_khz = new_clocks->dppclk_khz; + + update_dppclk = true; + } + + if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz)) { + clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz; + + if (dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DISPCLK)) { + block_sequence[num_steps].params.update_hardmin_optimized_params.ppclk = PPCLK_DISPCLK; + block_sequence[num_steps].params.update_hardmin_optimized_params.freq_khz = clk_mgr_base->clks.dispclk_khz; + block_sequence[num_steps].params.update_hardmin_optimized_params.response = &clk_mgr_base->clks.actual_dispclk_khz; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_HARDMIN_PPCLK_OPTIMIZED; + num_steps++; + } + + update_dispclk = true; + } + + if (dc->config.forced_clocks == false || (force_reset && safe_to_lower)) { + if (dppclk_lowered) { + /* if clock is being lowered, increase DTO before lowering refclk */ + block_sequence[num_steps].params.update_dppclk_dto_params.context = context; + block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.dppclk_khz; + block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_DPPCLK_DTO; + num_steps++; + + if (dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DPPCLK)) { + block_sequence[num_steps].params.update_hardmin_optimized_params.ppclk = PPCLK_DPPCLK; + block_sequence[num_steps].params.update_hardmin_optimized_params.freq_khz = clk_mgr_base->clks.dppclk_khz; + block_sequence[num_steps].params.update_hardmin_optimized_params.response = &clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_HARDMIN_PPCLK_OPTIMIZED; + num_steps++; + + block_sequence[num_steps].params.update_dppclk_dto_params.context = context; + block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_DPPCLK_DTO; + num_steps++; + } + } else { + /* if clock is being raised, increase refclk before lowering DTO */ + if (update_dppclk && dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DPPCLK)) { + block_sequence[num_steps].params.update_hardmin_optimized_params.ppclk = PPCLK_DPPCLK; + block_sequence[num_steps].params.update_hardmin_optimized_params.freq_khz = clk_mgr_base->clks.dppclk_khz; + block_sequence[num_steps].params.update_hardmin_optimized_params.response = &clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_HARDMIN_PPCLK_OPTIMIZED; + num_steps++; + } + + block_sequence[num_steps].params.update_dppclk_dto_params.context = context; + block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz; + block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_DPPCLK_DTO; + num_steps++; + } + } + + if (update_dispclk && dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) { + /*update dmcu for wait_loop count*/ + block_sequence[num_steps].params.update_psr_wait_loop_params.dmcu = dmcu; + block_sequence[num_steps].params.update_psr_wait_loop_params.wait = clk_mgr_base->clks.dispclk_khz / 1000 / 7; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_PSR_WAIT_LOOP; + num_steps++; + } + + return num_steps; +} + +static uint32_t dcn60_get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mgr) +{ + (void)clk_mgr; + //TODO: Get VCO frequency + return 0; +} + +static bool dcn60_are_clock_states_equal(struct dc_clocks *a, + struct dc_clocks *b) +{ + if (a->dispclk_khz != b->dispclk_khz) + return false; + else if (a->dppclk_khz != b->dppclk_khz) + return false; + else if (a->dcfclk_khz != b->dcfclk_khz) + return false; + else if (a->dcfclk_deep_sleep_khz != b->dcfclk_deep_sleep_khz) + return false; + else if (a->dramclk_khz != b->dramclk_khz) + return false; + else if (a->p_state_change_support != b->p_state_change_support) + return false; + else if (a->fclk_p_state_change_support != b->fclk_p_state_change_support) + return false; + + return true; +} + +static bool dcn60_is_smu_present(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + return clk_mgr->smu_present; +} + +static int dcn60_get_dtb_ref_freq_khz(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + int dtb_ref_clk_khz = 0; + + if (clk_mgr->smu_present && dcn60_is_ppclk_dpm_enabled(clk_mgr, PPCLK_DTBCLK)) { + /* DPM enabled, use currently set value */ + dtb_ref_clk_khz = clk_mgr_base->clks.ref_dtbclk_khz; + } else { + /* DPM disabled, so use boot snapshot */ + dtb_ref_clk_khz = clk_mgr_base->boot_snapshot.dtbclk; + } + + return dtb_ref_clk_khz; +} + +static unsigned int dcn60_get_dc_mode_limit_mhz(const DpmClock_t *dpm_clk) +{ + if (dpm_clk->NumClocks + && dpm_clk->DcMaxClock == dpm_clk->Clocks[dpm_clk->NumClocks - 1]) + return 0; + + return dpm_clk->DcMaxClock; +} + +/** + * dcn60_populate_dc_mode_limit - Populate DC mode limits from DAL init table. + * @dc_limit: output DC mode limit to populate + * @init_table: DAL init table transferred from PMFW + * + * Sets the DC mode max frequency for each clock. If DcMaxClock equals the + * highest DPM level, the limit is set to 0 (no DC-specific cap). + */ +static void dcn60_populate_dc_mode_limit( + struct clk_limit_table_entry *dc_limit, + const DalInitTable_t *init_table) +{ + dc_limit->dcfclk_mhz = dcn60_get_dc_mode_limit_mhz(&init_table->PPClocks[PPCLK_DCFCLK]); + dc_limit->socclk_mhz = dcn60_get_dc_mode_limit_mhz(&init_table->PPClocks[PPCLK_SOCCLK]); + dc_limit->dtbclk_mhz = dcn60_get_dc_mode_limit_mhz(&init_table->PPClocks[PPCLK_DTBCLK]); + dc_limit->dispclk_mhz = dcn60_get_dc_mode_limit_mhz(&init_table->PPClocks[PPCLK_DISPCLK]); + dc_limit->memclk_mhz = dcn60_get_dc_mode_limit_mhz(&init_table->PPClocks[PPCLK_UCLK]); + dc_limit->fclk_mhz = dcn60_get_dc_mode_limit_mhz(&init_table->PPClocks[PPCLK_FCLK]); +} + +/** + * dcn60_populate_clk_table - Populate clock table from DAL init table. + * @clk_mgr: clock manager instance + * @clk_table: output clock table to populate + * @init_table: DAL init table transferred from PMFW + * + * Reads the DPM clock levels from the PPClocks array within the DalInitTable_t + * populated by PMFW via TABLE_DAL_INIT transfer. + */ +static void dcn60_populate_clk_table(struct clk_mgr_internal *clk_mgr, + struct clk_limit_table *clk_table, + const DalInitTable_t *init_table) +{ + struct clk_limit_num_entries *num_entries = &clk_table->num_entries_per_clk; + const DpmClock_t *dpm_clk; + unsigned int i; + + /* DCFCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_DCFCLK]; + num_entries->num_dcfclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].dcfclk_mhz = dpm_clk->Clocks[i]; + + /* SOCCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_SOCCLK]; + num_entries->num_socclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].socclk_mhz = dpm_clk->Clocks[i]; + + /* DTBCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_DTBCLK]; + num_entries->num_dtbclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].dtbclk_mhz = dpm_clk->Clocks[i]; + + /* DISPCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_DISPCLK]; + num_entries->num_dispclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].dispclk_mhz = dpm_clk->Clocks[i]; + + /* DPPCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_DPPCLK]; + num_entries->num_dppclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].dppclk_mhz = dpm_clk->Clocks[i]; + + /* UCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_UCLK]; + num_entries->num_memclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].memclk_mhz = dpm_clk->Clocks[i]; + if (num_entries->num_memclk_levels) + clk_mgr->base.bw_params->max_memclk_mhz = + clk_table->entries[num_entries->num_memclk_levels - 1].memclk_mhz; + + /* FCLK */ + dpm_clk = &init_table->PPClocks[PPCLK_FCLK]; + num_entries->num_fclk_levels = dpm_clk->NumClocks; + for (i = 0; i < dpm_clk->NumClocks && i < NUM_CLOCK_LEVELS; i++) + clk_table->entries[i].fclk_mhz = dpm_clk->Clocks[i]; + + if (num_entries->num_memclk_levels >= num_entries->num_fclk_levels) + clk_table->num_entries = num_entries->num_memclk_levels; + else + clk_table->num_entries = num_entries->num_fclk_levels; + if (!clk_table->num_entries) + clk_table->num_entries = 1; +} + +static void dcn60_override_bw_params(struct clk_mgr_internal *clk_mgr, + struct clk_bw_params *bw_params) +{ + struct clk_limit_table *clk_table = &bw_params->clk_table; + unsigned int i; + + if (clk_mgr->base.ctx->dc->debug.min_disp_clk_khz) { + for (i = 0; i < clk_table->num_entries_per_clk.num_dispclk_levels; i++) + if (clk_table->entries[i].dispclk_mhz + < (unsigned int)khz_to_mhz_ceil(clk_mgr->base.ctx->dc->debug.min_disp_clk_khz)) + clk_table->entries[i].dispclk_mhz + = (unsigned int)khz_to_mhz_ceil(clk_mgr->base.ctx->dc->debug.min_disp_clk_khz); + } + + if (clk_mgr->base.ctx->dc->debug.min_dpp_clk_khz) { + for (i = 0; i < clk_table->num_entries_per_clk.num_dppclk_levels; i++) + if (clk_table->entries[i].dppclk_mhz + < (unsigned int)khz_to_mhz_ceil(clk_mgr->base.ctx->dc->debug.min_dpp_clk_khz)) + clk_table->entries[i].dppclk_mhz + = (unsigned int)khz_to_mhz_ceil(clk_mgr->base.ctx->dc->debug.min_dpp_clk_khz); + } + + if (clk_mgr->base.ctx->dc->debug.disable_dtb_ref_clk_switch) + bw_params->dc_mode_limit.dtbclk_mhz = 0; + + bw_params->dc_mode_softmax_memclk = bw_params->dc_mode_limit.memclk_mhz; +} + +/** + * dcn60_fetch_dal_init_table - Transfer and apply the DAL init table from PMFW. + * @clk_mgr: clock manager instance + * + * Issues TABLE_DAL_INIT transfer, populates clock table and bounding box. + * + * Return: true on success, false if transfer failed + */ +static void dcn60_populate_utm_qos_model( + struct clk_mgr_internal *clk_mgr, + const struct utm_qos_model **qos_model, + const DalInitTable_t *init_table) +{ + struct dcn60_clk_mgr *dcn60_clk_mgr = + container_of(clk_mgr, struct dcn60_clk_mgr, base); + struct utm_qos_model *model = &dcn60_clk_mgr->utm_qos_model; + struct utm_qos_model_dchub_v3 *dchub = &dcn60_clk_mgr->dchub_v3; + const SocUtmTable_t *utm_table = &init_table->UtmTable; + unsigned int ll, sop; + + memset(dchub, 0, sizeof(*dchub)); + dchub->load_level_count = (uint8_t)utm_table->Header.LoadLevelCount; + dchub->sop_count = (uint8_t)utm_table->Header.SopCount; + + for (ll = 0; ll < dchub->load_level_count + && ll < UTM_QOS_MODEL_V3_MAX_LOAD_LEVEL_COUNT; ll++) { + for (sop = 0; sop < dchub->sop_count + && sop < UTM_QOS_MODEL_V3_MAX_SOP_COUNT; sop++) { + const SocUtmSopEntry_t *src = &utm_table->Sops[ll][sop]; + struct utm_qos_model_dchub_v3_sop_entry *dst = + &dchub->sops[ll][sop]; + + dst->urgent_ramp_ps = src->UrgentRampPs; + dst->t_trip_ps = src->TripPs; + dst->meta_trip_to_mem_ps = src->MetaTripToMemPs; + dst->max_req_latency_urg_ps = src->MaxReqLatencyUrgPs; + dst->avg_req_latency_urg_ps = src->AvgReqLatencyUrgPs; + dst->max_req_latency_non_urg_ps = src->MaxReqLatencyNonUrgPs; + dst->avg_req_latency_non_urg_ps = src->AvgReqLatencyNonUrgPs; + dst->df_response_time_ps = src->DfResponseTimePs; + dst->urgent_bandwidth_KBps = src->UrgentBandwidthKBps; + dst->nominal_bandwidth_KBps = src->NominalBandwidthKBps; + dst->lsdma_bandwidth_KBps = src->LsdmaBandwidthKBps; + } + } + + model->version = utm_qos_model_version_v3; + model->dchub_v3 = dchub; + *qos_model = model; +} + +static bool dcn60_fetch_dal_init_table(struct clk_mgr_internal *clk_mgr) +{ + struct clk_bw_params *bw_params = clk_mgr->base.bw_params; + const DalInitTable_t *init_table; + + if (!dcn60_smu_get_dal_init_table(clk_mgr, &init_table)) + return false; + + clk_mgr->smu_ver = init_table->Header.SmuVersion; + + dcn60_populate_clk_table(clk_mgr, &bw_params->clk_table, init_table); + dcn60_populate_dc_mode_limit(&bw_params->dc_mode_limit, init_table); + + bw_params->num_channels = init_table->MemoryConfig.NumUmcChannels; + bw_params->dram_channel_width_bytes = + clk_mgr->base.ctx->dc_bios->vram_info.dram_channel_width_bytes; + + dcn60_populate_utm_qos_model(clk_mgr, &bw_params->utm_qos_model, init_table); + + dcn60_override_bw_params(clk_mgr, bw_params); + + return true; +} + +void dcn60_init_clocks(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + uint32_t smu_header_ver = 0; + + memset(&(clk_mgr_base->clks), 0, sizeof(struct dc_clocks)); + clk_mgr_base->clks.p_state_change_support = true; + clk_mgr_base->clks.fclk_p_state_change_support = false; + clk_mgr_base->force_smu_not_present = true; // temporary until SMU ready + clk_mgr->smu_present = !clk_mgr_base->force_smu_not_present /* not force-disabled */ + && dcn60_smu_get_msg_header_version(clk_mgr, &smu_header_ver) + && smu_header_ver != 0; + + clk_mgr->dpm_present = clk_mgr->smu_present + && dcn60_fetch_dal_init_table(clk_mgr) + && clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels + && clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels + && clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels; + + if (clk_mgr->dpm_present) + clk_mgr_base->ctx->dc->res_pool->funcs->update_bw_bounding_box( + clk_mgr_base->ctx->dc, clk_mgr_base->bw_params); +} + +static inline uint32_t count_to_khz(uint32_t count, uint32_t timer_ths, uint32_t refclk_khz) +{ + if (timer_ths == 0) + return 0; + + return (uint32_t)div_u64((uint64_t)count * (uint64_t)refclk_khz, timer_ths); +} + +static void dcn60_dump_clk_registers_internal(struct dcn42_clk_internal *internal, struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + // read ths + REG_GET(CLK8_CLK_TICK_CNT_CONFIG_REG, TIMER_THRESHOLD, &internal->CLK8_CLK_TICK_CNT__TIMER_THRESHOLD); + + // read dtbclk + internal->CLK8_CLK4_CURRENT_CNT = REG_READ(CLK8_CLK4_CURRENT_CNT); + internal->CLK8_CLK4_BYPASS_CNTL = REG_READ(CLK8_CLK4_BYPASS_CNTL); + + // read dcfclk + internal->CLK8_CLK0_CURRENT_CNT = REG_READ(CLK8_CLK0_CURRENT_CNT); + internal->CLK8_CLK0_BYPASS_CNTL = REG_READ(CLK8_CLK0_BYPASS_CNTL); + + // read dcf deep sleep divider + internal->CLK8_CLK0_DS_CNTL = REG_READ(CLK8_CLK0_DS_CNTL); + internal->CLK8_CLK3_DS_CNTL = REG_READ(CLK8_CLK3_DS_CNTL); + + // read dppclk + internal->CLK8_CLK2_CURRENT_CNT = REG_READ(CLK8_CLK2_CURRENT_CNT); + internal->CLK8_CLK2_BYPASS_CNTL = REG_READ(CLK8_CLK2_BYPASS_CNTL); + + // read dprefclk + internal->CLK8_CLK3_CURRENT_CNT = REG_READ(CLK8_CLK3_CURRENT_CNT); + internal->CLK8_CLK3_BYPASS_CNTL = REG_READ(CLK8_CLK3_BYPASS_CNTL); + + // read dispclk + internal->CLK8_CLK1_CURRENT_CNT = REG_READ(CLK8_CLK1_CURRENT_CNT); + internal->CLK8_CLK1_BYPASS_CNTL = REG_READ(CLK8_CLK1_BYPASS_CNTL); +} + +static void dcn60_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, + struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) +{ + (void)log_info; + struct dcn42_clk_internal internal = {0}; + char *bypass_clks[5] = {"0x0 DFS", "0x1 REFCLK", "0x2 ERROR", "0x3 400 FCH", "0x4 600 FCH"}; + const uint32_t refclk_khz = DCN60_CLKIP_REFCLK_KHZ; + uint32_t timer_ths; + + dcn60_dump_clk_registers_internal(&internal, clk_mgr_base); + regs_and_bypass->timer_threshold = internal.CLK8_CLK_TICK_CNT__TIMER_THRESHOLD; + timer_ths = internal.CLK8_CLK_TICK_CNT__TIMER_THRESHOLD; + + regs_and_bypass->dcfclk = count_to_khz(internal.CLK8_CLK0_CURRENT_CNT, timer_ths, refclk_khz); + regs_and_bypass->dcf_deep_sleep_divider = internal.CLK8_CLK0_DS_CNTL / 10; + regs_and_bypass->dcf_deep_sleep_allow = true; + regs_and_bypass->dprefclk = count_to_khz(internal.CLK8_CLK3_CURRENT_CNT, timer_ths, refclk_khz); + regs_and_bypass->dispclk = count_to_khz(internal.CLK8_CLK1_CURRENT_CNT, timer_ths, refclk_khz); + regs_and_bypass->dppclk = count_to_khz(internal.CLK8_CLK2_CURRENT_CNT, timer_ths, refclk_khz); + regs_and_bypass->dtbclk = count_to_khz(internal.CLK8_CLK4_CURRENT_CNT, timer_ths, refclk_khz); + + regs_and_bypass->dispclk_bypass = get_reg_field_value(internal.CLK8_CLK0_BYPASS_CNTL, CLK8_CLK0_BYPASS_CNTL, CLK0_BYPASS_SEL); + regs_and_bypass->dppclk_bypass = get_reg_field_value(internal.CLK8_CLK1_BYPASS_CNTL, CLK8_CLK1_BYPASS_CNTL, CLK1_BYPASS_SEL); + regs_and_bypass->dprefclk_bypass = get_reg_field_value(internal.CLK8_CLK2_BYPASS_CNTL, CLK8_CLK2_BYPASS_CNTL, CLK2_BYPASS_SEL); + regs_and_bypass->dcfclk_bypass = get_reg_field_value(internal.CLK8_CLK3_BYPASS_CNTL, CLK8_CLK3_BYPASS_CNTL, CLK3_BYPASS_SEL); + + if (clk_mgr_base->ctx->dc->debug.pstate_enabled) { + DC_LOG_SMU("clk_type,clk_value,deepsleep_cntl,deepsleep_allow,bypass\n"); + + DC_LOG_SMU("dcfclk,%d,%d,%d,%s\n", + regs_and_bypass->dcfclk, + regs_and_bypass->dcf_deep_sleep_divider, + regs_and_bypass->dcf_deep_sleep_allow, + bypass_clks[(int) regs_and_bypass->dcfclk_bypass]); + + DC_LOG_SMU("dprefclk,%d,N/A,N/A,%s\n", + regs_and_bypass->dprefclk, + bypass_clks[(int) regs_and_bypass->dprefclk_bypass]); + + DC_LOG_SMU("dispclk,%d,N/A,N/A,%s\n", + regs_and_bypass->dispclk, + bypass_clks[(int) regs_and_bypass->dispclk_bypass]); + + //split + DC_LOG_SMU("SPLIT\n"); + + // REGISTER VALUES + DC_LOG_SMU("reg_name,value,clk_type\n"); + DC_LOG_SMU("CLK1_CLK3_CURRENT_CNT,%d,dcfclk\n", + internal.CLK8_CLK3_CURRENT_CNT); + + DC_LOG_SMU("CLK1_CLK3_DS_CNTL,%d,dcf_deep_sleep_divider\n", + internal.CLK8_CLK3_DS_CNTL); + + DC_LOG_SMU("CLK1_CLK3_ALLOW_DS,%d,dcf_deep_sleep_allow\n", + (internal.CLK8_CLK3_DS_CNTL & 0x10)); + + DC_LOG_SMU("CLK1_CLK2_CURRENT_CNT,%d,dprefclk\n", + internal.CLK8_CLK2_CURRENT_CNT); + + DC_LOG_SMU("CLK1_CLK0_CURRENT_CNT,%d,dispclk\n", + internal.CLK8_CLK0_CURRENT_CNT); + + DC_LOG_SMU("CLK1_CLK1_CURRENT_CNT,%d,dppclk\n", + internal.CLK8_CLK1_CURRENT_CNT); + + DC_LOG_SMU("CLK1_CLK3_BYPASS_CNTL,%d,dcfclk_bypass\n", + internal.CLK8_CLK3_BYPASS_CNTL); + + DC_LOG_SMU("CLK1_CLK2_BYPASS_CNTL,%d,dprefclk_bypass\n", + internal.CLK8_CLK2_BYPASS_CNTL); + + DC_LOG_SMU("CLK1_CLK0_BYPASS_CNTL,%d,dispclk_bypass\n", + internal.CLK8_CLK0_BYPASS_CNTL); + + DC_LOG_SMU("CLK1_CLK1_BYPASS_CNTL,%d,dppclk_bypass\n", + internal.CLK8_CLK1_BYPASS_CNTL); + } +} + +static void dcn60_auto_dpm_test_log( + struct dc_clocks *new_clocks, + struct clk_mgr_internal *clk_mgr, + struct dc_state *context) +{ + unsigned int mall_ss_size_bytes; + int dramclk_khz_override, fclk_khz_override, num_fclk_levels; + struct clk_mgr *clk_mgr_base = &clk_mgr->base; + + struct pipe_ctx *pipe_ctx_list[MAX_PIPES]; + int active_pipe_count = 0; + + for (int i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream && dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) { + pipe_ctx_list[active_pipe_count] = pipe_ctx; + active_pipe_count++; + } + } + + msleep(5); + + mall_ss_size_bytes = context->bw_ctx.bw.dcn.mall_ss_size_bytes; + + struct clk_log_info log_info = {0}; + struct clk_state_registers_and_bypass clk_register_dump; + + dcn60_dump_clk_registers(&clk_register_dump, clk_mgr_base, &log_info); + + // Overrides for these clocks in case there is no p_state change support + dramclk_khz_override = new_clocks->dramclk_khz; + fclk_khz_override = new_clocks->fclk_khz; + + num_fclk_levels = clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_fclk_levels - 1; + + if (!new_clocks->p_state_change_support) + dramclk_khz_override = clk_mgr->base.bw_params->max_memclk_mhz * 1000; + + if (!new_clocks->fclk_p_state_change_support) + fclk_khz_override = clk_mgr->base.bw_params->clk_table.entries[num_fclk_levels].fclk_mhz * 1000; + + //////////////////////////////////////////////////////////////////////////// + // IMPORTANT: When adding more clocks to these logs, do NOT put a newline + // anywhere other than at the very end of the string. + // + // Formatting example (make sure to have " - " between each entry): + // + // AutoDPMTest: clk1:%d - clk2:%d - clk3:%d - clk4:%d\n" + //////////////////////////////////////////////////////////////////////////// + if (active_pipe_count > 0 && + new_clocks->dramclk_khz > 0 && + new_clocks->fclk_khz > 0 && + new_clocks->dcfclk_khz > 0 && + new_clocks->dppclk_khz > 0) { + + uint32_t pix_clk_list[MAX_PIPES] = {0}; + int p_state_list[MAX_PIPES] = {0}; + int disp_src_width_list[MAX_PIPES] = {0}; + int disp_src_height_list[MAX_PIPES] = {0}; + uint64_t disp_src_refresh_list[MAX_PIPES] = {0}; + bool is_scaled_list[MAX_PIPES] = {0}; + + for (int i = 0; i < active_pipe_count; i++) { + struct pipe_ctx *curr_pipe_ctx = pipe_ctx_list[i]; + uint64_t refresh_rate; + + pix_clk_list[i] = curr_pipe_ctx->stream->timing.pix_clk_100hz; + p_state_list[i] = curr_pipe_ctx->p_state_type; + + refresh_rate = (curr_pipe_ctx->stream->timing.pix_clk_100hz * (uint64_t)100 + + curr_pipe_ctx->stream->timing.v_total + * (uint64_t) curr_pipe_ctx->stream->timing.h_total - (uint64_t)1); + refresh_rate = div_u64(refresh_rate, curr_pipe_ctx->stream->timing.v_total); + refresh_rate = div_u64(refresh_rate, curr_pipe_ctx->stream->timing.h_total); + disp_src_refresh_list[i] = refresh_rate; + + if (curr_pipe_ctx->plane_state) { + is_scaled_list[i] = !(dcn60_check_native_scaling(curr_pipe_ctx)); + disp_src_width_list[i] = curr_pipe_ctx->plane_state->src_rect.width; + disp_src_height_list[i] = curr_pipe_ctx->plane_state->src_rect.height; + } + } + + DC_LOG_AUTO_DPM_TEST("AutoDPMTest: dramclk:%d - fclk:%d - " + "dcfclk:%d - dppclk:%d - dispclk_hw:%d - " + "dppclk_hw:%d - dprefclk_hw:%d - dcfclk_hw:%d - " + "dtbclk_hw:%d - fclk_hw:%d - pix_clk_0:%d - pix_clk_1:%d - " + "pix_clk_2:%d - pix_clk_3:%d - mall_ss_size:%d - p_state_type_0:%d - " + "p_state_type_1:%d - p_state_type_2:%d - p_state_type_3:%d - " + "pix_width_0:%d - pix_height_0:%d - refresh_rate_0:%lld - is_scaled_0:%d - " + "pix_width_1:%d - pix_height_1:%d - refresh_rate_1:%lld - is_scaled_1:%d - " + "pix_width_2:%d - pix_height_2:%d - refresh_rate_2:%lld - is_scaled_2:%d - " + "pix_width_3:%d - pix_height_3:%d - refresh_rate_3:%lld - is_scaled_3:%d - LOG_END\n", + dramclk_khz_override, + fclk_khz_override, + new_clocks->dcfclk_khz, + new_clocks->dppclk_khz, + clk_register_dump.dispclk, + clk_register_dump.dppclk, + clk_register_dump.dprefclk, + clk_register_dump.dcfclk, + clk_register_dump.dtbclk, + clk_register_dump.fclk, + pix_clk_list[0], pix_clk_list[1], pix_clk_list[3], pix_clk_list[2], + mall_ss_size_bytes, + p_state_list[0], p_state_list[1], p_state_list[2], p_state_list[3], + disp_src_width_list[0], disp_src_height_list[0], disp_src_refresh_list[0], is_scaled_list[0], + disp_src_width_list[1], disp_src_height_list[1], disp_src_refresh_list[1], is_scaled_list[1], + disp_src_width_list[2], disp_src_height_list[2], disp_src_refresh_list[2], is_scaled_list[2], + disp_src_width_list[3], disp_src_height_list[3], disp_src_refresh_list[3], is_scaled_list[3]); + } +} + +static void dcn60_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned int num_steps) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn60_clk_mgr *clk_mgr60 = TO_DCN60_CLK_MGR(clk_mgr_internal); + + unsigned int i; + union dcn60_clk_mgr_block_sequence_params *params; + + /* execute sequence */ + for (i = 0; i < num_steps; i++) { + params = &clk_mgr60->block_sequence[i].params; + + switch (clk_mgr60->block_sequence[i].func) { + case CLK_MGR60_UPDATE_HARDMIN_PPCLK: + if (params->update_hardmin_params.response) + *params->update_hardmin_params.response = dcn60_smu_set_hard_min_by_freq( + clk_mgr_internal, + params->update_hardmin_params.ppclk, + params->update_hardmin_params.freq_mhz); + else + dcn60_smu_set_hard_min_by_freq(clk_mgr_internal, + params->update_hardmin_params.ppclk, + params->update_hardmin_params.freq_mhz); + break; + case CLK_MGR60_UPDATE_HARDMIN_PPCLK_OPTIMIZED: + if (params->update_hardmin_optimized_params.response) + *params->update_hardmin_optimized_params.response = + dcn60_set_hard_min_by_freq_optimized( + clk_mgr_internal, + params->update_hardmin_optimized_params.ppclk, + params->update_hardmin_optimized_params.freq_khz); + else + dcn60_set_hard_min_by_freq_optimized(clk_mgr_internal, + params->update_hardmin_optimized_params.ppclk, + params->update_hardmin_optimized_params.freq_khz); + break; + case CLK_MGR60_UPDATE_DEEP_SLEEP_DCFCLK: + dcn60_smu_set_min_deep_sleep_dcfclk( + clk_mgr_internal, + params->update_deep_sleep_dcfclk_params.freq_mhz); + break; + case CLK_MGR60_INDICATE_PSTATE_STATUS: + dcn60_smu_indicate_pstate_status( + clk_mgr_internal, + params->indicate_pstate_status_params.allow_fclk, + params->indicate_pstate_status_params.allow_uclk, + params->indicate_pstate_status_params.wait_resp, + params->indicate_pstate_status_params.drr_enable, + params->indicate_pstate_status_params.alt_ch_enable); + break; + case CLK_MGR60_UPDATE_DPPCLK_DTO: + dcn60_update_clocks_update_dpp_dto( + clk_mgr_internal, + params->update_dppclk_dto_params.context, + params->update_dppclk_dto_params.safe_to_lower, + *params->update_dppclk_dto_params.ref_dppclk_khz); + break; + case CLK_MGR60_UPDATE_DTBCLK_DTO: + dcn60_update_clocks_update_dtb_dto( + clk_mgr_internal, + params->update_dtbclk_dto_params.context, + *params->update_dtbclk_dto_params.ref_dtbclk_khz); + break; + case CLK_MGR60_UPDATE_PSR_WAIT_LOOP: + params->update_psr_wait_loop_params.dmcu->funcs->set_psr_wait_loop( + params->update_psr_wait_loop_params.dmcu, + params->update_psr_wait_loop_params.wait); + break; + case CLK_MGR60_UPDATE_STUTTER_EFFICIENCY: + dcn60_smu_set_stutter_efficiency( + clk_mgr_internal, + params->update_stutter_efficiency_params.base_efficiency, + params->update_stutter_efficiency_params.low_power_efficiency); + break; + case CLK_MGR60_UPDATE_UTM_QOS_REQUEST: + dcn60_smu_update_utm_qos_request(clk_mgr_internal, + params->update_utm_qos_request_params.utm_latency_ub_index, + params->update_utm_qos_request_params.utm_nominal_bandwidth_lb_KBps, + params->update_utm_qos_request_params.utm_urgent_bandwidth_lb_KBps, + params->update_utm_qos_request_params.utm_lsdma_bandwidth_lb_KBps); + break; + default: + /* this should never happen */ + BREAK_TO_DEBUGGER(); + break; + } + } +} + +/** + * dcn60_make_bandwidth_clocks_update_action - Determine what clock and p-state + * updates are needed by comparing new_clocks against current clk_mgr state. + * @action: output action struct, zeroed and populated by this function + * @clk_mgr_base: current clock manager state (read-only) + * @context: dc state with active plane information + * @new_clocks: requested clock values to compare against current state + * @safe_to_lower: whether clocks are allowed to decrease + */ +static void dcn60_make_bandwidth_clocks_update_action( + struct dcn60_bandwidth_clocks_update_action *action, + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + struct dc_clocks *new_clocks, + bool safe_to_lower) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + int total_plane_count = clk_mgr_helper_get_active_plane_cnt( + clk_mgr_base->ctx->dc, context); + bool dcfclk_dpm_enabled = dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, + PPCLK_DCFCLK); + bool uclk_pstate_supported = new_clocks->p_state_change_support + || (total_plane_count == 0); + + bool fclk_pstate_supported = new_clocks->fclk_p_state_change_support + || (total_plane_count == 0); + + memset(action, 0, sizeof(*action)); + /* clock actions */ + if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, + clk_mgr_base->clks.dcfclk_khz)) { + action->dcfclk.update = true; + action->dcfclk.send_message = dcfclk_dpm_enabled; + } + + if (should_set_clock(safe_to_lower, new_clocks->dcfclk_deep_sleep_khz, + clk_mgr_base->clks.dcfclk_deep_sleep_khz)) { + action->deep_sleep_dcfclk.update = true; + action->deep_sleep_dcfclk.send_message = dcfclk_dpm_enabled; + } + + if (should_set_clock(safe_to_lower, new_clocks->socclk_khz, + clk_mgr_base->clks.socclk_khz)) { + action->socclk.update = true; + /* state tracking only — no SMU hardmin message for socclk */ + action->socclk.send_message = false; + } + + if (clk_mgr_base->clks.stutter_efficiency.base_efficiency + != new_clocks->stutter_efficiency.base_efficiency + || clk_mgr_base->clks.stutter_efficiency.low_power_efficiency + != new_clocks->stutter_efficiency.low_power_efficiency) { + action->stutter.update = true; + action->stutter.send_message = true; + } + + if (new_clocks->utm_nominal_bandwidth_lb_KBps != clk_mgr_base->clks.utm_nominal_bandwidth_lb_KBps + || new_clocks->utm_urgent_bandwidth_lb_KBps != clk_mgr_base->clks.utm_urgent_bandwidth_lb_KBps + || new_clocks->utm_lsdma_bandwidth_lb_KBps != clk_mgr_base->clks.utm_lsdma_bandwidth_lb_KBps + || new_clocks->utm_latency_ub_index != clk_mgr_base->clks.utm_latency_ub_index) { + action->utm_qos.update = true; + action->utm_qos.send_message = true; + } + + /* p-state actions */ + if (should_update_pstate_support(safe_to_lower, + uclk_pstate_supported, + clk_mgr_base->clks.p_state_change_support)) { + action->uclk_pstate.enable = uclk_pstate_supported; + action->uclk_pstate.disable = !uclk_pstate_supported; + action->uclk_pstate.send_message = + dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_UCLK); + } + + if (should_update_pstate_support(safe_to_lower, + fclk_pstate_supported, + clk_mgr_base->clks.fclk_p_state_change_support)) { + action->fclk_pstate.enable = fclk_pstate_supported; + action->fclk_pstate.disable = !fclk_pstate_supported; + action->fclk_pstate.send_message = + dcn60_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_FCLK); + } + + if (new_clocks->fw_based_mclk_switching + != clk_mgr_base->clks.fw_based_mclk_switching) { + action->fams.enable = new_clocks->fw_based_mclk_switching; + action->fams.disable = !new_clocks->fw_based_mclk_switching + && safe_to_lower; + action->fams.send_message = action->fams.enable + || action->fams.disable; + } + + if (new_clocks->alt_ch_pstate_switch != clk_mgr_base->clks.alt_ch_pstate_switch) { + action->alt_ch.enable = new_clocks->alt_ch_pstate_switch; + action->alt_ch.disable = !new_clocks->alt_ch_pstate_switch + && safe_to_lower; + action->alt_ch.send_message = action->alt_ch.enable + || action->alt_ch.disable; + } +} + +/** + * dcn60_build_bandwidth_clocks_block_sequence_with_action - Build the SMU + * message sequence from the action struct without mutating clk_mgr state. + * @block_sequence: output array of block sequence steps + * @action: action struct populated by dcn60_make_bandwidth_clocks_update_action + * @clk_mgr_base: current clock manager state (read-only, used for original values) + * @new_clocks: requested clock values for message parameters + * + * Return: number of steps written to block_sequence + */ +static unsigned int dcn60_build_bandwidth_clocks_block_sequence_with_action( + struct dcn60_clk_mgr_block_sequence *block_sequence, + struct dcn60_bandwidth_clocks_update_action *action, + struct clk_mgr *clk_mgr_base, + struct dc_clocks *new_clocks) +{ + unsigned int num_steps = 0; + + /* CLK_MGR60_INDICATE_PSTATE_STATUS — enable */ + if ((action->uclk_pstate.enable && action->uclk_pstate.send_message) + || (action->fclk_pstate.enable && action->fclk_pstate.send_message) + || (action->fams.enable && action->fams.send_message) + || (action->alt_ch.enable && action->alt_ch.send_message)) { + block_sequence[num_steps].params.indicate_pstate_status_params.allow_uclk = + action->uclk_pstate.enable + || clk_mgr_base->clks.p_state_change_support; + block_sequence[num_steps].params.indicate_pstate_status_params.allow_fclk = + action->fclk_pstate.enable + || clk_mgr_base->clks.fclk_p_state_change_support; + block_sequence[num_steps].params.indicate_pstate_status_params.drr_enable = + action->fams.enable + || clk_mgr_base->clks.fw_based_mclk_switching; + block_sequence[num_steps].params.indicate_pstate_status_params.wait_resp = + action->fams.enable; + block_sequence[num_steps].params.indicate_pstate_status_params.alt_ch_enable = + action->alt_ch.enable + || clk_mgr_base->clks.alt_ch_pstate_switch; + block_sequence[num_steps].func = CLK_MGR60_INDICATE_PSTATE_STATUS; + num_steps++; + } + + /* CLK_MGR60_UPDATE_HARDMIN_PPCLK — DCFCLK */ + if (action->dcfclk.send_message) { + block_sequence[num_steps].params.update_hardmin_params.ppclk = PPCLK_DCFCLK; + block_sequence[num_steps].params.update_hardmin_params.freq_mhz = + (uint16_t)khz_to_mhz_ceil(new_clocks->dcfclk_khz); + block_sequence[num_steps].params.update_hardmin_params.response = NULL; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_HARDMIN_PPCLK; + num_steps++; + } + + /* CLK_MGR60_UPDATE_DEEP_SLEEP_DCFCLK */ + if (action->deep_sleep_dcfclk.send_message) { + block_sequence[num_steps].params.update_deep_sleep_dcfclk_params.freq_mhz = + (uint16_t)khz_to_mhz_ceil(new_clocks->dcfclk_deep_sleep_khz); + block_sequence[num_steps].func = CLK_MGR60_UPDATE_DEEP_SLEEP_DCFCLK; + num_steps++; + } + + /* CLK_MGR60_UPDATE_UTM_QOS_REQUEST */ + if (action->utm_qos.send_message) { + block_sequence[num_steps].params.update_utm_qos_request_params.utm_nominal_bandwidth_lb_KBps = + new_clocks->utm_nominal_bandwidth_lb_KBps; + block_sequence[num_steps].params.update_utm_qos_request_params.utm_urgent_bandwidth_lb_KBps = + new_clocks->utm_urgent_bandwidth_lb_KBps; + block_sequence[num_steps].params.update_utm_qos_request_params.utm_lsdma_bandwidth_lb_KBps = + new_clocks->utm_lsdma_bandwidth_lb_KBps; + block_sequence[num_steps].params.update_utm_qos_request_params.utm_latency_ub_index = + new_clocks->utm_latency_ub_index; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_UTM_QOS_REQUEST; + num_steps++; + } + + /* CLK_MGR60_INDICATE_PSTATE_STATUS — disable */ + if ((action->uclk_pstate.disable && action->uclk_pstate.send_message) + || (action->fclk_pstate.disable && action->fclk_pstate.send_message) + || (action->fams.disable && action->fams.send_message) + || (action->alt_ch.disable && action->alt_ch.send_message)) { + block_sequence[num_steps].params.indicate_pstate_status_params.allow_uclk = + !action->uclk_pstate.disable + && (action->uclk_pstate.enable + || clk_mgr_base->clks.p_state_change_support); + block_sequence[num_steps].params.indicate_pstate_status_params.allow_fclk = + !action->fclk_pstate.disable + && (action->fclk_pstate.enable + || clk_mgr_base->clks.fclk_p_state_change_support); + block_sequence[num_steps].params.indicate_pstate_status_params.drr_enable = + !action->fams.disable + && (action->fams.enable + || clk_mgr_base->clks.fw_based_mclk_switching); + block_sequence[num_steps].params.indicate_pstate_status_params.alt_ch_enable = + !action->alt_ch.disable + && (action->alt_ch.enable + || clk_mgr_base->clks.alt_ch_pstate_switch); + block_sequence[num_steps].params.indicate_pstate_status_params.wait_resp = false; + block_sequence[num_steps].func = CLK_MGR60_INDICATE_PSTATE_STATUS; + num_steps++; + } + + /* CLK_MGR60_UPDATE_STUTTER_EFFICIENCY */ + if (action->stutter.send_message) { + block_sequence[num_steps].params.update_stutter_efficiency_params.base_efficiency = + new_clocks->stutter_efficiency.base_efficiency; + block_sequence[num_steps].params.update_stutter_efficiency_params.low_power_efficiency = + new_clocks->stutter_efficiency.low_power_efficiency; + block_sequence[num_steps].func = CLK_MGR60_UPDATE_STUTTER_EFFICIENCY; + num_steps++; + } + + return num_steps; +} + +/** + * dcn60_update_bandwidth_clocks_state_with_action - Apply all pending clock + * and p-state changes to clk_mgr state based on the action struct. + * @clk_mgr_base: clock manager state to update + * @action: action struct populated by dcn60_make_bandwidth_clocks_update_action + * @new_clocks: new clock values to copy into clk_mgr state + */ +static void dcn60_update_bandwidth_clocks_state_with_action( + struct clk_mgr *clk_mgr_base, + struct dcn60_bandwidth_clocks_update_action *action, + struct dc_clocks *new_clocks) +{ + if (action->uclk_pstate.enable) + clk_mgr_base->clks.p_state_change_support = true; + if (action->uclk_pstate.disable) + clk_mgr_base->clks.p_state_change_support = false; + if (action->fclk_pstate.enable) + clk_mgr_base->clks.fclk_p_state_change_support = true; + if (action->fclk_pstate.disable) + clk_mgr_base->clks.fclk_p_state_change_support = false; + if (action->fams.enable) + clk_mgr_base->clks.fw_based_mclk_switching = true; + if (action->fams.disable) + clk_mgr_base->clks.fw_based_mclk_switching = false; + if (action->alt_ch.enable) + clk_mgr_base->clks.alt_ch_pstate_switch = true; + if (action->alt_ch.disable) + clk_mgr_base->clks.alt_ch_pstate_switch = false; + if (action->dcfclk.update) + clk_mgr_base->clks.dcfclk_khz = new_clocks->dcfclk_khz; + if (action->deep_sleep_dcfclk.update) + clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz; + if (action->socclk.update) + clk_mgr_base->clks.socclk_khz = new_clocks->socclk_khz; + if (action->utm_qos.update) { + clk_mgr_base->clks.utm_nominal_bandwidth_lb_KBps = new_clocks->utm_nominal_bandwidth_lb_KBps; + clk_mgr_base->clks.utm_urgent_bandwidth_lb_KBps = new_clocks->utm_urgent_bandwidth_lb_KBps; + clk_mgr_base->clks.utm_lsdma_bandwidth_lb_KBps = new_clocks->utm_lsdma_bandwidth_lb_KBps; + clk_mgr_base->clks.utm_latency_ub_index = new_clocks->utm_latency_ub_index; + } + if (action->stutter.update) { + clk_mgr_base->clks.stutter_efficiency.base_efficiency = + new_clocks->stutter_efficiency.base_efficiency; + clk_mgr_base->clks.stutter_efficiency.low_power_efficiency = + new_clocks->stutter_efficiency.low_power_efficiency; + } +} + +/** + * dcn60_build_update_bandwidth_clocks_sequence - Build block sequence for + * bandwidth clock updates and apply new clock values to clk_mgr state. + * @clk_mgr_base: clock manager to update + * @context: dc state with active plane information + * @new_clocks: requested clock values from bandwidth calculation + * @safe_to_lower: whether clocks are allowed to decrease + * + * Return: number of block sequence steps to execute + */ +static unsigned int dcn60_build_update_bandwidth_clocks_sequence( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + struct dc_clocks *new_clocks, + bool safe_to_lower) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn60_clk_mgr *clk_mgr60 = TO_DCN60_CLK_MGR(clk_mgr_internal); + struct dcn60_bandwidth_clocks_update_action action = {0}; + unsigned int num_steps; + + dcn60_make_bandwidth_clocks_update_action(&action, + clk_mgr_base, context, new_clocks, safe_to_lower); + + num_steps = dcn60_build_bandwidth_clocks_block_sequence_with_action( + clk_mgr60->block_sequence, &action, + clk_mgr_base, new_clocks); + + dcn60_update_bandwidth_clocks_state_with_action(clk_mgr_base, &action, new_clocks); + + return num_steps; +} + +static void dcn60_update_clocks(struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower) +{ + struct dc *dc = clk_mgr_base->ctx->dc; + unsigned int num_steps = 0; + + /* TODO: mutates context bw clocks. This is a side effect */ + if (dc->debug.force_min_dcfclk_mhz > 0) { + int force_min_dcfclk_khz = + dc->debug.force_min_dcfclk_mhz * 1000; + + if (context->bw_ctx.bw.dcn.clk.dcfclk_khz < force_min_dcfclk_khz) + context->bw_ctx.bw.dcn.clk.dcfclk_khz = force_min_dcfclk_khz; + } + + /* build bandwidth related clocks update sequence */ + num_steps = dcn60_build_update_bandwidth_clocks_sequence(clk_mgr_base, + context, + &context->bw_ctx.bw.dcn.clk, + safe_to_lower); + + /* execute sequence */ + dcn60_execute_block_sequence(clk_mgr_base, num_steps); + + /* build display related clocks update sequence */ + num_steps = dcn60_build_update_display_clocks_sequence(clk_mgr_base, + context, + &context->bw_ctx.bw.dcn.clk, + safe_to_lower, + 0); + + /* execute sequence */ + dcn60_execute_block_sequence(clk_mgr_base, num_steps); + + if (dc->config.enable_auto_dpm_test_logs) + dcn60_auto_dpm_test_log(&context->bw_ctx.bw.dcn.clk, TO_CLK_MGR_INTERNAL(clk_mgr_base), context); + +} + +static void dcn60_clock_read_ss_info(struct clk_mgr_internal *clk_mgr) +{ + struct dc_bios *bp = clk_mgr->base.ctx->dc_bios; + int ss_info_num = bp->funcs->get_ss_entry_number( + bp, AS_SIGNAL_TYPE_GPU_PLL); + + if (ss_info_num) { + struct spread_spectrum_info info = { { 0 } }; + enum bp_result result = bp->funcs->get_spread_spectrum_info( + bp, AS_SIGNAL_TYPE_GPU_PLL, 0, &info); + + /* SSInfo.spreadSpectrumPercentage !=0 would be sign + * that SS is enabled + */ + if (result == BP_RESULT_OK && + info.spread_spectrum_percentage != 0) { + clk_mgr->ss_on_dprefclk = true; + clk_mgr->dprefclk_ss_divider = info.spread_percentage_divider; + + if (info.type.CENTER_MODE == 0) { + /* Currently for DP Reference clock we + * need only SS percentage for + * downspread + */ + clk_mgr->dprefclk_ss_percentage = + info.spread_spectrum_percentage; + } + } + } +} + +/* Set min memclk to minimum, either constrained by the current mode or DPM0 */ +static void dcn60_set_hard_min_memclk(struct clk_mgr *clk_mgr_base, bool current_mode) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + const struct dc *dc = clk_mgr->base.ctx->dc; + struct dc_state *context = dc->current_state; + struct dc_clocks new_clocks; + int num_steps; + + if (!clk_mgr->smu_present || !dcn60_is_ppclk_dpm_enabled(clk_mgr, PPCLK_UCLK)) + return; + + /* build clock update */ + memcpy(&new_clocks, &clk_mgr_base->clks, sizeof(struct dc_clocks)); + + if (current_mode) { + new_clocks.dramclk_khz = context->bw_ctx.bw.dcn.clk.dramclk_khz; + new_clocks.idle_dramclk_khz = context->bw_ctx.bw.dcn.clk.idle_dramclk_khz; + new_clocks.p_state_change_support = context->bw_ctx.bw.dcn.clk.p_state_change_support; + } else { + new_clocks.dramclk_khz = clk_mgr_base->bw_params->clk_table.entries[0].memclk_mhz * 1000; + new_clocks.idle_dramclk_khz = new_clocks.dramclk_khz; + new_clocks.p_state_change_support = true; + } + + num_steps = dcn60_build_update_bandwidth_clocks_sequence(clk_mgr_base, + context, + &new_clocks, + true); + + /* execute sequence */ + dcn60_execute_block_sequence(clk_mgr_base, num_steps); +} + +static void dcn60_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + if (!clk_mgr->smu_present) + return; + + if (!dcn60_fetch_dal_init_table(clk_mgr)) + return; + + clk_mgr_base->ctx->dc->res_pool->funcs->update_bw_bounding_box( + clk_mgr_base->ctx->dc, clk_mgr_base->bw_params); +} + +static void dcn60_enable_pme_wa(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + if (!clk_mgr->smu_present) + return; + + dcn60_smu_set_pme_workaround(clk_mgr); +} + +static unsigned int dcn60_get_max_active_utm_bandwidth_kbps( + struct clk_mgr *clk_mgr_base) +{ + const struct utm_qos_model *qos_model = clk_mgr_base->bw_params->utm_qos_model; + const struct utm_qos_model_dchub_v3 *dchub; + unsigned int highest; + + if (!qos_model + || qos_model->version != utm_qos_model_version_v3 + || !qos_model->dchub_v3) + return 0; + + dchub = qos_model->dchub_v3; + highest = dchub->sop_count - 1; + + return dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE] + [highest].nominal_bandwidth_KBps; +} + +static void dcn60_get_requested_memory_qos( + struct clk_mgr *clk_mgr_base, + struct dc_requested_memory_qos *qos) +{ + const struct dc_clocks *clks = &clk_mgr_base->clks; + + qos->bandwidth_lb_in_mbps = clks->utm_nominal_bandwidth_lb_KBps / 1000; + qos->calculated_avg_bw_in_mbps = clks->required_avg_active_bandwidth_KBps / 1000; + qos->max_latency_ub_in_ns = clks->utm_nominal_max_latency_ub_ns; + qos->avg_latency_ub_in_ns = clks->utm_nominal_avg_latency_ub_ns; + qos->max_bw_budget_in_mbps = dcn60_get_max_active_utm_bandwidth_kbps(clk_mgr_base) / 1000; +} + +static unsigned int dcn60_override_memory_bandwidth_request( + struct clk_mgr *clk_mgr_base, + unsigned int bw_kbps) +{ + const struct dc *dc = clk_mgr_base->ctx->dc; + struct dc_clocks new_clocks; + unsigned int max_bw_kbps; + unsigned int capped_bw_kbps; + int num_steps; + + memcpy(&new_clocks, &clk_mgr_base->clks, sizeof(struct dc_clocks)); + + if (bw_kbps == 0) { + new_clocks.utm_nominal_bandwidth_lb_KBps = 0; + new_clocks.utm_urgent_bandwidth_lb_KBps = 0; + } else { + max_bw_kbps = dcn60_get_max_active_utm_bandwidth_kbps(clk_mgr_base); + capped_bw_kbps = (bw_kbps > max_bw_kbps) ? max_bw_kbps : bw_kbps; + + new_clocks.utm_nominal_bandwidth_lb_KBps = capped_bw_kbps; + if (new_clocks.utm_urgent_bandwidth_lb_KBps < capped_bw_kbps) + new_clocks.utm_urgent_bandwidth_lb_KBps = capped_bw_kbps; + } + + num_steps = dcn60_build_update_bandwidth_clocks_sequence(clk_mgr_base, + dc->current_state, &new_clocks, true); + + dcn60_execute_block_sequence(clk_mgr_base, num_steps); + + return new_clocks.utm_nominal_bandwidth_lb_KBps; +} + +static void dcn60_set_idle_power_optimizations(struct clk_mgr *clk_mgr_base, bool enable) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + if (!clk_mgr->smu_present) + return; + + dcn60_smu_set_display_idle_optimization(clk_mgr, enable); +} + +/* + * Build-for-BLS functions. + * These build both bandwidth and display clock sequences into the clk_mgr's + * internal block sequence array, then add a single CLK_MGR_UPDATE_CLOCKS step + * to the HWSS block sequence whose executor will call + * execute_clk_mgr_block_sequence to dispatch all accumulated steps. + */ +void dcn60_build_clock_update_for_bls( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower, + struct block_sequence_state *seq_state) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn60_clk_mgr *clk_mgr60 = TO_DCN60_CLK_MGR(clk_mgr_internal); + unsigned int num_bw_steps; + unsigned int total_steps; + + /* Build bandwidth clocks sequence starting at index 0 */ + num_bw_steps = dcn60_build_update_bandwidth_clocks_sequence(clk_mgr_base, + context, + &context->bw_ctx.bw.dcn.clk, + safe_to_lower); + + /* Build display clocks sequence appended after bandwidth steps */ + total_steps = dcn60_build_update_display_clocks_sequence(clk_mgr_base, + context, + &context->bw_ctx.bw.dcn.clk, + safe_to_lower, + num_bw_steps); + + /* Store total step count for the executor */ + clk_mgr60->num_block_sequence_steps = total_steps; + + /* Add single HWSS step that will execute all clk_mgr block sequence steps */ + hwss_add_clk_mgr_update_clocks(seq_state, clk_mgr_base); +} + +static void dcn60_execute_clk_mgr_block_sequence_bls(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn60_clk_mgr *clk_mgr60 = TO_DCN60_CLK_MGR(clk_mgr_internal); + + dcn60_execute_block_sequence(clk_mgr_base, clk_mgr60->num_block_sequence_steps); +} + +static struct clk_mgr_funcs dcn60_funcs = { + .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, + .get_dtb_ref_clk_frequency = dcn60_get_dtb_ref_freq_khz, + .update_clocks = dcn60_update_clocks, + .dump_clk_registers = dcn60_dump_clk_registers, + .init_clocks = dcn60_init_clocks, + .set_hard_min_memclk = dcn60_set_hard_min_memclk, + .get_memclk_states_from_smu = dcn60_get_memclk_states_from_smu, + .are_clock_states_equal = dcn60_are_clock_states_equal, + .enable_pme_wa = dcn60_enable_pme_wa, + .is_smu_present = dcn60_is_smu_present, + .get_dispclk_from_dentist = NULL, + .get_max_clock_khz = dcn401_get_max_clock_khz, + .override_memory_bandwidth_request = dcn60_override_memory_bandwidth_request, + .get_requested_memory_qos = dcn60_get_requested_memory_qos, + .set_idle_power_optimizations = dcn60_set_idle_power_optimizations, + .build_clock_update_for_bls = dcn60_build_clock_update_for_bls, + .execute_clk_mgr_block_sequence = dcn60_execute_clk_mgr_block_sequence_bls, +}; + +struct clk_mgr_internal *dcn60_clk_mgr_construct( + struct dc_context *ctx, + struct dccg *dccg) +{ + struct clk_log_info log_info = {0}; + struct dcn60_clk_mgr *clk_mgr60 = kzalloc(sizeof(struct dcn60_clk_mgr), GFP_KERNEL); + struct clk_mgr_internal *clk_mgr; + + if (!clk_mgr60) + return NULL; + + clk_mgr = &clk_mgr60->base; + clk_mgr->base.ctx = ctx; + clk_mgr->base.funcs = &dcn60_funcs; + clk_mgr->regs = &clk_mgr_regs_dcn60; + clk_mgr->clk_mgr_shift = &clk_mgr_shift_dcn60; + clk_mgr->clk_mgr_mask = &clk_mgr_mask_dcn60; + + clk_mgr->dccg = dccg; + clk_mgr->dfs_bypass_disp_clk = 0; + + clk_mgr->dprefclk_ss_percentage = 0; + clk_mgr->dprefclk_ss_divider = 1000; + clk_mgr->ss_on_dprefclk = false; + clk_mgr->dfs_ref_freq_khz = 100000; + + /* Changed from DCN3.2_clock_frequency doc to match + * dcn60_dump_clk_registers from 4 * dentist_vco_freq_khz / + * dprefclk DID divider + */ + clk_mgr->base.dprefclk_khz = 720000; + + /* integer part is now VCO frequency in kHz */ + clk_mgr->base.dentist_vco_freq_khz = dcn60_get_vco_frequency_from_reg(clk_mgr); + + /* in case we don't get a value from the register, use default */ + if (clk_mgr->base.dentist_vco_freq_khz == 0) + clk_mgr->base.dentist_vco_freq_khz = 4500000; + + dcn60_dump_clk_registers(&clk_mgr->base.boot_snapshot, &clk_mgr->base, &log_info); + + if (ctx->dc->debug.disable_dtb_ref_clk_switch && + clk_mgr->base.clks.ref_dtbclk_khz != clk_mgr->base.boot_snapshot.dtbclk) { + clk_mgr->base.clks.ref_dtbclk_khz = clk_mgr->base.boot_snapshot.dtbclk; + } + + if (clk_mgr->base.boot_snapshot.dprefclk != 0) + clk_mgr->base.dprefclk_khz = clk_mgr->base.boot_snapshot.dprefclk; + dcn60_clock_read_ss_info(clk_mgr); + + clk_mgr->dfs_bypass_enabled = false; + + clk_mgr->smu_present = false; + + clk_mgr->base.bw_params = kzalloc(sizeof(*clk_mgr->base.bw_params), GFP_KERNEL); + if (!clk_mgr->base.bw_params) + goto fail; + + clk_mgr->dal_init_table = dm_helpers_allocate_gpu_mem(clk_mgr->base.ctx, + DC_MEM_ALLOC_TYPE_GART, sizeof(DalInitTable_t), + &clk_mgr->dal_init_table_addr); + if (!clk_mgr->dal_init_table) + goto fail; + + return &clk_mgr60->base; + +fail: + BREAK_TO_DEBUGGER(); + dcn60_clk_mgr_destroy(clk_mgr); + kfree(clk_mgr60); + return NULL; +} + +void dcn60_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr) +{ + kfree(clk_mgr->base.bw_params); + + if (clk_mgr->dal_init_table) + dm_helpers_free_gpu_mem(clk_mgr->base.ctx, DC_MEM_ALLOC_TYPE_GART, + (void *)clk_mgr->dal_init_table); +} diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h new file mode 100644 index 000000000000..1c9cd4e95aac --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DCN60_CLK_MGR_H_ +#define __DCN60_CLK_MGR_H_ + +#ifdef CONFIG_DRM_AMD_DC_FP +#include "bounding_boxes/utm_qos_model_types.h" +#include "bounding_boxes/utm_qos_model_dchub_v3.h" +#endif + +union dcn60_clk_mgr_block_sequence_params { + struct { + /* inputs */ + uint32_t ppclk; + uint16_t freq_mhz; + /* outputs */ + int *response; + } update_hardmin_params; + struct { + /* inputs */ + uint32_t ppclk; + int freq_khz; + /* outputs */ + int *response; + } update_hardmin_optimized_params; + struct { + /* inputs */ + uint16_t freq_mhz; + } update_deep_sleep_dcfclk_params; + struct { + /* inputs */ + bool allow_fclk; + bool allow_uclk; + bool wait_resp; + bool drr_enable; + bool alt_ch_enable; + } indicate_pstate_status_params; + struct { + /* inputs */ + struct dc_state *context; + int *ref_dppclk_khz; + bool safe_to_lower; + } update_dppclk_dto_params; + struct { + /* inputs */ + struct dc_state *context; + int *ref_dtbclk_khz; + } update_dtbclk_dto_params; + struct { + /* inputs */ + struct dc_state *context; + } update_dentist_params; + struct { + /* inputs */ + struct dmcu *dmcu; + unsigned int wait; + } update_psr_wait_loop_params; + struct { + /* inputs */ + uint8_t base_efficiency; + uint8_t low_power_efficiency; + } update_stutter_efficiency_params; + struct { + unsigned int utm_urgent_bandwidth_lb_KBps; + unsigned int utm_nominal_bandwidth_lb_KBps; + unsigned int utm_lsdma_bandwidth_lb_KBps; + unsigned int utm_latency_ub_index; + } update_utm_qos_request_params; +}; + +enum dcn60_clk_mgr_block_sequence_func { + CLK_MGR60_READ_CLOCKS_FROM_DENTIST, + CLK_MGR60_UPDATE_HARDMIN_PPCLK, + CLK_MGR60_UPDATE_HARDMIN_PPCLK_OPTIMIZED, + CLK_MGR60_UPDATE_DEEP_SLEEP_DCFCLK, + CLK_MGR60_INDICATE_PSTATE_STATUS, + CLK_MGR60_UPDATE_DPPCLK_DTO, + CLK_MGR60_UPDATE_DTBCLK_DTO, + CLK_MGR60_UPDATE_DENTIST, + CLK_MGR60_UPDATE_PSR_WAIT_LOOP, + CLK_MGR60_UPDATE_STUTTER_EFFICIENCY, + CLK_MGR60_UPDATE_UTM_QOS_REQUEST +}; + +struct dcn60_clk_mgr_block_sequence { + union dcn60_clk_mgr_block_sequence_params params; + enum dcn60_clk_mgr_block_sequence_func func; +}; + +struct dcn60_update_action { + /** clk_mgr state needs updating */ + bool update; + /** SMU message required */ + bool send_message; +}; + +struct dcn60_enablement_action { + /** feature transitioning to enabled */ + bool enable; + /** feature transitioning to disabled */ + bool disable; + /** SMU message required for this transition */ + bool send_message; +}; + +/** + * struct dcn60_bandwidth_clocks_update_action - captures what clock and + * p-state changes are needed for a bandwidth update, separating the action + * logic from block sequence construction and state mutation. + */ +struct dcn60_bandwidth_clocks_update_action { + struct dcn60_update_action dcfclk; + struct dcn60_update_action deep_sleep_dcfclk; + struct dcn60_update_action socclk; + struct dcn60_update_action stutter; + struct dcn60_update_action utm_qos; + struct dcn60_enablement_action uclk_pstate; + struct dcn60_enablement_action fclk_pstate; + struct dcn60_enablement_action fams; + struct dcn60_enablement_action alt_ch; +}; + +struct dcn60_clk_mgr { + struct clk_mgr_internal base; + struct dcn60_clk_mgr_block_sequence block_sequence[DCN401_CLK_MGR_MAX_SEQUENCE_SIZE]; +#ifdef CONFIG_DRM_AMD_DC_FP + struct utm_qos_model utm_qos_model; + struct utm_qos_model_dchub_v3 dchub_v3; +#endif + unsigned int num_block_sequence_steps; +}; + +void dcn60_init_clocks(struct clk_mgr *clk_mgr_base); + +struct block_sequence_state; + +void dcn60_build_clock_update_for_bls( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower, + struct block_sequence_state *seq_state); + +struct clk_mgr_internal *dcn60_clk_mgr_construct(struct dc_context *ctx, + struct dccg *dccg); + +void dcn60_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr); + +#endif /* __DCN60_CLK_MGR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.c new file mode 100644 index 000000000000..697ed8f747d4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dcn60_clk_mgr_smu_msg.h" + +#include "clk_mgr_internal.h" +#include "reg_helper.h" + +#include "dalsmc.h" +#include "dcn401/dcn401_smu14_driver_if.h" + +/* MMIO = MP1_BASE__INST0_SEG1 (0x16200 from at2_offset.h) + * + regMP1_SMN_C2PMSG_N (sequential from 0xA2) + * MSG=C2PMSG_98, RESP=C2PMSG_99, ARG0..3=C2PMSG_100..103 + */ +#ifndef mmDAL_MSG_REG +#define mmDAL_MSG_REG 0x162A2 +#endif +#ifndef mmDAL_RESP_REG +#define mmDAL_RESP_REG 0x162A3 +#endif +#ifndef mmDAL_ARG_REG +#define mmDAL_ARG_REG 0x162A4 +#endif +#ifndef mmDAL_ARG_REG_0 +#define mmDAL_ARG_REG_0 0x162A4 +#endif +#ifndef mmDAL_ARG_REG_1 +#define mmDAL_ARG_REG_1 0x162A5 +#endif +#ifndef mmDAL_ARG_REG_2 +#define mmDAL_ARG_REG_2 0x162A6 +#endif +#ifndef mmDAL_ARG_REG_3 +#define mmDAL_ARG_REG_3 0x162A7 +#endif + +#define REG(reg_name) \ + mm ## reg_name + +#include "logger_types.h" + + +#define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } + +/** + * dcn60_smu_wait_for_response - Poll DAL_RESP_REG until non-zero. + * @clk_mgr: clock manager instance + * @delay_us: microseconds to wait between each poll iteration + * @max_retries: maximum number of poll iterations before giving up + * @total_delay_us: if non-NULL, accumulates the total poll delay + * + * Cannot use REG_WAIT because the condition is "not equal to zero" + * and the translation in msg_if.h is incompatible with REG_WAIT. + * + * Return: RESP_REG value (non-zero on success, 0 on timeout) + */ +static uint32_t dcn60_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, + unsigned int delay_us, unsigned int max_retries, + unsigned int *total_delay_us) +{ + uint32_t reg = 0; + + if (total_delay_us) + *total_delay_us = 0; + + do { + reg = REG_READ(DAL_RESP_REG); + if (reg) + break; + + if (delay_us >= 1000) + msleep(delay_us/1000); + else if (delay_us > 0) + udelay(delay_us); + if (total_delay_us) + *total_delay_us += delay_us; + } while (max_retries--); + + return reg; +} + +/** + * dcn60_smu_send_msg_with_args - Send a DALSMC message with four argument registers. + * @clk_mgr: clock manager instance + * @msg_id: DALSMC_MSG_* message identifier + * @args: four-register argument payload + * @param_out: if non-NULL, receives ARG_REG_0 on success + * @total_delay_us: if non-NULL, accumulates the total poll delay + */ +static bool dcn60_smu_send_msg_with_args(struct clk_mgr_internal *clk_mgr, + uint32_t msg_id, DALSMC_args_t args, uint32_t *param_out, + unsigned int *total_delay_us) +{ + unsigned int delay1_us = 0, delay2_us = 0; + + if (total_delay_us) + *total_delay_us = 0; + + /* Wait for response register to be ready */ + dcn60_smu_wait_for_response(clk_mgr, 10, 200000, + total_delay_us ? &delay1_us : NULL); + + smu_print("SMU msg 0x%x enter: arg0=0x%08x arg1=0x%08x arg2=0x%08x arg3=0x%08x\n", + msg_id, args.Reg0, args.Reg1, args.Reg2, args.Reg3); + TRACE_SMU_MSG_ENTER(msg_id, args.Reg0, clk_mgr->base.ctx); + + /* Clear response register */ + REG_WRITE(DAL_RESP_REG, 0); + + /* Write all four argument registers */ + REG_WRITE(DAL_ARG_REG_0, args.Reg0); + REG_WRITE(DAL_ARG_REG_1, args.Reg1); + REG_WRITE(DAL_ARG_REG_2, args.Reg2); + REG_WRITE(DAL_ARG_REG_3, args.Reg3); + + /* Trigger the message transaction by writing the message ID */ + REG_WRITE(DAL_MSG_REG, msg_id); + + /* Wait for response */ + if (dcn60_smu_wait_for_response(clk_mgr, 10, 200000, + total_delay_us ? &delay2_us : NULL) == DALSMC_Result_OK) { + if (param_out) + *param_out = REG_READ(DAL_ARG_REG_0); + + if (total_delay_us) + *total_delay_us = delay1_us + delay2_us; + + smu_print("SMU msg 0x%x exit: ok resp=0x%08x\n", + msg_id, param_out ? *param_out : 0); + TRACE_SMU_MSG_EXIT(true, param_out ? *param_out : 0, clk_mgr->base.ctx); + return true; + } + + if (total_delay_us) + *total_delay_us = delay1_us + 2000000; + + smu_print("SMU msg 0x%x exit: failed\n", msg_id); + TRACE_SMU_MSG_EXIT(false, 0, clk_mgr->base.ctx); + return false; +} + + +static unsigned int dcn60_smu_get_hard_min_status(struct clk_mgr_internal *clk_mgr, + bool *no_timeout, unsigned int *total_delay_us) +{ + DALSMC_args_t args = {}; + uint32_t response = 0; + + *no_timeout = dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_ReturnHardMinStatus, args, &response, + total_delay_us); + + smu_print("SMU Get hard min status: no_timeout %d delay %d us clk bits %x\n", + *no_timeout, *total_delay_us, response); + + return response; +} + + +static bool dcn60_smu_wait_hard_min_status(struct clk_mgr_internal *clk_mgr, uint32_t ppclk) +{ + const unsigned int max_delay_us = 1000000; + + unsigned int hardmin_status_mask = (1 << ppclk); + unsigned int total_delay_us = 0; + bool hardmin_done = false; + + while (!hardmin_done && total_delay_us < max_delay_us) { + unsigned int hardmin_status; + unsigned int read_total_delay_us; + bool no_timeout; + + if (!hardmin_done && total_delay_us > 0) { + /* hardmin not yet fulfilled, wait 500us and retry*/ + udelay(500); + total_delay_us += 500; + + smu_print("SMU Wait hard min status for %d us\n", total_delay_us); + } + + hardmin_status = dcn60_smu_get_hard_min_status(clk_mgr, &no_timeout, &read_total_delay_us); + total_delay_us += read_total_delay_us; + hardmin_done = hardmin_status & hardmin_status_mask; + } + + return hardmin_done; +} + +/* TODO: update callers to pass kHz directly for finer granularity + * now that the DALSMC interface supports 24-bit kHz encoding. + */ +unsigned int dcn60_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz) +{ + DALSMC_SetHardMinByFreq_arg_t arg = {}; + uint32_t response = 0; + bool hard_min_done = false; + + smu_print("SMU Set hard min by freq: clk = %d, freq_mhz = %d MHz\n", clk, freq_mhz); + + /* New interface encodes frequency in kHz (24-bit) and PPCLK in bits [31:24] */ + arg.FreqKhz = (uint32_t)freq_mhz * 1000; + arg.Ppclk = clk; + + dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_SetHardMinByFreq, arg.Args, &response, NULL); + + /* wait until hardmin acknowledged */ + hard_min_done = dcn60_smu_wait_hard_min_status(clk_mgr, clk); + smu_print("SMU Frequency set = %d KHz hard_min_done %d\n", response, hard_min_done); + + return response; +} + + +void dcn60_smu_set_stutter_efficiency(struct clk_mgr_internal *clk_mgr, uint8_t base_efficiency, uint8_t low_power_efficiency) +{ + DALSMC_SetStutterEfficiency_arg_t arg = {}; + + smu_print("SMU Set stutter efficiencies: base(LP1) = %d percent, low power(LP2) = %d percent\n", + base_efficiency, low_power_efficiency); + + arg.BaseEfficiencyPct = base_efficiency; + arg.LowPowerEfficiencyPct = low_power_efficiency; + + dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_SetStutterEfficiency, arg.Args, NULL, NULL); +} + +void dcn60_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz) +{ + DALSMC_SetMinDeepSleepDcfclk_arg_t arg = {}; + + smu_print("SMU Set min deep sleep dcfclk: freq_mhz = %d MHz\n", freq_mhz); + + arg.MinDcfclkMhz = freq_mhz; + + dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_SetMinDeepSleepDcfclk, arg.Args, NULL, NULL); +} + +void dcn60_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr) +{ + DALSMC_args_t args = {}; + + smu_print("SMU Set PME workaround (BacoAudioD3PME)\n"); + + dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_BacoAudioD3PME, args, NULL, NULL); +} + +void dcn60_smu_indicate_pstate_status(struct clk_mgr_internal *clk_mgr, + bool allow_fclk, bool allow_uclk, + bool wait_resp, bool drr_enable, bool alt_ch_enable) +{ + DALSMC_IndicatePstateStatus_arg_t arg = {}; + + smu_print("SMU Indicate pstate status: allow_fclk=%d allow_uclk=%d wait_resp=%d drr_enable=%d alt_ch_enable=%d\n", + allow_fclk, allow_uclk, wait_resp, drr_enable, alt_ch_enable); + + arg.AllowFclk = allow_fclk ? 1 : 0; + arg.AllowUclk = allow_uclk ? 1 : 0; + arg.WaitResp = wait_resp ? 1 : 0; + arg.DrrEnable = drr_enable ? 1 : 0; + arg.AltCh = alt_ch_enable ? 1 : 0; + + dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_IndicatePstateStatus, arg.Args, NULL, NULL); +} + +static bool dcn60_smu_transfer_table_smu_2_dram(struct clk_mgr_internal *clk_mgr, + uint32_t table_id, long long dram_addr) +{ + DALSMC_TransferTable_arg_t arg = {}; + + smu_print("SMU TransferTableSmu2Dram: table_id=0x%x addr=0x%08x_%08x\n", + table_id, + (uint32_t)(dram_addr >> 32), + (uint32_t)(dram_addr & 0xFFFFFFFF)); + + arg.TableId = table_id; + arg.AddrLow = (uint32_t)(dram_addr & 0xFFFFFFFF); + arg.AddrHigh = (uint32_t)(dram_addr >> 32); + + return dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_TransferTableSmu2Dram, arg.Args, NULL, NULL); +} + +static bool dcn60_smu_transfer_table_dram_2_smu(struct clk_mgr_internal *clk_mgr, + uint32_t table_id, long long dram_addr) +{ + DALSMC_TransferTable_arg_t arg = {}; + + smu_print("SMU TransferTableDram2Smu: table_id=0x%x addr=0x%08x_%08x\n", + table_id, + (uint32_t)(dram_addr >> 32), + (uint32_t)(dram_addr & 0xFFFFFFFF)); + + arg.TableId = table_id; + arg.AddrLow = (uint32_t)(dram_addr & 0xFFFFFFFF); + arg.AddrHigh = (uint32_t)(dram_addr >> 32); + + return dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_TransferTableDram2Smu, arg.Args, NULL, NULL); +} + +bool dcn60_smu_set_soc_utm_table(struct clk_mgr_internal *clk_mgr, + long long dram_addr) +{ + return dcn60_smu_transfer_table_dram_2_smu(clk_mgr, + TABLE_SOC_UTM, dram_addr); +} + +bool dcn60_smu_get_dal_init_table(struct clk_mgr_internal *clk_mgr, + const DalInitTable_t **init_table) +{ + if (!dcn60_smu_transfer_table_smu_2_dram(clk_mgr, + TABLE_DAL_INIT, clk_mgr->dal_init_table_addr)) + return false; + + *init_table = (const DalInitTable_t *)clk_mgr->dal_init_table; + return true; +} + +bool dcn60_smu_update_utm_qos_request(struct clk_mgr_internal *clk_mgr, + uint32_t latency_sop_index, + uint32_t nominal_bandwidth_KBps, + uint32_t urgent_bandwidth_KBps, + uint32_t lsdma_bandwidth_KBps) +{ + DALSMC_UpdateUTMQoSRequest_arg_t arg = {}; + + smu_print("SMU UpdateUTMQoSRequest: sop_idx=%d nominal=%d urgent=%d lsdma=%d KBps\n", + latency_sop_index, nominal_bandwidth_KBps, + urgent_bandwidth_KBps, lsdma_bandwidth_KBps); + + arg.LatencySopIndex = latency_sop_index; + arg.NominalBandwidthKBps = nominal_bandwidth_KBps; + arg.UrgentBandwidthKBps = urgent_bandwidth_KBps; + arg.LsdmaBandwidthKBps = lsdma_bandwidth_KBps; + + return dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_UpdateUTMQoSRequest, arg.Args, NULL, NULL); +} + +bool dcn60_smu_get_msg_header_version(struct clk_mgr_internal *clk_mgr, + uint32_t *version) +{ + DALSMC_args_t args = {}; + + return dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_GetMsgHeaderVersion, args, version, NULL); +} + +void dcn60_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, bool is_idle) +{ + DALSMC_SetDisplayIdleOptimizations_arg_t arg = {}; + + arg.DfRequestDisabled = is_idle ? 1 : 0; + arg.PhyRefClkOff = is_idle ? 1 : 0; + arg.S0i2Rdy = is_idle ? 1 : 0; + + smu_print("SMU SetDisplayIdleOptimizations: DfRequestDisabled=%d PhyRefClkOff=%d S0i2Rdy=%d\n", + arg.DfRequestDisabled, arg.PhyRefClkOff, arg.S0i2Rdy); + + dcn60_smu_send_msg_with_args(clk_mgr, + DALSMC_MSG_SetDisplayIdleOptimizations, arg.Args, NULL, NULL); +} diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.h new file mode 100644 index 000000000000..b1a212246c54 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr_smu_msg.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DCN60_CLK_MGR_SMU_MSG_H_ +#define __DCN60_CLK_MGR_SMU_MSG_H_ + +#include "os_types.h" +#include "core_types.h" +#include "dalsmc.h" + +struct clk_mgr_internal; + +unsigned int dcn60_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz); +void dcn60_smu_set_stutter_efficiency(struct clk_mgr_internal *clk_mgr, + uint8_t base_efficiency, uint8_t low_power_efficiency); +void dcn60_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz); +void dcn60_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr); +void dcn60_smu_indicate_pstate_status(struct clk_mgr_internal *clk_mgr, + bool allow_fclk, bool allow_uclk, + bool wait_resp, bool drr_enable, bool alt_ch_enable); +bool dcn60_smu_update_utm_qos_request(struct clk_mgr_internal *clk_mgr, + uint32_t latency_sop_index, + uint32_t nominal_bandwidth_KBps, + uint32_t urgent_bandwidth_KBps, + uint32_t lsdma_bandwidth_KBps); +bool dcn60_smu_set_soc_utm_table(struct clk_mgr_internal *clk_mgr, + long long dram_addr); +bool dcn60_smu_get_dal_init_table(struct clk_mgr_internal *clk_mgr, + const DalInitTable_t **init_table); +bool dcn60_smu_get_msg_header_version(struct clk_mgr_internal *clk_mgr, + uint32_t *version); +void dcn60_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, bool is_idle); + +#endif /* __DCN60_CLK_MGR_SMU_MSG_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c new file mode 100644 index 000000000000..8e2f88913e4c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "reg_helper.h" +#include "core_types.h" +#include "dcn42/dcn42_dccg.h" +#include "dcn60_dccg.h" + +#define TO_DCN_DCCG(dccg)\ + container_of(dccg, struct dcn_dccg, base) + +#define REG(reg) \ + (dccg_dcn->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + dccg_dcn->dccg_shift->field_name, dccg_dcn->dccg_mask->field_name + +#define CTX \ + dccg_dcn->base.ctx +#define DC_LOGGER \ + dccg->ctx->logger + +static void dccg60_set_pixel_rate_div( + struct dccg *dccg, + uint32_t otg_inst, + enum pixel_rate_div tmds_div, + enum pixel_rate_div unused) +{ + (void)unused; + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + uint32_t cur_tmds_div = PIXEL_RATE_DIV_NA; + uint32_t dp_dto_int; + uint32_t reg_val; + + // only 2 is valid on dcn401 + if (tmds_div != PIXEL_RATE_DIV_BY_2 && tmds_div != PIXEL_RATE_DIV_BY_4) { + return; + } + + dccg401_get_pixel_rate_div(dccg, otg_inst, &cur_tmds_div, &dp_dto_int); + if (tmds_div == cur_tmds_div) + return; + + // encode enum to register value + reg_val = tmds_div == PIXEL_RATE_DIV_BY_4 ? 1 : 0; + + switch (otg_inst) { + case 0: + REG_UPDATE(OTG_PIXEL_RATE_DIV, + OTG0_TMDS_PIXEL_RATE_DIV, reg_val); + break; + case 1: + REG_UPDATE(OTG_PIXEL_RATE_DIV, + OTG1_TMDS_PIXEL_RATE_DIV, reg_val); + break; + case 2: + REG_UPDATE(OTG_PIXEL_RATE_DIV, + OTG2_TMDS_PIXEL_RATE_DIV, reg_val); + break; + case 3: + REG_UPDATE(OTG_PIXEL_RATE_DIV, + OTG3_TMDS_PIXEL_RATE_DIV, reg_val); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +static void dccg60_set_dto_dscclk(struct dccg *dccg, uint32_t inst, + uint32_t num_slices_h) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + switch (inst) { + case 0: + REG_UPDATE_2(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, 1, + DSCCLK0_DTO_MODULO, 1); + REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK0_EN, 1); + + /* + * Source for dscclk should be set when dto tuned clock is used. + * For 1 slice config, set src to dprefclk. + * For 2 or more slice config, set src to dispclk. + */ + if (num_slices_h == 1) + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK0_SRC_SEL, 1); + else + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK0_SRC_SEL, 0); + + break; + case 1: + REG_UPDATE_2(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, 1, + DSCCLK1_DTO_MODULO, 1); + REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK1_EN, 1); + + if (num_slices_h == 1) + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK1_SRC_SEL, 1); + else + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK1_SRC_SEL, 0); + + break; + case 2: + REG_UPDATE_2(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_PHASE, 1, + DSCCLK2_DTO_MODULO, 1); + REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK2_EN, 1); + + if (num_slices_h == 1) + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK2_SRC_SEL, 1); + else + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK2_SRC_SEL, 0); + + break; + case 3: + REG_UPDATE_2(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, 1, + DSCCLK3_DTO_MODULO, 1); + REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK3_EN, 1); + + if (num_slices_h == 1) + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK3_SRC_SEL, 1); + else + REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK3_SRC_SEL, 0); + + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +static const struct dccg_funcs dccg60_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, + .set_dpstreamclk = dccg401_set_dpstreamclk, + .enable_symclk32_se = dccg31_enable_symclk32_se, + .disable_symclk32_se = dccg31_disable_symclk32_se, + .enable_symclk32_le = dccg401_enable_symclk32_le, + .disable_symclk32_le = dccg401_disable_symclk32_le, + .set_physymclk = dccg401_set_physymclk, + .set_dtbclk_dto = NULL, + .set_dto_dscclk = dccg60_set_dto_dscclk, + .set_ref_dscclk = dccg401_set_ref_dscclk, + .set_valid_pixel_rate = NULL, + .set_fifo_errdet_ovr_en = dccg2_set_fifo_errdet_ovr_en, + .set_audio_dtbclk_dto = NULL, + .otg_add_pixel = dccg42_otg_add_pixel, + .otg_drop_pixel = dccg42_otg_drop_pixel, + .set_pixel_rate_div = dccg60_set_pixel_rate_div, + .get_pixel_rate_div = dccg401_get_pixel_rate_div, + .set_dp_dto = dccg401_set_dp_dto, + .enable_symclk_se = dccg401_enable_symclk_se, + .disable_symclk_se = dccg401_disable_symclk_se, + .set_dtbclk_p_src = dccg401_set_dtbclk_p_src, + .dccg_read_reg_state = dccg31_read_reg_state, + .allow_clock_gating = dccg2_allow_clock_gating +}; + +struct dccg *dccg60_create( + struct dc_context *ctx, + const struct dccg_registers *regs, + const struct dccg_shift *dccg_shift, + const struct dccg_mask *dccg_mask) +{ + struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_KERNEL); + struct dccg *base; + + if (dccg_dcn == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + base = &dccg_dcn->base; + base->ctx = ctx; + base->funcs = &dccg60_funcs; + + dccg_dcn->regs = regs; + dccg_dcn->dccg_shift = dccg_shift; + dccg_dcn->dccg_mask = dccg_mask; + + return &dccg_dcn->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.h new file mode 100644 index 000000000000..3f068ccfcdd5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.h @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DCN60_DCCG_H__ +#define __DCN60_DCCG_H__ + +#include "dcn401/dcn401_dccg.h" + +#define DCCG_MASK_SH_LIST_DCN60(mask_sh) \ + DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\ + DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 1, mask_sh),\ + DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 2, mask_sh),\ + DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 3, mask_sh),\ + DCCG_SF(DPPCLK_CTRL, DPPCLK0_EN, mask_sh),\ + DCCG_SF(DPPCLK_CTRL, DPPCLK1_EN, mask_sh),\ + DCCG_SF(DPPCLK_CTRL, DPPCLK2_EN, mask_sh),\ + DCCG_SF(DPPCLK_CTRL, DPPCLK3_EN, mask_sh),\ + DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_PHASE, mask_sh),\ + DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_MODULO, mask_sh),\ + DCCG_SF(HDMICHARCLK0_CLOCK_CNTL, HDMICHARCLK0_EN, mask_sh),\ + DCCG_SF(HDMICHARCLK0_CLOCK_CNTL, HDMICHARCLK0_SRC_SEL, mask_sh),\ + DCCG_SF(PHYASYMCLK_CLOCK_CNTL, PHYASYMCLK_EN, mask_sh),\ + DCCG_SF(PHYASYMCLK_CLOCK_CNTL, PHYASYMCLK_SRC_SEL, mask_sh),\ + DCCG_SF(PHYBSYMCLK_CLOCK_CNTL, PHYBSYMCLK_EN, mask_sh),\ + DCCG_SF(PHYBSYMCLK_CLOCK_CNTL, PHYBSYMCLK_SRC_SEL, mask_sh),\ + DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_EN, mask_sh),\ + DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_SRC_SEL, mask_sh),\ + DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_EN, mask_sh),\ + DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_SRC_SEL, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_SRC_SEL, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_SRC_SEL, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_SRC_SEL, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_SRC_SEL, mask_sh),\ + DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_EN, mask_sh),\ + DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_EN, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_EN, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_EN, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_EN, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_EN, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_EN, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 0, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 1, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 2, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 3, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG0_ADD_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG1_ADD_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG2_ADD_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG3_ADD_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG0_DROP_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG1_DROP_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG2_DROP_PIXEL, mask_sh),\ + DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG3_DROP_PIXEL, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, OTG0_TMDS_PIXEL_RATE_DIV, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO0_INT, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, OTG1_TMDS_PIXEL_RATE_DIV, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO1_INT, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, OTG2_TMDS_PIXEL_RATE_DIV, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO2_INT, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, OTG3_TMDS_PIXEL_RATE_DIV, mask_sh),\ + DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO3_INT, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P0_SRC_SEL, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P0_EN, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P1_SRC_SEL, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P1_EN, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P2_SRC_SEL, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P2_EN, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P3_SRC_SEL, mask_sh),\ + DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P3_EN, mask_sh),\ + DCCG_SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO_SEL, mask_sh),\ + DCCG_SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_SOURCE_SEL, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYASYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYBSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYCSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 0, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 1, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 2, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 3, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 0, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 1, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 2, mask_sh),\ + DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 3, mask_sh),\ + DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK0_EN, mask_sh),\ + DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK1_EN, mask_sh),\ + DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK2_EN, mask_sh),\ + DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh),\ + DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, mask_sh),\ + DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_MODULO, mask_sh),\ + DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, mask_sh),\ + DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_MODULO, mask_sh),\ + DCCG_SF(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_PHASE, mask_sh),\ + DCCG_SF(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_MODULO, mask_sh),\ + DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, mask_sh),\ + DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_MODULO, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, HDMICHARCLK0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, HDMISTREAMCLK0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE1_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE1_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE1_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE1_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, HDMICHARCLK0_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYA_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYB_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYC_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYD_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P1_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_FE_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_FE_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_FE_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK3_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK0_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK1_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK2_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK3_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_CLOCK_ENABLE, mask_sh),\ + DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_CLOCK_ENABLE, mask_sh),\ + DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_CLOCK_ENABLE, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_CLOCK_ENABLE, mask_sh),\ + DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_EN, mask_sh),\ + DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_EN, mask_sh),\ + DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_EN, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_EN, mask_sh),\ + DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_SRC_SEL, mask_sh),\ + DCCG_SF(DSCCLK_SRC_SEL, DSCCLK0_SRC_SEL, mask_sh),\ + DCCG_SF(DSCCLK_SRC_SEL, DSCCLK1_SRC_SEL, mask_sh),\ + DCCG_SF(DSCCLK_SRC_SEL, DSCCLK2_SRC_SEL, mask_sh),\ + DCCG_SF(DSCCLK_SRC_SEL, DSCCLK3_SRC_SEL, mask_sh),\ + +struct dccg *dccg60_create( + struct dc_context *ctx, + const struct dccg_registers *regs, + const struct dccg_shift *dccg_shift, + const struct dccg_mask *dccg_mask); + +#endif //__DCN60_DCCG_H__ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.c new file mode 100644 index 000000000000..86ac881879cf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "reg_helper.h" + +#include "core_types.h" +#include "link_encoder.h" +#include "dcn31/dcn31_dio_link_encoder.h" +#include "dcn32/dcn32_dio_link_encoder.h" +#include "dcn401/dcn401_dio_link_encoder.h" +#include "dcn42/dcn42_dio_link_encoder.h" +#include "dcn60_dio_link_encoder.h" +#include "stream_encoder.h" +#include "dc_bios_types.h" + +#include "gpio_service_interface.h" + +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +#define CTX \ + enc10->base.ctx +#define DC_LOGGER \ + enc10->base.ctx->logger + +#define REG(reg)\ + (enc10->link_regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + enc10->link_shift->field_name, enc10->link_mask->field_name + +#define AUX_REG(reg)\ + (enc10->aux_regs->reg) + +#define AUX_REG_READ(reg_name) \ + dm_read_reg(CTX, AUX_REG(reg_name)) + +#define AUX_REG_WRITE(reg_name, val) \ + dm_write_reg(CTX, AUX_REG(reg_name), val) + +#define AUX_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_ex(CTX, \ + AUX_REG(reg_name), \ + n, __VA_ARGS__) + +#define AUX_REG_UPDATE_1(reg_name, field, val) \ + AUX_REG_UPDATE_N(reg_name, 1, \ + FN(reg_name, field), val) + +#define HPD_REG(reg)\ + (enc10->hpd_regs->reg) + +#define HPD_REG_GET(reg_name, field, val) \ + generic_reg_get(CTX, HPD_REG(reg_name), \ + FN(reg_name, field), val) + +#define HPD_REG_SET_N(reg_name, n, initial_val, ...) \ + generic_reg_set_ex(CTX, \ + HPD_REG(reg_name), \ + initial_val, \ + n, __VA_ARGS__) + +#define HPD_REG_SET_2(reg, init_value, f1, v1, f2, v2) \ + HPD_REG_SET_N(reg, 2, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2) + +#define HPD_REG_SET_1(reg, init_value, f1, v1) \ + HPD_REG_SET_N(reg, 1, init_value, \ + FN(reg, f1), v1) + +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +// DP2/HDMI FRL PRESET_LEVEL EQ options for FFE byte +// Preset level 0-32 bits[0:4] +#define PRESET_LEVEL_BYTE__LEVEL__SHIFT 0x0u +#define PRESET_LEVEL_BYTE__LEVEL__MASK 0x1Fu +// Enable no preshoot bit[5] +#define PRESET_LEVEL_BYTE__NO_PRE__SHIFT 0x5u +#define PRESET_LEVEL_BYTE__NO_PRE__MASK 0x20u +// Enable no demphasis bit[6] +#define PRESET_LEVEL_BYTE__NO_DEMPH__SHIFT 0x6u +#define PRESET_LEVEL_BYTE__NO_DEMPH__MASK 0x40u +// Enable method 2 bit[7] +#define PRESET_LEVEL_BYTE__METHOD_2__SHIFT 0x7u +#define PRESET_LEVEL_BYTE__METHOD_2__MASK 0x80u + +void enc60_hw_init(struct link_encoder *enc) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + + switch (enc10->base.connector.id) { + case CONNECTOR_ID_DISPLAY_PORT: + case CONNECTOR_ID_EDP: + AUX_REG_WRITE(AUX_DPHY_RX_CONTROL0, 0x103d1110); + AUX_REG_WRITE(AUX_DPHY_TX_CONTROL, 0x21c7a); + dcn10_aux_initialize(enc10); + break; + default: + break; + } + REG_UPDATE(TMDS_CTL_BITS, TMDS_CTL0, 1); + + // Polarity update is handled by DMU init. + // AUX_PAD_MODE update done by DMU init +} + +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 dpcs60_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) +{ + const uint8_t max_ffe_level = 0x7; + 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 <= max_ffe_level) + 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 > max_ffe_level) + 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 ? 1u : 0u) << PRESET_LEVEL_BYTE__NO_PRE__SHIFT) | + ((pre_shoot_only ? 1u : 0u) << PRESET_LEVEL_BYTE__NO_DEMPH__SHIFT) | + ((enc10->base.txffe_state & PRESET_LEVEL_BYTE__LEVEL__MASK) + << PRESET_LEVEL_BYTE__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: + cntl.pixel_clock = 666667 / 10; + break; + case HDMI_FRL_LINK_RATE_16GBPS: + cntl.pixel_clock = 888889 / 10; + break; + case HDMI_FRL_LINK_RATE_20GBPS: + default: + cntl.pixel_clock = 1111111 / 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 dcn60_link_enc_funcs = { + .read_state = link_enc2_read_state, + .validate_output_with_stream = + dcn30_link_encoder_validate_output_with_stream, + .hw_init = enc60_hw_init, + .setup = dcn401_link_encoder_setup, + .enable_tmds_output = dcn10_link_encoder_enable_tmds_output, + .enable_dp_output = dcn401_link_encoder_enable_dp_output, + .enable_dp_mst_output = dcn10_link_encoder_enable_dp_mst_output, + .disable_output = dcn10_link_encoder_disable_output, + .dp_set_lane_settings = dcn10_link_encoder_dp_set_lane_settings, + .dp_set_phy_pattern = dcn10_link_encoder_dp_set_phy_pattern, + .update_mst_stream_allocation_table = + dcn10_link_encoder_update_mst_stream_allocation_table, + .psr_program_dp_dphy_fast_training = + dcn10_psr_program_dp_dphy_fast_training, + .psr_program_secondary_packet = dcn10_psr_program_secondary_packet, + .connect_dig_be_to_fe = dcn10_link_encoder_connect_dig_be_to_fe, + .enable_hpd = dcn10_link_encoder_enable_hpd, + .disable_hpd = dcn10_link_encoder_disable_hpd, + .is_dig_enabled = dcn401_is_dig_enabled, + .destroy = dcn10_link_encoder_destroy, + .fec_set_enable = enc2_fec_set_enable, + .fec_set_ready = enc2_fec_set_ready, + .fec_is_active = enc2_fec_is_active, + .get_dig_frontend = dcn10_get_dig_frontend, + .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 = dpcs60_program_eq_setting, + .get_txffe = dpcs401_get_txffe, + .set_txffe = dpcs401_set_txffe, + .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, + .setup_ri_pj_check_in_sw_or_hw_mode = dcn401_setup_ri_pj_check_in_sw_or_hw_mode, + .get_hpd_state = dcn42_get_hpd_state, + .program_hpd_filter = dcn42_program_hpd_filter, +}; + +void dcn60_link_encoder_construct( + struct dcn20_link_encoder *enc20, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + const struct dcn10_link_enc_registers *link_regs, + const struct dcn10_link_enc_aux_registers *aux_regs, + const struct dcn10_link_enc_hpd_registers *hpd_regs, + const struct dcn10_link_enc_shift *link_shift, + const struct dcn10_link_enc_mask *link_mask) +{ + struct bp_connector_speed_cap_info bp_cap_info = {0}; + const struct dc_vbios_funcs *bp_funcs = init_data->ctx->dc_bios->funcs; + enum bp_result result = BP_RESULT_OK; + struct dcn10_link_encoder *enc10 = &enc20->enc10; + + enc10->base.funcs = &dcn60_link_enc_funcs; + enc10->base.ctx = init_data->ctx; + enc10->base.id = init_data->encoder; + + enc10->base.hpd_source = init_data->hpd_source; + enc10->base.hpd_active_high = init_data->hpd_active_high; + enc10->base.connector = init_data->connector; + + enc10->base.preferred_engine = ENGINE_ID_UNKNOWN; + + enc10->base.features = *enc_features; + if (enc10->base.connector.id == CONNECTOR_ID_USBC) + enc10->base.features.flags.bits.DP_IS_USB_C = 1; + + enc10->base.transmitter = init_data->transmitter; + + /* set the flag to indicate whether driver poll the I2C data pin + * while doing the DP sink detect + */ + +/* if (dal_adapter_service_is_feature_supported(as, + FEATURE_DP_SINK_DETECT_POLL_DATA_PIN)) + enc10->base.features.flags.bits. + DP_SINK_DETECT_POLL_DATA_PIN = true;*/ + + enc10->base.output_signals = + SIGNAL_TYPE_DVI_SINGLE_LINK | + SIGNAL_TYPE_DVI_DUAL_LINK | + SIGNAL_TYPE_LVDS | + SIGNAL_TYPE_DISPLAY_PORT | + SIGNAL_TYPE_DISPLAY_PORT_MST | + SIGNAL_TYPE_EDP | + SIGNAL_TYPE_HDMI_TYPE_A; + + enc10->link_regs = link_regs; + enc10->aux_regs = aux_regs; + enc10->hpd_regs = hpd_regs; + enc10->link_shift = link_shift; + enc10->link_mask = link_mask; + + switch (enc10->base.transmitter) { + case TRANSMITTER_UNIPHY_A: + enc10->base.preferred_engine = ENGINE_ID_DIGA; + break; + case TRANSMITTER_UNIPHY_B: + enc10->base.preferred_engine = ENGINE_ID_DIGB; + break; + case TRANSMITTER_UNIPHY_C: + enc10->base.preferred_engine = ENGINE_ID_DIGC; + break; + case TRANSMITTER_UNIPHY_D: + enc10->base.preferred_engine = ENGINE_ID_DIGD; + break; + case TRANSMITTER_UNIPHY_E: + enc10->base.preferred_engine = ENGINE_ID_DIGE; + break; + default: + ASSERT_CRITICAL(false); + enc10->base.preferred_engine = ENGINE_ID_UNKNOWN; + } + + /* default to one to mirror Windows behavior */ + enc10->base.features.flags.bits.HDMI_6GB_EN = 1; + + if (bp_funcs->get_connector_speed_cap_info) + result = bp_funcs->get_connector_speed_cap_info(enc10->base.ctx->dc_bios, + enc10->base.connector, &bp_cap_info); + + /* Override features with DCE-specific values */ + if (result == BP_RESULT_OK) { + enc10->base.features.flags.bits.IS_HBR2_CAPABLE = + bp_cap_info.DP_HBR2_EN; + enc10->base.features.flags.bits.IS_HBR3_CAPABLE = + bp_cap_info.DP_HBR3_EN; + enc10->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; + enc10->base.features.flags.bits.IS_DP2_CAPABLE = 1; + 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 || + bp_cap_info.FRL_16G_EN || bp_cap_info.FRL_20G_EN || bp_cap_info.FRL_24G_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.features.flags.bits.IS_FRL_16G_CAPABLE = bp_cap_info.FRL_16G_EN; + enc10->base.features.flags.bits.IS_FRL_20G_CAPABLE = bp_cap_info.FRL_20G_EN; + enc10->base.features.flags.bits.IS_FRL_24G_CAPABLE = bp_cap_info.FRL_24G_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__, + result); + } + 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; + enc10->base.features.flags.bits.IS_FRL_16G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_20G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_24G_CAPABLE = 0; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.h new file mode 100644 index 000000000000..74a78845731b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_link_encoder.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_LINK_ENCODER__DCN60_H__ +#define __DC_LINK_ENCODER__DCN60_H__ + +#include "dcn30/dcn30_dio_link_encoder.h" + +#define LINK_ENCODER_MASK_SH_LIST_DCN60(mask_sh) \ + LE_SF(DIG0_HDCP_I2C_CONTROL_0, HDCP_I2C_DISABLE, mask_sh),\ + LE_SF(DIG0_HDCP_I2C_CONTROL_0, HDCP_I2C_DDC_SELECT, mask_sh),\ + LE_SF(DIG0_HDCP_INT_CONTROL, HDCP_I2C_XFER_REQ_MASK, mask_sh),\ + LE_SF(HPD0_DC_HPD_INT_STATUS, DC_HPD_SENSE, mask_sh),\ + LE_SF(HPD0_DC_HPD_TOGGLE_FILT_CNTL, DC_HPD_CONNECT_INT_DELAY, mask_sh),\ + LE_SF(HPD0_DC_HPD_TOGGLE_FILT_CNTL, DC_HPD_DISCONNECT_INT_DELAY, mask_sh),\ + LE_SF(DC_GPIO_DDC1_MASK, AUX_PAD1_MODE, mask_sh),\ + SF(HPD_CTRL, HPD1_Y_POL_INVERT, mask_sh),\ + SF(HPD_CTRL, HPD2_Y_POL_INVERT, mask_sh),\ + SF(HPD_CTRL, HPD3_Y_POL_INVERT, mask_sh),\ + SF(HPD_CTRL, HPD4_Y_POL_INVERT, mask_sh) + +void dcn60_link_encoder_construct( + struct dcn20_link_encoder *enc20, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + const struct dcn10_link_enc_registers *link_regs, + const struct dcn10_link_enc_aux_registers *aux_regs, + const struct dcn10_link_enc_hpd_registers *hpd_regs, + const struct dcn10_link_enc_shift *link_shift, + const struct dcn10_link_enc_mask *link_mask); + +void dpcs60_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 enc60_hw_init(struct link_encoder *enc); + +#endif /* __DC_LINK_ENCODER__DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.c new file mode 100644 index 000000000000..b76cc891857d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dc_bios_types.h" +#include "dcn30/dcn30_dio_stream_encoder.h" +#include "dcn32/dcn32_dio_stream_encoder.h" +#include "dcn35/dcn35_dio_stream_encoder.h" +#include "dcn401/dcn401_dio_stream_encoder.h" + +#include "dcn60_dio_stream_encoder.h" +#include "reg_helper.h" +#include "hw_shared.h" +#include "link_service.h" +#include "dpcd_defs.h" + +#define DC_LOGGER \ + enc1->base.ctx->logger + +#define REG(reg)\ + (enc1->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + (uint8_t)enc1->se_shift->field_name, enc1->se_mask->field_name + +#define VBI_LINE_0 0 +#define HDMI_CLOCK_CHANNEL_RATE_MORE_340M 340000 + +#define CTX \ + enc1->base.ctx + +static void enc60_dp_set_odm_combine( + struct stream_encoder *enc, + bool odm_combine) +{ + (void)enc; + (void)odm_combine; +} + +/* setup stream encoder in hdmi mode */ +static void enc60_stream_encoder_hdmi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + int actual_pix_clk_khz, + bool enable_audio) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + if (!enc->ctx->dc->debug.avoid_vbios_exec_table) { + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc1->base.id; + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.enable_dp_audio = enable_audio; + cntl.pixel_clock = actual_pix_clk_khz; + cntl.lanes_number = LANE_COUNT_FOUR; + + if (enc1->base.bp->funcs->encoder_control( + enc1->base.bp, &cntl) != BP_RESULT_OK) + return; + + } else { + + //Set pattern for clock channel, default vlue 0x63 does not work + REG_UPDATE(DIG_CLOCK_PATTERN, DIG_CLOCK_PATTERN, 0x1F); + + //DIG_BE_TMDS_HDMI_MODE : TMDS-HDMI mode is already set in link_encoder_setup + + //DIG_SOURCE_SELECT is already set in dig_connect_to_otg + + /* DIG_START is removed from the register spec */ + } + + /* Configure pixel encoding */ + enc401_stream_encoder_set_stream_attribute_helper(enc1, crtc_timing); + + /* setup HDMI engine */ + REG_UPDATE_4(HDMI_CONTROL, + HDMI_DEEP_COLOR_ENABLE, 0, + HDMI_DATA_SCRAMBLE_EN, 0, + HDMI_NO_EXTRA_NULL_PACKET_FILLED, 1, + HDMI_CLOCK_CHANNEL_RATE, 0); + + /* Configure color depth */ + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + REG_UPDATE(HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, 0); + break; + case COLOR_DEPTH_101010: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_CONTROL, + 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_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + case COLOR_DEPTH_161616: + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 3, + HDMI_DEEP_COLOR_ENABLE, 1); + break; + default: + break; + } + + if (actual_pix_clk_khz >= HDMI_CLOCK_CHANNEL_RATE_MORE_340M) { + /* enable HDMI data scrambler + * HDMI_CLOCK_CHANNEL_RATE_MORE_340M + * Clock channel frequency is 1/4 of character rate. + */ + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DATA_SCRAMBLE_EN, 1, + HDMI_CLOCK_CHANNEL_RATE, 1); + } else if (crtc_timing->flags.LTE_340MCSC_SCRAMBLE) { + + /* TODO: New feature for DCE11, still need to implement */ + + /* enable HDMI data scrambler + * HDMI_CLOCK_CHANNEL_FREQ_EQUAL_TO_CHAR_RATE + * Clock channel frequency is the same + * as character rate + */ + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DATA_SCRAMBLE_EN, 1, + HDMI_CLOCK_CHANNEL_RATE, 0); + } + + /* Enable transmission of General Control packet on every frame */ + REG_UPDATE_3(HDMI_VBI_PACKET_CONTROL, + HDMI_GC_CONT, 1, + HDMI_GC_SEND, 1, + HDMI_NULL_SEND, 1); + + /* Disable Audio Content Protection packet transmission */ + REG_UPDATE(HDMI_VBI_PACKET_CONTROL, HDMI_ACP_SEND, 0); + /* following belongs to audio */ + /* Enable Audio InfoFrame packet transmission. */ + REG_UPDATE(HDMI_INFOFRAME_CONTROL0, 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_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_LINE, + VBI_LINE_0 + 2); + + /* set HDMI GC AVMUTE */ + REG_UPDATE(HDMI_GC, HDMI_GC_AVMUTE, 0); +} + +static void enc60_stream_encoder_update_hdmi_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + /* for bring up, disable dp double TODO */ + REG_UPDATE(HDMI_DB_CONTROL, HDMI_DB_DISABLE, 1); + REG_UPDATE(DIG_FE_AUDIO_CNTL, APG_CLOCK_ENABLE, 1); + + /*Always add mandatory packets first followed by optional ones*/ + enc3_update_hdmi_info_packet(enc1, 0, &info_frame->avi); + enc3_update_hdmi_info_packet(enc1, 5, &info_frame->hfvsif); + enc3_update_hdmi_info_packet(enc1, 2, &info_frame->gamut); + enc3_update_hdmi_info_packet(enc1, 1, &info_frame->vendor); + enc3_update_hdmi_info_packet(enc1, 3, &info_frame->spd); + enc3_update_hdmi_info_packet(enc1, 4, &info_frame->hdrsmd); + enc3_update_hdmi_info_packet(enc1, 6, &info_frame->vtem); +} + +static void enc60_se_enable_audio_clock( + struct stream_encoder *enc, + bool enable) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + REG_UPDATE(DIG_FE_AUDIO_CNTL, APG_CLOCK_ENABLE, enable); +} + +static void enc60_se_setup_hdmi_audio( + struct stream_encoder *enc, + const struct audio_crtc_info *crtc_info) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + struct audio_clock_info audio_clock_info = {0}; + + /* HDMI_AUDIO_PACKET_CONTROL */ + REG_UPDATE(HDMI_AUDIO_PACKET_CONTROL, + HDMI_AUDIO_DELAY_EN, 1); + + /* HDMI_ACR_PACKET_CONTROL */ + REG_UPDATE_2(HDMI_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND, 1, + HDMI_ACR_SOURCE, 0); + + /* Program audio clock sample/regeneration parameters */ + get_audio_clock_info(crtc_info->color_depth, + crtc_info->requested_pixel_clock_100Hz, + crtc_info->calculated_pixel_clock_100Hz, + &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); + + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + REG_UPDATE(HDMI_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz); + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + REG_UPDATE(HDMI_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz); + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + REG_UPDATE(HDMI_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz); + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + REG_UPDATE(HDMI_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz); + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + REG_UPDATE(HDMI_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz); + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + REG_UPDATE(HDMI_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz); + + /* 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 interruppt callback + */ +} + +static void enc60_se_hdmi_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + enc60_se_enable_audio_clock(enc, true); + enc60_se_setup_hdmi_audio(enc, audio_crtc_info); + + REG_UPDATE(DIG_FE_AUDIO_CNTL, + DIG_FE_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, az_inst); + ASSERT (enc->apg); + enc->apg->funcs->se_audio_setup(enc->apg, az_inst, info); +} + +static void enc60_se_hdmi_audio_disable( + struct stream_encoder *enc) +{ + ASSERT (enc->apg); + if (enc->apg && enc->apg->funcs->disable_apg) + enc->apg->funcs->disable_apg(enc->apg); + + enc60_se_enable_audio_clock(enc, false); +} + +static void enc60_stream_encoder_stop_dp_info_packets( + struct stream_encoder *enc) +{ + /* stop generic packets on DP */ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + uint32_t value = 0; + + REG_SET_9(DP_SEC_CNTL, 0, + DP_SEC_GSP0_ENABLE, 0, + DP_SEC_GSP1_ENABLE, 0, + DP_SEC_GSP2_ENABLE, 0, + DP_SEC_GSP3_ENABLE, 0, + DP_SEC_GSP4_ENABLE, 0, + DP_SEC_GSP5_ENABLE, 0, + DP_SEC_GSP6_ENABLE, 0, + DP_SEC_GSP7_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + + /* this register shared with audio info frame. + * therefore we need to keep master enabled + * if at least one of the fields is not 0 */ + value = REG_READ(DP_SEC_CNTL); + if (value) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); + +} + +#define DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT 0x8000 +#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC 1 + +static void enc60_se_setup_dp_audio( + struct stream_encoder *enc) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + /* --- DP Audio packet configurations --- */ + + /* ATP Configuration */ + REG_SET(DP_SEC_AUD_N, 0, + DP_SEC_AUD_N, DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT); + + /* Async/auto-calc timestamp mode */ + REG_SET(DP_SEC_TIMESTAMP, 0, DP_SEC_TIMESTAMP_MODE, + DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC); +} + +static void enc60_se_dp_audio_enable( + struct stream_encoder *enc) +{ + enc60_se_enable_audio_clock(enc, true); + enc60_se_setup_dp_audio(enc); + enc1_se_enable_dp_audio(enc); + + /* Enable APG block */ + enc->apg->funcs->enable_apg(enc->apg); +} + +static void enc60_se_disable_dp_audio( + struct stream_encoder *enc) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + uint32_t value = 0; + + /* Disable Audio packets */ + REG_UPDATE_5(DP_SEC_CNTL, + DP_SEC_ASP_ENABLE, 0, + DP_SEC_ATP_ENABLE, 0, + DP_SEC_AIP_ENABLE, 0, + DP_SEC_ACM_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + + /* This register shared with encoder info frame. Therefore we need to + * keep master enabled if at least on of the fields is not 0 + */ + value = REG_READ(DP_SEC_CNTL); + if (value != 0) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); +} + +static void enc60_se_dp_audio_disable( + struct stream_encoder *enc) +{ + enc60_se_disable_dp_audio(enc); + + /* Disable APG block */ + enc->apg->funcs->disable_apg(enc->apg); + + enc60_se_enable_audio_clock(enc, false); +} + +static void enc60_audio_mute_control( + struct 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 void enc60_se_dp_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + enc60_se_enable_audio_clock(enc, true); + + /*ACR setup*/ + REG_UPDATE(DIG_FE_AUDIO_CNTL, + DIG_FE_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, az_inst); + + ASSERT(enc->apg); + enc->apg->funcs->se_audio_setup(enc->apg, az_inst, info); +} + +static void enc60_dp_set_dsc_pps_info_packet(struct stream_encoder *enc, + bool enable, + uint8_t *dsc_packed_pps, + bool immediate_update) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + if (enable) { + struct dc_info_packet pps_sdp; + int i; + + /* Configure for PPS packet size (128 bytes) */ + REG_UPDATE(DP_SEC_CNTL2, DP_SEC_GSP11_PPS, 1); + + /* Load PPS into infoframe (SDP) registers */ + pps_sdp.valid = true; + pps_sdp.hb0 = 0; + pps_sdp.hb1 = DC_DP_INFOFRAME_TYPE_PPS; + pps_sdp.hb2 = 127; + pps_sdp.hb3 = 0; + + for (i = 0; i < 4; i++) { + memcpy(pps_sdp.sb, &dsc_packed_pps[i * 32], 32); + enc1->base.vpg->funcs->update_generic_info_packet( + enc1->base.vpg, + 11 + i, + &pps_sdp, + immediate_update); + } + + /* SW should make sure VBID[6] update line number is bigger + * than PPS transmit line number + */ + REG_UPDATE(DP_GSP11_CNTL, + DP_SEC_GSP11_LINE_NUM, 2); + REG_UPDATE_2(DP_MSA_VBID_MISC, + DP_VBID6_LINE_REFERENCE, 0, + DP_VBID6_LINE_NUM, 3); + + /* Send PPS data at the line number specified above. + * DP spec requires PPS to be sent only when it changes, however since + * decoder has to be able to handle its change on every frame, we're + * sending it always (i.e. on every frame) to reduce the chance it'd be + * missed by decoder. If it turns out required to send PPS only when it + * changes, we can use DP_SEC_GSP11_SEND register. + */ + REG_UPDATE(DP_GSP11_CNTL, + DP_SEC_GSP11_ENABLE, 1); + REG_UPDATE(DP_SEC_CNTL, + DP_SEC_STREAM_ENABLE, 1); + } else { + /* Disable Generic Stream Packet 11 (GSP) transmission */ + REG_UPDATE(DP_GSP11_CNTL, DP_SEC_GSP11_ENABLE, 0); + REG_UPDATE(DP_SEC_CNTL2, DP_SEC_GSP11_PPS, 0); + } +} + +static void enc60_reset_hdmi_stream_attribute( + struct stream_encoder *enc) +{ + struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); + + REG_UPDATE_3(HDMI_CONTROL, + HDMI_DEEP_COLOR_ENABLE, 0, + HDMI_DATA_SCRAMBLE_EN, 0, + HDMI_CLOCK_CHANNEL_RATE, 0); +} + +static const struct stream_encoder_funcs dcn60_str_enc_funcs = { + .dp_set_odm_combine = + enc60_dp_set_odm_combine, + .dp_set_stream_attribute = + enc401_stream_encoder_dp_set_stream_attribute, + .hdmi_set_stream_attribute = + enc60_stream_encoder_hdmi_set_stream_attribute, + .dvi_set_stream_attribute = + enc401_stream_encoder_dvi_set_stream_attribute, + .set_throttled_vcp_size = + enc1_stream_encoder_set_throttled_vcp_size, + .update_hdmi_info_packets = + enc60_stream_encoder_update_hdmi_info_packets, + .stop_hdmi_info_packets = + enc3_stream_encoder_stop_hdmi_info_packets, + .update_dp_info_packets_sdp_line_num = + enc3_stream_encoder_update_dp_info_packets_sdp_line_num, + .update_dp_info_packets = + enc3_stream_encoder_update_dp_info_packets, + .stop_dp_info_packets = + enc60_stream_encoder_stop_dp_info_packets, + .dp_blank = + enc1_stream_encoder_dp_blank, + .dp_unblank = + enc401_stream_encoder_dp_unblank, + .audio_mute_control = enc60_audio_mute_control, + + .dp_audio_setup = enc60_se_dp_audio_setup, + .dp_audio_enable = enc60_se_dp_audio_enable, + .dp_audio_disable = enc60_se_dp_audio_disable, + + .hdmi_audio_setup = enc60_se_hdmi_audio_setup, + .hdmi_audio_disable = enc60_se_hdmi_audio_disable, + .setup_stereo_sync = enc1_setup_stereo_sync, + .set_avmute = enc1_stream_encoder_set_avmute, + .dig_connect_to_otg = enc1_dig_connect_to_otg, + .dig_source_otg = enc1_dig_source_otg, + + .dp_get_pixel_format = enc1_stream_encoder_dp_get_pixel_format, + + .enc_read_state = enc401_read_state, + .dp_set_dsc_config = NULL, + .dp_set_dsc_pps_info_packet = enc60_dp_set_dsc_pps_info_packet, + .set_dynamic_metadata = enc401_set_dynamic_metadata, + .hdmi_reset_stream_attribute = enc60_reset_hdmi_stream_attribute, + .enable_stream = enc401_stream_encoder_enable, + + .set_input_mode = enc401_set_dig_input_mode, + .enable_fifo = enc35_enable_fifo, + .disable_fifo = enc35_disable_fifo, + .map_stream_to_link = enc401_stream_encoder_map_to_link, +}; + +void dcn60_dio_stream_encoder_construct( + struct dcn10_stream_encoder *enc1, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct apg *apg, + const struct dcn10_stream_enc_registers *regs, + const struct dcn10_stream_encoder_shift *se_shift, + const struct dcn10_stream_encoder_mask *se_mask) +{ + enc1->base.funcs = &dcn60_str_enc_funcs; + enc1->base.ctx = ctx; + enc1->base.id = eng_id; + enc1->base.bp = bp; + enc1->base.vpg = vpg; + enc1->base.apg = apg; + enc1->regs = regs; + enc1->se_shift = se_shift; + enc1->se_mask = se_mask; + enc1->base.stream_enc_inst = vpg->inst; +} diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.h new file mode 100644 index 000000000000..64a8c3ebdb99 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn60/dcn60_dio_stream_encoder.h @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_DIO_STREAM_ENCODER_DCN60_H__ +#define __DC_DIO_STREAM_ENCODER_DCN60_H__ + +#include "dcn30/dcn30_vpg.h" +#include "dcn30/dcn30_afmt.h" +#include "dcn31/dcn31_apg.h" +#include "stream_encoder.h" +#include "dcn20/dcn20_stream_encoder.h" + + +#define SE_COMMON_MASK_SH_LIST_DCN60(mask_sh)\ + SE_SF(DP0_DP_PIXEL_FORMAT, PIXEL_ENCODING_TYPE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, UNCOMPRESSED_PIXEL_FORMAT, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, UNCOMPRESSED_COMPONENT_DEPTH, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, COMPRESSED_PIXEL_FORMAT, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_NO_EXTRA_NULL_PACKET_FILLED, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_ACP_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GC, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_X, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_Y, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP1_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL1, DP_SEC_GSP5_LINE_REFERENCE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP4_SEND, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP4_SEND_PENDING, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL4, DP_SEC_GSP4_LINE_NUM, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL5, DP_SEC_GSP5_LINE_NUM, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP4_SEND_ANY_LINE, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, mask_sh),\ + SE_SF(DP0_DP_STEER_FIFO, DP_STEER_FIFO_RESET, mask_sh),\ + SE_SF(DP0_DP_STEER_FIFO, DP_STEER_FIFO_ENABLE, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_M_N_GEN_EN, mask_sh),\ + SE_SF(DP0_DP_VID_N, DP_VID_N, mask_sh),\ + SE_SF(DP0_DP_VID_M, DP_VID_M, mask_sh),\ + SE_SF(DIG0_HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_DELAY_EN, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(DP0_DP_SEC_AUD_N, DP_SEC_AUD_N, mask_sh),\ + SE_SF(DP0_DP_SEC_TIMESTAMP, DP_SEC_TIMESTAMP_MODE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ASP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ATP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_AIP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ACM_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(DIG1_HDMI_CONTROL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG1_HDMI_CONTROL, TMDS_COLOR_FORMAT, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP4_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP5_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP6_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP7_SEND, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL6, DP_SEC_GSP7_LINE_NUM, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP11_PPS, mask_sh),\ + SE_SF(DP0_DP_GSP11_CNTL, DP_SEC_GSP11_ENABLE, mask_sh),\ + SE_SF(DP0_DP_GSP11_CNTL, DP_SEC_GSP11_LINE_NUM, mask_sh),\ + SE_SF(DP0_DP_DB_CNTL, DP_DB_DISABLE, mask_sh),\ + SE_SF(DP0_DP_MSA_COLORIMETRY, DP_MSA_MISC0, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_HTOTAL, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_VTOTAL, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_HSTART, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_VSTART, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCPOLARITY, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCPOLARITY, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_HWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_VHEIGHT, mask_sh),\ + SE_SF(DIG0_HDMI_DB_CONTROL, HDMI_DB_DISABLE, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_N_INTERVAL, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_SOURCE_SELECT, mask_sh), \ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC8_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC8_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC9_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC9_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC10_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC10_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC11_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC11_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC12_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC12_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC13_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC13_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC14_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC14_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL1, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL1, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL2, HDMI_GENERIC2_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL2, HDMI_GENERIC3_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL3, HDMI_GENERIC4_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL3, HDMI_GENERIC5_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL4, HDMI_GENERIC6_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL4, HDMI_GENERIC7_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL7, HDMI_GENERIC8_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL7, HDMI_GENERIC9_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL8, HDMI_GENERIC10_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL8, HDMI_GENERIC11_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL9, HDMI_GENERIC12_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL9, HDMI_GENERIC13_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL10, HDMI_GENERIC14_LINE, mask_sh),\ + SE_SF(DP0_DP_MSA_VBID_MISC, DP_VBID6_LINE_REFERENCE, mask_sh),\ + SE_SF(DP0_DP_MSA_VBID_MISC, DP_VBID6_LINE_NUM, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_ENGINE_EN, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_HUBP_REQUESTOR_ID, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_STREAM_TYPE, mask_sh),\ + SE_SF(DP0_DP_SEC_METADATA_TRANSMISSION, DP_SEC_METADATA_PACKET_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_METADATA_TRANSMISSION, DP_SEC_METADATA_PACKET_LINE_REFERENCE, mask_sh),\ + SE_SF(DP0_DP_SEC_METADATA_TRANSMISSION, DP_SEC_METADATA_PACKET_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE_REFERENCE, mask_sh),\ + SE_SF(DIG0_HDMI_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, DOLBY_VISION_EN, mask_sh),\ + SE_SF(DIG0_DIG_FE_EN_CNTL, DIG_FE_ENABLE, mask_sh),\ + SE_SF(DIG0_DIG_FE_CLK_CNTL, DIG_FE_MODE, mask_sh),\ + SE_SF(DIG0_DIG_FE_CLK_CNTL, DIG_FE_CLK_EN, mask_sh),\ + SE_SF(DIG0_DIG_FE_CLK_CNTL, DIG_FE_SOFT_RESET, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh),\ + SE_SF(DP0_DP_SEC_FRAMING4, DP_SST_SDP_SPLITTING, mask_sh),\ + SE_SF(DIG0_DIG_CLOCK_PATTERN, DIG_CLOCK_PATTERN, mask_sh),\ + SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_OUTPUT_PIXEL_PER_CYCLE, mask_sh),\ + SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_READ_START_LEVEL, mask_sh),\ + SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_ENABLE, mask_sh),\ + SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_RESET, mask_sh),\ + SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_RESET_DONE, mask_sh),\ + SE_SF(DIG0_STREAM_MAPPER_CONTROL, DIG_STREAM_LINK_TARGET, mask_sh),\ + SE_SF(DIG0_DIG_FE_AUDIO_CNTL, DIG_FE_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, mask_sh),\ + SE_SF(DIG0_DIG_FE_AUDIO_CNTL, APG_CLOCK_ENABLE, mask_sh), + +void dcn60_dio_stream_encoder_construct( + struct dcn10_stream_encoder *enc1, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct apg *apg, + const struct dcn10_stream_enc_registers *regs, + const struct dcn10_stream_encoder_shift *se_shift, + const struct dcn10_stream_encoder_mask *se_mask); + +#endif /* __DC_DIO_STREAM_ENCODER_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn5_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn5_soc_bb.h new file mode 100644 index 000000000000..a1fab09b0485 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn5_soc_bb.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DCN5_SOC_BB_H__ +#define __DCN5_SOC_BB_H__ + +#include "dml2_external_lib_deps.h" +#include "utm_qos_model_dchub_v1.h" +#include "dml_top_soc_parameter_types.h" + +static inline void dcn5_initialize_soc_bb(struct dml2_soc_bb *soc_bb) +{ + memset(soc_bb, 0, sizeof(struct dml2_soc_bb)); +} + +static inline void dcn5_initialize_ip_caps(struct dml2_ip_capabilities *ip_caps) +{ + memset(ip_caps, 0, sizeof(struct dml2_ip_capabilities)); +} + +static inline void dcn5_initialize_utm_qos_model(struct utm_qos_model *qos_model, struct utm_qos_model_dchub_v1 *dchub) +{ + memset(qos_model, 0, sizeof(struct utm_qos_model)); + memset(dchub, 0, sizeof(struct utm_qos_model_dchub_v1)); + qos_model->dchub_v1 = dchub; +} + +static inline void dcn5or_initialize_utm_qos_model(struct utm_qos_model *qos_model, struct utm_qos_model_dchub_v1 *dchub) +{ + memset(qos_model, 0, sizeof(struct utm_qos_model)); + memset(dchub, 0, sizeof(struct utm_qos_model_dchub_v1)); + qos_model->dchub_v1 = dchub; +} + +#endif /* __DCN5_SOC_BB_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn6_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn6_soc_bb.h new file mode 100644 index 000000000000..19d43e5b7cc2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn6_soc_bb.h @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DCN6_SOC_BB_H__ +#define __DCN6_SOC_BB_H__ +#include "dml2_external_lib_deps.h" +#include "utm_qos_model_dchub_v2.h" +#include "utm_qos_model_dchub_v3.h" +#include "dml_top_soc_parameter_types.h" + +static inline void dcn6_test_initialize_soc_bb(struct dml2_soc_bb *soc_bb) +{ + memset(soc_bb, 0, sizeof(struct dml2_soc_bb)); +} + +static inline void dcn6b_test_initialize_soc_bb(struct dml2_soc_bb *soc_bb) +{ + dcn6_test_initialize_soc_bb(soc_bb); +} + +static inline void dcn6_test_initialize_ip_caps(struct dml2_ip_capabilities *ip_caps) +{ + memset(ip_caps, 0, sizeof(struct dml2_ip_capabilities)); +} + +static inline void dcn6_initialize_utm_qos_model_with_fixed_allocation(struct utm_qos_model *qos_model, struct utm_qos_model_dchub_v2 *dchub) +{ + memset(qos_model, 0, sizeof(struct utm_qos_model)); + memset(dchub, 0, sizeof(struct utm_qos_model_dchub_v2)); + qos_model->dchub_v2 = dchub; +} + +static inline void dcn6_test_initialize_utm_qos_model(struct utm_qos_model *qos_model, struct utm_qos_model_dchub_v2 *dchub) +{ +} + +static inline void dcn6b_test_initialize_utm_qos_model(struct utm_qos_model *qos_model, struct utm_qos_model_dchub_v2 *dchub) +{ + + dcn6_initialize_utm_qos_model_with_fixed_allocation(qos_model, dchub); +} + +static inline void dcn6_n_minus_1_initialize_utm_qos_model( + struct utm_qos_model *qos_model, struct utm_qos_model_dchub_v2 *dchub) +{ + memset(qos_model, 0, sizeof(struct utm_qos_model)); + memset(dchub, 0, sizeof(struct utm_qos_model_dchub_v2)); + qos_model->dchub_v2 = dchub; + qos_model->sops[0].fclk_khz = 300000; + qos_model->sops[0].uclk_khz = 97000; + qos_model->sops[1].fclk_khz = 503684; + qos_model->sops[1].uclk_khz = 435000; + qos_model->sops[2].fclk_khz = 1007368; + qos_model->sops[2].uclk_khz = 521000; + qos_model->sops[3].fclk_khz = 1206526; + qos_model->sops[3].uclk_khz = 731000; + qos_model->sops[4].fclk_khz = 1250000; + qos_model->sops[4].uclk_khz = 822000; + qos_model->sops[5].fclk_khz = 1250000; + qos_model->sops[5].uclk_khz = 962000; + qos_model->sops[6].fclk_khz = 1250000; + qos_model->sops[6].uclk_khz = 1069000; + qos_model->sops[7].fclk_khz = 1250000; + qos_model->sops[7].uclk_khz = 1187000; + qos_model->socbb.fabric_datapath_to_dcn_data_return_bytes = 64; + qos_model->socbb.dram_channel_width_bytes = 2; + qos_model->socbb.dram_channel_count = 16; + qos_model->socbb.dram_transactions_per_clock = 16; + qos_model->socbb.fabric_derate_percent_nominal = 57; + qos_model->socbb.fabric_derate_percent_urgent = 75; + qos_model->socbb.dram_derate_percent_nominal = 17; + qos_model->socbb.dram_derate_percent_urgent = 22; + qos_model->sop_count = 8; + + dchub->latencies[0].urgent_ramp_ps = 9865636; + dchub->latencies[0].t_trip_ps = 12650172; + dchub->latencies[0].meta_trip_to_mem_ps = 11227505; + dchub->latencies[0].max_req_latency_urg_ps = 2531615; + dchub->latencies[0].avg_req_latency_urg_ps = 1772436; + dchub->latencies[0].max_req_latency_non_urg_ps = 12650171; + dchub->latencies[0].avg_req_latency_non_urg_ps = 2548107; + dchub->latencies[0].df_response_time_ps = 1000000; + dchub->latencies[1].urgent_ramp_ps = 3411495; + dchub->latencies[1].t_trip_ps = 3467378; + dchub->latencies[1].meta_trip_to_mem_ps = 2620137; + dchub->latencies[1].max_req_latency_urg_ps = 1369447; + dchub->latencies[1].avg_req_latency_urg_ps = 834368; + dchub->latencies[1].max_req_latency_non_urg_ps = 3468646; + dchub->latencies[1].avg_req_latency_non_urg_ps = 970920; + dchub->latencies[1].df_response_time_ps = 595611; + dchub->latencies[2].urgent_ramp_ps = 2388169; + dchub->latencies[2].t_trip_ps = 2594969; + dchub->latencies[2].meta_trip_to_mem_ps = 2171306; + dchub->latencies[2].max_req_latency_urg_ps = 875775; + dchub->latencies[2].avg_req_latency_urg_ps = 526390; + dchub->latencies[2].max_req_latency_non_urg_ps = 2595139; + dchub->latencies[2].avg_req_latency_non_urg_ps = 630268; + dchub->latencies[2].df_response_time_ps = 297805; + dchub->latencies[3].urgent_ramp_ps = 1896062; + dchub->latencies[3].t_trip_ps = 1934965; + dchub->latencies[3].meta_trip_to_mem_ps = 1581238; + dchub->latencies[3].max_req_latency_urg_ps = 768344; + dchub->latencies[3].avg_req_latency_urg_ps = 445581; + dchub->latencies[3].max_req_latency_non_urg_ps = 1935135; + dchub->latencies[3].avg_req_latency_non_urg_ps = 506977; + dchub->latencies[3].df_response_time_ps = 248647; + dchub->latencies[4].urgent_ramp_ps = 1769285; + dchub->latencies[4].t_trip_ps = 1769285; + dchub->latencies[4].meta_trip_to_mem_ps = 1416798; + dchub->latencies[4].max_req_latency_urg_ps = 742910; + dchub->latencies[4].avg_req_latency_urg_ps = 425284; + dchub->latencies[4].max_req_latency_non_urg_ps = 1758238; + dchub->latencies[4].avg_req_latency_non_urg_ps = 475065; + dchub->latencies[4].df_response_time_ps = 240000; + dchub->latencies[5].urgent_ramp_ps = 1652902; + dchub->latencies[5].t_trip_ps = 1652902; + dchub->latencies[5].meta_trip_to_mem_ps = 1230505; + dchub->latencies[5].max_req_latency_urg_ps = 734108; + dchub->latencies[5].avg_req_latency_urg_ps = 416888; + dchub->latencies[5].max_req_latency_non_urg_ps = 1571945; + dchub->latencies[5].avg_req_latency_non_urg_ps = 451191; + dchub->latencies[5].df_response_time_ps = 240000; + dchub->latencies[6].urgent_ramp_ps = 1582791; + dchub->latencies[6].t_trip_ps = 1582791; + dchub->latencies[6].meta_trip_to_mem_ps = 1122960; + dchub->latencies[6].max_req_latency_urg_ps = 727450; + dchub->latencies[6].avg_req_latency_urg_ps = 410145; + dchub->latencies[6].max_req_latency_non_urg_ps = 1464400; + dchub->latencies[6].avg_req_latency_non_urg_ps = 436076; + dchub->latencies[6].df_response_time_ps = 240000; + dchub->latencies[7].urgent_ramp_ps = 1520802; + dchub->latencies[7].t_trip_ps = 1520802; + dchub->latencies[7].meta_trip_to_mem_ps = 1022428; + dchub->latencies[7].max_req_latency_urg_ps = 722083; + dchub->latencies[7].avg_req_latency_urg_ps = 404088; + dchub->latencies[7].max_req_latency_non_urg_ps = 1363868; + dchub->latencies[7].avg_req_latency_non_urg_ps = 422992; + dchub->latencies[7].df_response_time_ps = 240000; + + /* + * TODO: currently both utm budget percent and derate percent are both included in derate percent params. Need + * to separate them. So we can use the actual utm budget percent values below. + */ + dchub->max_nominal_utm_budget_percent = 100; + dchub->min_nominal_utm_budget_percent = 100; + dchub->max_urgent_utm_budget_percent = 100; + dchub->min_urgent_utm_budget_percent = 100; +} + + +static inline unsigned int dcn6a_test_initialize_sop_clocks( + struct utm_soc_operating_point *sop_clocks) +{ + return 0; +} + +static inline unsigned int dcn6b_test_initialize_sop_clocks( + struct utm_soc_operating_point *sop_clocks) +{ + return 0; +} + +/** + * dcn6_test_initialize_v3_sop_latencies - Set latencies for one SOP entry. + */ +static inline void dcn6_test_initialize_v3_sop_latencies( + struct utm_qos_model_dchub_v3_sop_entry *entry, + uint32_t urgent_ramp_ps, uint32_t t_trip_ps, + uint32_t meta_trip_to_mem_ps, + uint32_t max_urg_ps, uint32_t avg_urg_ps, + uint32_t max_non_urg_ps, uint32_t avg_non_urg_ps, + uint32_t df_response_time_ps) +{ + entry->urgent_ramp_ps = urgent_ramp_ps; + entry->t_trip_ps = t_trip_ps; + entry->meta_trip_to_mem_ps = meta_trip_to_mem_ps; + entry->max_req_latency_urg_ps = max_urg_ps; + entry->avg_req_latency_urg_ps = avg_urg_ps; + entry->max_req_latency_non_urg_ps = max_non_urg_ps; + entry->avg_req_latency_non_urg_ps = avg_non_urg_ps; + entry->df_response_time_ps = df_response_time_ps; +} + +/** + * dcn6_test_initialize_v3_sop_latencies_all_levels - Set identical latencies + * across all load levels for one SOP index. + */ +static inline void dcn6_test_initialize_v3_sop_latencies_all_levels( + struct utm_qos_model_dchub_v3 *dchub, unsigned int sop_index, + uint32_t urgent_ramp_ps, uint32_t t_trip_ps, + uint32_t meta_trip_to_mem_ps, + uint32_t max_urg_ps, uint32_t avg_urg_ps, + uint32_t max_non_urg_ps, uint32_t avg_non_urg_ps, + uint32_t df_response_time_ps) +{ + unsigned int ll; + + for (ll = 0; ll < dchub->load_level_count; ll++) + dcn6_test_initialize_v3_sop_latencies( + &dchub->sops[ll][sop_index], + urgent_ramp_ps, t_trip_ps, + meta_trip_to_mem_ps, + max_urg_ps, avg_urg_ps, + max_non_urg_ps, avg_non_urg_ps, + df_response_time_ps); +} + +static inline void dcn6_test_initialize_utm_qos_model_v3( + struct utm_qos_model *qos_model, + struct utm_qos_model_dchub_v3 *dchub) +{ + memset(dchub, 0, sizeof(struct utm_qos_model_dchub_v3)); + memset(qos_model, 0, sizeof(struct utm_qos_model)); + qos_model->version = utm_qos_model_version_v3; + qos_model->dchub_v3 = dchub; +} + +static inline void dcn6b_test_initialize_utm_qos_model_v3( + struct utm_qos_model *qos_model, + struct utm_qos_model_dchub_v3 *dchub) +{ + memset(dchub, 0, sizeof(struct utm_qos_model_dchub_v3)); + memset(qos_model, 0, sizeof(struct utm_qos_model)); + qos_model->version = utm_qos_model_version_v3; + qos_model->dchub_v3 = dchub; +} + +#endif /* __DCN6_SOC_BB_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v1.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v1.h new file mode 100644 index 000000000000..b13b54aff67a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v1.h @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#ifndef UTM_QOS_MODEL_DCHUB_V1_H +#define UTM_QOS_MODEL_DCHUB_V1_H +#include "utm_qos_model_types.h" + +struct utm_qos_model_dchub_memory_path_latency_v1 { + uint32_t urgent_ramp_ps; + uint32_t t_trip_ps; + uint32_t meta_trip_to_mem_ps; + uint32_t max_req_latency_urg_ps; + uint32_t avg_req_latency_urg_ps; + uint32_t max_req_latency_non_urg_ps; + uint32_t avg_req_latency_non_urg_ps; + uint32_t df_response_time_ps; +}; + +struct utm_qos_model_dchub_memory_path_bandwidth_v1 { + uint32_t nominal_bandwidth_KBps; + uint32_t urgent_bandwidth_KBps; +}; + +struct utm_qos_model_dchub_memory_path_qos_v1 { + struct utm_qos_model_dchub_memory_path_latency_v1 latency_upper_bound; + struct utm_qos_model_dchub_memory_path_bandwidth_v1 bandwidth_lower_bound; +}; + +struct utm_qos_model_dchub_v1 { + struct utm_qos_model_dchub_memory_path_latency_v1 latencies[MAX_UTM_SOP_COUNT]; + struct utm_qos_model_dchub_memory_path_bandwidth_v1 bandwidths[MAX_UTM_SOP_COUNT]; + uint32_t dcfclks_khz[MAX_UTM_SOP_COUNT]; + uint32_t socclks_khz[MAX_UTM_SOP_COUNT]; +}; + +static inline bool dchub_v1_is_qos_latency_supported_by_sop(const struct utm_qos_model *model, + const struct utm_qos_model_dchub_memory_path_latency_v1 *qos_latency, + uint8_t sop_index) +{ + const struct utm_qos_model_dchub_memory_path_latency_v1 *sop_latency = &model->dchub_v1->latencies[sop_index]; + + return (qos_latency->urgent_ramp_ps >= sop_latency->urgent_ramp_ps && + qos_latency->t_trip_ps >= sop_latency->t_trip_ps && + qos_latency->meta_trip_to_mem_ps >= sop_latency->meta_trip_to_mem_ps && + qos_latency->max_req_latency_urg_ps >= sop_latency->max_req_latency_urg_ps && + qos_latency->avg_req_latency_urg_ps >= sop_latency->avg_req_latency_urg_ps && + qos_latency->max_req_latency_non_urg_ps >= sop_latency->max_req_latency_non_urg_ps && + qos_latency->avg_req_latency_non_urg_ps >= sop_latency->avg_req_latency_non_urg_ps && + qos_latency->df_response_time_ps >= sop_latency->df_response_time_ps); +} + +static inline bool dchub_v1_is_qos_bandwidth_supported_by_sop( + const struct utm_qos_model *model, + const struct utm_qos_model_dchub_memory_path_bandwidth_v1 *qos_bandwidth, + uint8_t sop_index) +{ + return (model->dchub_v1->bandwidths[sop_index].nominal_bandwidth_KBps >= qos_bandwidth->nominal_bandwidth_KBps + && model->dchub_v1->bandwidths[sop_index].urgent_bandwidth_KBps >= qos_bandwidth->urgent_bandwidth_KBps); + +// const struct utm_soc_operating_point *sop = &model->sops[sop_index]; +// const struct utm_qos_model_socbb *socbb = &model->socbb; +// uint64_t available_bandwidth_KBps; +// uint64_t min_available_bandwidth_KBps; +// +// min_available_bandwidth_KBps = (uint64_t) sop->uclk_khz +// * socbb->dram_channel_count +// * socbb->dram_channel_width_bytes +// * socbb->dram_transactions_per_clock +// * socbb->dram_derate_percent_nominal / 100; +// +// available_bandwidth_KBps = (uint64_t) sop->fclk_khz +// * socbb->fabric_datapath_to_dcn_data_return_bytes +// * socbb->fabric_derate_percent_nominal / 100; +// if (min_available_bandwidth_KBps > available_bandwidth_KBps) +// min_available_bandwidth_KBps = available_bandwidth_KBps; +// +// available_bandwidth_KBps = (uint64_t) model->dchub_v1->dcfclks_khz[sop_index] +// * model->dchub_v1->return_bus_width_bytes +// * model->dchub_v1->sdp_derate_percent_nominal / 100; +// if (min_available_bandwidth_KBps > available_bandwidth_KBps) +// min_available_bandwidth_KBps = available_bandwidth_KBps; +// +// if ((min_available_bandwidth_KBps * nominal_utm_budget_percent / 100) < qos_bandwidth->nominal_bandwidth_KBps) +// return false; +// +// min_available_bandwidth_KBps = (uint64_t) sop->uclk_khz +// * socbb->dram_channel_count +// * socbb->dram_channel_width_bytes +// * socbb->dram_transactions_per_clock +// * socbb->dram_derate_percent_urgent / 100; +// +// available_bandwidth_KBps = (uint64_t) sop->fclk_khz +// * socbb->fabric_datapath_to_dcn_data_return_bytes +// * socbb->fabric_derate_percent_urgent / 100; +// if (min_available_bandwidth_KBps > available_bandwidth_KBps) +// min_available_bandwidth_KBps = available_bandwidth_KBps; +// +// available_bandwidth_KBps = (uint64_t) model->dchub_v1->dcfclks_khz[sop_index] +// * model->dchub_v1->return_bus_width_bytes +// * model->dchub_v1->sdp_derate_percent_urgent / 100; +// if (min_available_bandwidth_KBps > available_bandwidth_KBps) +// min_available_bandwidth_KBps = available_bandwidth_KBps; +// +// if ((min_available_bandwidth_KBps * urgent_utm_budget_percent / 100) < qos_bandwidth->urgent_bandwidth_KBps) +// return false; +// +// return true; +} +#endif /* #ifndef UTM_QOS_MODEL_DCHUB_V1_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v2.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v2.h new file mode 100644 index 000000000000..ebca5ddda284 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v2.h @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#ifndef UTM_QOS_MODEL_DCHUB_V2_H +#define UTM_QOS_MODEL_DCHUB_V2_H +#include "utm_qos_model_types.h" + +struct utm_qos_model_dchub_memory_path_latency_v2 { + uint32_t urgent_ramp_ps; + uint32_t t_trip_ps; + uint32_t meta_trip_to_mem_ps; + uint32_t max_req_latency_urg_ps; + uint32_t avg_req_latency_urg_ps; + uint32_t max_req_latency_non_urg_ps; + uint32_t avg_req_latency_non_urg_ps; + uint32_t df_response_time_ps; +}; + +struct utm_qos_model_dchub_memory_path_bandwidth_v2 { + uint32_t nominal_bandwidth_KBps; + uint32_t urgent_bandwidth_KBps; +}; + +struct utm_qos_model_dchub_memory_path_qos_v2 { + struct utm_qos_model_dchub_memory_path_latency_v2 latency_upper_bound; + struct utm_qos_model_dchub_memory_path_bandwidth_v2 bandwidth_lower_bound; +}; + +struct utm_qos_model_dchub_v2 { + struct utm_qos_model_dchub_memory_path_latency_v2 latencies[MAX_UTM_SOP_COUNT]; + + uint8_t max_nominal_utm_budget_percent; + uint8_t min_nominal_utm_budget_percent; + uint8_t max_urgent_utm_budget_percent; + uint8_t min_urgent_utm_budget_percent; +}; + +static inline bool dchub_v2_is_qos_latency_supported_by_sop(const struct utm_qos_model *model, + const struct utm_qos_model_dchub_memory_path_latency_v2 *qos_latency, + uint8_t sop_index) +{ + const struct utm_qos_model_dchub_memory_path_latency_v2 *sop_latency = &model->dchub_v2->latencies[sop_index]; + + return (qos_latency->urgent_ramp_ps >= sop_latency->urgent_ramp_ps && + qos_latency->t_trip_ps >= sop_latency->t_trip_ps && + qos_latency->meta_trip_to_mem_ps >= sop_latency->meta_trip_to_mem_ps && + qos_latency->max_req_latency_urg_ps >= sop_latency->max_req_latency_urg_ps && + qos_latency->avg_req_latency_urg_ps >= sop_latency->avg_req_latency_urg_ps && + qos_latency->max_req_latency_non_urg_ps >= sop_latency->max_req_latency_non_urg_ps && + qos_latency->avg_req_latency_non_urg_ps >= sop_latency->avg_req_latency_non_urg_ps && + qos_latency->df_response_time_ps >= sop_latency->df_response_time_ps); +} + +static inline void dchub_v2_get_sop_total_available_bandwidth_KBps( + const struct utm_qos_model *model, + struct utm_qos_model_dchub_memory_path_bandwidth_v2 *total_available_bandwidth, + uint8_t sop_index) +{ + const struct utm_soc_operating_point *sop = &model->sops[sop_index]; + const struct utm_qos_model_socbb *socbb = &model->socbb; + uint64_t dram_available_bandwidth_KBps_nominal; + uint64_t fabric_available_bandwidth_KBps_nominal; + uint64_t dram_available_bandwidth_KBps_urgent; + uint64_t fabric_available_bandwidth_KBps_urgent; + + dram_available_bandwidth_KBps_nominal = (uint64_t) sop->uclk_khz + * socbb->dram_channel_count + * socbb->dram_channel_width_bytes + * socbb->dram_transactions_per_clock + * socbb->dram_derate_percent_nominal / 100; + dram_available_bandwidth_KBps_urgent = (uint64_t) sop->uclk_khz + * socbb->dram_channel_count + * socbb->dram_channel_width_bytes + * socbb->dram_transactions_per_clock + * socbb->dram_derate_percent_urgent / 100; + fabric_available_bandwidth_KBps_nominal = (uint64_t) sop->fclk_khz + * socbb->fabric_datapath_to_dcn_data_return_bytes + * socbb->fabric_derate_percent_nominal / 100; + fabric_available_bandwidth_KBps_urgent = (uint64_t) sop->fclk_khz + * socbb->fabric_datapath_to_dcn_data_return_bytes + * socbb->fabric_derate_percent_urgent / 100; + + total_available_bandwidth->nominal_bandwidth_KBps = + dram_available_bandwidth_KBps_nominal < fabric_available_bandwidth_KBps_nominal ? + (uint32_t) dram_available_bandwidth_KBps_nominal : + (uint32_t) fabric_available_bandwidth_KBps_nominal; + total_available_bandwidth->urgent_bandwidth_KBps = + dram_available_bandwidth_KBps_urgent < fabric_available_bandwidth_KBps_urgent ? + (uint32_t) dram_available_bandwidth_KBps_urgent : + (uint32_t) fabric_available_bandwidth_KBps_urgent; +} + +static inline bool dchub_v2_is_qos_bandwidth_supported_by_sop( + const struct utm_qos_model *model, + const struct utm_qos_model_dchub_memory_path_bandwidth_v2 *qos_bandwidth, + uint8_t sop_index, + uint8_t nominal_utm_budget_percent, + uint8_t urgent_utm_budget_percent) +{ + struct utm_qos_model_dchub_memory_path_bandwidth_v2 available_bandwidth = {0}; + const struct utm_qos_model_dchub_v2 *dchub = model->dchub_v2; + uint64_t nominal_available_bandwidth_KBps; + uint64_t urgent_available_bandwidth_KBps; + + if (nominal_utm_budget_percent > dchub->max_nominal_utm_budget_percent || + nominal_utm_budget_percent < dchub->min_nominal_utm_budget_percent || + urgent_utm_budget_percent > dchub->max_urgent_utm_budget_percent || + urgent_utm_budget_percent < dchub->min_urgent_utm_budget_percent) + return false; + + dchub_v2_get_sop_total_available_bandwidth_KBps(model, &available_bandwidth, sop_index); + nominal_available_bandwidth_KBps = available_bandwidth.nominal_bandwidth_KBps; + urgent_available_bandwidth_KBps = available_bandwidth.urgent_bandwidth_KBps; + + if ((nominal_available_bandwidth_KBps * nominal_utm_budget_percent / 100) < qos_bandwidth->nominal_bandwidth_KBps) + return false; + else if ((urgent_available_bandwidth_KBps * urgent_utm_budget_percent / 100) < qos_bandwidth->urgent_bandwidth_KBps) + return false; + else + return true; +} +#endif /* #ifndef UTM_QOS_MODEL_DCHUB_V2_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v3.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v3.h new file mode 100644 index 000000000000..9390cad31691 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_dchub_v3.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#ifndef UTM_QOS_MODEL_DCHUB_V3_H +#define UTM_QOS_MODEL_DCHUB_V3_H + +/* Must match DALSMC_MAX_UTM_SOP_COUNT in dalsmc.h without including it */ +#define UTM_QOS_MODEL_V3_MAX_LOAD_LEVEL_COUNT 3 +#define UTM_QOS_MODEL_V3_MAX_SOP_COUNT 5 + +#define UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE 0 +#define UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE 1 +#define UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE 2 + +/** + * utm_qos_model_dchub_v3_sop_entry - Per-SOP QoS parameters for one load level. + * + * All latency fields are in picoseconds. All bandwidth fields are in KBps. + * Budget percentage and derate are pre-applied — callers use values + * directly without further scaling. + */ +struct utm_qos_model_dchub_v3_sop_entry { + /* latencies */ + uint32_t urgent_ramp_ps; + uint32_t t_trip_ps; + uint32_t meta_trip_to_mem_ps; + uint32_t max_req_latency_urg_ps; + uint32_t avg_req_latency_urg_ps; + uint32_t max_req_latency_non_urg_ps; + uint32_t avg_req_latency_non_urg_ps; + uint32_t df_response_time_ps; + /* bandwidths (budget allocation and derate pre-applied) */ + uint32_t urgent_bandwidth_KBps; + uint32_t nominal_bandwidth_KBps; + uint32_t lsdma_bandwidth_KBps; +}; + +/** + * utm_qos_model_dchub_v3 - DCN6 flat UTM QoS table. + * + * Indexed as sops[load_level][sop_index]. Load level constants: + * UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE (max budget %) + * UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE (min budget %) + * UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE (same as alt pstate, lsdma=0) + */ +struct utm_qos_model_dchub_v3 { + uint8_t load_level_count; + uint8_t sop_count; + struct utm_qos_model_dchub_v3_sop_entry + sops[UTM_QOS_MODEL_V3_MAX_LOAD_LEVEL_COUNT][UTM_QOS_MODEL_V3_MAX_SOP_COUNT]; +}; + +#endif /* #ifndef UTM_QOS_MODEL_DCHUB_V3_H */ \ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_types.h new file mode 100644 index 000000000000..b3b72210d42d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/utm_qos_model_types.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#ifndef UTM_QOS_MODEL_TYPES_H +#define UTM_QOS_MODEL_TYPES_H + +struct utm_soc_operating_point { + uint32_t uclk_khz; + uint32_t fclk_khz; +}; + +struct utm_qos_model_socbb { + uint8_t fabric_datapath_to_dcn_data_return_bytes; + uint8_t dram_channel_width_bytes; + uint8_t dram_channel_count; + uint8_t dram_transactions_per_clock; + uint8_t fabric_derate_percent_nominal; + uint8_t fabric_derate_percent_urgent; + uint8_t dram_derate_percent_nominal; + uint8_t dram_derate_percent_urgent; + uint8_t lsdma_fabric_derate_percent; + uint8_t lsdma_dram_derate_percent; + uint8_t fabric_datapath_to_lsdma_data_return_bytes; +}; + +#define MAX_UTM_SOP_COUNT 20 + +enum utm_qos_model_version { + utm_qos_model_version_v1, + utm_qos_model_version_v2, + utm_qos_model_version_v3, +}; + +struct utm_qos_model_dchub_v1; +struct utm_qos_model_dchub_v2; +struct utm_qos_model_dchub_v3; +struct utm_qos_model_lsdma; +struct utm_qos_model { + int version; + struct utm_soc_operating_point sops[MAX_UTM_SOP_COUNT]; + union { + const struct utm_qos_model_dchub_v1 *dchub_v1; + const struct utm_qos_model_dchub_v2 *dchub_v2; + const struct utm_qos_model_dchub_v3 *dchub_v3; + }; + const struct utm_qos_model_lsdma *lsdma; + struct utm_qos_model_socbb socbb; + uint8_t sop_count; +}; + +#endif /* #ifndef UTM_QOS_MODEL_TYPES_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_dmub_cmd.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_dmub_cmd.h new file mode 100644 index 000000000000..f73d5aa483b1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_dmub_cmd.h @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef DML_DMUB_CMD_H +#define DML_DMUB_CMD_H + + /* always include for now */ +#include +#include +#include + +#define DMUB_MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + +#define DMUB_MIN(X, Y) ((X) < (Y) ? (X) : (Y)) + +/* Define to ensure that the "common" members always appear in the same + * order in different structs for back compat purposes + */ +#define COMMON_STREAM_STATIC_SUB_STATE \ + struct dmub_fams2_cmd_legacy_stream_static_state legacy; \ + struct dmub_fams2_cmd_subvp_stream_static_state subvp; \ + struct dmub_fams2_cmd_drr_stream_static_state drr; + +/* Maximum number of streams on any ASIC. */ +#define DMUB_MAX_STREAMS 6 + +/* Maximum number of planes on any ASIC. */ +#define DMUB_MAX_PLANES 6 + +/* Maximum number of phantom planes on any ASIC */ +#define DMUB_MAX_PHANTOM_PLANES (DMUB_MAX_PLANES) / 2 + +/* Flattened structure containing SOC BB parameters stored in the VBIOS + * It is not practical to store the entire bounding box in VBIOS since the bounding box struct can gain new parameters. + * This also prevents alighment issues when new parameters are added to the SoC BB. + * The following parameters should be added since these values can't be obtained elsewhere: + * -dml2_soc_power_management_parameters + * -dml2_soc_vmin_clock_limits + */ +struct dmub_soc_bb_params { + uint32_t dram_clk_change_blackout_ns; + uint32_t dram_clk_change_read_only_ns; + uint32_t dram_clk_change_write_only_ns; + uint32_t fclk_change_blackout_ns; + uint32_t g7_ppt_blackout_ns; + uint32_t stutter_enter_plus_exit_latency_ns; + uint32_t stutter_exit_latency_ns; + uint32_t z8_stutter_enter_plus_exit_latency_ns; + uint32_t z8_stutter_exit_latency_ns; + uint32_t z8_min_idle_time_ns; + uint32_t type_b_dram_clk_change_blackout_ns; + uint32_t type_b_ppt_blackout_ns; + uint32_t vmin_limit_dispclk_khz; + uint32_t vmin_limit_dcfclk_khz; + uint32_t g7_temperature_read_blackout_ns; +}; + +struct dmub_rect16 { + /** + * Dirty rect x offset. + */ + uint16_t x; // src_x + + /** + * Dirty rect y offset. + */ + uint16_t y; // src_y + + /** + * Dirty rect width. + */ + uint16_t width; // dest_width, rect_x + + /** + * Dirty rect height. + */ + uint16_t height; // dest_height, rect_y +}; + +union fw_assisted_mclk_switch_version { + struct { + uint8_t minor : 5; + uint8_t major : 3; + }; + uint8_t ver; +}; + +/* generic structures and enums */ +struct dmub_optc_position { + uint32_t vpos; + uint32_t hpos; + uint32_t frame; +}; + +/* HW and FW global configuration data for FAMS2 */ +/* FAMS2 types and structs */ +enum fams2_stream_type { + FAMS2_STREAM_TYPE_NONE = 0, + FAMS2_STREAM_TYPE_VBLANK = 1, + FAMS2_STREAM_TYPE_VACTIVE = 2, + FAMS2_STREAM_TYPE_DRR = 3, + FAMS2_STREAM_TYPE_SUBVP = 4, + FAMS2_STREAM_TYPE_ALTERNATE = 5, +}; + +struct plane_pipe_rect { + struct dmub_rect16 luma; + struct dmub_rect16 chroma; +}; + +/** + * Structure to hold the LSDMA source / dest copy parameters. + * Each field is an array of [2][4]: + * [2] - Instance 0 is the copy for current frame, instance 1 is the copy for next frame (instance 1 potentially unused if no next) + * [4] - One instance per pipe + */ +struct lsdma_outputs { + uint16_t src_x[2][4]; // src x position for the copy. Array of [2][4] for curr vs. next and each pipe + uint16_t src_y[2][4]; // src y position for the copy + uint16_t dst_x[2][4]; // dst x position for the copy (can change for curr vs. next) + uint16_t dst_y[2][4]; // dst y position for the copy (can change for curr vs. next) + uint16_t width[2][4]; // src and dst width for the copy (src and dst must match) + uint16_t height[2][4]; // src and dst height for the copy (src and dst must match) + uint16_t dst_pitch[4]; // dst pitch for the copy (same for curr and next) +}; + +struct dmub_fams2_alternate_stream_dynamic_state { + uint64_t earliest_init_tick; // track earliest possible init tick for calculating is_tick_in_allow + uint32_t otg_frame_pending_clear[3]; // In this context pending means prefetch has never been completed for this frame yet + uint8_t flip_pending_clear_order[3]; + uint8_t num_pending_flips; + uint32_t prefetch_start_line_x1000[3]; // can compute from existing params, but store because we use this multiple times + uint16_t prefetch_end_line[3]; // can compute from existing params, but store because we use this multiple times + uint16_t recout_y[3]; + uint8_t flip_pending[3]; + uint8_t copy_from_earliest[3]; + uint16_t lsdma_bandwidth_mbps; + uint16_t vstartup_line; + uint16_t vready_line; + uint8_t cursor_size[3]; // Cursor array per plane for now - if we assume a single cursor, then we don't need an array + uint8_t pad; // to maintain alignment for below fields - re-arrange structure once all fields are finalized + /* outputs: */ + uint16_t subvp_start_line_a[3]; + uint16_t subvp_height_a[3]; + uint16_t subvp_next_start_line_a[3]; + uint16_t subvp_next_height_a[3]; + uint16_t subvp_start_line_b[3]; + uint16_t subvp_height_b[3]; + uint16_t subvp_next_start_line_b[3]; + uint16_t subvp_next_height_b[3]; + uint16_t subvp_c_start_line_a[3]; + uint16_t subvp_c_height_a[3]; + uint16_t subvp_c_next_start_line_a[3]; + uint16_t subvp_c_next_height_a[3]; + uint16_t subvp_c_start_line_b[3]; + uint16_t subvp_c_height_b[3]; + uint16_t subvp_c_next_start_line_b[3]; + uint16_t subvp_c_next_height_b[3]; + uint8_t subvp_position[3]; + uint8_t copy_from_primary[3]; + uint8_t pad1[2]; // to maintain alignment for below fields - re-arrange structure once all fields are finalized + uint32_t program_go_line; + uint32_t program_go_frame_count; + uint16_t svp0_start_dst_line; + uint16_t svp0_end_dst_line; + uint16_t svp1_start_dst_line; + uint16_t svp1_end_dst_line; + struct lsdma_outputs lsdma[2]; // [2] - instance per SVP0 and SVP1 + struct lsdma_outputs lsdma_c[2]; // [2] - instance per SVP0 and SVP1 +}; + +/* dynamic stream state */ +struct dmub_fams2_legacy_stream_dynamic_state { + uint8_t force_allow_at_vblank; + uint8_t pad[3]; +}; + +struct dmub_fams2_subvp_stream_dynamic_state { + uint16_t viewport_start_hubp_vline; + uint16_t viewport_height_hubp_vlines; + uint16_t viewport_start_c_hubp_vline; + uint16_t viewport_height_c_hubp_vlines; + uint16_t phantom_viewport_height_hubp_vlines; + uint16_t phantom_viewport_height_c_hubp_vlines; + uint16_t microschedule_start_otg_vline; + uint16_t mall_start_otg_vline; + uint16_t mall_start_hubp_vline; + uint16_t mall_start_c_hubp_vline; + uint8_t force_allow_at_vblank_only; + uint8_t swath_height; + uint8_t swath_height_c; + uint8_t pad; +}; + +struct dmub_fams2_drr_stream_dynamic_state { + uint16_t stretched_vtotal; + uint8_t use_cur_vtotal; + uint8_t pad; +}; + +struct dmub_fams2_cmd_alternate_stream_static_state { + uint32_t total_bytes_to_copy; + uint16_t svp0_dst_lines; // per stream + uint16_t svp1_dst_lines; // per stream + uint16_t min_lead_dst_lines; // per stream, should be max(nominal_req_limit, vstartup_to_vactive). Does not have to be maxed over all planes + uint16_t svp_req_limit; // per stream, should be the same value in time between all streams max(2 swaths, dst_y_pre) over all planes + uint16_t fw_delays; + uint16_t vstartup_start; + uint16_t rec_height[3]; + uint16_t viewport_start[3]; + uint16_t viewport_size[3]; // for now size will be the number of lines perpendicular to scan direction (height for 0 / 180, width for 90 and 270) + uint16_t viewport_start_c[3]; + uint16_t viewport_size_c[3]; + uint16_t surface_pitch[3]; + uint16_t surface_pitch_c[3]; + uint16_t surface_height[3]; + uint16_t surface_height_c[3]; + uint8_t element_size[3]; + uint8_t element_size_c[3]; + uint8_t swizzle_mode[3]; // TODO: Add mapping, should be value used in LSDMA command + uint8_t vready_offset_lines; // vready offset from vstartup in lines (rounded up, as the actual offset may be a fraction of a line) + uint16_t dst_y_prefetch_x1000[3]; + uint16_t total_swaths[3]; + uint16_t total_swaths_c[3]; + uint8_t prefetch_swaths[3]; + uint8_t prefetch_swaths_c[3]; + uint8_t swath_height[3]; + uint8_t swath_height_c[3]; + uint16_t block_256b_width[3]; + uint16_t block_256b_height[3]; + uint16_t block_256b_width_c[3]; + uint16_t block_256b_height_c[3]; + uint16_t macro_tile_width[3]; + uint16_t macro_tile_width_c[3]; + union { + struct { + uint8_t is_multi_planar : 1; + uint8_t is_yuv420 : 1; + uint8_t prefetch_relative_vblank : 1; + uint8_t vertical_access : 1; // vertical_access = 1 means 90 or 270 rotation + uint8_t access_direction : 1; // access_direction = 1 means bigger to smaller coordinations (e.g., scan from 2160 to 0 as opposed to regular 0 to 2160) + uint8_t dcc : 1; + uint8_t tmz : 1; // TODO: Need to assign outside of DML (DML not aware of TMZ) + } bits; + uint8_t all; + } config[3]; + uint8_t max_cursor_size; + uint16_t pre_hdl_delta_x1000[3]; + uint16_t pre_hdl_delta_c_x1000[3]; + uint16_t rec_hdl_delta_x1000[3]; + uint16_t rec_hdl_delta_c_x1000[3]; + uint16_t dst_y_per_vm_vblank_x1000[3]; + uint16_t dst_y_per_row_vblank_x1000[3]; + uint16_t dst_y_after_scaler[3]; + uint16_t vinit_prefill[3]; + uint16_t vinit_prefill_c[3]; + uint16_t vratio_x1000[3]; + uint16_t vratio_c_x1000[3]; + struct plane_pipe_rect pipe_viewports[4]; + /* TODO - remove these deprecated vars */ + uint32_t pipe_copy_offset[2][4]; // [2] - SVP0/1, [4] - 4 pipes + uint32_t pipe_copy_offset_c[2][4]; + /* bits 47:16 of the surface address */ + uint32_t pipe_copy_addr_47_16[2][4]; // [2] - SVP0/1, [4] - 4 pipes + uint32_t pipe_copy_addr_47_16_c[2][4]; + uint32_t pipe_copy_max_size[2][4]; + uint32_t pipe_copy_max_size_c[2][4]; +}; + +struct dmub_fams2_stream_dynamic_state { + uint64_t ref_tick; + uint32_t cur_vtotal; + uint16_t adjusted_allow_end_otg_vline; + uint8_t pad[2]; + struct dmub_optc_position ref_otg_pos; + struct dmub_optc_position target_otg_pos; + union { + struct dmub_fams2_legacy_stream_dynamic_state legacy; + struct dmub_fams2_subvp_stream_dynamic_state subvp; + struct dmub_fams2_drr_stream_dynamic_state drr; + struct dmub_fams2_alternate_stream_dynamic_state alternate; + } sub_state; +}; + +/* static stream state */ +struct dmub_fams2_legacy_stream_static_state { + uint8_t vactive_det_fill_delay_otg_vlines; + uint8_t programming_delay_otg_vlines; +}; //v0 + +struct dmub_fams2_subvp_stream_static_state { + uint16_t vratio_numerator; + uint16_t vratio_denominator; + uint16_t phantom_vtotal; + uint16_t phantom_vactive; + union { + struct { + uint8_t is_multi_planar : 1; + uint8_t is_yuv420 : 1; + } bits; + uint8_t all; + } config; + uint8_t programming_delay_otg_vlines; + uint8_t prefetch_to_mall_otg_vlines; + uint8_t phantom_otg_inst; + uint8_t phantom_pipe_mask; + uint8_t phantom_plane_pipe_masks[DMUB_MAX_PHANTOM_PLANES]; // phantom pipe mask per plane (for flip passthrough) +}; //v0 + +struct dmub_fams2_drr_stream_static_state { + uint16_t nom_stretched_vtotal; + uint8_t programming_delay_otg_vlines; + uint8_t only_stretch_if_required; + uint8_t pad[2]; +}; //v0 + +struct dmub_fams2_cmd_legacy_stream_static_state { + uint16_t vactive_det_fill_delay_otg_vlines; + uint16_t programming_delay_otg_vlines; + uint32_t disallow_time_us; +}; //v1 + +struct dmub_fams2_cmd_subvp_stream_static_state { + uint16_t vratio_numerator; + uint16_t vratio_denominator; + uint16_t phantom_vtotal; + uint16_t phantom_vactive; + uint16_t programming_delay_otg_vlines; + uint16_t prefetch_to_mall_otg_vlines; + union { + struct { + uint8_t is_multi_planar : 1; + uint8_t is_yuv420 : 1; + } bits; + uint8_t all; + } config; + uint8_t phantom_otg_inst; + uint8_t phantom_pipe_mask; + uint8_t pad0; + uint8_t phantom_plane_pipe_masks[DMUB_MAX_PHANTOM_PLANES]; // phantom pipe mask per plane (for flip passthrough) + uint8_t pad1[4 - (DMUB_MAX_PHANTOM_PLANES % 4)]; +}; //v1 + +struct dmub_fams2_cmd_drr_stream_static_state { + uint16_t nom_stretched_vtotal; + uint16_t programming_delay_otg_vlines; + uint8_t only_stretch_if_required; + uint8_t pad[3]; +}; //v1 + +union dmub_fams2_stream_static_sub_state { + struct dmub_fams2_legacy_stream_static_state legacy; + struct dmub_fams2_subvp_stream_static_state subvp; + struct dmub_fams2_drr_stream_static_state drr; +}; //v0 + +union dmub_fams2_cmd_stream_static_sub_state { + COMMON_STREAM_STATIC_SUB_STATE +}; //v1 + +union dmub_fams2_stream_static_sub_state_v2 { + COMMON_STREAM_STATIC_SUB_STATE + struct dmub_fams2_cmd_alternate_stream_static_state alternate; +}; //v2 + +struct dmub_fams2_stream_static_state { + enum fams2_stream_type type; + uint32_t otg_vline_time_ns; + uint32_t otg_vline_time_ticks; + uint16_t htotal; + uint16_t vtotal; // nominal vtotal + uint16_t vblank_start; + uint16_t vblank_end; + uint16_t max_vtotal; + uint16_t allow_start_otg_vline; + uint16_t allow_end_otg_vline; + uint16_t drr_keepout_otg_vline; // after this vline, vtotal cannot be changed + uint8_t scheduling_delay_otg_vlines; // min time to budget for ready to microschedule start + uint8_t contention_delay_otg_vlines; // time to budget for contention on execution + uint8_t vline_int_ack_delay_otg_vlines; // min time to budget for vertical interrupt firing + uint8_t allow_to_target_delay_otg_vlines; // time from allow vline to target vline + union { + struct { + uint8_t is_drr : 1; // stream is DRR enabled + uint8_t clamp_vtotal_min : 1; // clamp vtotal to min instead of nominal + uint8_t min_ttu_vblank_usable : 1; // if min ttu vblank is above wm, no force pstate is needed in blank + } bits; + uint8_t all; + } config; + uint8_t otg_inst; + uint8_t pipe_mask; // pipe mask for the whole config + uint8_t num_planes; + uint8_t plane_pipe_masks[DMUB_MAX_PLANES]; // pipe mask per plane (for flip passthrough) + uint8_t pad[DMUB_MAX_PLANES % 4]; + union dmub_fams2_stream_static_sub_state sub_state; +}; //v0 + +struct dmub_fams2_cmd_stream_static_base_state { + enum fams2_stream_type type; + uint32_t otg_vline_time_ns; + uint32_t otg_vline_time_ticks; + uint16_t htotal; + uint16_t vtotal; // nominal vtotal + uint16_t vblank_start; + uint16_t vblank_end; + uint16_t max_vtotal; + uint16_t allow_start_otg_vline; + uint16_t allow_end_otg_vline; + uint16_t drr_keepout_otg_vline; // after this vline, vtotal cannot be changed + uint16_t scheduling_delay_otg_vlines; // min time to budget for ready to microschedule start + uint16_t contention_delay_otg_vlines; // time to budget for contention on execution + uint16_t vline_int_ack_delay_otg_vlines; // min time to budget for vertical interrupt firing + uint16_t allow_to_target_delay_otg_vlines; // time from allow vline to target vline + union { + struct { + uint8_t is_drr : 1; // stream is DRR enabled + uint8_t clamp_vtotal_min : 1; // clamp vtotal to min instead of nominal + uint8_t min_ttu_vblank_usable : 1; // if min ttu vblank is above wm, no force pstate is needed in blank + } bits; + uint8_t all; + } config; + uint8_t otg_inst; + uint8_t pipe_mask; // pipe mask for the whole config + uint8_t num_planes; + uint8_t plane_pipe_masks[DMUB_MAX_PLANES]; // pipe mask per plane (for flip passthrough) + uint8_t pad[DMUB_MAX_PLANES % 4]; +}; //v1 + +struct dmub_fams2_stream_static_state_v1 { + struct dmub_fams2_cmd_stream_static_base_state base; + union dmub_fams2_stream_static_sub_state_v2 sub_state; +}; //v1 + +/** + * enum dmub_fams2_allow_delay_check_mode - macroscheduler mode for breaking on excessive + * p-state request to allow latency + */ +enum dmub_fams2_allow_delay_check_mode { + /* No check for request to allow delay */ + FAMS2_ALLOW_DELAY_CHECK_NONE = 0, + /* Check for request to allow delay */ + FAMS2_ALLOW_DELAY_CHECK_FROM_START = 1, + /* Check for prepare to allow delay */ + FAMS2_ALLOW_DELAY_CHECK_FROM_PREPARE = 2, +}; + +union dmub_fams2_global_feature_config { + struct { + uint32_t enable : 1; + uint32_t enable_ppt_check : 1; + uint32_t enable_stall_recovery : 1; + uint32_t enable_debug : 1; + uint32_t enable_offload_flip : 1; + uint32_t enable_visual_confirm : 1; + uint32_t allow_delay_check_mode : 2; + uint32_t legacy_method_no_fams2 : 1; + uint32_t reserved : 23; + } bits; + uint32_t all; +}; + +struct dmub_cmd_fams2_global_config { + uint32_t max_allow_delay_us; // max delay to assert allow from uclk change begin + uint32_t lock_wait_time_us; // time to forecast acquisition of lock + uint32_t num_streams; + union dmub_fams2_global_feature_config features; + uint32_t recovery_timeout_us; + uint32_t hwfq_flip_programming_delay_us; + uint32_t max_allow_to_target_delta_us; // how early DCN could assert P-State allow compared to the P-State target +}; + +union dmub_cmd_fams2_config { + struct dmub_cmd_fams2_global_config global; + struct dmub_fams2_stream_static_state stream; //v0 + union { + struct dmub_fams2_cmd_stream_static_base_state base; + union dmub_fams2_cmd_stream_static_sub_state sub_state; + } stream_v1; //v1 +}; + +struct dmub_fams2_config_v2 { + struct dmub_cmd_fams2_global_config global; + struct dmub_fams2_stream_static_state_v1 stream_v1[DMUB_MAX_STREAMS]; //v1 +}; + +/** + * OS/FW agnostic memcpy + */ +#ifndef dmub_memcpy +#define dmub_memcpy(dest, source, bytes) memcpy((dest), (source), (bytes)) +#endif + + /** + * OS/FW agnostic memset + */ +#ifndef dmub_memset +#define dmub_memset(dest, val, bytes) memset((dest), (val), (bytes)) +#endif + + //#endif +#endif /* _DML_DMUB_CMD_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.c new file mode 100644 index 000000000000..8c3dd030e775 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#include "dml2_cga_dcn6.h" +#include "lib_float_math.h" +#include "dml2_debug.h" + +#define DFS_DIVIDER_RANGE_SCALE_FACTOR 4.0 +#define CLOCK_UNIT_GRANULARITY 0.001 +#define DPPREFCLK_DIVIDER 255 + +static double cga_dcn6_add_overhead_percent(double clk, double overhead_percent) +{ + return clk * (1 + overhead_percent / 100.0); +} + +static double cga_dcn6_calculate_refclk_mhz(const struct dml2_clock_granularity_adjuster *adjuster, + const double *clks_mhz, unsigned int count) +{ + unsigned int i; + double max_clk_mhz = 0; + double refclk_mhz; + + for (i = 0; i < count; i++) + max_clk_mhz = math_max2(clks_mhz[i], max_clk_mhz); + + refclk_mhz = math_floor2(max_clk_mhz, CLOCK_UNIT_GRANULARITY); + refclk_mhz = cga_dcn6_add_overhead_percent(refclk_mhz, adjuster->dcn_downspread_percent); + refclk_mhz = math_floor2(refclk_mhz, CLOCK_UNIT_GRANULARITY); + + return refclk_mhz; +} + +static double cga_dcn6_calculate_actual_dispclk_mhz(const struct dml2_clock_granularity_adjuster *adjuster, double dispclk_mhz) +{ + double dispclk_with_downspread_mhz; + double dispclk_with_ramp_margin_mhz; + + dispclk_mhz = math_floor2(dispclk_mhz, CLOCK_UNIT_GRANULARITY); + dispclk_with_downspread_mhz = cga_dcn6_add_overhead_percent(dispclk_mhz, adjuster->dcn_downspread_percent); + dispclk_with_ramp_margin_mhz = cga_dcn6_add_overhead_percent( + dispclk_with_downspread_mhz, adjuster->dispclk_ramp_margin_percent); + if (dispclk_with_downspread_mhz <= adjuster->max_dispclk_mhz && + dispclk_with_ramp_margin_mhz > adjuster->max_dispclk_mhz) + /* when dispclk with ramp margin is slightly over max, clamp the ramp margin to the max dispclk */ + return math_floor2(adjuster->max_dispclk_mhz, CLOCK_UNIT_GRANULARITY); + else if (dispclk_with_downspread_mhz > adjuster->max_dispclk_mhz) + return math_floor2(dispclk_with_downspread_mhz, CLOCK_UNIT_GRANULARITY); + else + return math_floor2(dispclk_with_ramp_margin_mhz, CLOCK_UNIT_GRANULARITY); +} + +static double cga_dcn6_adjust_to_dfs_clock_value_mhz(const struct dml2_clock_granularity_adjuster *adjuster, double clk_mhz) +{ + double vco_speed_scaled_mhz; + double vco_divider; + double adjusted_clock_mhz; + + DML_ASSERT_MSG(adjuster->dispclk_dppclk_vco_speed_mhz > 1, "invalid dispclk_dppclk_vco_speed_mhz value!\n"); + if (clk_mhz == 0) + /* There are cases when a clock is not needed */ + return 0; + + vco_speed_scaled_mhz = math_floor2(adjuster->dispclk_dppclk_vco_speed_mhz, 0.001); + vco_speed_scaled_mhz *= DFS_DIVIDER_RANGE_SCALE_FACTOR; + vco_divider = vco_speed_scaled_mhz / clk_mhz; + vco_divider = math_floor(vco_divider); + adjusted_clock_mhz = vco_speed_scaled_mhz / vco_divider; + adjusted_clock_mhz = math_floor2(adjusted_clock_mhz, CLOCK_UNIT_GRANULARITY); + return adjusted_clock_mhz; +} + +static double dga_dcn6_calculate_adjusted_dppclk_mhz(const struct dml2_clock_granularity_adjuster *adjuster, + double dpprefclk_mhz, double dppclk_mhz) +{ + double granularity_mhz = dpprefclk_mhz / DPPREFCLK_DIVIDER; + + dppclk_mhz = math_floor2(dppclk_mhz, CLOCK_UNIT_GRANULARITY); + dppclk_mhz = cga_dcn6_add_overhead_percent(dppclk_mhz, adjuster->dcn_downspread_percent); + dppclk_mhz = math_ceil2(dppclk_mhz, granularity_mhz); + dppclk_mhz = math_floor2(dppclk_mhz, CLOCK_UNIT_GRANULARITY); + + return dppclk_mhz; +} + +static double dga_dcn6_calculate_adjusted_dtbclk_mhz( + const struct dml2_clock_granularity_adjuster *adjuster, double dppclk_mhz) +{ + (void)adjuster; + return math_floor2(dppclk_mhz, CLOCK_UNIT_GRANULARITY); +} + +static double cga_dcn6_adjust_dispclk_mhz(const struct dml2_clock_granularity_adjuster *adjuster, double dispclk_mhz) +{ + double adjusted_dispclk_mhz; + + adjusted_dispclk_mhz = cga_dcn6_calculate_actual_dispclk_mhz(adjuster, dispclk_mhz); + adjusted_dispclk_mhz = cga_dcn6_adjust_to_dfs_clock_value_mhz(adjuster, adjusted_dispclk_mhz); + + return adjusted_dispclk_mhz; +} + +static void cga_dcn6_adjust_dppclks_mhz(const struct dml2_clock_granularity_adjuster *adjuster, unsigned int count, + const double *dppclks_mhz, double *adjusted_dppclks_mhz, double *adjusted_dpprefclk_mhz) +{ + unsigned int i; + double dpprefclk_mhz; + + dpprefclk_mhz = cga_dcn6_calculate_refclk_mhz(adjuster, dppclks_mhz, count); + *adjusted_dpprefclk_mhz = cga_dcn6_adjust_to_dfs_clock_value_mhz(adjuster, dpprefclk_mhz); + for (i = 0; i < count; i++) + adjusted_dppclks_mhz[i] = dga_dcn6_calculate_adjusted_dppclk_mhz( + adjuster, *adjusted_dpprefclk_mhz, dppclks_mhz[i]); +} + +static void cga_dcn6_adjust_dtbclks_mhz(const struct dml2_clock_granularity_adjuster *adjuster, unsigned int count, + const double *dtbclks_mhz, double *adjusted_dtbclks_mhz, double *adjusted_dtbrefclk_mhz) +{ + unsigned int i; + double dtbrefclk_mhz; + + dtbrefclk_mhz = cga_dcn6_calculate_refclk_mhz(adjuster, dtbclks_mhz, count); + *adjusted_dtbrefclk_mhz = cga_dcn6_adjust_to_dfs_clock_value_mhz(adjuster, dtbrefclk_mhz); + for (i = 0; i < count; i++) + adjusted_dtbclks_mhz[i] = dga_dcn6_calculate_adjusted_dtbclk_mhz( + adjuster, dtbclks_mhz[i]); +} + +static double cga_dcn6_adjust_dcfclk_deepsleep_mhz(const struct dml2_clock_granularity_adjuster *adjuster, + double dcfclk_deepsleep_mhz) +{ + (void)adjuster; + return math_ceil2(dcfclk_deepsleep_mhz, CLOCK_UNIT_GRANULARITY); +} + +static void cga_dcn6_initialize(const struct dml2_cga_initialize_in_out *in_out) +{ + in_out->adjuster->dcn_downspread_percent = in_out->soc_bb->dcn_downspread_percent; + in_out->adjuster->dispclk_dppclk_vco_speed_mhz = in_out->soc_bb->dispclk_dppclk_vco_speed_mhz; + in_out->adjuster->dispclk_ramp_margin_percent = in_out->ip->dispclk_ramp_margin_percent; + in_out->adjuster->max_dispclk_mhz = + in_out->soc_bb->clk_table.dispclk.clk_values_khz[in_out->soc_bb->clk_table.dispclk.num_clk_values - 1] / 1000.0; +} + +void cga_dcn6_create(struct dml2_clock_granularity_adjuster *adjuster) +{ + adjuster->initialize = cga_dcn6_initialize; + adjuster->adjust_dispclk_mhz = cga_dcn6_adjust_dispclk_mhz; + adjuster->adjust_dppclks_mhz = cga_dcn6_adjust_dppclks_mhz; + adjuster->adjust_dtbclks_mhz = cga_dcn6_adjust_dtbclks_mhz; + adjuster->adjust_dcfclk_deepsleep_mhz = cga_dcn6_adjust_dcfclk_deepsleep_mhz; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.h new file mode 100644 index 000000000000..834e7f93a949 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_dcn6.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#ifndef __DML2_CGA_DCN6_H__ +#define __DML2_CGA_DCN6_H__ + +#include "dml2_internal_shared_types.h" +void cga_dcn6_create(struct dml2_clock_granularity_adjuster *adjuster); +#endif /* #ifndef __DML2_CGA_DCN6_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.c new file mode 100644 index 000000000000..92cdc11c1892 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#include "dml2_cga_factory.h" +#include "dml2_cga_dcn6.h" + +bool dml2_cga_create(enum dml2_project_id project_id, struct dml2_clock_granularity_adjuster *adjuster) +{ + bool result = false; + + if (adjuster == NULL) + return false; + + memset(adjuster, 0, sizeof(struct dml2_clock_granularity_adjuster)); + + switch (project_id) { + case dml2_project_dcn4x_stage1: + case dml2_project_dcn42: + case dml2_project_dcn4x_stage2: + case dml2_project_dcn4x_stage2_auto_drr_svp: + case dml2_project_dcn4x_utm: + case dml2_project_dcn5x: + case dml2_project_dcn5x_utm: + memset(adjuster, 0, sizeof(*adjuster)); + result = true; + break; + case dml2_project_dcn6x_soc_var_a: + case dml2_project_dcn6x_soc_var_b: + cga_dcn6_create(adjuster); + result = true; + break; + case dml2_project_invalid: + default: + break; + } + + return result; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.h new file mode 100644 index 000000000000..298bfa6636f0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_cga/dml2_cga_factory.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#ifndef __DML2_CGA_FACTORY_H__ +#define __DML2_CGA_FACTORY_H__ + +#include "dml2_internal_shared_types.h" +bool dml2_cga_create(enum dml2_project_id project_id, struct dml2_clock_granularity_adjuster *adjuster); +#endif /* #ifndef __DML2_CGA_FACTORY_H__ */ \ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_calcs_dsc_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_calcs_dsc_shared_types.h new file mode 100644 index 000000000000..fb70c4793665 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_calcs_dsc_shared_types.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_CALCS_DSC_SHARED_TYPES_H__ +#define __DML2_CORE_CALCS_DSC_SHARED_TYPES_H__ + +#include "dml_top_display_cfg_types.h" + +// Delay and uncertainty structure +typedef struct { + int delay; + int uncertainty; +} delay_uncertainty_t; + +// Latency structure with group, pipeline, and pixel delays +typedef struct { + int groups; // latency in groups - Number of groups needed to be sent before output can begin + int pipeline; // pipeline delay latency - Propagation delay through the bitstream construction layer in number of pixel containers + int pixels; // latency in pixels - Number of groups multiplied by cycles per group + + // Extra variables needed for functional coverage + int additional_group_delay; + int lines_to_reach_ixd; + int groups_to_reach_ixd; + int slice_width_groups; + int initial_xmit_delay; + int number_of_lines_to_reach_ixd; + int slice_width_modified; +} latency_t; + +#endif /* __DML2_CORE_CALCS_DSC_SHARED_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c new file mode 100644 index 000000000000..05a99c4f761b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c @@ -0,0 +1,5893 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn5_calcs_dchub.h" +#include "dml2_core_utils.h" + +void dcn5_calculate_max_det_and_min_compressed_buffer_size( + unsigned int ConfigReturnBufferSizeInKByte, + unsigned int ConfigReturnBufferSegmentSizeInKByte, + unsigned int ROBBufferSizeInKByte, + unsigned int MaxNumDPP, + unsigned int nomDETInKByteOverrideEnable, // VBA_DELTA, allow DV to override default DET size + unsigned int nomDETInKByteOverrideValue, // VBA_DELTA + bool is_mrq_present, + + // Output + unsigned int *MaxTotalDETInKByte, + unsigned int *nomDETInKByte, + unsigned int *MinCompressedBufferSizeInKByte) +{ + if (is_mrq_present) + *MaxTotalDETInKByte = (unsigned int) math_ceil2((double)(ConfigReturnBufferSizeInKByte + ROBBufferSizeInKByte)*4/5, 64); + else + *MaxTotalDETInKByte = ConfigReturnBufferSizeInKByte - ConfigReturnBufferSegmentSizeInKByte; + + *nomDETInKByte = (unsigned int)(math_floor2((double)*MaxTotalDETInKByte / (double)MaxNumDPP, ConfigReturnBufferSegmentSizeInKByte)); + *MinCompressedBufferSizeInKByte = ConfigReturnBufferSizeInKByte - *MaxTotalDETInKByte; + + DML_LOG_VERBOSE("DML::%s: is_mrq_present = %u\n", __func__, is_mrq_present); + DML_LOG_VERBOSE("DML::%s: ConfigReturnBufferSizeInKByte = %u\n", __func__, ConfigReturnBufferSizeInKByte); + DML_LOG_VERBOSE("DML::%s: ConfigReturnBufferSegmentSizeInKByte = %u\n", __func__, ConfigReturnBufferSegmentSizeInKByte); + DML_LOG_VERBOSE("DML::%s: ROBBufferSizeInKByte = %u\n", __func__, ROBBufferSizeInKByte); + DML_LOG_VERBOSE("DML::%s: MaxNumDPP = %u\n", __func__, MaxNumDPP); + DML_LOG_VERBOSE("DML::%s: MaxTotalDETInKByte = %u\n", __func__, *MaxTotalDETInKByte); + DML_LOG_VERBOSE("DML::%s: nomDETInKByte = %u\n", __func__, *nomDETInKByte); + DML_LOG_VERBOSE("DML::%s: MinCompressedBufferSizeInKByte = %u\n", __func__, *MinCompressedBufferSizeInKByte); + + if (nomDETInKByteOverrideEnable) { + *nomDETInKByte = nomDETInKByteOverrideValue; + DML_LOG_VERBOSE("DML::%s: nomDETInKByte = %u (overrided)\n", __func__, *nomDETInKByte); + } +} + +void dcn5_calculate_byte_per_pixel_and_block_sizes( + enum dml2_source_format_class SourcePixelFormat, + enum dml2_swizzle_mode SurfaceTiling, + unsigned int pitch_y, + unsigned int pitch_c, + + // Output + unsigned int *BytePerPixelY, + unsigned int *BytePerPixelC, + double *BytePerPixelDETY, + double *BytePerPixelDETC, + unsigned int *BlockHeight256BytesY, + unsigned int *BlockHeight256BytesC, + unsigned int *BlockWidth256BytesY, + unsigned int *BlockWidth256BytesC, + unsigned int *MacroTileHeightY, + unsigned int *MacroTileHeightC, + unsigned int *MacroTileWidthY, + unsigned int *MacroTileWidthC, + bool *surf_linear128_l, + bool *surf_linear128_c) +{ + *BytePerPixelDETY = 0; + *BytePerPixelDETC = 0; + *BytePerPixelY = 0; + *BytePerPixelC = 0; + + if (SourcePixelFormat == dml2_444_64) { + *BytePerPixelDETY = 8; + *BytePerPixelDETC = 0; + *BytePerPixelY = 8; + *BytePerPixelC = 0; + } else if (SourcePixelFormat == dml2_444_32 || SourcePixelFormat == dml2_rgbe || SourcePixelFormat == dml2_422_packed_10 || SourcePixelFormat == dml2_422_packed_12) { + *BytePerPixelDETY = 4; + *BytePerPixelDETC = 0; + *BytePerPixelY = 4; + *BytePerPixelC = 0; + } else if (SourcePixelFormat == dml2_422_packed_10) { + *BytePerPixelDETY = (double)(8.0 / 3); + *BytePerPixelDETC = 0; + *BytePerPixelY = 4; + *BytePerPixelC = 0; + } else if (SourcePixelFormat == dml2_444_16 || SourcePixelFormat == dml2_mono_16 || SourcePixelFormat == dml2_422_packed_8) { + *BytePerPixelDETY = 2; + *BytePerPixelDETC = 0; + *BytePerPixelY = 2; + *BytePerPixelC = 0; + } else if (SourcePixelFormat == dml2_444_8 || SourcePixelFormat == dml2_mono_8) { + *BytePerPixelDETY = 1; + *BytePerPixelDETC = 0; + *BytePerPixelY = 1; + *BytePerPixelC = 0; + } else if (SourcePixelFormat == dml2_rgbe_alpha) { + *BytePerPixelDETY = 4; + *BytePerPixelDETC = 1; + *BytePerPixelY = 4; + *BytePerPixelC = 1; + } else if (SourcePixelFormat == dml2_420_8 || SourcePixelFormat == dml2_422_planar_8) { + *BytePerPixelDETY = 1; + *BytePerPixelDETC = 2; + *BytePerPixelY = 1; + *BytePerPixelC = 2; + } else if (SourcePixelFormat == dml2_420_12 || SourcePixelFormat == dml2_422_planar_12) { + *BytePerPixelDETY = 2; + *BytePerPixelDETC = 4; + *BytePerPixelY = 2; + *BytePerPixelC = 4; + } else if (SourcePixelFormat == dml2_420_10 || SourcePixelFormat == dml2_422_planar_10) { + *BytePerPixelDETY = (double)(4.0 / 3); + *BytePerPixelDETC = (double)(8.0 / 3); + *BytePerPixelY = 2; + *BytePerPixelC = 4; + } else { + DML_LOG_VERBOSE("ERROR: DML::%s: SourcePixelFormat = %u not supported!\n", __func__, SourcePixelFormat); + DML_ASSERT(0); + } + + DML_LOG_VERBOSE("DML::%s: SourcePixelFormat = %u\n", __func__, SourcePixelFormat); + DML_LOG_VERBOSE("DML::%s: BytePerPixelDETY = %f\n", __func__, *BytePerPixelDETY); + DML_LOG_VERBOSE("DML::%s: BytePerPixelDETC = %f\n", __func__, *BytePerPixelDETC); + DML_LOG_VERBOSE("DML::%s: BytePerPixelY = %u\n", __func__, *BytePerPixelY); + DML_LOG_VERBOSE("DML::%s: BytePerPixelC = %u\n", __func__, *BytePerPixelC); + DML_LOG_VERBOSE("DML::%s: pitch_y = %u\n", __func__, pitch_y); + DML_LOG_VERBOSE("DML::%s: pitch_c = %u\n", __func__, pitch_c); + + unsigned int pixel_per_element = dml2_core_utils_is_422_packed(SourcePixelFormat) ? 2 : 1; + if (dml2_core_utils_get_gfx_version(SurfaceTiling) == 11) { + *surf_linear128_l = 0; + *surf_linear128_c = 0; + } else { + if (SurfaceTiling == dml2_sw_linear) { + *surf_linear128_l = (((pitch_y * pixel_per_element * *BytePerPixelY) % 256) != 0); + + if (dml2_core_utils_is_420(SourcePixelFormat) || dml2_core_utils_is_422_planar(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha) + *surf_linear128_c = (((pitch_c * *BytePerPixelC) % 256) != 0); + } + } + DML_LOG_VERBOSE("DML::%s: surf_linear128_l = %u\n", __func__, *surf_linear128_l); + DML_LOG_VERBOSE("DML::%s: surf_linear128_c = %u\n", __func__, *surf_linear128_c); + + if (!(dml2_core_utils_is_420(SourcePixelFormat) || dml2_core_utils_is_422_planar(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha)) { + if (SurfaceTiling == dml2_sw_linear) { + *BlockHeight256BytesY = 1; + } else if (SourcePixelFormat == dml2_444_64 || SourcePixelFormat == dml2_422_packed_10 || SourcePixelFormat == dml2_422_packed_12) { + *BlockHeight256BytesY = 4; + } else if (SourcePixelFormat == dml2_444_8) { + *BlockHeight256BytesY = 16; + } else { + *BlockHeight256BytesY = 8; + } + *BlockWidth256BytesY = 256U / *BytePerPixelY / *BlockHeight256BytesY; + *BlockHeight256BytesC = 0; + *BlockWidth256BytesC = 0; + } else { // dual plane + if (SurfaceTiling == dml2_sw_linear) { + *BlockHeight256BytesY = 1; + *BlockHeight256BytesC = 1; + } else if (SourcePixelFormat == dml2_rgbe_alpha) { + *BlockHeight256BytesY = 8; + *BlockHeight256BytesC = 16; + } else if (SourcePixelFormat == dml2_420_8 || SourcePixelFormat == dml2_422_planar_8) { + *BlockHeight256BytesY = 16; + *BlockHeight256BytesC = 8; + } else { + *BlockHeight256BytesY = 8; + *BlockHeight256BytesC = 8; + } + *BlockWidth256BytesY = 256U / *BytePerPixelY / *BlockHeight256BytesY; + *BlockWidth256BytesC = 256U / *BytePerPixelC / *BlockHeight256BytesC; + } + DML_LOG_VERBOSE("DML::%s: BlockWidth256BytesY = %u\n", __func__, *BlockWidth256BytesY); + DML_LOG_VERBOSE("DML::%s: BlockHeight256BytesY = %u\n", __func__, *BlockHeight256BytesY); + DML_LOG_VERBOSE("DML::%s: BlockWidth256BytesC = %u\n", __func__, *BlockWidth256BytesC); + DML_LOG_VERBOSE("DML::%s: BlockHeight256BytesC = %u\n", __func__, *BlockHeight256BytesC); + + if (dml2_core_utils_get_gfx_version(SurfaceTiling) == 11) { + if (SurfaceTiling == dml2_gfx11_sw_linear) { + *MacroTileHeightY = *BlockHeight256BytesY; + *MacroTileWidthY = 256 / *BytePerPixelY / *MacroTileHeightY; + *MacroTileHeightC = *BlockHeight256BytesC; + if (*MacroTileHeightC == 0) { + *MacroTileWidthC = 0; + } else { + *MacroTileWidthC = 256 / *BytePerPixelC / *MacroTileHeightC; + } + } else if (SurfaceTiling == dml2_gfx11_sw_64kb_d || SurfaceTiling == dml2_gfx11_sw_64kb_d_t || SurfaceTiling == dml2_gfx11_sw_64kb_d_x || SurfaceTiling == dml2_gfx11_sw_64kb_r_x) { + *MacroTileHeightY = 16 * *BlockHeight256BytesY; + *MacroTileWidthY = 65536 / *BytePerPixelY / *MacroTileHeightY; + *MacroTileHeightC = 16 * *BlockHeight256BytesC; + if (*MacroTileHeightC == 0) { + *MacroTileWidthC = 0; + } else { + *MacroTileWidthC = 65536 / *BytePerPixelC / *MacroTileHeightC; + } + } else { + *MacroTileHeightY = 32 * *BlockHeight256BytesY; + *MacroTileWidthY = 65536 * 4 / *BytePerPixelY / *MacroTileHeightY; + *MacroTileHeightC = 32 * *BlockHeight256BytesC; + if (*MacroTileHeightC == 0) { + *MacroTileWidthC = 0; + } else { + *MacroTileWidthC = 65536 * 4 / *BytePerPixelC / *MacroTileHeightC; + } + } + } else { + unsigned int macro_tile_size_bytes_y = dml2_core_utils_get_tile_block_size_bytes(SurfaceTiling, *BytePerPixelY); + unsigned int macro_tile_size_bytes_c = dml2_core_utils_get_tile_block_size_bytes(SurfaceTiling, *BytePerPixelY); + unsigned int macro_tile_scale = 1; // macro tile to 256B req scaling + + if (SurfaceTiling == dml2_sw_linear) { + macro_tile_scale = 1; + } else if (SurfaceTiling == dml2_sw_4kb_2d) { + macro_tile_scale = 4; + } else if (SurfaceTiling == dml2_sw_64kb_2d) { + macro_tile_scale = 16; + } else if (SurfaceTiling == dml2_sw_256kb_2d) { + macro_tile_scale = 32; + } else { + DML_LOG_VERBOSE("ERROR: Invalid SurfaceTiling setting! val=%u\n", SurfaceTiling); + DML_ASSERT(0); + } + + *MacroTileHeightY = macro_tile_scale * *BlockHeight256BytesY; + *MacroTileWidthY = macro_tile_size_bytes_y / *BytePerPixelY / *MacroTileHeightY; + *MacroTileHeightC = macro_tile_scale * *BlockHeight256BytesC; + if (*MacroTileHeightC == 0) { + *MacroTileWidthC = 0; + } else { + *MacroTileWidthC = macro_tile_size_bytes_c / *BytePerPixelC / *MacroTileHeightC; + } + } + + DML_LOG_VERBOSE("DML::%s: MacroTileWidthY = %u\n", __func__, *MacroTileWidthY); + DML_LOG_VERBOSE("DML::%s: MacroTileHeightY = %u\n", __func__, *MacroTileHeightY); + DML_LOG_VERBOSE("DML::%s: MacroTileWidthC = %u\n", __func__, *MacroTileWidthC); + DML_LOG_VERBOSE("DML::%s: MacroTileHeightC = %u\n", __func__, *MacroTileHeightC); +} + +void dcn5_calculate_swath_width( + const struct dml2_display_cfg *display_cfg, + bool ForceSingleDPP, + unsigned int NumberOfActiveSurfaces, + enum dml2_odm_mode ODMMode[], + unsigned int BytePerPixY[], + unsigned int BytePerPixC[], + unsigned int Read256BytesBlockHeightY[], + unsigned int Read256BytesBlockHeightC[], + unsigned int Read256BytesBlockWidthY[], + unsigned int Read256BytesBlockWidthC[], + bool surf_linear128_l[], + bool surf_linear128_c[], + unsigned int DPPPerSurface[], + + // Output + unsigned int req_per_swath_ub_l[], + unsigned int req_per_swath_ub_c[], + unsigned int SwathWidthSingleDPPY[], + unsigned int SwathWidthSingleDPPC[], + unsigned int SwathWidthY[], // per-pipe + unsigned int SwathWidthC[], // per-pipe + unsigned int MaximumSwathHeightY[], + unsigned int MaximumSwathHeightC[], + unsigned int swath_width_luma_ub[], // per-pipe + unsigned int swath_width_chroma_ub[], // per-pipe + unsigned int swath_width_luma_ub_single_dpp[], + unsigned int swath_width_chroma_ub_single_dpp[]) +{ + (void)BytePerPixY; + enum dml2_odm_mode MainSurfaceODMMode; + double odm_hactive_factor = 1.0; + unsigned int req_width_horz_y; + unsigned int req_width_horz_c; + unsigned int surface_width_ub_l; + unsigned int surface_height_ub_l; + unsigned int surface_width_ub_c; + unsigned int surface_height_ub_c; + + DML_LOG_VERBOSE("DML::%s: ForceSingleDPP = %u\n", __func__, ForceSingleDPP); + DML_LOG_VERBOSE("DML::%s: NumberOfActiveSurfaces = %u\n", __func__, NumberOfActiveSurfaces); + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + if (!dml2_core_utils_is_vertical_rotation(display_cfg->plane_descriptors[k].composition.rotation_angle)) { + SwathWidthSingleDPPY[k] = (unsigned int)display_cfg->plane_descriptors[k].composition.viewport.plane0.width; + } else { + SwathWidthSingleDPPY[k] = (unsigned int)display_cfg->plane_descriptors[k].composition.viewport.plane0.height; + } + + DML_LOG_VERBOSE("DML::%s: k=%u ViewportWidth=%lu\n", __func__, k, display_cfg->plane_descriptors[k].composition.viewport.plane0.width); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportHeight=%lu\n", __func__, k, display_cfg->plane_descriptors[k].composition.viewport.plane0.height); + DML_LOG_VERBOSE("DML::%s: k=%u DPPPerSurface=%u\n", __func__, k, DPPPerSurface[k]); + + MainSurfaceODMMode = ODMMode[k]; + + if (ForceSingleDPP) { + SwathWidthY[k] = SwathWidthSingleDPPY[k]; + } else { + if (MainSurfaceODMMode == dml2_odm_mode_combine_4to1) + odm_hactive_factor = 4.0; + else if (MainSurfaceODMMode == dml2_odm_mode_combine_3to1) + odm_hactive_factor = 3.0; + else if (MainSurfaceODMMode == dml2_odm_mode_combine_2to1) + odm_hactive_factor = 2.0; + + if (MainSurfaceODMMode == dml2_odm_mode_combine_4to1 || MainSurfaceODMMode == dml2_odm_mode_combine_3to1 || MainSurfaceODMMode == dml2_odm_mode_combine_2to1) { + SwathWidthY[k] = (unsigned int)(math_min2((double)SwathWidthSingleDPPY[k], math_round((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_active / odm_hactive_factor * display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio))); + } else if (DPPPerSurface[k] == 2) { + SwathWidthY[k] = SwathWidthSingleDPPY[k] / 2; + } else { + SwathWidthY[k] = SwathWidthSingleDPPY[k]; + } + } + + DML_LOG_VERBOSE("DML::%s: k=%u HActive=%lu\n", __func__, k, display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_active); + DML_LOG_VERBOSE("DML::%s: k=%u HRatio=%f\n", __func__, k, display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u MainSurfaceODMMode=%u\n", __func__, k, MainSurfaceODMMode); + DML_LOG_VERBOSE("DML::%s: k=%u SwathWidthSingleDPPY=%u\n", __func__, k, SwathWidthSingleDPPY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u SwathWidthY=%u\n", __func__, k, SwathWidthY[k]); + + if (dml2_core_utils_is_420(display_cfg->plane_descriptors[k].pixel_format) || dml2_core_utils_is_422_planar(display_cfg->plane_descriptors[k].pixel_format)) { + SwathWidthC[k] = SwathWidthY[k] / 2; + SwathWidthSingleDPPC[k] = SwathWidthSingleDPPY[k] / 2; + } else { + SwathWidthC[k] = SwathWidthY[k]; + SwathWidthSingleDPPC[k] = SwathWidthSingleDPPY[k]; + } + + if (ForceSingleDPP == true) { + SwathWidthY[k] = SwathWidthSingleDPPY[k]; + SwathWidthC[k] = SwathWidthSingleDPPC[k]; + } + + req_width_horz_y = Read256BytesBlockWidthY[k]; + req_width_horz_c = Read256BytesBlockWidthC[k]; + + if (surf_linear128_l[k]) + req_width_horz_y = req_width_horz_y / 2; + + if (surf_linear128_c[k]) + req_width_horz_c = req_width_horz_c / 2; + + surface_width_ub_l = (unsigned int)math_ceil2((double)display_cfg->plane_descriptors[k].surface.plane0.width, req_width_horz_y); + surface_height_ub_l = (unsigned int)math_ceil2((double)display_cfg->plane_descriptors[k].surface.plane0.height, Read256BytesBlockHeightY[k]); + surface_width_ub_c = (unsigned int)math_ceil2((double)display_cfg->plane_descriptors[k].surface.plane1.width, req_width_horz_c); + surface_height_ub_c = (unsigned int)math_ceil2((double)display_cfg->plane_descriptors[k].surface.plane1.height, Read256BytesBlockHeightC[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u surface_width_ub_l=%u\n", __func__, k, surface_width_ub_l); + DML_LOG_VERBOSE("DML::%s: k=%u surface_height_ub_l=%u\n", __func__, k, surface_height_ub_l); + DML_LOG_VERBOSE("DML::%s: k=%u surface_width_ub_c=%u\n", __func__, k, surface_width_ub_c); + DML_LOG_VERBOSE("DML::%s: k=%u surface_height_ub_c=%u\n", __func__, k, surface_height_ub_c); + DML_LOG_VERBOSE("DML::%s: k=%u req_width_horz_y=%u\n", __func__, k, req_width_horz_y); + DML_LOG_VERBOSE("DML::%s: k=%u req_width_horz_c=%u\n", __func__, k, req_width_horz_c); + DML_LOG_VERBOSE("DML::%s: k=%u Read256BytesBlockWidthY=%u\n", __func__, k, Read256BytesBlockWidthY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u Read256BytesBlockHeightY=%u\n", __func__, k, Read256BytesBlockHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u Read256BytesBlockWidthC=%u\n", __func__, k, Read256BytesBlockWidthC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u Read256BytesBlockHeightC=%u\n", __func__, k, Read256BytesBlockHeightC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u req_width_horz_y=%u\n", __func__, k, req_width_horz_y); + DML_LOG_VERBOSE("DML::%s: k=%u req_width_horz_c=%u\n", __func__, k, req_width_horz_c); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportStationary=%u\n", __func__, k, display_cfg->plane_descriptors[k].composition.viewport.stationary); + DML_LOG_VERBOSE("DML::%s: k=%u DPPPerSurface=%u\n", __func__, k, DPPPerSurface[k]); + + req_per_swath_ub_l[k] = 0; + req_per_swath_ub_c[k] = 0; + if (!dml2_core_utils_is_vertical_rotation(display_cfg->plane_descriptors[k].composition.rotation_angle)) { + MaximumSwathHeightY[k] = Read256BytesBlockHeightY[k]; + MaximumSwathHeightC[k] = Read256BytesBlockHeightC[k]; + if (display_cfg->plane_descriptors[k].composition.viewport.stationary && DPPPerSurface[k] == 1) { + swath_width_luma_ub[k] = (unsigned int)(math_min2(surface_width_ub_l, + math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane0.x_start + + SwathWidthY[k] + req_width_horz_y - 1, req_width_horz_y) + - math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane0.x_start, req_width_horz_y))); + swath_width_luma_ub_single_dpp[k] = swath_width_luma_ub[k]; + } else { + swath_width_luma_ub[k] = (unsigned int)(math_min2(surface_width_ub_l, math_ceil2((double)SwathWidthY[k] - 1, + req_width_horz_y) + req_width_horz_y)); + swath_width_luma_ub_single_dpp[k] = (unsigned int)(math_min2(surface_width_ub_l, math_ceil2((double)SwathWidthSingleDPPY[k] - 1, + req_width_horz_y) + req_width_horz_y)); + } + req_per_swath_ub_l[k] = swath_width_luma_ub[k] / req_width_horz_y; + + if (BytePerPixC[k] > 0) { + if (display_cfg->plane_descriptors[k].composition.viewport.stationary && DPPPerSurface[k] == 1) { + swath_width_chroma_ub[k] = (unsigned int)(math_min2(surface_width_ub_c, + math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane1.y_start + + SwathWidthC[k] + req_width_horz_c - 1, req_width_horz_c) + - math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane1.y_start, req_width_horz_c))); + swath_width_chroma_ub_single_dpp[k] = swath_width_chroma_ub[k]; + } else { + swath_width_chroma_ub[k] = (unsigned int)(math_min2(surface_width_ub_c, math_ceil2((double)SwathWidthC[k] - 1, + req_width_horz_c) + req_width_horz_c)); + swath_width_chroma_ub_single_dpp[k] = (unsigned int)(math_min2(surface_width_ub_c, math_ceil2((double)SwathWidthSingleDPPC[k] - 1, + req_width_horz_c) + req_width_horz_c)); + } + req_per_swath_ub_c[k] = swath_width_chroma_ub[k] / req_width_horz_c; + } else { + swath_width_chroma_ub[k] = 0; + } + } else { + MaximumSwathHeightY[k] = Read256BytesBlockWidthY[k]; + MaximumSwathHeightC[k] = Read256BytesBlockWidthC[k]; + + if (display_cfg->plane_descriptors[k].composition.viewport.stationary && DPPPerSurface[k] == 1) { + swath_width_luma_ub[k] = (unsigned int)(math_min2(surface_height_ub_l, + math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane0.y_start + + SwathWidthY[k] + Read256BytesBlockHeightY[k] - 1, Read256BytesBlockHeightY[k]) + - math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane0.y_start, Read256BytesBlockHeightY[k]))); + swath_width_luma_ub_single_dpp[k] = swath_width_luma_ub[k]; + } else { + swath_width_luma_ub[k] = (unsigned int)(math_min2(surface_height_ub_l, math_ceil2((double)SwathWidthY[k] - 1, + Read256BytesBlockHeightY[k]) + Read256BytesBlockHeightY[k])); + swath_width_luma_ub_single_dpp[k] = (unsigned int)(math_min2(surface_height_ub_l, math_ceil2((double)SwathWidthSingleDPPY[k] - 1, + Read256BytesBlockHeightY[k]) + Read256BytesBlockHeightY[k])); + } + req_per_swath_ub_l[k] = swath_width_luma_ub[k] / Read256BytesBlockHeightY[k]; + if (BytePerPixC[k] > 0) { + if (display_cfg->plane_descriptors[k].composition.viewport.stationary && DPPPerSurface[k] == 1) { + swath_width_chroma_ub[k] = (unsigned int)(math_min2(surface_height_ub_c, + math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane1.y_start + + SwathWidthC[k] + Read256BytesBlockHeightC[k] - 1, Read256BytesBlockHeightC[k]) + - math_floor2(display_cfg->plane_descriptors[k].composition.viewport.plane1.y_start, Read256BytesBlockHeightC[k]))); + swath_width_chroma_ub_single_dpp[k] = swath_width_chroma_ub[k]; + } else { + swath_width_chroma_ub[k] = (unsigned int)(math_min2(surface_height_ub_c, math_ceil2((double)SwathWidthC[k] - 1, + Read256BytesBlockHeightC[k]) + Read256BytesBlockHeightC[k])); + swath_width_chroma_ub_single_dpp[k] = (unsigned int)(math_min2(surface_height_ub_c, math_ceil2((double)SwathWidthSingleDPPC[k] - 1, + Read256BytesBlockHeightC[k]) + Read256BytesBlockHeightC[k])); + } + req_per_swath_ub_c[k] = swath_width_chroma_ub[k] / Read256BytesBlockHeightC[k]; + } else { + swath_width_chroma_ub[k] = 0; + } + } + + DML_LOG_VERBOSE("DML::%s: k=%u swath_width_luma_ub=%u\n", __func__, k, swath_width_luma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u swath_width_chroma_ub=%u\n", __func__, k, swath_width_chroma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathHeightY=%u\n", __func__, k, MaximumSwathHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathHeightC=%u\n", __func__, k, MaximumSwathHeightC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u req_per_swath_ub_l=%u\n", __func__, k, req_per_swath_ub_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u req_per_swath_ub_c=%u\n", __func__, k, req_per_swath_ub_c[k]); + } +} + +static bool dcn5_is_unbounded_request(bool unb_req_force_en, bool unb_req_force_val, unsigned int TotalNumberOfActiveDPP, bool NoChromaOrLinear) +{ + bool unb_req_ok = false; + bool unb_req_en = false; + + unb_req_ok = (TotalNumberOfActiveDPP == 1 && NoChromaOrLinear); + unb_req_en = unb_req_ok; + + if (unb_req_force_en) { + unb_req_en = unb_req_force_val && unb_req_ok; + } + DML_LOG_VERBOSE("DML::%s: unb_req_force_en = %u\n", __func__, unb_req_force_en); + DML_LOG_VERBOSE("DML::%s: unb_req_force_val = %u\n", __func__, unb_req_force_val); + DML_LOG_VERBOSE("DML::%s: unb_req_ok = %u\n", __func__, unb_req_ok); + DML_LOG_VERBOSE("DML::%s: unb_req_en = %u\n", __func__, unb_req_en); + return unb_req_en; +} + +static void dcn5_calculate_det_buffer_size( + struct dml2_core_shared_CalculateDETBufferSize_locals *l, + const struct dml2_display_cfg *display_cfg, + bool ForceSingleDPP, + unsigned int NumberOfActiveSurfaces, + bool UnboundedRequestEnabled, + unsigned int nomDETInKByte, + unsigned int MaxTotalDETInKByte, + unsigned int ConfigReturnBufferSizeInKByte, + unsigned int MinCompressedBufferSizeInKByte, + unsigned int ConfigReturnBufferSegmentSizeInkByte, + unsigned int CompressedBufferSegmentSizeInkByte, + double ReadBandwidthLuma[], + double ReadBandwidthChroma[], + unsigned int full_swath_bytes_l[], + unsigned int full_swath_bytes_c[], + unsigned int DPPPerSurface[], + // Output + unsigned int DETBufferSizeInKByte[], + unsigned int *CompressedBufferSizeInkByte) +{ + memset(l, 0, sizeof(struct dml2_core_shared_CalculateDETBufferSize_locals)); + + bool DETPieceAssignedToThisSurfaceAlready[DML2_MAX_PLANES]; + bool NextPotentialSurfaceToAssignDETPieceFound; + bool MinimizeReallocationSuccess = false; + + DML_LOG_VERBOSE("DML::%s: ForceSingleDPP = %u\n", __func__, ForceSingleDPP); + DML_LOG_VERBOSE("DML::%s: nomDETInKByte = %u\n", __func__, nomDETInKByte); + DML_LOG_VERBOSE("DML::%s: NumberOfActiveSurfaces = %u\n", __func__, NumberOfActiveSurfaces); + DML_LOG_VERBOSE("DML::%s: UnboundedRequestEnabled = %u\n", __func__, UnboundedRequestEnabled); + DML_LOG_VERBOSE("DML::%s: MaxTotalDETInKByte = %u\n", __func__, MaxTotalDETInKByte); + DML_LOG_VERBOSE("DML::%s: ConfigReturnBufferSizeInKByte = %u\n", __func__, ConfigReturnBufferSizeInKByte); + DML_LOG_VERBOSE("DML::%s: MinCompressedBufferSizeInKByte = %u\n", __func__, MinCompressedBufferSizeInKByte); + DML_LOG_VERBOSE("DML::%s: CompressedBufferSegmentSizeInkByte = %u\n", __func__, CompressedBufferSegmentSizeInkByte); + DML_LOG_VERBOSE("DML::%s: ConfigReturnBufferSegmentSizeInkByte = %u\n", __func__, ConfigReturnBufferSegmentSizeInkByte); + + // Note: Will use default det size if that fits 2 swaths + if (UnboundedRequestEnabled) { + if (display_cfg->plane_descriptors[0].overrides.det_size_override_kb > 0) { + DETBufferSizeInKByte[0] = display_cfg->plane_descriptors[0].overrides.det_size_override_kb; + } else { + DETBufferSizeInKByte[0] = (unsigned int)math_max2(128.0, math_ceil2(2.0 * ((double)full_swath_bytes_l[0] + (double)full_swath_bytes_c[0]) / 1024.0, ConfigReturnBufferSegmentSizeInkByte)); + } + *CompressedBufferSizeInkByte = ConfigReturnBufferSizeInKByte - DETBufferSizeInKByte[0]; + } else { + l->DETBufferSizePoolInKByte = MaxTotalDETInKByte; + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + DETBufferSizeInKByte[k] = 0; + if (dml2_core_utils_is_420(display_cfg->plane_descriptors[k].pixel_format) || dml2_core_utils_is_422_planar(display_cfg->plane_descriptors[k].pixel_format)) { + l->max_minDET = nomDETInKByte - ConfigReturnBufferSegmentSizeInkByte; + } else { + l->max_minDET = nomDETInKByte; + } + l->minDET = 128; + l->minDET_pipe = 0; + + // add DET resource until can hold 2 full swaths + while (l->minDET <= l->max_minDET && l->minDET_pipe == 0) { + if (2.0 * ((double)full_swath_bytes_l[k] + (double)full_swath_bytes_c[k]) / 1024.0 <= l->minDET) + l->minDET_pipe = l->minDET; + l->minDET = l->minDET + ConfigReturnBufferSegmentSizeInkByte; + } + + DML_LOG_VERBOSE("DML::%s: k=%u minDET = %u\n", __func__, k, l->minDET); + DML_LOG_VERBOSE("DML::%s: k=%u max_minDET = %u\n", __func__, k, l->max_minDET); + DML_LOG_VERBOSE("DML::%s: k=%u minDET_pipe = %u\n", __func__, k, l->minDET_pipe); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_l = %u\n", __func__, k, full_swath_bytes_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_c = %u\n", __func__, k, full_swath_bytes_c[k]); + if (l->minDET_pipe == 0) { + l->minDET_pipe = (unsigned int)(math_max2(128, math_ceil2(((double)full_swath_bytes_l[k] + (double)full_swath_bytes_c[k]) / 1024.0, ConfigReturnBufferSegmentSizeInkByte))); + DML_LOG_VERBOSE("DML::%s: k=%u minDET_pipe = %u (assume each plane take half DET)\n", __func__, k, l->minDET_pipe); + } + + if (display_cfg->plane_descriptors[k].overrides.det_size_override_kb > 0) { + DETBufferSizeInKByte[k] = display_cfg->plane_descriptors[k].overrides.det_size_override_kb; + l->DETBufferSizePoolInKByte = l->DETBufferSizePoolInKByte - (ForceSingleDPP ? 1 : DPPPerSurface[k]) * display_cfg->plane_descriptors[k].overrides.det_size_override_kb; + } else if ((ForceSingleDPP ? 1 : DPPPerSurface[k]) * l->minDET_pipe <= l->DETBufferSizePoolInKByte) { + DETBufferSizeInKByte[k] = l->minDET_pipe; + l->DETBufferSizePoolInKByte = l->DETBufferSizePoolInKByte - (ForceSingleDPP ? 1 : DPPPerSurface[k]) * l->minDET_pipe; + } + + DML_LOG_VERBOSE("DML::%s: k=%u DPPPerSurface = %u\n", __func__, k, DPPPerSurface[k]); + DML_LOG_VERBOSE("DML::%s: k=%u DETSizeOverride = %u\n", __func__, k, display_cfg->plane_descriptors[k].overrides.det_size_override_kb); + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeInKByte = %u\n", __func__, k, DETBufferSizeInKByte[k]); + DML_LOG_VERBOSE("DML::%s: DETBufferSizePoolInKByte = %u\n", __func__, l->DETBufferSizePoolInKByte); + } + + if (display_cfg->minimize_det_reallocation) { + MinimizeReallocationSuccess = true; + // To minimize det reallocation, we don't distribute based on each surfaces bandwidth proportional to the global + // but rather distribute DET across streams proportionally based on pixel rate, and only distribute based on + // bandwidth between the planes on the same stream. This ensures that large scale re-distribution only on a + // stream count and/or pixel rate change, which is must less likely then general bandwidth changes per plane. + + // Calculate total pixel rate + for (unsigned int k = 0; k < display_cfg->num_streams; ++k) { + l->TotalPixelRate += display_cfg->stream_descriptors[k].timing.pixel_clock_khz; + } + + // Calculate per stream DET budget + for (unsigned int k = 0; k < display_cfg->num_streams; ++k) { + l->DETBudgetPerStream[k] = (unsigned int)((double) display_cfg->stream_descriptors[k].timing.pixel_clock_khz * MaxTotalDETInKByte / l->TotalPixelRate); + l->RemainingDETBudgetPerStream[k] = l->DETBudgetPerStream[k]; + } + + // Calculate the per stream total bandwidth + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + l->TotalBandwidthPerStream[display_cfg->plane_descriptors[k].stream_index] += (unsigned int)(ReadBandwidthLuma[k] + ReadBandwidthChroma[k]); + + // Check the minimum can be satisfied by budget + if (l->RemainingDETBudgetPerStream[display_cfg->plane_descriptors[k].stream_index] >= DETBufferSizeInKByte[k] * (ForceSingleDPP ? 1 : DPPPerSurface[k])) { + l->RemainingDETBudgetPerStream[display_cfg->plane_descriptors[k].stream_index] -= DETBufferSizeInKByte[k] * (ForceSingleDPP ? 1 : DPPPerSurface[k]); + } else { + MinimizeReallocationSuccess = false; + break; + } + } + + if (MinimizeReallocationSuccess) { + // Since a fixed budget per stream is sufficient to satisfy the minimums, just re-distribute each streams + // budget proportionally across its planes + l->ResidualDETAfterRounding = MaxTotalDETInKByte; + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + l->IdealDETBudget = (unsigned int)(((ReadBandwidthLuma[k] + ReadBandwidthChroma[k]) / l->TotalBandwidthPerStream[display_cfg->plane_descriptors[k].stream_index]) + * l->DETBudgetPerStream[display_cfg->plane_descriptors[k].stream_index]); + + if (l->IdealDETBudget > DETBufferSizeInKByte[k]) { + l->DeltaDETBudget = l->IdealDETBudget - DETBufferSizeInKByte[k]; + if (l->DeltaDETBudget > l->RemainingDETBudgetPerStream[display_cfg->plane_descriptors[k].stream_index]) + l->DeltaDETBudget = l->RemainingDETBudgetPerStream[display_cfg->plane_descriptors[k].stream_index]; + + /* split the additional budgeted DET among the pipes per plane */ + DETBufferSizeInKByte[k] += (unsigned int)((double)l->DeltaDETBudget / (ForceSingleDPP ? 1 : DPPPerSurface[k])); + l->RemainingDETBudgetPerStream[display_cfg->plane_descriptors[k].stream_index] -= l->DeltaDETBudget; + } + + // Round down to segment size + DETBufferSizeInKByte[k] = (DETBufferSizeInKByte[k] / ConfigReturnBufferSegmentSizeInkByte) * ConfigReturnBufferSegmentSizeInkByte; + + l->ResidualDETAfterRounding -= DETBufferSizeInKByte[k] * (ForceSingleDPP ? 1 : DPPPerSurface[k]); + } + } + } + + if (!MinimizeReallocationSuccess) { + l->TotalBandwidth = 0; + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + l->TotalBandwidth = l->TotalBandwidth + ReadBandwidthLuma[k] + ReadBandwidthChroma[k]; + } + DML_LOG_VERBOSE("DML::%s: --- Before bandwidth adjustment ---\n", __func__); + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeInKByte = %u\n", __func__, k, DETBufferSizeInKByte[k]); + } + DML_LOG_VERBOSE("DML::%s: --- DET allocation with bandwidth ---\n", __func__); + DML_LOG_VERBOSE("DML::%s: TotalBandwidth = %f\n", __func__, l->TotalBandwidth); + l->BandwidthOfSurfacesNotAssignedDETPiece = l->TotalBandwidth; + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + if (display_cfg->plane_descriptors[k].overrides.det_size_override_kb > 0 || (((double)(ForceSingleDPP ? 1 : DPPPerSurface[k]) * (double)DETBufferSizeInKByte[k] / (double)MaxTotalDETInKByte) >= ((ReadBandwidthLuma[k] + ReadBandwidthChroma[k]) / l->TotalBandwidth))) { + DETPieceAssignedToThisSurfaceAlready[k] = true; + l->BandwidthOfSurfacesNotAssignedDETPiece = l->BandwidthOfSurfacesNotAssignedDETPiece - ReadBandwidthLuma[k] - ReadBandwidthChroma[k]; + } else { + DETPieceAssignedToThisSurfaceAlready[k] = false; + } + DML_LOG_VERBOSE("DML::%s: k=%u DETPieceAssignedToThisSurfaceAlready = %u\n", __func__, k, DETPieceAssignedToThisSurfaceAlready[k]); + DML_LOG_VERBOSE("DML::%s: k=%u BandwidthOfSurfacesNotAssignedDETPiece = %f\n", __func__, k, l->BandwidthOfSurfacesNotAssignedDETPiece); + } + + for (unsigned int j = 0; j < NumberOfActiveSurfaces; ++j) { + NextPotentialSurfaceToAssignDETPieceFound = false; + l->NextSurfaceToAssignDETPiece = 0; + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, ReadBandwidthLuma[k] = %f\n", __func__, j, k, ReadBandwidthLuma[k]); + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, ReadBandwidthChroma[k] = %f\n", __func__, j, k, ReadBandwidthChroma[k]); + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, ReadBandwidthLuma[Next] = %f\n", __func__, j, k, ReadBandwidthLuma[l->NextSurfaceToAssignDETPiece]); + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, ReadBandwidthChroma[Next] = %f\n", __func__, j, k, ReadBandwidthChroma[l->NextSurfaceToAssignDETPiece]); + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, NextSurfaceToAssignDETPiece = %u\n", __func__, j, k, l->NextSurfaceToAssignDETPiece); + if (!DETPieceAssignedToThisSurfaceAlready[k] && (!NextPotentialSurfaceToAssignDETPieceFound || + ReadBandwidthLuma[k] + ReadBandwidthChroma[k] < ReadBandwidthLuma[l->NextSurfaceToAssignDETPiece] + ReadBandwidthChroma[l->NextSurfaceToAssignDETPiece])) { + l->NextSurfaceToAssignDETPiece = k; + NextPotentialSurfaceToAssignDETPieceFound = true; + } + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, DETPieceAssignedToThisSurfaceAlready = %u\n", __func__, j, k, DETPieceAssignedToThisSurfaceAlready[k]); + DML_LOG_VERBOSE("DML::%s: j=%u k=%u, NextPotentialSurfaceToAssignDETPieceFound = %u\n", __func__, j, k, NextPotentialSurfaceToAssignDETPieceFound); + } + + if (NextPotentialSurfaceToAssignDETPieceFound) { + l->NextDETBufferPieceInKByte = (unsigned int)(math_min2( + math_round((double)l->DETBufferSizePoolInKByte * (ReadBandwidthLuma[l->NextSurfaceToAssignDETPiece] + ReadBandwidthChroma[l->NextSurfaceToAssignDETPiece]) / l->BandwidthOfSurfacesNotAssignedDETPiece / + ((ForceSingleDPP ? 1 : DPPPerSurface[l->NextSurfaceToAssignDETPiece]) * ConfigReturnBufferSegmentSizeInkByte)) + * (ForceSingleDPP ? 1 : DPPPerSurface[l->NextSurfaceToAssignDETPiece]) * ConfigReturnBufferSegmentSizeInkByte, + math_floor2((double)l->DETBufferSizePoolInKByte, (ForceSingleDPP ? 1 : DPPPerSurface[l->NextSurfaceToAssignDETPiece]) * ConfigReturnBufferSegmentSizeInkByte))); + + DML_LOG_VERBOSE("DML::%s: j=%u, DETBufferSizePoolInKByte = %u\n", __func__, j, l->DETBufferSizePoolInKByte); + DML_LOG_VERBOSE("DML::%s: j=%u, NextSurfaceToAssignDETPiece = %u\n", __func__, j, l->NextSurfaceToAssignDETPiece); + DML_LOG_VERBOSE("DML::%s: j=%u, ReadBandwidthLuma[%u] = %f\n", __func__, j, l->NextSurfaceToAssignDETPiece, ReadBandwidthLuma[l->NextSurfaceToAssignDETPiece]); + DML_LOG_VERBOSE("DML::%s: j=%u, ReadBandwidthChroma[%u] = %f\n", __func__, j, l->NextSurfaceToAssignDETPiece, ReadBandwidthChroma[l->NextSurfaceToAssignDETPiece]); + DML_LOG_VERBOSE("DML::%s: j=%u, BandwidthOfSurfacesNotAssignedDETPiece = %f\n", __func__, j, l->BandwidthOfSurfacesNotAssignedDETPiece); + DML_LOG_VERBOSE("DML::%s: j=%u, NextDETBufferPieceInKByte = %u\n", __func__, j, l->NextDETBufferPieceInKByte); + DML_LOG_VERBOSE("DML::%s: j=%u, DETBufferSizeInKByte[%u] increases from %u ", __func__, j, l->NextSurfaceToAssignDETPiece, DETBufferSizeInKByte[l->NextSurfaceToAssignDETPiece]); + + DETBufferSizeInKByte[l->NextSurfaceToAssignDETPiece] = DETBufferSizeInKByte[l->NextSurfaceToAssignDETPiece] + l->NextDETBufferPieceInKByte / (ForceSingleDPP ? 1 : DPPPerSurface[l->NextSurfaceToAssignDETPiece]); + DML_LOG_VERBOSE("to %u\n", DETBufferSizeInKByte[l->NextSurfaceToAssignDETPiece]); + + l->DETBufferSizePoolInKByte = l->DETBufferSizePoolInKByte - l->NextDETBufferPieceInKByte; + DETPieceAssignedToThisSurfaceAlready[l->NextSurfaceToAssignDETPiece] = true; + l->BandwidthOfSurfacesNotAssignedDETPiece = l->BandwidthOfSurfacesNotAssignedDETPiece - (ReadBandwidthLuma[l->NextSurfaceToAssignDETPiece] + ReadBandwidthChroma[l->NextSurfaceToAssignDETPiece]); + } + } + } + *CompressedBufferSizeInkByte = MinCompressedBufferSizeInKByte; + } + *CompressedBufferSizeInkByte = *CompressedBufferSizeInkByte * CompressedBufferSegmentSizeInkByte / ConfigReturnBufferSegmentSizeInkByte; + + DML_LOG_VERBOSE("DML::%s: --- After bandwidth adjustment ---\n", __func__); + DML_LOG_VERBOSE("DML::%s: CompressedBufferSizeInkByte = %u\n", __func__, *CompressedBufferSizeInkByte); + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeInKByte = %u (TotalReadBandWidth=%f)\n", __func__, k, DETBufferSizeInKByte[k], ReadBandwidthLuma[k] + ReadBandwidthChroma[k]); + } +} + +void dcn5_calculate_swath_and_det_configuration(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *p) +{ + unsigned int MaximumSwathHeightY[DML2_MAX_PLANES] = { 0 }; + unsigned int MaximumSwathHeightC[DML2_MAX_PLANES] = { 0 }; + unsigned int RoundedUpSwathSizeBytesY[DML2_MAX_PLANES] = { 0 }; + unsigned int RoundedUpSwathSizeBytesC[DML2_MAX_PLANES] = { 0 }; + + unsigned int TotalActiveDPP = 0; + bool NoChromaOrLinear = true; + unsigned int SurfaceDoingUnboundedRequest = 0; + unsigned int DETBufferSizeInKByteForSwathCalculation; + + const long TTUFIFODEPTH = 8; + const long MAXIMUMCOMPRESSION = 4; + + DML_LOG_VERBOSE("DML::%s: ForceSingleDPP = %u\n", __func__, p->ForceSingleDPP); + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + DML_LOG_VERBOSE("DML::%s: DPPPerSurface[%u] = %u\n", __func__, k, p->DPPPerSurface[k]); + } + dcn5_calculate_swath_width( + p->display_cfg, + p->ForceSingleDPP, + p->NumberOfActiveSurfaces, + p->ODMMode, + p->BytePerPixY, + p->BytePerPixC, + p->Read256BytesBlockHeightY, + p->Read256BytesBlockHeightC, + p->Read256BytesBlockWidthY, + p->Read256BytesBlockWidthC, + p->surf_linear128_l, + p->surf_linear128_c, + p->DPPPerSurface, + + // Output + p->req_per_swath_ub_l, + p->req_per_swath_ub_c, + p->dummy[0], + p->dummy[1], + p->SwathWidth, + p->SwathWidthChroma, + MaximumSwathHeightY, + MaximumSwathHeightC, + p->swath_width_luma_ub, + p->swath_width_chroma_ub, + p->swath_width_luma_ub_single_dpp, + p->swath_width_chroma_ub_single_dpp); + + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + p->full_swath_bytes_single_dpp_l[k] = (unsigned int)(p->swath_width_luma_ub_single_dpp[k] * p->BytePerPixDETY[k] * MaximumSwathHeightY[k]); + p->full_swath_bytes_single_dpp_c[k] = (unsigned int)(p->swath_width_chroma_ub_single_dpp[k] * p->BytePerPixDETC[k] * MaximumSwathHeightC[k]); + p->full_swath_bytes_l[k] = (unsigned int)(p->swath_width_luma_ub[k] * p->BytePerPixDETY[k] * MaximumSwathHeightY[k]); + p->full_swath_bytes_c[k] = (unsigned int)(p->swath_width_chroma_ub[k] * p->BytePerPixDETC[k] * MaximumSwathHeightC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u DPPPerSurface = %u\n", __func__, k, p->DPPPerSurface[k]); + DML_LOG_VERBOSE("DML::%s: k=%u swath_width_luma_ub = %u\n", __func__, k, p->swath_width_luma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u BytePerPixDETY = %f\n", __func__, k, p->BytePerPixDETY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathHeightY = %u\n", __func__, k, MaximumSwathHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_l = %u\n", __func__, k, p->full_swath_bytes_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u swath_width_chroma_ub = %u\n", __func__, k, p->swath_width_chroma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u BytePerPixDETC = %f\n", __func__, k, p->BytePerPixDETC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathHeightC = %u\n", __func__, k, MaximumSwathHeightC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_c = %u\n", __func__, k, p->full_swath_bytes_c[k]); + if (p->display_cfg->plane_descriptors[k].pixel_format == dml2_420_10 + || p->display_cfg->plane_descriptors[k].pixel_format == dml2_422_planar_10 + || p->display_cfg->plane_descriptors[k].pixel_format == dml2_422_packed_10) { + p->full_swath_bytes_l[k] = (unsigned int)(math_ceil2((double)p->full_swath_bytes_l[k], 256)); + p->full_swath_bytes_c[k] = (unsigned int)(math_ceil2((double)p->full_swath_bytes_c[k], 256)); + p->full_swath_bytes_single_dpp_l[k] = (unsigned int)(math_ceil2((double)p->full_swath_bytes_single_dpp_l[k], 256)); + p->full_swath_bytes_single_dpp_c[k] = (unsigned int)(math_ceil2((double)p->full_swath_bytes_single_dpp_c[k], 256)); + } + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + TotalActiveDPP = TotalActiveDPP + (p->ForceSingleDPP ? 1 : p->DPPPerSurface[k]); + if (p->DPPPerSurface[k] > 0) + SurfaceDoingUnboundedRequest = k; + if (dml2_core_utils_is_420(p->display_cfg->plane_descriptors[k].pixel_format) + || dml2_core_utils_is_422_planar(p->display_cfg->plane_descriptors[k].pixel_format) + || p->display_cfg->plane_descriptors[k].pixel_format == dml2_rgbe_alpha + || dml2_core_utils_is_linear(p->display_cfg->plane_descriptors[k].surface.tiling)) { + NoChromaOrLinear = false; + } + } + + *p->UnboundedRequestEnabled = dcn5_is_unbounded_request(p->display_cfg->overrides.hw.force_unbounded_requesting.enable, p->display_cfg->overrides.hw.force_unbounded_requesting.value, TotalActiveDPP, NoChromaOrLinear); + + dcn5_calculate_det_buffer_size( + &scratch->CalculateDETBufferSize_locals, + p->display_cfg, + p->ForceSingleDPP, + p->NumberOfActiveSurfaces, + *p->UnboundedRequestEnabled, + p->nomDETInKByte, + p->MaxTotalDETInKByte, + p->ConfigReturnBufferSizeInKByte, + p->MinCompressedBufferSizeInKByte, + p->ConfigReturnBufferSegmentSizeInkByte, + p->CompressedBufferSegmentSizeInkByte, + p->ReadBandwidthLuma, + p->ReadBandwidthChroma, + p->full_swath_bytes_l, + p->full_swath_bytes_c, + p->DPPPerSurface, + + // Output + p->DETBufferSizeInKByte, // per hubp pipe + p->CompressedBufferSizeInkByte); + + DML_LOG_VERBOSE("DML::%s: TotalActiveDPP = %u\n", __func__, TotalActiveDPP); + DML_LOG_VERBOSE("DML::%s: nomDETInKByte = %u\n", __func__, p->nomDETInKByte); + DML_LOG_VERBOSE("DML::%s: ConfigReturnBufferSizeInKByte = %u\n", __func__, p->ConfigReturnBufferSizeInKByte); + DML_LOG_VERBOSE("DML::%s: UnboundedRequestEnabled = %u\n", __func__, *p->UnboundedRequestEnabled); + DML_LOG_VERBOSE("DML::%s: CompressedBufferSizeInkByte = %u\n", __func__, *p->CompressedBufferSizeInkByte); + + *p->ViewportSizeSupport = true; + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + + DETBufferSizeInKByteForSwathCalculation = p->DETBufferSizeInKByte[k]; + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeInKByteForSwathCalculation = %u\n", __func__, k, DETBufferSizeInKByteForSwathCalculation); + if (dml2_core_utils_is_linear(p->display_cfg->plane_descriptors[k].surface.tiling)) { + p->SwathHeightY[k] = MaximumSwathHeightY[k]; + p->SwathHeightC[k] = MaximumSwathHeightC[k]; + RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k]; + RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k]; + + if (p->surf_linear128_l[k]) + p->request_size_bytes_luma[k] = 128; + else + p->request_size_bytes_luma[k] = 256; + + if (p->surf_linear128_c[k]) + p->request_size_bytes_chroma[k] = 128; + else + p->request_size_bytes_chroma[k] = 256; + + } else if (p->full_swath_bytes_l[k] + p->full_swath_bytes_c[k] <= DETBufferSizeInKByteForSwathCalculation * 1024 / 2) { + p->SwathHeightY[k] = MaximumSwathHeightY[k]; + p->SwathHeightC[k] = MaximumSwathHeightC[k]; + RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k]; + RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k]; + p->request_size_bytes_luma[k] = 256; + p->request_size_bytes_chroma[k] = 256; + + } else if (p->full_swath_bytes_l[k] >= 1.5 * p->full_swath_bytes_c[k] && p->full_swath_bytes_l[k] / 2 + p->full_swath_bytes_c[k] <= DETBufferSizeInKByteForSwathCalculation * 1024 / 2) { + p->SwathHeightY[k] = MaximumSwathHeightY[k] / 2; + p->SwathHeightC[k] = MaximumSwathHeightC[k]; + RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k] / 2; + RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k]; + p->request_size_bytes_luma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixY[k], p->display_cfg->plane_descriptors[k].surface.tiling) + == dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128; + p->request_size_bytes_chroma[k] = 256; + + } else if (p->full_swath_bytes_l[k] < 1.5 * p->full_swath_bytes_c[k] && p->full_swath_bytes_l[k] + p->full_swath_bytes_c[k] / 2 <= DETBufferSizeInKByteForSwathCalculation * 1024 / 2) { + p->SwathHeightY[k] = MaximumSwathHeightY[k]; + p->SwathHeightC[k] = MaximumSwathHeightC[k] / 2; + RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k]; + RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k] / 2; + p->request_size_bytes_luma[k] = 256; + p->request_size_bytes_chroma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixC[k], p->display_cfg->plane_descriptors[k].surface.tiling) + == dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128; + + } else { + p->SwathHeightY[k] = MaximumSwathHeightY[k] / 2; + p->SwathHeightC[k] = MaximumSwathHeightC[k] / 2; + RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k] / 2; + RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k] / 2; + p->request_size_bytes_luma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixY[k], p->display_cfg->plane_descriptors[k].surface.tiling) + == dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128; + p->request_size_bytes_chroma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixC[k], p->display_cfg->plane_descriptors[k].surface.tiling) + == dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128; + } + + if (p->SwathHeightC[k] == 0) + p->request_size_bytes_chroma[k] = 0; + + if ((p->full_swath_bytes_l[k] / 2 + p->full_swath_bytes_c[k] / 2 > DETBufferSizeInKByteForSwathCalculation * 1024 / 2) || + p->SwathWidth[k] > p->MaximumSwathWidthLuma[k] || (p->SwathHeightC[k] > 0 && p->SwathWidthChroma[k] > p->MaximumSwathWidthChroma[k])) { + *p->ViewportSizeSupport = false; + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_l=%u\n", __func__, k, p->full_swath_bytes_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_c=%u\n", __func__, k, p->full_swath_bytes_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeInKByteForSwathCalculation=%u\n", __func__, k, DETBufferSizeInKByteForSwathCalculation); + DML_LOG_VERBOSE("DML::%s: k=%u SwathWidth=%u\n", __func__, k, p->SwathWidth[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthLuma=%f\n", __func__, k, p->MaximumSwathWidthLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u SwathWidthChroma=%d\n", __func__, k, p->SwathWidthChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthChroma=%f\n", __func__, k, p->MaximumSwathWidthChroma[k]); + p->ViewportSizeSupportPerSurface[k] = false; + } else { + p->ViewportSizeSupportPerSurface[k] = true; + } + + if (p->SwathHeightC[k] == 0) { + DML_LOG_VERBOSE("DML::%s: k=%u, All DET will be used for plane0\n", __func__, k); + p->DETBufferSizeY[k] = p->DETBufferSizeInKByte[k] * 1024; + p->DETBufferSizeC[k] = 0; + } else if (RoundedUpSwathSizeBytesY[k] <= 1.5 * RoundedUpSwathSizeBytesC[k]) { + DML_LOG_VERBOSE("DML::%s: k=%u, Half DET will be used for plane0, and half for plane1\n", __func__, k); + p->DETBufferSizeY[k] = p->DETBufferSizeInKByte[k] * 1024 / 2; + p->DETBufferSizeC[k] = p->DETBufferSizeInKByte[k] * 1024 / 2; + } else { + DML_LOG_VERBOSE("DML::%s: k=%u, 2/3 DET will be used for plane0, and 1/3 for plane1\n", __func__, k); + p->DETBufferSizeY[k] = (unsigned int)(math_floor2(p->DETBufferSizeInKByte[k] * 1024 * 2 / 3, 1024)); + p->DETBufferSizeC[k] = p->DETBufferSizeInKByte[k] * 1024 - p->DETBufferSizeY[k]; + } + + DML_LOG_VERBOSE("DML::%s: k=%u SwathHeightY = %u\n", __func__, k, p->SwathHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u SwathHeightC = %u\n", __func__, k, p->SwathHeightC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_l = %u\n", __func__, k, p->full_swath_bytes_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u full_swath_bytes_c = %u\n", __func__, k, p->full_swath_bytes_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%u RoundedUpSwathSizeBytesY = %u\n", __func__, k, RoundedUpSwathSizeBytesY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u RoundedUpSwathSizeBytesC = %u\n", __func__, k, RoundedUpSwathSizeBytesC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeInKByte = %u\n", __func__, k, p->DETBufferSizeInKByte[k]); + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeY = %u\n", __func__, k, p->DETBufferSizeY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u DETBufferSizeC = %u\n", __func__, k, p->DETBufferSizeC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportSizeSupportPerSurface = %u\n", __func__, k, p->ViewportSizeSupportPerSurface[k]); + + } + + *p->compbuf_reserved_space_64b = 2 * p->pixel_chunk_size_kbytes * 1024 / 64; + if (*p->UnboundedRequestEnabled) { + *p->compbuf_reserved_space_64b = (unsigned int)math_ceil2(math_max2(*p->compbuf_reserved_space_64b, + (double)(p->rob_buffer_size_kbytes * 1024 / 64) - (double)(RoundedUpSwathSizeBytesY[SurfaceDoingUnboundedRequest] * TTUFIFODEPTH / (p->mrq_present ? MAXIMUMCOMPRESSION : 1) / 64)), 1.0); + DML_LOG_VERBOSE("DML::%s: RoundedUpSwathSizeBytesY[%d] = %u\n", __func__, SurfaceDoingUnboundedRequest, RoundedUpSwathSizeBytesY[SurfaceDoingUnboundedRequest]); + DML_LOG_VERBOSE("DML::%s: rob_buffer_size_kbytes = %u\n", __func__, p->rob_buffer_size_kbytes); + } + DML_LOG_VERBOSE("DML::%s: compbuf_reserved_space_64b = %u\n", __func__, *p->compbuf_reserved_space_64b); + + *p->hw_debug5 = false; + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (!(p->mrq_present) && (!p->UnboundedRequestEnabled) && (TotalActiveDPP == 1) + && p->display_cfg->plane_descriptors[k].surface.dcc.enable + && ((p->rob_buffer_size_kbytes * 1024 * (p->mrq_present ? MAXIMUMCOMPRESSION : 1) + + *p->CompressedBufferSizeInkByte * MAXIMUMCOMPRESSION * 1024) > TTUFIFODEPTH * (RoundedUpSwathSizeBytesY[k] + RoundedUpSwathSizeBytesC[k]))) + *p->hw_debug5 = true; + DML_LOG_VERBOSE("DML::%s: k=%u UnboundedRequestEnabled = %u\n", __func__, k, *p->UnboundedRequestEnabled); + DML_LOG_VERBOSE("DML::%s: k=%u MAXIMUMCOMPRESSION = %lu\n", __func__, k, MAXIMUMCOMPRESSION); + DML_LOG_VERBOSE("DML::%s: k=%u TTUFIFODEPTH = %lu\n", __func__, k, TTUFIFODEPTH); + DML_LOG_VERBOSE("DML::%s: k=%u CompressedBufferSizeInkByte = %u\n", __func__, k, *p->CompressedBufferSizeInkByte); + DML_LOG_VERBOSE("DML::%s: k=%u RoundedUpSwathSizeBytesC = %u\n", __func__, k, RoundedUpSwathSizeBytesC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u hw_debug5 = %u\n", __func__, k, *p->hw_debug5); + } +} + +static unsigned int dcn5_calculate_host_vm_dynamic_levels( + bool GPUVMEnable, + bool HostVMEnable, + unsigned int HostVMMinPageSize, + unsigned int HostVMMaxNonCachedPageTableLevels) +{ + unsigned int HostVMDynamicLevels = 0; + + if (GPUVMEnable && HostVMEnable) { + if (HostVMMinPageSize < 2048) + HostVMDynamicLevels = HostVMMaxNonCachedPageTableLevels; + else if (HostVMMinPageSize >= 2048 && HostVMMinPageSize < 1048576) + HostVMDynamicLevels = (unsigned int)math_max2(0, (double)HostVMMaxNonCachedPageTableLevels - 1); + else + HostVMDynamicLevels = (unsigned int)math_max2(0, (double)HostVMMaxNonCachedPageTableLevels - 2); + } else { + HostVMDynamicLevels = 0; + } + return HostVMDynamicLevels; +} + +static unsigned int dcn5_calculate_prefetch_source_lines( + double VRatio, + unsigned int VTaps, + bool UPSPEnabled, + unsigned int UPSPVTaps, + enum dml2_sample_positioning UPSPSamplePositioning, + bool PixelFormatIs420, + bool Interlace, + bool ProgressiveToInterlaceUnitInOPP, + unsigned int SwathHeight, + enum dml2_rotation_angle RotationAngle, + bool mirrored, + bool ViewportStationary, + unsigned int SwathWidth, + unsigned int ViewportHeight, + unsigned int ViewportXStart, + unsigned int ViewportYStart, + + // Output + unsigned int *VInitPreFill, + unsigned int *MaxNumSwath) +{ + + unsigned int vp_start_rot = 0; + unsigned int sw0_tmp = 0; + unsigned int MaxPartialSwath = 0; + unsigned int VInitPreFillUPSP = 0; + unsigned int VInitPreFillDSCL = 0; + const float UPSPVratio = 0.5; + double numLines = 0; + + DML_LOG_VERBOSE("DML::%s: VRatio = %f\n", __func__, VRatio); + DML_LOG_VERBOSE("DML::%s: VTaps = %u\n", __func__, VTaps); + DML_LOG_VERBOSE("DML::%s: ViewportXStart = %u\n", __func__, ViewportXStart); + DML_LOG_VERBOSE("DML::%s: ViewportYStart = %u\n", __func__, ViewportYStart); + DML_LOG_VERBOSE("DML::%s: ViewportStationary = %u\n", __func__, ViewportStationary); + DML_LOG_VERBOSE("DML::%s: SwathHeight = %u\n", __func__, SwathHeight); + + if (UPSPEnabled && PixelFormatIs420) { + //VRatio = (DSCL Vratio)/2 + VInitPreFillUPSP = (unsigned int)(math_floor2((UPSPVTaps + UPSPVratio + 1) / 2.0 + ((UPSPSamplePositioning == dml2_cosited) ? 0.25 : 0), 1)); + VInitPreFillDSCL = (unsigned int)(math_floor2((2 * VRatio + (double)VTaps + 1) / 2.0, 1)); // DSCL vratio is 2 * Vratio, so the total Vratio does not change + *VInitPreFill = (unsigned int)(math_floor2(VInitPreFillUPSP + (VInitPreFillDSCL - 1) * UPSPVratio, 1)); + } else if (ProgressiveToInterlaceUnitInOPP) { + *VInitPreFill = (unsigned int)(math_floor2((VRatio + (double)VTaps + 1) / 2.0, 1)); + } else { + *VInitPreFill = (unsigned int)(math_floor2((VRatio + (double)VTaps + 1 + (Interlace ? 1 : 0) * 0.5 * VRatio) / 2.0, 1)); + } + + if (ViewportStationary) { + if (RotationAngle == dml2_rotation_180) { + vp_start_rot = SwathHeight - (((unsigned int)(ViewportYStart + ViewportHeight - 1) % SwathHeight) + 1); + } else if ((RotationAngle == dml2_rotation_270 && !mirrored) || (RotationAngle == dml2_rotation_90 && mirrored)) { + vp_start_rot = ViewportXStart; + } else if ((RotationAngle == dml2_rotation_90 && !mirrored) || (RotationAngle == dml2_rotation_270 && mirrored)) { + vp_start_rot = SwathHeight - (((unsigned int)(ViewportYStart + SwathWidth - 1) % SwathHeight) + 1); + } else { + vp_start_rot = ViewportYStart; + } + sw0_tmp = SwathHeight - (vp_start_rot % SwathHeight); + if (sw0_tmp < *VInitPreFill) { + *MaxNumSwath = (unsigned int)(math_ceil2((*VInitPreFill - sw0_tmp) / (double)SwathHeight, 1) + 1); + } else { + *MaxNumSwath = 1; + } + MaxPartialSwath = (unsigned int)(math_max2(1, (unsigned int)(vp_start_rot + *VInitPreFill - 1) % SwathHeight)); + } else { + *MaxNumSwath = (unsigned int)(math_ceil2((*VInitPreFill - 1.0) / (double)SwathHeight, 1) + 1); + if (*VInitPreFill > 1) { + MaxPartialSwath = (unsigned int)(math_max2(1, (unsigned int)(*VInitPreFill - 2) % SwathHeight)); + } else { + MaxPartialSwath = (unsigned int)(math_max2(1, (unsigned int)(*VInitPreFill + SwathHeight - 2) % SwathHeight)); + } + } + numLines = *MaxNumSwath * SwathHeight + MaxPartialSwath; + + DML_LOG_VERBOSE("DML::%s: vp_start_rot = %u\n", __func__, vp_start_rot); + DML_LOG_VERBOSE("DML::%s: VInitPreFill = %u\n", __func__, *VInitPreFill); + DML_LOG_VERBOSE("DML::%s: MaxPartialSwath = %u\n", __func__, MaxPartialSwath); + DML_LOG_VERBOSE("DML::%s: MaxNumSwath = %u\n", __func__, *MaxNumSwath); + DML_LOG_VERBOSE("DML::%s: Prefetch source lines = %3.2f\n", __func__, numLines); + return (unsigned int)(numLines); + +} + +static void dcn5_calculate_row_bandwidth( + bool GPUVMEnable, + bool use_one_row_for_frame, + enum dml2_source_format_class SourcePixelFormat, + double VRatio, + double VRatioChroma, + bool DCCEnable, + double LineTime, + unsigned int PixelPTEBytesPerRowLuma, + unsigned int PixelPTEBytesPerRowChroma, + unsigned int dpte_row_height_luma, + unsigned int dpte_row_height_chroma, + + bool mrq_present, + unsigned int meta_row_bytes_per_row_ub_l, + unsigned int meta_row_bytes_per_row_ub_c, + unsigned int meta_row_height_luma, + unsigned int meta_row_height_chroma, + + // Output + double *dpte_row_bw, + double *meta_row_bw) +{ + if (!DCCEnable || !mrq_present) { + *meta_row_bw = 0; + } else if (dml2_core_utils_is_420(SourcePixelFormat) || dml2_core_utils_is_422_planar(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha) { + *meta_row_bw = VRatio * meta_row_bytes_per_row_ub_l / (meta_row_height_luma * LineTime) + + VRatioChroma * meta_row_bytes_per_row_ub_c / (meta_row_height_chroma * LineTime); + } else { + *meta_row_bw = VRatio * meta_row_bytes_per_row_ub_l / (meta_row_height_luma * LineTime); + } + + if (GPUVMEnable != true || use_one_row_for_frame) { + *dpte_row_bw = 0; + } else if (dml2_core_utils_is_420(SourcePixelFormat) || dml2_core_utils_is_422_planar(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha) { + *dpte_row_bw = VRatio * PixelPTEBytesPerRowLuma / (dpte_row_height_luma * LineTime) + + VRatioChroma * PixelPTEBytesPerRowChroma / (dpte_row_height_chroma * LineTime); + } else { + *dpte_row_bw = VRatio * PixelPTEBytesPerRowLuma / (dpte_row_height_luma * LineTime); + } +} + +unsigned int dcn5_calculate_vm_and_row_bytes(struct dml2_core_shared_calculate_vm_and_row_bytes_params *p) +{ + unsigned int extra_dpde_bytes; + unsigned int extra_mpde_bytes; + unsigned int MacroTileSizeBytes; + unsigned int vp_height_dpte_ub; + + unsigned int meta_surface_bytes; + unsigned int vm_bytes; + unsigned int vp_height_meta_ub; + unsigned int PixelPTEReqWidth_linear = 0; // VBA_DELTA. VBA doesn't calculate this + + *p->MetaRequestHeight = 8 * p->BlockHeight256Bytes; + *p->MetaRequestWidth = 8 * p->BlockWidth256Bytes; + if (dml2_core_utils_is_linear(p->SurfaceTiling)) { + *p->meta_row_height = 32; + *p->meta_row_width = (unsigned int)(math_floor2(p->ViewportXStart + p->SwathWidth + *p->MetaRequestWidth - 1, *p->MetaRequestWidth) - math_floor2(p->ViewportXStart, *p->MetaRequestWidth)); + *p->meta_row_bytes = (unsigned int)(*p->meta_row_width * *p->MetaRequestHeight * p->BytePerPixel / 256.0); // FIXME_DCN4SW missing in old code but no dcc for linear anyways? + } else if (!dml2_core_utils_is_vertical_rotation(p->RotationAngle)) { + *p->meta_row_height = *p->MetaRequestHeight; + if (p->ViewportStationary && p->NumberOfDPPs == 1) { + *p->meta_row_width = (unsigned int)(math_floor2(p->ViewportXStart + p->SwathWidth + *p->MetaRequestWidth - 1, *p->MetaRequestWidth) - math_floor2(p->ViewportXStart, *p->MetaRequestWidth)); + } else { + *p->meta_row_width = (unsigned int)(math_ceil2(p->SwathWidth - 1, *p->MetaRequestWidth) + *p->MetaRequestWidth); + } + *p->meta_row_bytes = (unsigned int)(*p->meta_row_width * *p->MetaRequestHeight * p->BytePerPixel / 256.0); + } else { + *p->meta_row_height = *p->MetaRequestWidth; + if (p->ViewportStationary && p->NumberOfDPPs == 1) { + *p->meta_row_width = (unsigned int)(math_floor2(p->ViewportYStart + p->ViewportHeight + *p->MetaRequestHeight - 1, *p->MetaRequestHeight) - math_floor2(p->ViewportYStart, *p->MetaRequestHeight)); + } else { + *p->meta_row_width = (unsigned int)(math_ceil2(p->SwathWidth - 1, *p->MetaRequestHeight) + *p->MetaRequestHeight); + } + *p->meta_row_bytes = (unsigned int)(*p->meta_row_width * *p->MetaRequestWidth * p->BytePerPixel / 256.0); + } + + if (p->ViewportStationary && p->is_phantom && (p->NumberOfDPPs == 1 || !dml2_core_utils_is_vertical_rotation(p->RotationAngle))) { + vp_height_meta_ub = (unsigned int)(math_floor2(p->ViewportYStart + p->ViewportHeight + 64 * p->BlockHeight256Bytes - 1, 64 * p->BlockHeight256Bytes) - math_floor2(p->ViewportYStart, 64 * p->BlockHeight256Bytes)); + } else if (!dml2_core_utils_is_vertical_rotation(p->RotationAngle)) { + vp_height_meta_ub = (unsigned int)(math_ceil2(p->ViewportHeight - 1, 64 * p->BlockHeight256Bytes) + 64 * p->BlockHeight256Bytes); + } else { + vp_height_meta_ub = (unsigned int)(math_ceil2(p->SwathWidth - 1, 64 * p->BlockHeight256Bytes) + 64 * p->BlockHeight256Bytes); + } + + meta_surface_bytes = (unsigned int)(p->DCCMetaPitch * vp_height_meta_ub * p->BytePerPixel / 256.0); + DML_LOG_VERBOSE("DML::%s: DCCMetaPitch = %u\n", __func__, p->DCCMetaPitch); + DML_LOG_VERBOSE("DML::%s: meta_surface_bytes = %u\n", __func__, meta_surface_bytes); + if (p->GPUVMEnable == true) { + double meta_vmpg_bytes = 4.0 * 1024.0; + *p->meta_pte_bytes_per_frame_ub = (unsigned int)((math_ceil2((double) (meta_surface_bytes - meta_vmpg_bytes) / (8 * meta_vmpg_bytes), 1) + 1) * 64); + extra_mpde_bytes = 128 * (p->GPUVMMaxPageTableLevels - 1); + } else { + *p->meta_pte_bytes_per_frame_ub = 0; + extra_mpde_bytes = 0; + } + + if (!p->DCCEnable || !p->mrq_present) { + *p->meta_pte_bytes_per_frame_ub = 0; + extra_mpde_bytes = 0; + *p->meta_row_bytes = 0; + } + + if (!p->GPUVMEnable) { + *p->PixelPTEBytesPerRow = 0; + *p->PixelPTEBytesPerRowStorage = 0; + *p->dpte_row_width_ub = 0; + *p->dpte_row_height = 0; + *p->dpte_row_height_linear = 0; + *p->PixelPTEBytesPerRow_one_row_per_frame = 0; + *p->dpte_row_width_ub_one_row_per_frame = 0; + *p->dpte_row_height_one_row_per_frame = 0; + *p->vmpg_width = 0; + *p->vmpg_height = 0; + *p->PixelPTEReqWidth = 0; + *p->PixelPTEReqHeight = 0; + *p->PTERequestSize = 0; + *p->dpde0_bytes_per_frame_ub = 0; + return 0; + } + + MacroTileSizeBytes = p->MacroTileWidth * p->BytePerPixel * p->MacroTileHeight; + + if (p->ViewportStationary && p->is_phantom && (p->NumberOfDPPs == 1 || !dml2_core_utils_is_vertical_rotation(p->RotationAngle))) { + vp_height_dpte_ub = (unsigned int)(math_floor2(p->ViewportYStart + p->ViewportHeight + p->MacroTileHeight - 1, p->MacroTileHeight) - math_floor2(p->ViewportYStart, p->MacroTileHeight)); + } else if (!dml2_core_utils_is_vertical_rotation(p->RotationAngle)) { + vp_height_dpte_ub = (unsigned int)(math_ceil2((double)p->ViewportHeight - 1, p->MacroTileHeight) + p->MacroTileHeight); + } else { + vp_height_dpte_ub = (unsigned int)(math_ceil2((double)p->SwathWidth - 1, p->MacroTileHeight) + p->MacroTileHeight); + } + + unsigned int pixel_per_element = dml2_core_utils_is_422_packed(p->SourcePixelFormat) ? 2 : 1; + if (p->GPUVMEnable == true && p->GPUVMMaxPageTableLevels > 1) { + *p->dpde0_bytes_per_frame_ub = (unsigned int)(64 * (math_ceil2((double)(p->Pitch * pixel_per_element * vp_height_dpte_ub * p->BytePerPixel - MacroTileSizeBytes) / (double)(8 * 2097152), 1) + 1)); + extra_dpde_bytes = 128 * (p->GPUVMMaxPageTableLevels - 2); + } else { + *p->dpde0_bytes_per_frame_ub = 0; + extra_dpde_bytes = 0; + } + + vm_bytes = *p->meta_pte_bytes_per_frame_ub + extra_mpde_bytes + *p->dpde0_bytes_per_frame_ub + extra_dpde_bytes; + + DML_LOG_VERBOSE("DML::%s: DCCEnable = %u\n", __func__, p->DCCEnable); + DML_LOG_VERBOSE("DML::%s: GPUVMEnable = %u\n", __func__, p->GPUVMEnable); + DML_LOG_VERBOSE("DML::%s: SwModeLinear = %u\n", __func__, p->SurfaceTiling == dml2_sw_linear); + DML_LOG_VERBOSE("DML::%s: BytePerPixel = %u\n", __func__, p->BytePerPixel); + DML_LOG_VERBOSE("DML::%s: GPUVMMaxPageTableLevels = %u\n", __func__, p->GPUVMMaxPageTableLevels); + DML_LOG_VERBOSE("DML::%s: BlockHeight256Bytes = %u\n", __func__, p->BlockHeight256Bytes); + DML_LOG_VERBOSE("DML::%s: BlockWidth256Bytes = %u\n", __func__, p->BlockWidth256Bytes); + DML_LOG_VERBOSE("DML::%s: MacroTileHeight = %u\n", __func__, p->MacroTileHeight); + DML_LOG_VERBOSE("DML::%s: MacroTileWidth = %u\n", __func__, p->MacroTileWidth); + DML_LOG_VERBOSE("DML::%s: meta_pte_bytes_per_frame_ub = %u\n", __func__, *p->meta_pte_bytes_per_frame_ub); + DML_LOG_VERBOSE("DML::%s: dpde0_bytes_per_frame_ub = %u\n", __func__, *p->dpde0_bytes_per_frame_ub); + DML_LOG_VERBOSE("DML::%s: extra_mpde_bytes = %u\n", __func__, extra_mpde_bytes); + DML_LOG_VERBOSE("DML::%s: extra_dpde_bytes = %u\n", __func__, extra_dpde_bytes); + DML_LOG_VERBOSE("DML::%s: vm_bytes = %u\n", __func__, vm_bytes); + DML_LOG_VERBOSE("DML::%s: ViewportHeight = %u\n", __func__, p->ViewportHeight); + DML_LOG_VERBOSE("DML::%s: SwathWidth = %u\n", __func__, p->SwathWidth); + DML_LOG_VERBOSE("DML::%s: vp_height_dpte_ub = %u\n", __func__, vp_height_dpte_ub); + + if (dml2_core_utils_is_linear(p->SurfaceTiling)) { + *p->PixelPTEReqHeight = 1; + *p->PixelPTEReqWidth = p->GPUVMMinPageSizeKBytes * 1024 * 8 / p->BytePerPixel; + PixelPTEReqWidth_linear = p->GPUVMMinPageSizeKBytes * 1024 * 8 / p->BytePerPixel; + *p->PTERequestSize = 64; + + *p->vmpg_height = 1; + *p->vmpg_width = p->GPUVMMinPageSizeKBytes * 1024 / p->BytePerPixel; + } else if (p->GPUVMMinPageSizeKBytes * 1024 >= dml2_core_utils_get_tile_block_size_bytes(p->SurfaceTiling, p->BytePerPixel)) { // 1 64B 8x1 PTE + *p->PixelPTEReqHeight = p->MacroTileHeight; + *p->PixelPTEReqWidth = 8 * 1024 * p->GPUVMMinPageSizeKBytes / (p->MacroTileHeight * p->BytePerPixel); + *p->PTERequestSize = 64; + + *p->vmpg_height = p->MacroTileHeight; + *p->vmpg_width = 1024 * p->GPUVMMinPageSizeKBytes / (p->MacroTileHeight * p->BytePerPixel); + + } else if (p->GPUVMMinPageSizeKBytes == 4 && dml2_core_utils_get_tile_block_size_bytes(p->SurfaceTiling, p->BytePerPixel) == 65536) { // 2 64B PTE requests to get 16 PTEs to cover the 64K tile + // one 64KB tile, is 16x16x256B req + *p->PixelPTEReqHeight = 16 * p->BlockHeight256Bytes; + *p->PixelPTEReqWidth = 16 * p->BlockWidth256Bytes; + *p->PTERequestSize = 128; + + *p->vmpg_height = *p->PixelPTEReqHeight; + *p->vmpg_width = *p->PixelPTEReqWidth; + } else { + // default for rest of calculation to go through, when vm is disable, the calulated pte related values shouldnt be used anyways + *p->PixelPTEReqHeight = p->MacroTileHeight; + *p->PixelPTEReqWidth = 8 * 1024 * p->GPUVMMinPageSizeKBytes / (p->MacroTileHeight * p->BytePerPixel); + *p->PTERequestSize = 64; + + *p->vmpg_height = p->MacroTileHeight; + *p->vmpg_width = 1024 * p->GPUVMMinPageSizeKBytes / (p->MacroTileHeight * p->BytePerPixel); + + if (p->GPUVMEnable == true) { + DML_LOG_VERBOSE("DML::%s: GPUVMMinPageSizeKBytes=%u and sw_mode=%u (tile_size=%d) not supported!\n", + __func__, p->GPUVMMinPageSizeKBytes, p->SurfaceTiling, dml2_core_utils_get_tile_block_size_bytes(p->SurfaceTiling, p->BytePerPixel)); + DML_ASSERT(0); + } + } + + DML_LOG_VERBOSE("DML::%s: GPUVMMinPageSizeKBytes = %u\n", __func__, p->GPUVMMinPageSizeKBytes); + DML_LOG_VERBOSE("DML::%s: PixelPTEReqHeight = %u\n", __func__, *p->PixelPTEReqHeight); + DML_LOG_VERBOSE("DML::%s: PixelPTEReqWidth = %u\n", __func__, *p->PixelPTEReqWidth); + DML_LOG_VERBOSE("DML::%s: PixelPTEReqWidth_linear = %u\n", __func__, PixelPTEReqWidth_linear); + DML_LOG_VERBOSE("DML::%s: PTERequestSize = %u\n", __func__, *p->PTERequestSize); + DML_LOG_VERBOSE("DML::%s: Pitch = %u\n", __func__, p->Pitch); + DML_LOG_VERBOSE("DML::%s: vmpg_width = %u\n", __func__, *p->vmpg_width); + DML_LOG_VERBOSE("DML::%s: vmpg_height = %u\n", __func__, *p->vmpg_height); + + *p->dpte_row_height_one_row_per_frame = vp_height_dpte_ub; + *p->dpte_row_width_ub_one_row_per_frame = (unsigned int)((math_ceil2(((double)p->Pitch * pixel_per_element * (double)*p->dpte_row_height_one_row_per_frame / (double)*p->PixelPTEReqHeight - 1) / (double)*p->PixelPTEReqWidth, 1) + 1) * (double)*p->PixelPTEReqWidth); + *p->PixelPTEBytesPerRow_one_row_per_frame = (unsigned int)((double)*p->dpte_row_width_ub_one_row_per_frame / (double)*p->PixelPTEReqWidth * *p->PTERequestSize); + *p->dpte_row_height_linear = 0; + + if (dml2_core_utils_is_linear(p->SurfaceTiling)) { + *p->dpte_row_height = (unsigned int)(math_min2(128, (double)(1ULL << (unsigned int)math_floor2(math_log((float)(p->PTEBufferSizeInRequests * *p->PixelPTEReqWidth / pixel_per_element / p->Pitch), 2.0), 1)))); + *p->dpte_row_width_ub = (unsigned int)(math_ceil2(((double)p->Pitch * pixel_per_element * (double)*p->dpte_row_height - 1), (double)*p->PixelPTEReqWidth) + *p->PixelPTEReqWidth); + *p->PixelPTEBytesPerRow = (unsigned int)((double)*p->dpte_row_width_ub / (double)*p->PixelPTEReqWidth * *p->PTERequestSize); + + // VBA_DELTA, VBA doesn't have programming value for pte row height linear. + *p->dpte_row_height_linear = (unsigned int)1 << (unsigned int)math_floor2(math_log((float)(p->PTEBufferSizeInRequests * PixelPTEReqWidth_linear / pixel_per_element / p->Pitch), 2.0), 1); + if (*p->dpte_row_height_linear > 128) + *p->dpte_row_height_linear = 128; + + DML_LOG_VERBOSE("DML::%s: dpte_row_width_ub = %u (linear)\n", __func__, *p->dpte_row_width_ub); + + } else if (!dml2_core_utils_is_vertical_rotation(p->RotationAngle)) { + *p->dpte_row_height = *p->PixelPTEReqHeight; + + if (p->GPUVMMinPageSizeKBytes > 64) { + *p->dpte_row_width_ub = (unsigned int)((math_ceil2(((double)p->Pitch * pixel_per_element * (double)*p->dpte_row_height / (double)*p->PixelPTEReqHeight - 1) / (double)*p->PixelPTEReqWidth, 1) + 1) * *p->PixelPTEReqWidth); + } else if (p->ViewportStationary && (p->NumberOfDPPs == 1)) { + *p->dpte_row_width_ub = (unsigned int)(math_floor2(p->ViewportXStart + p->SwathWidth + *p->PixelPTEReqWidth - 1, *p->PixelPTEReqWidth) - math_floor2(p->ViewportXStart, *p->PixelPTEReqWidth)); + } else { + *p->dpte_row_width_ub = (unsigned int)((math_ceil2((double)(p->SwathWidth - 1) / (double)*p->PixelPTEReqWidth, 1) + 1.0) * *p->PixelPTEReqWidth); + } + DML_LOG_VERBOSE("DML::%s: dpte_row_width_ub = %u (tiled horz)\n", __func__, *p->dpte_row_width_ub); + *p->PixelPTEBytesPerRow = *p->dpte_row_width_ub / *p->PixelPTEReqWidth * *p->PTERequestSize; + } else { + *p->dpte_row_height = (unsigned int)(math_min2(*p->PixelPTEReqWidth, p->MacroTileWidth)); + + if (p->ViewportStationary && (p->NumberOfDPPs == 1)) { + *p->dpte_row_width_ub = (unsigned int)(math_floor2(p->ViewportYStart + p->ViewportHeight + *p->PixelPTEReqHeight - 1, *p->PixelPTEReqHeight) - math_floor2(p->ViewportYStart, *p->PixelPTEReqHeight)); + } else { + *p->dpte_row_width_ub = (unsigned int)((math_ceil2((double)(p->SwathWidth - 1) / (double)*p->PixelPTEReqHeight, 1) + 1) * *p->PixelPTEReqHeight); + } + + *p->PixelPTEBytesPerRow = (unsigned int)((double)*p->dpte_row_width_ub / (double)*p->PixelPTEReqHeight * *p->PTERequestSize); + DML_LOG_VERBOSE("DML::%s: dpte_row_width_ub = %u (tiled vert)\n", __func__, *p->dpte_row_width_ub); + } + + if (p->GPUVMEnable != true) { + *p->PixelPTEBytesPerRow = 0; + *p->PixelPTEBytesPerRow_one_row_per_frame = 0; + } + + *p->PixelPTEBytesPerRowStorage = *p->PixelPTEBytesPerRow; + + DML_LOG_VERBOSE("DML::%s: GPUVMMinPageSizeKBytes = %u\n", __func__, p->GPUVMMinPageSizeKBytes); + DML_LOG_VERBOSE("DML::%s: GPUVMEnable = %u\n", __func__, p->GPUVMEnable); + DML_LOG_VERBOSE("DML::%s: meta_row_height = %u\n", __func__, *p->meta_row_height); + DML_LOG_VERBOSE("DML::%s: dpte_row_height = %u\n", __func__, *p->dpte_row_height); + DML_LOG_VERBOSE("DML::%s: dpte_row_height_linear = %u\n", __func__, *p->dpte_row_height_linear); + DML_LOG_VERBOSE("DML::%s: dpte_row_width_ub = %u\n", __func__, *p->dpte_row_width_ub); + DML_LOG_VERBOSE("DML::%s: PixelPTEBytesPerRow = %u\n", __func__, *p->PixelPTEBytesPerRow); + DML_LOG_VERBOSE("DML::%s: PixelPTEBytesPerRowStorage = %u\n", __func__, *p->PixelPTEBytesPerRowStorage); + DML_LOG_VERBOSE("DML::%s: PTEBufferSizeInRequests = %u\n", __func__, p->PTEBufferSizeInRequests); + DML_LOG_VERBOSE("DML::%s: dpte_row_height_one_row_per_frame = %u\n", __func__, *p->dpte_row_height_one_row_per_frame); + DML_LOG_VERBOSE("DML::%s: dpte_row_width_ub_one_row_per_frame = %u\n", __func__, *p->dpte_row_width_ub_one_row_per_frame); + DML_LOG_VERBOSE("DML::%s: PixelPTEBytesPerRow_one_row_per_frame = %u\n", __func__, *p->PixelPTEBytesPerRow_one_row_per_frame); + + return vm_bytes; +} + +void dcn5_calculate_vm_row_and_swath(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateVMRowAndSwath_params *p) +{ + struct dml2_core_calcs_CalculateVMRowAndSwath_locals *s = &scratch->CalculateVMRowAndSwath_locals; + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + s->HostVMDynamicLevels = dcn5_calculate_host_vm_dynamic_levels(p->display_cfg->gpuvm_enable, p->display_cfg->hostvm_enable, p->display_cfg->plane_descriptors[k].overrides.hostvm_min_page_size_kbytes, + p->display_cfg->hostvm_max_non_cached_page_table_levels); + + if (p->display_cfg->gpuvm_enable == true) { + p->vm_group_bytes[k] = 512; + p->dpte_group_bytes[k] = 512; + } else { + p->vm_group_bytes[k] = 0; + p->dpte_group_bytes[k] = 0; + } + + if (dml2_core_utils_is_420(p->myPipe[k].SourcePixelFormat) || dml2_core_utils_is_422_planar(p->myPipe[k].SourcePixelFormat) || p->myPipe[k].SourcePixelFormat == dml2_rgbe_alpha) { + if ((p->myPipe[k].SourcePixelFormat == dml2_420_10 || p->myPipe[k].SourcePixelFormat == dml2_420_12 + || p->myPipe[k].SourcePixelFormat == dml2_422_planar_10 || p->myPipe[k].SourcePixelFormat == dml2_422_planar_12) + && !dml2_core_utils_is_vertical_rotation(p->myPipe[k].RotationAngle)) { + s->PTEBufferSizeInRequestsForLuma[k] = (p->PTEBufferSizeInRequestsLuma + p->PTEBufferSizeInRequestsChroma) / 2; + s->PTEBufferSizeInRequestsForChroma[k] = s->PTEBufferSizeInRequestsForLuma[k]; + } else { + s->PTEBufferSizeInRequestsForLuma[k] = p->PTEBufferSizeInRequestsLuma; + s->PTEBufferSizeInRequestsForChroma[k] = p->PTEBufferSizeInRequestsChroma; + } + + scratch->calculate_vm_and_row_bytes_params.ViewportStationary = p->myPipe[k].ViewportStationary; + scratch->calculate_vm_and_row_bytes_params.DCCEnable = p->myPipe[k].DCCEnable; + scratch->calculate_vm_and_row_bytes_params.NumberOfDPPs = p->myPipe[k].DPPPerSurface; + scratch->calculate_vm_and_row_bytes_params.BlockHeight256Bytes = p->myPipe[k].BlockHeight256BytesC; + scratch->calculate_vm_and_row_bytes_params.BlockWidth256Bytes = p->myPipe[k].BlockWidth256BytesC; + scratch->calculate_vm_and_row_bytes_params.SourcePixelFormat = p->myPipe[k].SourcePixelFormat; + scratch->calculate_vm_and_row_bytes_params.SurfaceTiling = p->myPipe[k].SurfaceTiling; + scratch->calculate_vm_and_row_bytes_params.BytePerPixel = p->myPipe[k].BytePerPixelC; + scratch->calculate_vm_and_row_bytes_params.RotationAngle = p->myPipe[k].RotationAngle; + scratch->calculate_vm_and_row_bytes_params.SwathWidth = p->SwathWidthC[k]; + scratch->calculate_vm_and_row_bytes_params.ViewportHeight = p->myPipe[k].ViewportHeightC; + scratch->calculate_vm_and_row_bytes_params.ViewportXStart = p->myPipe[k].ViewportXStartC; + scratch->calculate_vm_and_row_bytes_params.ViewportYStart = p->myPipe[k].ViewportYStartC; + scratch->calculate_vm_and_row_bytes_params.GPUVMEnable = p->display_cfg->gpuvm_enable; + scratch->calculate_vm_and_row_bytes_params.GPUVMMaxPageTableLevels = p->display_cfg->gpuvm_max_page_table_levels; + scratch->calculate_vm_and_row_bytes_params.GPUVMMinPageSizeKBytes = p->display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes; + scratch->calculate_vm_and_row_bytes_params.PTEBufferSizeInRequests = s->PTEBufferSizeInRequestsForChroma[k]; + scratch->calculate_vm_and_row_bytes_params.Pitch = p->myPipe[k].PitchC; + scratch->calculate_vm_and_row_bytes_params.MacroTileWidth = p->myPipe[k].BlockWidthC; + scratch->calculate_vm_and_row_bytes_params.MacroTileHeight = p->myPipe[k].BlockHeightC; + scratch->calculate_vm_and_row_bytes_params.DCCMetaPitch = p->myPipe[k].DCCMetaPitchC; + scratch->calculate_vm_and_row_bytes_params.mrq_present = p->mrq_present; + + scratch->calculate_vm_and_row_bytes_params.PixelPTEBytesPerRow = &s->PixelPTEBytesPerRowC[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEBytesPerRowStorage = &s->PixelPTEBytesPerRowStorageC[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_width_ub = &p->dpte_row_width_chroma_ub[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_height = &p->dpte_row_height_chroma[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_height_linear = &p->dpte_row_height_linear_chroma[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEBytesPerRow_one_row_per_frame = &s->PixelPTEBytesPerRowC_one_row_per_frame[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_width_ub_one_row_per_frame = &s->dpte_row_width_chroma_ub_one_row_per_frame[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_height_one_row_per_frame = &s->dpte_row_height_chroma_one_row_per_frame[k]; + scratch->calculate_vm_and_row_bytes_params.vmpg_width = &p->vmpg_width_c[k]; + scratch->calculate_vm_and_row_bytes_params.vmpg_height = &p->vmpg_height_c[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEReqWidth = &p->PixelPTEReqWidthC[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEReqHeight = &p->PixelPTEReqHeightC[k]; + scratch->calculate_vm_and_row_bytes_params.PTERequestSize = &p->PTERequestSizeC[k]; + scratch->calculate_vm_and_row_bytes_params.dpde0_bytes_per_frame_ub = &p->dpde0_bytes_per_frame_ub_c[k]; + + scratch->calculate_vm_and_row_bytes_params.meta_row_bytes = &s->meta_row_bytes_per_row_ub_c[k]; + scratch->calculate_vm_and_row_bytes_params.MetaRequestWidth = &p->meta_req_width_chroma[k]; + scratch->calculate_vm_and_row_bytes_params.MetaRequestHeight = &p->meta_req_height_chroma[k]; + scratch->calculate_vm_and_row_bytes_params.meta_row_width = &p->meta_row_width_chroma[k]; + scratch->calculate_vm_and_row_bytes_params.meta_row_height = &p->meta_row_height_chroma[k]; + scratch->calculate_vm_and_row_bytes_params.meta_pte_bytes_per_frame_ub = &p->meta_pte_bytes_per_frame_ub_c[k]; + + s->vm_bytes_c = dcn5_calculate_vm_and_row_bytes(&scratch->calculate_vm_and_row_bytes_params); + + p->PrefetchSourceLinesC[k] = dcn5_calculate_prefetch_source_lines( + p->myPipe[k].VRatioChroma, + p->myPipe[k].VTapsChroma, + p->myPipe[k].UPSPEnabled, + p->myPipe[k].UPSPVTaps, + p->myPipe[k].UPSPSamplePositioning, + dml2_core_utils_is_420(p->myPipe[k].SourcePixelFormat), + p->myPipe[k].InterlaceEnable, + p->myPipe[k].ProgressiveToInterlaceUnitInOPP, + p->myPipe[k].SwathHeightC, + p->myPipe[k].RotationAngle, + p->myPipe[k].mirrored, + p->myPipe[k].ViewportStationary, + p->SwathWidthC[k], + p->myPipe[k].ViewportHeightC, + p->myPipe[k].ViewportXStartC, + p->myPipe[k].ViewportYStartC, + + // Output + &p->VInitPreFillC[k], + &p->MaxNumSwathC[k]); + } else { + s->PTEBufferSizeInRequestsForLuma[k] = p->PTEBufferSizeInRequestsLuma + p->PTEBufferSizeInRequestsChroma; + s->PTEBufferSizeInRequestsForChroma[k] = 0; + s->PixelPTEBytesPerRowC[k] = 0; + s->PixelPTEBytesPerRowStorageC[k] = 0; + s->vm_bytes_c = 0; + p->MaxNumSwathC[k] = 0; + p->PrefetchSourceLinesC[k] = 0; + s->dpte_row_height_chroma_one_row_per_frame[k] = 0; + s->dpte_row_width_chroma_ub_one_row_per_frame[k] = 0; + s->PixelPTEBytesPerRowC_one_row_per_frame[k] = 0; + } + + scratch->calculate_vm_and_row_bytes_params.ViewportStationary = p->myPipe[k].ViewportStationary; + scratch->calculate_vm_and_row_bytes_params.DCCEnable = p->myPipe[k].DCCEnable; + scratch->calculate_vm_and_row_bytes_params.NumberOfDPPs = p->myPipe[k].DPPPerSurface; + scratch->calculate_vm_and_row_bytes_params.BlockHeight256Bytes = p->myPipe[k].BlockHeight256BytesY; + scratch->calculate_vm_and_row_bytes_params.BlockWidth256Bytes = p->myPipe[k].BlockWidth256BytesY; + scratch->calculate_vm_and_row_bytes_params.SourcePixelFormat = p->myPipe[k].SourcePixelFormat; + scratch->calculate_vm_and_row_bytes_params.SurfaceTiling = p->myPipe[k].SurfaceTiling; + scratch->calculate_vm_and_row_bytes_params.BytePerPixel = p->myPipe[k].BytePerPixelY; + scratch->calculate_vm_and_row_bytes_params.RotationAngle = p->myPipe[k].RotationAngle; + scratch->calculate_vm_and_row_bytes_params.SwathWidth = p->SwathWidthY[k]; + scratch->calculate_vm_and_row_bytes_params.ViewportHeight = p->myPipe[k].ViewportHeight; + scratch->calculate_vm_and_row_bytes_params.ViewportXStart = p->myPipe[k].ViewportXStart; + scratch->calculate_vm_and_row_bytes_params.ViewportYStart = p->myPipe[k].ViewportYStart; + scratch->calculate_vm_and_row_bytes_params.GPUVMEnable = p->display_cfg->gpuvm_enable; + scratch->calculate_vm_and_row_bytes_params.GPUVMMaxPageTableLevels = p->display_cfg->gpuvm_max_page_table_levels; + scratch->calculate_vm_and_row_bytes_params.GPUVMMinPageSizeKBytes = p->display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes; + scratch->calculate_vm_and_row_bytes_params.PTEBufferSizeInRequests = s->PTEBufferSizeInRequestsForLuma[k]; + scratch->calculate_vm_and_row_bytes_params.Pitch = p->myPipe[k].PitchY; + scratch->calculate_vm_and_row_bytes_params.MacroTileWidth = p->myPipe[k].BlockWidthY; + scratch->calculate_vm_and_row_bytes_params.MacroTileHeight = p->myPipe[k].BlockHeightY; + scratch->calculate_vm_and_row_bytes_params.DCCMetaPitch = p->myPipe[k].DCCMetaPitchY; + scratch->calculate_vm_and_row_bytes_params.mrq_present = p->mrq_present; + + scratch->calculate_vm_and_row_bytes_params.PixelPTEBytesPerRow = &s->PixelPTEBytesPerRowY[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEBytesPerRowStorage = &s->PixelPTEBytesPerRowStorageY[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_width_ub = &p->dpte_row_width_luma_ub[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_height = &p->dpte_row_height_luma[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_height_linear = &p->dpte_row_height_linear_luma[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEBytesPerRow_one_row_per_frame = &s->PixelPTEBytesPerRowY_one_row_per_frame[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_width_ub_one_row_per_frame = &s->dpte_row_width_luma_ub_one_row_per_frame[k]; + scratch->calculate_vm_and_row_bytes_params.dpte_row_height_one_row_per_frame = &s->dpte_row_height_luma_one_row_per_frame[k]; + scratch->calculate_vm_and_row_bytes_params.vmpg_width = &p->vmpg_width_y[k]; + scratch->calculate_vm_and_row_bytes_params.vmpg_height = &p->vmpg_height_y[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEReqWidth = &p->PixelPTEReqWidthY[k]; + scratch->calculate_vm_and_row_bytes_params.PixelPTEReqHeight = &p->PixelPTEReqHeightY[k]; + scratch->calculate_vm_and_row_bytes_params.PTERequestSize = &p->PTERequestSizeY[k]; + scratch->calculate_vm_and_row_bytes_params.dpde0_bytes_per_frame_ub = &p->dpde0_bytes_per_frame_ub_l[k]; + + scratch->calculate_vm_and_row_bytes_params.meta_row_bytes = &s->meta_row_bytes_per_row_ub_l[k]; + scratch->calculate_vm_and_row_bytes_params.MetaRequestWidth = &p->meta_req_width_luma[k]; + scratch->calculate_vm_and_row_bytes_params.MetaRequestHeight = &p->meta_req_height_luma[k]; + scratch->calculate_vm_and_row_bytes_params.meta_row_width = &p->meta_row_width_luma[k]; + scratch->calculate_vm_and_row_bytes_params.meta_row_height = &p->meta_row_height_luma[k]; + scratch->calculate_vm_and_row_bytes_params.meta_pte_bytes_per_frame_ub = &p->meta_pte_bytes_per_frame_ub_l[k]; + + s->vm_bytes_l = dcn5_calculate_vm_and_row_bytes(&scratch->calculate_vm_and_row_bytes_params); + + p->PrefetchSourceLinesY[k] = dcn5_calculate_prefetch_source_lines( + p->myPipe[k].VRatio, + p->myPipe[k].VTaps, + 0, //No upsampler in Luma + p->myPipe[k].UPSPVTaps, + p->myPipe[k].UPSPSamplePositioning, + dml2_core_utils_is_420(p->myPipe[k].SourcePixelFormat), + p->myPipe[k].InterlaceEnable, + p->myPipe[k].ProgressiveToInterlaceUnitInOPP, + p->myPipe[k].SwathHeightY, + p->myPipe[k].RotationAngle, + p->myPipe[k].mirrored, + p->myPipe[k].ViewportStationary, + p->SwathWidthY[k], + p->myPipe[k].ViewportHeight, + p->myPipe[k].ViewportXStart, + p->myPipe[k].ViewportYStart, + + // Output + &p->VInitPreFillY[k], + &p->MaxNumSwathY[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, vm_bytes_l = %u (before hvm level)\n", __func__, k, s->vm_bytes_l); + DML_LOG_VERBOSE("DML::%s: k=%u, vm_bytes_c = %u (before hvm level)\n", __func__, k, s->vm_bytes_c); + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_bytes_per_row_ub_l = %u\n", __func__, k, s->meta_row_bytes_per_row_ub_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_bytes_per_row_ub_c = %u\n", __func__, k, s->meta_row_bytes_per_row_ub_c[k]); + p->vm_bytes[k] = (s->vm_bytes_l + s->vm_bytes_c) * (1 + 8 * s->HostVMDynamicLevels); + p->meta_row_bytes[k] = s->meta_row_bytes_per_row_ub_l[k] + s->meta_row_bytes_per_row_ub_c[k]; + p->meta_row_bytes_per_row_ub_l[k] = s->meta_row_bytes_per_row_ub_l[k]; + p->meta_row_bytes_per_row_ub_c[k] = s->meta_row_bytes_per_row_ub_c[k]; + + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_bytes = %u\n", __func__, k, p->meta_row_bytes[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, vm_bytes = %u (after hvm level)\n", __func__, k, p->vm_bytes[k]); + if (s->PixelPTEBytesPerRowStorageY[k] <= 64 * s->PTEBufferSizeInRequestsForLuma[k] && s->PixelPTEBytesPerRowStorageC[k] <= 64 * s->PTEBufferSizeInRequestsForChroma[k]) { + p->PTEBufferSizeNotExceeded[k] = true; + } else { + p->PTEBufferSizeNotExceeded[k] = false; + } + + s->one_row_per_frame_fits_in_buffer[k] = (s->PixelPTEBytesPerRowY_one_row_per_frame[k] <= 64 * 2 * s->PTEBufferSizeInRequestsForLuma[k] && + s->PixelPTEBytesPerRowC_one_row_per_frame[k] <= 64 * 2 * s->PTEBufferSizeInRequestsForChroma[k]); + if (p->PTEBufferSizeNotExceeded[k] == 0 || s->one_row_per_frame_fits_in_buffer[k] == 0) { + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowY = %u (before hvm level)\n", __func__, k, s->PixelPTEBytesPerRowY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowC = %u (before hvm level)\n", __func__, k, s->PixelPTEBytesPerRowC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowStorageY = %u\n", __func__, k, s->PixelPTEBytesPerRowStorageY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowStorageC = %u\n", __func__, k, s->PixelPTEBytesPerRowStorageC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PTEBufferSizeInRequestsForLuma = %u\n", __func__, k, s->PTEBufferSizeInRequestsForLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PTEBufferSizeInRequestsForChroma = %u\n", __func__, k, s->PTEBufferSizeInRequestsForChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PTEBufferSizeNotExceeded (not one_row_per_frame) = %u\n", __func__, k, p->PTEBufferSizeNotExceeded[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, HostVMDynamicLevels = %u\n", __func__, k, s->HostVMDynamicLevels); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowY_one_row_per_frame = %u\n", __func__, k, s->PixelPTEBytesPerRowY_one_row_per_frame[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowC_one_row_per_frame = %u\n", __func__, k, s->PixelPTEBytesPerRowC_one_row_per_frame[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, one_row_per_frame_fits_in_buffer = %u\n", __func__, k, s->one_row_per_frame_fits_in_buffer[k]); + } + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (p->display_cfg->gpuvm_enable) { + DML_LOG_VERBOSE("DML::%s: k=%u, force_pte_buffer_mode.enable = %u\n", __func__, k, p->display_cfg->plane_descriptors[k].overrides.hw.force_pte_buffer_mode.enable); + DML_LOG_VERBOSE("DML::%s: k=%u, force_pte_buffer_mode.value = %u\n", __func__, k, p->display_cfg->plane_descriptors[k].overrides.hw.force_pte_buffer_mode.value); + DML_LOG_VERBOSE("DML::%s: k=%u, gpuvm_min_page_size_kbytes = %u\n", __func__, k, p->display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes); + DML_LOG_VERBOSE("DML::%s: k=%u, uclk_pstate_switch_modes = %u\n", __func__, k, p->uclk_pstate_switch_modes[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, FORCE_ONE_ROW_FOR_FRAME = %u\n", __func__, k, p->myPipe[k].FORCE_ONE_ROW_FOR_FRAME); + + if (p->display_cfg->plane_descriptors[k].overrides.hw.force_pte_buffer_mode.enable == 1) { + p->PTE_BUFFER_MODE[k] = p->display_cfg->plane_descriptors[k].overrides.hw.force_pte_buffer_mode.value; + } else { + p->PTE_BUFFER_MODE[k] = p->myPipe[k].FORCE_ONE_ROW_FOR_FRAME + || (p->display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes > 64) + || (p->uclk_pstate_switch_modes[k] == dml2_pstate_method_alternate); + p->BIGK_FRAGMENT_SIZE[k] = (unsigned int)(math_log((float)p->display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes * 1024, 2) - 12); + } + } else { + p->PTE_BUFFER_MODE[k] = 0; + p->BIGK_FRAGMENT_SIZE[k] = 0; + } + DML_LOG_VERBOSE("DML::%s: k=%u, PTE_BUFFER_MODE = %u\n", __func__, k, p->PTE_BUFFER_MODE[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, BIGK_FRAGMENT_SIZE = %u\n", __func__, k, p->BIGK_FRAGMENT_SIZE[k]); + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + s->HostVMDynamicLevels = dcn5_calculate_host_vm_dynamic_levels(p->display_cfg->gpuvm_enable, p->display_cfg->hostvm_enable, p->display_cfg->plane_descriptors[k].overrides.hostvm_min_page_size_kbytes, + p->display_cfg->hostvm_max_non_cached_page_table_levels); + + p->DCCMetaBufferSizeNotExceeded[k] = true; + if (p->display_cfg->gpuvm_enable) { + p->use_one_row_for_frame[k] = p->myPipe[k].FORCE_ONE_ROW_FOR_FRAME + || (p->display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes > 64 && dml2_core_utils_is_vertical_rotation(p->myPipe[k].RotationAngle)) + || (p->uclk_pstate_switch_modes[k] == dml2_pstate_method_alternate); + } + + p->use_one_row_for_frame_flip[k] = p->use_one_row_for_frame[k]; + + if (p->use_one_row_for_frame[k]) { + p->dpte_row_height_luma[k] = s->dpte_row_height_luma_one_row_per_frame[k]; + p->dpte_row_width_luma_ub[k] = s->dpte_row_width_luma_ub_one_row_per_frame[k]; + s->PixelPTEBytesPerRowY[k] = s->PixelPTEBytesPerRowY_one_row_per_frame[k]; + p->dpte_row_height_chroma[k] = s->dpte_row_height_chroma_one_row_per_frame[k]; + p->dpte_row_width_chroma_ub[k] = s->dpte_row_width_chroma_ub_one_row_per_frame[k]; + s->PixelPTEBytesPerRowC[k] = s->PixelPTEBytesPerRowC_one_row_per_frame[k]; + p->PTEBufferSizeNotExceeded[k] = s->one_row_per_frame_fits_in_buffer[k]; + } + + if (p->meta_row_bytes[k] <= p->DCCMetaBufferSizeBytes) { + p->DCCMetaBufferSizeNotExceeded[k] = true; + } else { + p->DCCMetaBufferSizeNotExceeded[k] = false; + DML_LOG_VERBOSE("DML::%s: k=%d, meta_row_bytes = %d\n", __func__, k, p->meta_row_bytes[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, DCCMetaBufferSizeBytes = %d\n", __func__, k, p->DCCMetaBufferSizeBytes); + DML_LOG_VERBOSE("DML::%s: k=%d, DCCMetaBufferSizeNotExceeded = %d\n", __func__, k, p->DCCMetaBufferSizeNotExceeded[k]); + } + + s->PixelPTEBytesPerRowY[k] = s->PixelPTEBytesPerRowY[k] * (1 + 8 * s->HostVMDynamicLevels); + s->PixelPTEBytesPerRowC[k] = s->PixelPTEBytesPerRowC[k] * (1 + 8 * s->HostVMDynamicLevels); + p->PixelPTEBytesPerRow[k] = s->PixelPTEBytesPerRowY[k] + s->PixelPTEBytesPerRowC[k]; + p->dpte_row_bytes_per_row_l[k] = s->PixelPTEBytesPerRowY[k]; + p->dpte_row_bytes_per_row_c[k] = s->PixelPTEBytesPerRowC[k]; + + // if one row of dPTEs is meant to span the entire frame, then for these calculations, we will pretend like that one big row is fetched in two halfs + if (p->use_one_row_for_frame[k]) + p->PixelPTEBytesPerRow[k] = p->PixelPTEBytesPerRow[k] / 2; + + dcn5_calculate_row_bandwidth( + p->display_cfg->gpuvm_enable, + p->use_one_row_for_frame[k], + p->myPipe[k].SourcePixelFormat, + p->myPipe[k].VRatio, + p->myPipe[k].VRatioChroma, + p->myPipe[k].DCCEnable, + p->myPipe[k].HTotal / p->myPipe[k].PixelClock, + s->PixelPTEBytesPerRowY[k], + s->PixelPTEBytesPerRowC[k], + p->dpte_row_height_luma[k], + p->dpte_row_height_chroma[k], + + p->mrq_present, + p->meta_row_bytes_per_row_ub_l[k], + p->meta_row_bytes_per_row_ub_c[k], + p->meta_row_height_luma[k], + p->meta_row_height_chroma[k], + + // Output + &p->dpte_row_bw[k], + &p->meta_row_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, use_one_row_for_frame = %u\n", __func__, k, p->use_one_row_for_frame[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, use_one_row_for_frame_flip = %u\n", __func__, k, p->use_one_row_for_frame_flip[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_row_height_luma = %u\n", __func__, k, p->dpte_row_height_luma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_row_width_luma_ub = %u\n", __func__, k, p->dpte_row_width_luma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowY = %u (after hvm level)\n", __func__, k, s->PixelPTEBytesPerRowY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_row_height_chroma = %u\n", __func__, k, p->dpte_row_height_chroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_row_width_chroma_ub = %u\n", __func__, k, p->dpte_row_width_chroma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRowC = %u (after hvm level)\n", __func__, k, s->PixelPTEBytesPerRowC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEBytesPerRow = %u\n", __func__, k, p->PixelPTEBytesPerRow[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PTEBufferSizeNotExceeded = %u\n", __func__, k, p->PTEBufferSizeNotExceeded[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, gpuvm_enable = %u\n", __func__, k, p->display_cfg->gpuvm_enable); + } +} + +void dcn5_calculate_bytes_to_fetch_required_to_hide_latency( + struct dml2_core_calcs_calculate_bytes_to_fetch_required_to_hide_latency_params *p) +{ + unsigned int dst_lines_to_hide; + unsigned int src_lines_to_hide_l; + unsigned int src_lines_to_hide_c; + unsigned int plane_index; + unsigned int stream_index; + + for (plane_index = 0; plane_index < p->num_active_planes; plane_index++) { + stream_index = p->display_cfg->plane_descriptors[plane_index].stream_index; + + dst_lines_to_hide = (unsigned int)math_ceil(p->latency_to_hide_us[plane_index] / + ((double)p->display_cfg->stream_descriptors[stream_index].timing.h_total / + (double)p->display_cfg->stream_descriptors[stream_index].timing.pixel_clock_khz * 1000.0)); + + src_lines_to_hide_l = (unsigned int)math_ceil2(p->display_cfg->plane_descriptors[plane_index].composition.scaler_info.plane0.v_ratio * dst_lines_to_hide, + p->swath_height_l[plane_index]); + p->bytes_required_l[plane_index] = src_lines_to_hide_l * p->num_of_dpp[plane_index] * p->swath_width_l[plane_index] * p->byte_per_pix_l[plane_index]; + + src_lines_to_hide_c = (unsigned int)math_ceil2(p->display_cfg->plane_descriptors[plane_index].composition.scaler_info.plane1.v_ratio * dst_lines_to_hide, + p->swath_height_c[plane_index]); + p->bytes_required_c[plane_index] = src_lines_to_hide_c * p->num_of_dpp[plane_index] * p->swath_width_c[plane_index] * p->byte_per_pix_c[plane_index]; + + if (p->display_cfg->plane_descriptors[plane_index].surface.dcc.enable && p->mrq_present) { + p->bytes_required_l[plane_index] += (unsigned int)math_ceil((double)src_lines_to_hide_l / p->meta_row_height_l[plane_index]) * p->meta_row_bytes_per_row_ub_l[plane_index]; + if (p->meta_row_height_c[plane_index]) { + p->bytes_required_c[plane_index] += (unsigned int)math_ceil((double)src_lines_to_hide_c / p->meta_row_height_c[plane_index]) * p->meta_row_bytes_per_row_ub_c[plane_index]; + } + } + + if (p->display_cfg->gpuvm_enable == true) { + p->bytes_required_l[plane_index] += (unsigned int)math_ceil((double)src_lines_to_hide_l / p->dpte_row_height_l[plane_index]) * p->dpte_bytes_per_row_l[plane_index]; + if (p->dpte_row_height_c[plane_index]) { + p->bytes_required_c[plane_index] += (unsigned int)math_ceil((double)src_lines_to_hide_c / p->dpte_row_height_c[plane_index]) * p->dpte_bytes_per_row_c[plane_index]; + } + } + } +} + +void dcn5_calculate_excess_vactive_bandwidth_required( + const struct dml2_display_cfg *display_cfg, + unsigned int num_active_planes, + unsigned int bytes_required_l[], + unsigned int bytes_required_c[], + /* outputs */ + double excess_vactive_fill_bw_l[], + double excess_vactive_fill_bw_c[]) +{ + unsigned int plane_index; + + for (plane_index = 0; plane_index < num_active_planes; plane_index++) { + excess_vactive_fill_bw_l[plane_index] = 0.0; + excess_vactive_fill_bw_c[plane_index] = 0.0; + + if (display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk] > 0) { + excess_vactive_fill_bw_l[plane_index] = (double)bytes_required_l[plane_index] / (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk]; + excess_vactive_fill_bw_c[plane_index] = (double)bytes_required_c[plane_index] / (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk]; + } + } +} + +void dcn5_calculate_cursor_req_attributes( + unsigned int cursor_width, + unsigned int cursor_bpp, + + // output + unsigned int *cursor_lines_per_chunk, + unsigned int *cursor_bytes_per_line, + unsigned int *cursor_bytes_per_chunk, + unsigned int *cursor_bytes) +{ + unsigned int cursor_bytes_per_req = 0; + unsigned int cursor_width_bytes = 0; + + //SW determines the cursor pitch to support the maximum cursor_width that will be used but the following restrictions apply. + //- For 2bpp, cursor_pitch = 256 pixels due to min cursor request size of 64B + //- For 32 or 64 bpp, cursor_pitch = 64, 128 or 256 pixels depending on the cursor width + + //The cursor requestor uses a cursor request size of 64B, 128B, or 256B depending on the cursor_width and cursor_bpp as follows. + + cursor_width_bytes = (unsigned int)math_ceil2((double)cursor_width * cursor_bpp / 8, 1); + if (cursor_width_bytes <= 64) + cursor_bytes_per_req = 64; + else if (cursor_width_bytes <= 128) + cursor_bytes_per_req = 128; + else + cursor_bytes_per_req = 256; + + //If cursor_width_bytes is greater than 256B, then multiple 256B requests are issued to fetch the entire cursor line. + *cursor_bytes_per_line = (unsigned int)math_ceil2((double)cursor_width_bytes, cursor_bytes_per_req); + + //Nominally, the cursor chunk is 1KB or 2KB but it is restricted to a power of 2 number of lines with a maximum of 16 lines. + if (cursor_bpp == 2) { + *cursor_lines_per_chunk = 16; + } else if (cursor_bpp == 32) { + if (cursor_width <= 32) + *cursor_lines_per_chunk = 16; + else if (cursor_width <= 64) + *cursor_lines_per_chunk = 8; + else if (cursor_width <= 128) + *cursor_lines_per_chunk = 4; + else + *cursor_lines_per_chunk = 2; + } else if (cursor_bpp == 64) { + if (cursor_width <= 16) + *cursor_lines_per_chunk = 16; + else if (cursor_width <= 32) + *cursor_lines_per_chunk = 8; + else if (cursor_width <= 64) + *cursor_lines_per_chunk = 4; + else if (cursor_width <= 128) + *cursor_lines_per_chunk = 2; + else + *cursor_lines_per_chunk = 1; + } else { + if (cursor_width > 0) { + DML_LOG_VERBOSE("DML::%s: Invalid cursor_bpp = %d\n", __func__, cursor_bpp); + DML_ASSERT(0); + } + } + + *cursor_bytes_per_chunk = *cursor_bytes_per_line * *cursor_lines_per_chunk; + + // For the cursor implementation, all requested data is stored in the return buffer. Given this fact, the cursor_bytes can be directly compared with the CursorBufferSize. + // Only cursor_width is provided for worst case sizing so assume that the cursor is square + *cursor_bytes = *cursor_bytes_per_line * cursor_width; + DML_LOG_VERBOSE("DML::%s: cursor_bpp = %d\n", __func__, cursor_bpp); + DML_LOG_VERBOSE("DML::%s: cursor_width = %d\n", __func__, cursor_width); + DML_LOG_VERBOSE("DML::%s: cursor_width_bytes = %d\n", __func__, cursor_width_bytes); + DML_LOG_VERBOSE("DML::%s: cursor_bytes_per_req = %d\n", __func__, cursor_bytes_per_req); + DML_LOG_VERBOSE("DML::%s: cursor_lines_per_chunk = %d\n", __func__, *cursor_lines_per_chunk); + DML_LOG_VERBOSE("DML::%s: cursor_bytes_per_line = %d\n", __func__, *cursor_bytes_per_line); + DML_LOG_VERBOSE("DML::%s: cursor_bytes_per_chunk = %d\n", __func__, *cursor_bytes_per_chunk); + DML_LOG_VERBOSE("DML::%s: cursor_bytes = %d\n", __func__, *cursor_bytes); + DML_LOG_VERBOSE("DML::%s: cursor_pitch = %d\n", __func__, cursor_bpp == 2 ? 256 : (unsigned int)1 << (unsigned int)math_ceil2(math_log((float)cursor_width, 2), 1)); +} + +void dcn5_calculate_cursor_urgent_burst_factor( + unsigned int CursorBufferSize, + unsigned int CursorWidth, + unsigned int cursor_bytes_per_chunk, + unsigned int cursor_lines_per_chunk, + double LineTime, + double UrgentLatency, + + double *UrgentBurstFactorCursor, + bool *NotEnoughUrgentLatencyHiding) +{ + unsigned int LinesInCursorBuffer = 0; + double CursorBufferSizeInTime = 0; + + if (CursorWidth > 0) { + LinesInCursorBuffer = (unsigned int)math_floor2(CursorBufferSize * 1024.0 / (double)cursor_bytes_per_chunk, 1) * cursor_lines_per_chunk; + + CursorBufferSizeInTime = LinesInCursorBuffer * LineTime; + if (CursorBufferSizeInTime - UrgentLatency <= 0) { + *NotEnoughUrgentLatencyHiding = 1; + *UrgentBurstFactorCursor = 1; + } else { + *NotEnoughUrgentLatencyHiding = 0; + *UrgentBurstFactorCursor = CursorBufferSizeInTime / (CursorBufferSizeInTime - UrgentLatency); + } + DML_LOG_VERBOSE("DML::%s: LinesInCursorBuffer = %u\n", __func__, LinesInCursorBuffer); + DML_LOG_VERBOSE("DML::%s: CursorBufferSizeInTime = %f\n", __func__, CursorBufferSizeInTime); + DML_LOG_VERBOSE("DML::%s: CursorBufferSize = %u (kbytes)\n", __func__, CursorBufferSize); + DML_LOG_VERBOSE("DML::%s: cursor_bytes_per_chunk = %u\n", __func__, cursor_bytes_per_chunk); + DML_LOG_VERBOSE("DML::%s: cursor_lines_per_chunk = %u\n", __func__, cursor_lines_per_chunk); + DML_LOG_VERBOSE("DML::%s: UrgentBurstFactorCursor = %f\n", __func__, *UrgentBurstFactorCursor); + DML_LOG_VERBOSE("DML::%s: NotEnoughUrgentLatencyHiding = %d\n", __func__, *NotEnoughUrgentLatencyHiding); + } +} + +void dcn5_calculate_urgent_burst_factor( + const struct dml2_plane_parameters *plane_cfg, + unsigned int swath_width_luma_ub, + unsigned int swath_width_chroma_ub, + unsigned int SwathHeightY, + unsigned int SwathHeightC, + double LineTime, + double UrgentLatency, + double VRatio, + double VRatioC, + double BytePerPixelInDETY, + double BytePerPixelInDETC, + unsigned int DETBufferSizeY, + unsigned int DETBufferSizeC, + // Output + double *UrgentBurstFactorLuma, + double *UrgentBurstFactorChroma, + bool *NotEnoughUrgentLatencyHiding) +{ + (void)plane_cfg; + double LinesInDETLuma; + double LinesInDETChroma; + double DETBufferSizeInTimeLuma; + double DETBufferSizeInTimeChroma; + + *NotEnoughUrgentLatencyHiding = 0; + *UrgentBurstFactorLuma = 0; + *UrgentBurstFactorChroma = 0; + + DML_LOG_VERBOSE("DML::%s: VRatio = %f\n", __func__, VRatio); + DML_LOG_VERBOSE("DML::%s: VRatioC = %f\n", __func__, VRatioC); + DML_LOG_VERBOSE("DML::%s: DETBufferSizeY = %d\n", __func__, DETBufferSizeY); + DML_LOG_VERBOSE("DML::%s: DETBufferSizeC = %d\n", __func__, DETBufferSizeC); + DML_LOG_VERBOSE("DML::%s: BytePerPixelInDETY = %f\n", __func__, BytePerPixelInDETY); + DML_LOG_VERBOSE("DML::%s: swath_width_luma_ub = %d\n", __func__, swath_width_luma_ub); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, LineTime); + DML_ASSERT(VRatio > 0); + + LinesInDETLuma = DETBufferSizeY / BytePerPixelInDETY / swath_width_luma_ub; + + DETBufferSizeInTimeLuma = math_floor2(LinesInDETLuma, SwathHeightY) * LineTime / VRatio; + if (DETBufferSizeInTimeLuma - UrgentLatency <= 0) { + *NotEnoughUrgentLatencyHiding = 1; + *UrgentBurstFactorLuma = 1; + } else { + *UrgentBurstFactorLuma = DETBufferSizeInTimeLuma / (DETBufferSizeInTimeLuma - UrgentLatency); + } + + if (BytePerPixelInDETC > 0) { + LinesInDETChroma = DETBufferSizeC / BytePerPixelInDETC / swath_width_chroma_ub; + + DETBufferSizeInTimeChroma = math_floor2(LinesInDETChroma, SwathHeightC) * LineTime / VRatioC; + if (DETBufferSizeInTimeChroma - UrgentLatency <= 0) { + *NotEnoughUrgentLatencyHiding = 1; + *UrgentBurstFactorChroma = 1; + } else { + *UrgentBurstFactorChroma = DETBufferSizeInTimeChroma / (DETBufferSizeInTimeChroma - UrgentLatency); + } + } + + DML_LOG_VERBOSE("DML::%s: LinesInDETLuma = %f\n", __func__, LinesInDETLuma); + DML_LOG_VERBOSE("DML::%s: UrgentLatency = %f\n", __func__, UrgentLatency); + DML_LOG_VERBOSE("DML::%s: DETBufferSizeInTimeLuma = %f\n", __func__, DETBufferSizeInTimeLuma); + DML_LOG_VERBOSE("DML::%s: UrgentBurstFactorLuma = %f\n", __func__, *UrgentBurstFactorLuma); + DML_LOG_VERBOSE("DML::%s: UrgentBurstFactorChroma = %f\n", __func__, *UrgentBurstFactorChroma); + DML_LOG_VERBOSE("DML::%s: NotEnoughUrgentLatencyHiding = %d\n", __func__, *NotEnoughUrgentLatencyHiding); +} + +void dcn5_calculate_dcfclk_deep_sleep( + const struct dml2_display_cfg *display_cfg, + unsigned int NumberOfActiveSurfaces, + unsigned int BytePerPixelY[], + unsigned int BytePerPixelC[], + unsigned int SwathWidthY[], + unsigned int SwathWidthC[], + unsigned int DPPPerSurface[], + double PSCL_THROUGHPUT[], + double PSCL_THROUGHPUT_CHROMA[], + double Dppclk[], + double ReadBandwidthLuma[], + double ReadBandwidthChroma[], + unsigned int ReturnBusWidth, + + // Output + double *DCFClkDeepSleep) +{ + double DisplayPipeLineDeliveryTimeLuma; + double DisplayPipeLineDeliveryTimeChroma; + double DCFClkDeepSleepPerSurface[DML2_MAX_PLANES]; + double ReadBandwidth = 0.0; + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + double pixel_rate_mhz = ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + + if (display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio <= 1) { + DisplayPipeLineDeliveryTimeLuma = SwathWidthY[k] * DPPPerSurface[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio / pixel_rate_mhz; + } else { + DisplayPipeLineDeliveryTimeLuma = SwathWidthY[k] / PSCL_THROUGHPUT[k] / Dppclk[k]; + } + if (BytePerPixelC[k] == 0) { + DisplayPipeLineDeliveryTimeChroma = 0; + } else { + if (display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio <= 1) { + DisplayPipeLineDeliveryTimeChroma = SwathWidthC[k] * DPPPerSurface[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio / pixel_rate_mhz; + } else { + DisplayPipeLineDeliveryTimeChroma = SwathWidthC[k] / PSCL_THROUGHPUT_CHROMA[k] / Dppclk[k]; + } + } + + if (BytePerPixelC[k] > 0) { + DCFClkDeepSleepPerSurface[k] = math_max2(__DML2_CALCS_DCFCLK_FACTOR__ * SwathWidthY[k] * BytePerPixelY[k] / 32.0 / DisplayPipeLineDeliveryTimeLuma, + __DML2_CALCS_DCFCLK_FACTOR__ * SwathWidthC[k] * BytePerPixelC[k] / 32.0 / DisplayPipeLineDeliveryTimeChroma); + } else { + DCFClkDeepSleepPerSurface[k] = __DML2_CALCS_DCFCLK_FACTOR__ * SwathWidthY[k] * BytePerPixelY[k] / 64.0 / DisplayPipeLineDeliveryTimeLuma; + } + DCFClkDeepSleepPerSurface[k] = math_max2(DCFClkDeepSleepPerSurface[k], pixel_rate_mhz / 16); + + DML_LOG_VERBOSE("DML::%s: k=%u, PixelClock = %f\n", __func__, k, pixel_rate_mhz); + DML_LOG_VERBOSE("DML::%s: k=%u, DCFClkDeepSleepPerSurface = %f\n", __func__, k, DCFClkDeepSleepPerSurface[k]); + } + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + ReadBandwidth = ReadBandwidth + ReadBandwidthLuma[k] + ReadBandwidthChroma[k]; + } + + *DCFClkDeepSleep = math_max2(8.0, __DML2_CALCS_DCFCLK_FACTOR__ * ReadBandwidth / (double)ReturnBusWidth); + + DML_LOG_VERBOSE("DML::%s: __DML2_CALCS_DCFCLK_FACTOR__ = %f\n", __func__, __DML2_CALCS_DCFCLK_FACTOR__); + DML_LOG_VERBOSE("DML::%s: ReadBandwidth = %f\n", __func__, ReadBandwidth); + DML_LOG_VERBOSE("DML::%s: ReturnBusWidth = %u\n", __func__, ReturnBusWidth); + DML_LOG_VERBOSE("DML::%s: DCFClkDeepSleep = %f\n", __func__, *DCFClkDeepSleep); + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + *DCFClkDeepSleep = math_max2(*DCFClkDeepSleep, DCFClkDeepSleepPerSurface[k]); + } + DML_LOG_VERBOSE("DML::%s: DCFClkDeepSleep = %f (final)\n", __func__, *DCFClkDeepSleep); +} + +unsigned int dcn5_calculate_max_vstartup( + bool ptoi_supported, + unsigned int vblank_nom_default_us, + const struct dml2_timing_cfg *timing, + double write_back_delay_us) +{ + unsigned int vblank_size = 0; + unsigned int max_vstartup_lines = 0; + + double line_time_us = (double)timing->h_total / ((double)timing->pixel_clock_khz / 1000); + unsigned int vblank_actual = timing->v_total - timing->v_active; + unsigned int vblank_nom_default_in_line = (unsigned int)math_floor2((double)vblank_nom_default_us / line_time_us, 1.0); + unsigned int vblank_avail = (timing->vblank_nom == 0) ? vblank_nom_default_in_line : (unsigned int)timing->vblank_nom; + + vblank_size = (unsigned int)math_min2(vblank_actual, vblank_avail); + + if (timing->interlaced && !ptoi_supported) + max_vstartup_lines = (unsigned int)(math_floor2((vblank_size - 1) / 2.0, 1.0)); + else + max_vstartup_lines = vblank_size - (unsigned int)math_max2(1.0, math_ceil2(write_back_delay_us / line_time_us, 1.0)); + max_vstartup_lines = (unsigned int)math_min2(max_vstartup_lines, __DML2_CALCS_MAX_VSTARTUP__); + + DML_LOG_VERBOSE("DML::%s: VBlankNom = %lu\n", __func__, timing->vblank_nom); + DML_LOG_VERBOSE("DML::%s: vblank_nom_default_us = %u\n", __func__, vblank_nom_default_us); + DML_LOG_VERBOSE("DML::%s: line_time_us = %f\n", __func__, line_time_us); + DML_LOG_VERBOSE("DML::%s: vblank_actual = %u\n", __func__, vblank_actual); + DML_LOG_VERBOSE("DML::%s: vblank_avail = %u\n", __func__, vblank_avail); + DML_LOG_VERBOSE("DML::%s: max_vstartup_lines = %u\n", __func__, max_vstartup_lines); + return max_vstartup_lines; +} + +static void dcn5_calculate_mcache_row_bytes( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_calculate_mcache_row_bytes_params *p) +{ + (void)scratch; + unsigned int vmpg_bytes = 0; + unsigned int blk_bytes = 0; + float meta_per_mvmpg_per_channel = 0; + unsigned int est_blk_per_vmpg = 2; + unsigned int mvmpg_per_row_ub = 0; + unsigned int full_vp_width_mvmpg_aligned = 0; + unsigned int full_vp_height_mvmpg_aligned = 0; + unsigned int meta_per_mvmpg_per_channel_ub = 0; + unsigned int mvmpg_per_mcache; + + DML_LOG_VERBOSE("DML::%s: num_chans = %u\n", __func__, p->num_chans); + DML_LOG_VERBOSE("DML::%s: mem_word_bytes = %u\n", __func__, p->mem_word_bytes); + DML_LOG_VERBOSE("DML::%s: mcache_line_size_bytes = %u\n", __func__, p->mcache_line_size_bytes); + DML_LOG_VERBOSE("DML::%s: mcache_size_bytes = %u\n", __func__, p->mcache_size_bytes); + DML_LOG_VERBOSE("DML::%s: gpuvm_enable = %u\n", __func__, p->gpuvm_enable); + DML_LOG_VERBOSE("DML::%s: gpuvm_page_size_kbytes = %u\n", __func__, p->gpuvm_page_size_kbytes); + DML_LOG_VERBOSE("DML::%s: vp_stationary = %u\n", __func__, p->vp_stationary); + DML_LOG_VERBOSE("DML::%s: tiling_mode = %u\n", __func__, p->tiling_mode); + DML_LOG_VERBOSE("DML::%s: vp_start_x = %u\n", __func__, p->vp_start_x); + DML_LOG_VERBOSE("DML::%s: vp_start_y = %u\n", __func__, p->vp_start_y); + DML_LOG_VERBOSE("DML::%s: full_vp_width = %u\n", __func__, p->full_vp_width); + DML_LOG_VERBOSE("DML::%s: full_vp_height = %u\n", __func__, p->full_vp_height); + DML_LOG_VERBOSE("DML::%s: blk_width = %u\n", __func__, p->blk_width); + DML_LOG_VERBOSE("DML::%s: blk_height = %u\n", __func__, p->blk_height); + DML_LOG_VERBOSE("DML::%s: vmpg_width = %u\n", __func__, p->vmpg_width); + DML_LOG_VERBOSE("DML::%s: vmpg_height = %u\n", __func__, p->vmpg_height); + DML_LOG_VERBOSE("DML::%s: full_swath_bytes = %u\n", __func__, p->full_swath_bytes); + DML_ASSERT(p->mcache_line_size_bytes != 0); + DML_ASSERT(p->mcache_size_bytes != 0); + + *p->mvmpg_width = 0; + *p->mvmpg_height = 0; + + if (p->full_vp_height == 0 && p->full_vp_width == 0) { + *p->num_mcaches = 0; + *p->mcache_row_bytes = 0; + *p->mcache_row_bytes_per_channel = 0; + } else { + blk_bytes = dml2_core_utils_get_tile_block_size_bytes(p->tiling_mode, p->bytes_per_pixel); + + // if gpuvm is not enable, the alignment boundary should be in terms of tiling block size + vmpg_bytes = p->gpuvm_page_size_kbytes * 1024; + + //With vmpg_bytes >= tile blk_bytes, the meta_row_width alignment equations are relative to the vmpg_width/height. + // But for 4KB page with 64KB tile block, we need the meta for all pages in the tile block. + // Therefore, the alignment is relative to the blk_width/height. The factor of 16 vmpg per 64KB tile block is applied at the end. + *p->mvmpg_width = p->blk_width; + *p->mvmpg_height = p->blk_height; + if (p->gpuvm_enable) { + if (vmpg_bytes >= blk_bytes) { + *p->mvmpg_width = p->vmpg_width; + *p->mvmpg_height = p->vmpg_height; + } else if (!((blk_bytes == 65536) && (vmpg_bytes == 4096))) { + DML_LOG_VERBOSE("ERROR: DML::%s: Tiling size and vm page size combination not supported\n", __func__); + DML_ASSERT(0); + } + } + + //For plane0 & 1, first calculate full_vp_width/height_l/c aligned to vmpg_width/height_l/c + full_vp_width_mvmpg_aligned = (unsigned int)(math_floor2((p->vp_start_x + p->full_vp_width) + *p->mvmpg_width - 1, *p->mvmpg_width) - math_floor2(p->vp_start_x, *p->mvmpg_width)); + full_vp_height_mvmpg_aligned = (unsigned int)(math_floor2((p->vp_start_y + p->full_vp_height) + *p->mvmpg_height - 1, *p->mvmpg_height) - math_floor2(p->vp_start_y, *p->mvmpg_height)); + + *p->full_vp_access_width_mvmpg_aligned = p->surf_vert ? full_vp_height_mvmpg_aligned : full_vp_width_mvmpg_aligned; + + //Use the equation for the exact alignment when possible. Note that the exact alignment cannot be used for horizontal access if vmpg_bytes > blk_bytes. + if (!p->surf_vert) { //horizontal access + if (p->vp_stationary == 1 && vmpg_bytes <= blk_bytes) + *p->meta_row_width_ub = full_vp_width_mvmpg_aligned; + else + *p->meta_row_width_ub = (unsigned int)math_ceil2((double)p->full_vp_width - 1, *p->mvmpg_width) + *p->mvmpg_width; + mvmpg_per_row_ub = *p->meta_row_width_ub / *p->mvmpg_width; + } else { //vertical access + if (p->vp_stationary == 1) + *p->meta_row_width_ub = full_vp_height_mvmpg_aligned; + else + *p->meta_row_width_ub = (unsigned int)math_ceil2((double)p->full_vp_height - 1, *p->mvmpg_height) + *p->mvmpg_height; + mvmpg_per_row_ub = *p->meta_row_width_ub / *p->mvmpg_height; + } + + if (p->gpuvm_enable) { + meta_per_mvmpg_per_channel = (float)vmpg_bytes / (float)256 / p->num_chans; + + //but using the est_blk_per_vmpg between 2 and 4, to be not as pessimestic + if (p->surf_vert && vmpg_bytes > blk_bytes) { + meta_per_mvmpg_per_channel = (float)est_blk_per_vmpg * blk_bytes / (float)256 / p->num_chans; + } + + *p->dcc_dram_bw_nom_overhead_factor = 1 + math_max2(1.0 / 256.0, math_ceil2(meta_per_mvmpg_per_channel, p->mem_word_bytes) / (256 * meta_per_mvmpg_per_channel)); // dcc_dr_oh_nom + } else { + meta_per_mvmpg_per_channel = (float) blk_bytes / (float)256 / p->num_chans; + + if (!p->surf_vert) + *p->dcc_dram_bw_nom_overhead_factor = 1 + 1.0 / 256.0; + else + *p->dcc_dram_bw_nom_overhead_factor = 1 + math_max2(1.0 / 256.0, math_ceil2(meta_per_mvmpg_per_channel, p->mem_word_bytes) / (256 * meta_per_mvmpg_per_channel)); + } + + meta_per_mvmpg_per_channel_ub = (unsigned int)math_ceil2((double)meta_per_mvmpg_per_channel, p->mcache_line_size_bytes); + + //but for 4KB vmpg with 64KB tile blk + if (p->gpuvm_enable && (blk_bytes == 65536) && (vmpg_bytes == 4096)) + meta_per_mvmpg_per_channel_ub = 16 * meta_per_mvmpg_per_channel_ub; + + // If this mcache_row_bytes for the full viewport of the surface is less than or equal to mcache_bytes, + // then one mcache can be used for this request stream. If not, it is useful to know the width of the viewport that can be supported in the mcache_bytes. + if (p->gpuvm_enable || p->surf_vert) { + *p->mcache_row_bytes_per_channel = mvmpg_per_row_ub * meta_per_mvmpg_per_channel_ub; + *p->mcache_row_bytes = *p->mcache_row_bytes_per_channel * p->num_chans; + } else { // horizontal and gpuvm disable + *p->mcache_row_bytes = *p->meta_row_width_ub * p->blk_height * p->bytes_per_pixel / 256; + *p->mcache_row_bytes_per_channel = (unsigned int)math_ceil2((double)*p->mcache_row_bytes / p->num_chans, p->mcache_line_size_bytes); + } + + *p->dcc_dram_bw_pref_overhead_factor = 1 + math_max2(1.0 / 256.0, (double)*p->mcache_row_bytes / (double)p->full_swath_bytes); // dcc_dr_oh_pref + *p->num_mcaches = (unsigned int)math_ceil2((double)*p->mcache_row_bytes_per_channel / p->mcache_size_bytes, 1); + + mvmpg_per_mcache = p->mcache_size_bytes / meta_per_mvmpg_per_channel_ub; + *p->mvmpg_per_mcache_lb = (unsigned int)math_floor2(mvmpg_per_mcache, 1); + + DML_LOG_VERBOSE("DML::%s: gpuvm_enable = %u\n", __func__, p->gpuvm_enable); + DML_LOG_VERBOSE("DML::%s: vmpg_bytes = %u\n", __func__, vmpg_bytes); + DML_LOG_VERBOSE("DML::%s: blk_bytes = %u\n", __func__, blk_bytes); + DML_LOG_VERBOSE("DML::%s: meta_per_mvmpg_per_channel = %f\n", __func__, meta_per_mvmpg_per_channel); + DML_LOG_VERBOSE("DML::%s: mvmpg_per_row_ub = %u\n", __func__, mvmpg_per_row_ub); + DML_LOG_VERBOSE("DML::%s: meta_row_width_ub = %u\n", __func__, *p->meta_row_width_ub); + DML_LOG_VERBOSE("DML::%s: mvmpg_width = %u\n", __func__, *p->mvmpg_width); + DML_LOG_VERBOSE("DML::%s: mvmpg_height = %u\n", __func__, *p->mvmpg_height); + DML_LOG_VERBOSE("DML::%s: dcc_dram_bw_nom_overhead_factor = %f\n", __func__, *p->dcc_dram_bw_nom_overhead_factor); + DML_LOG_VERBOSE("DML::%s: dcc_dram_bw_pref_overhead_factor = %f\n", __func__, *p->dcc_dram_bw_pref_overhead_factor); + } + + DML_LOG_VERBOSE("DML::%s: mcache_row_bytes = %u\n", __func__, *p->mcache_row_bytes); + DML_LOG_VERBOSE("DML::%s: mcache_row_bytes_per_channel = %u\n", __func__, *p->mcache_row_bytes_per_channel); + DML_LOG_VERBOSE("DML::%s: num_mcaches = %u\n", __func__, *p->num_mcaches); + DML_ASSERT(*p->num_mcaches > 0); +} + +void dcn5_calculate_mcache_setting( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_calculate_mcache_setting_params *p) +{ + unsigned int n; + + struct dml2_core_shared_calculate_mcache_setting_locals *l = &scratch->calculate_mcache_setting_locals; + memset(l, 0, sizeof(struct dml2_core_shared_calculate_mcache_setting_locals)); + + *p->num_mcaches_l = 0; + *p->mcache_row_bytes_l = 0; + *p->mcache_row_bytes_per_channel_l = 0; + *p->dcc_dram_bw_nom_overhead_factor_l = 1.0; + *p->dcc_dram_bw_pref_overhead_factor_l = 1.0; + + *p->num_mcaches_c = 0; + *p->mcache_row_bytes_c = 0; + *p->mcache_row_bytes_per_channel_c = 0; + *p->dcc_dram_bw_nom_overhead_factor_c = 1.0; + *p->dcc_dram_bw_pref_overhead_factor_c = 1.0; + + *p->mall_comb_mcache_l = 0; + *p->mall_comb_mcache_c = 0; + *p->lc_comb_mcache = 0; + + if (!p->dcc_enable) + return; + + l->is_dual_plane = dml2_core_utils_is_420(p->source_format) || dml2_core_utils_is_422_planar(p->source_format) || p->source_format == dml2_rgbe_alpha; + + l->l_p.num_chans = p->num_chans; + l->l_p.mem_word_bytes = p->mem_word_bytes; + l->l_p.mcache_size_bytes = p->mcache_size_bytes; + l->l_p.mcache_line_size_bytes = p->mcache_line_size_bytes; + l->l_p.gpuvm_enable = p->gpuvm_enable; + l->l_p.gpuvm_page_size_kbytes = p->gpuvm_page_size_kbytes; + l->l_p.surf_vert = p->surf_vert; + l->l_p.vp_stationary = p->vp_stationary; + l->l_p.tiling_mode = p->tiling_mode; + l->l_p.vp_start_x = p->vp_start_x_l; + l->l_p.vp_start_y = p->vp_start_y_l; + l->l_p.full_vp_width = p->full_vp_width_l; + l->l_p.full_vp_height = p->full_vp_height_l; + l->l_p.blk_width = p->blk_width_l; + l->l_p.blk_height = p->blk_height_l; + l->l_p.vmpg_width = p->vmpg_width_l; + l->l_p.vmpg_height = p->vmpg_height_l; + l->l_p.full_swath_bytes = p->full_swath_bytes_l; + l->l_p.bytes_per_pixel = p->bytes_per_pixel_l; + + // output + l->l_p.num_mcaches = p->num_mcaches_l; + l->l_p.mcache_row_bytes = p->mcache_row_bytes_l; + l->l_p.mcache_row_bytes_per_channel = p->mcache_row_bytes_per_channel_l; + l->l_p.dcc_dram_bw_nom_overhead_factor = p->dcc_dram_bw_nom_overhead_factor_l; + l->l_p.dcc_dram_bw_pref_overhead_factor = p->dcc_dram_bw_pref_overhead_factor_l; + l->l_p.mvmpg_width = &l->mvmpg_width_l; + l->l_p.mvmpg_height = &l->mvmpg_height_l; + l->l_p.full_vp_access_width_mvmpg_aligned = &l->full_vp_access_width_mvmpg_aligned_l; + l->l_p.meta_row_width_ub = &l->meta_row_width_l; + l->l_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_l; + + dcn5_calculate_mcache_row_bytes(scratch, &l->l_p); + DML_ASSERT(*p->num_mcaches_l > 0); + + if (l->is_dual_plane) { + l->c_p.num_chans = p->num_chans; + l->c_p.mem_word_bytes = p->mem_word_bytes; + l->c_p.mcache_size_bytes = p->mcache_size_bytes; + l->c_p.mcache_line_size_bytes = p->mcache_line_size_bytes; + l->c_p.gpuvm_enable = p->gpuvm_enable; + l->c_p.gpuvm_page_size_kbytes = p->gpuvm_page_size_kbytes; + l->c_p.surf_vert = p->surf_vert; + l->c_p.vp_stationary = p->vp_stationary; + l->c_p.tiling_mode = p->tiling_mode; + l->c_p.vp_start_x = p->vp_start_x_c; + l->c_p.vp_start_y = p->vp_start_y_c; + l->c_p.full_vp_width = p->full_vp_width_c; + l->c_p.full_vp_height = p->full_vp_height_c; + l->c_p.blk_width = p->blk_width_c; + l->c_p.blk_height = p->blk_height_c; + l->c_p.vmpg_width = p->vmpg_width_c; + l->c_p.vmpg_height = p->vmpg_height_c; + l->c_p.full_swath_bytes = p->full_swath_bytes_c; + l->c_p.bytes_per_pixel = p->bytes_per_pixel_c; + + // output + l->c_p.num_mcaches = p->num_mcaches_c; + l->c_p.mcache_row_bytes = p->mcache_row_bytes_c; + l->c_p.mcache_row_bytes_per_channel = p->mcache_row_bytes_per_channel_c; + l->c_p.dcc_dram_bw_nom_overhead_factor = p->dcc_dram_bw_nom_overhead_factor_c; + l->c_p.dcc_dram_bw_pref_overhead_factor = p->dcc_dram_bw_pref_overhead_factor_c; + l->c_p.mvmpg_width = &l->mvmpg_width_c; + l->c_p.mvmpg_height = &l->mvmpg_height_c; + l->c_p.full_vp_access_width_mvmpg_aligned = &l->full_vp_access_width_mvmpg_aligned_c; + l->c_p.meta_row_width_ub = &l->meta_row_width_c; + l->c_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_c; + + dcn5_calculate_mcache_row_bytes(scratch, &l->c_p); + DML_ASSERT(*p->num_mcaches_c > 0); + } + + // Sharing for iMALL access + l->mcache_remainder_l = *p->mcache_row_bytes_per_channel_l % p->mcache_size_bytes; + l->mcache_remainder_c = *p->mcache_row_bytes_per_channel_c % p->mcache_size_bytes; + l->mvmpg_access_width_l = p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l; + l->mvmpg_access_width_c = p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c; + + if (p->imall_enable) { + *p->mall_comb_mcache_l = (2 * l->mcache_remainder_l <= p->mcache_size_bytes); + + if (l->is_dual_plane) + *p->mall_comb_mcache_c = (2 * l->mcache_remainder_c <= p->mcache_size_bytes); + } + + if (!p->surf_vert) // horizonatal access + l->luma_time_factor = (double)l->mvmpg_height_c / l->mvmpg_height_l * 2; + else // vertical access + l->luma_time_factor = (double)l->mvmpg_width_c / l->mvmpg_width_l * 2; + + // The algorithm starts with computing a non-integer, avg_mcache_element_size_l/c: + l->avg_mcache_element_size_l = l->meta_row_width_l / *p->num_mcaches_l; + if (l->is_dual_plane) { + l->avg_mcache_element_size_c = l->meta_row_width_c / *p->num_mcaches_c; + + /* if either remainder is 0, then mcache sharing is not needed or not possible due to full utilization */ + if (l->mcache_remainder_l && l->mcache_remainder_c) { + if (!p->imall_enable || (*p->mall_comb_mcache_l == *p->mall_comb_mcache_c)) { + l->lc_comb_last_mcache_size = (unsigned int)((l->mcache_remainder_l * (*p->mall_comb_mcache_l ? 2 : 1) * l->luma_time_factor) + + (l->mcache_remainder_c * (*p->mall_comb_mcache_c ? 2 : 1))); + } + *p->lc_comb_mcache = (l->lc_comb_last_mcache_size <= p->mcache_size_bytes) && (*p->mall_comb_mcache_l == *p->mall_comb_mcache_c); + } + } + + DML_LOG_VERBOSE("DML::%s: imall_enable = %u\n", __func__, p->imall_enable); + DML_LOG_VERBOSE("DML::%s: is_dual_plane = %u\n", __func__, l->is_dual_plane); + DML_LOG_VERBOSE("DML::%s: surf_vert = %u\n", __func__, p->surf_vert); + DML_LOG_VERBOSE("DML::%s: mvmpg_width_l = %u\n", __func__, l->mvmpg_width_l); + DML_LOG_VERBOSE("DML::%s: mvmpg_height_l = %u\n", __func__, l->mvmpg_height_l); + DML_LOG_VERBOSE("DML::%s: mcache_remainder_l = %f\n", __func__, l->mcache_remainder_l); + DML_LOG_VERBOSE("DML::%s: num_mcaches_l = %u\n", __func__, *p->num_mcaches_l); + DML_LOG_VERBOSE("DML::%s: avg_mcache_element_size_l = %u\n", __func__, l->avg_mcache_element_size_l); + DML_LOG_VERBOSE("DML::%s: mvmpg_access_width_l = %u\n", __func__, l->mvmpg_access_width_l); + DML_LOG_VERBOSE("DML::%s: mall_comb_mcache_l = %u\n", __func__, *p->mall_comb_mcache_l); + + if (l->is_dual_plane) { + DML_LOG_VERBOSE("DML::%s: mvmpg_width_c = %u\n", __func__, l->mvmpg_width_c); + DML_LOG_VERBOSE("DML::%s: mvmpg_height_c = %u\n", __func__, l->mvmpg_height_c); + DML_LOG_VERBOSE("DML::%s: mcache_remainder_c = %f\n", __func__, l->mcache_remainder_c); + DML_LOG_VERBOSE("DML::%s: luma_time_factor = %f\n", __func__, l->luma_time_factor); + DML_LOG_VERBOSE("DML::%s: num_mcaches_c = %u\n", __func__, *p->num_mcaches_c); + DML_LOG_VERBOSE("DML::%s: avg_mcache_element_size_c = %u\n", __func__, l->avg_mcache_element_size_c); + DML_LOG_VERBOSE("DML::%s: mvmpg_access_width_c = %u\n", __func__, l->mvmpg_access_width_c); + DML_LOG_VERBOSE("DML::%s: mall_comb_mcache_c = %u\n", __func__, *p->mall_comb_mcache_c); + DML_LOG_VERBOSE("DML::%s: lc_comb_last_mcache_size = %u\n", __func__, l->lc_comb_last_mcache_size); + DML_LOG_VERBOSE("DML::%s: lc_comb_mcache = %u\n", __func__, *p->lc_comb_mcache); + } + // calculate split_coordinate + l->full_vp_access_width_l = p->surf_vert ? p->full_vp_height_l : p->full_vp_width_l; + l->full_vp_access_width_c = p->surf_vert ? p->full_vp_height_c : p->full_vp_width_c; + + for (n = 0; n < *p->num_mcaches_l - 1; n++) { + p->mcache_offsets_l[n] = (unsigned int)(math_floor2((n + 1) * l->avg_mcache_element_size_l / l->mvmpg_access_width_l, 1)) * l->mvmpg_access_width_l; + } + p->mcache_offsets_l[*p->num_mcaches_l - 1] = l->full_vp_access_width_l; + + if (l->is_dual_plane) { + for (n = 0; n < *p->num_mcaches_c - 1; n++) { + p->mcache_offsets_c[n] = (unsigned int)(math_floor2((n + 1) * l->avg_mcache_element_size_c / l->mvmpg_access_width_c, 1)) * l->mvmpg_access_width_c; + } + p->mcache_offsets_c[*p->num_mcaches_c - 1] = l->full_vp_access_width_c; + } + for (n = 0; n < *p->num_mcaches_l; n++) + DML_LOG_VERBOSE("DML::%s: mcache_offsets_l[%u] = %u\n", __func__, n, p->mcache_offsets_l[n]); + + if (l->is_dual_plane) { + for (n = 0; n < *p->num_mcaches_c; n++) + DML_LOG_VERBOSE("DML::%s: mcache_offsets_c[%u] = %u\n", __func__, n, p->mcache_offsets_c[n]); + } + + // Luma/Chroma combine in the last mcache + // In the case of Luma/Chroma combine-mCache (with lc_comb_mcache==1), all mCaches except the last segment are filled as much as possible, when stay aligned to mvmpg boundary + if (*p->lc_comb_mcache && l->is_dual_plane) { + for (n = 0; n < *p->num_mcaches_l - 1; n++) + p->mcache_offsets_l[n] = (n + 1) * l->mvmpg_per_mcache_lb_l * l->mvmpg_access_width_l; + p->mcache_offsets_l[*p->num_mcaches_l - 1] = l->full_vp_access_width_l; + + for (n = 0; n < *p->num_mcaches_c - 1; n++) + p->mcache_offsets_c[n] = (n + 1) * l->mvmpg_per_mcache_lb_c * l->mvmpg_access_width_c; + p->mcache_offsets_c[*p->num_mcaches_c - 1] = l->full_vp_access_width_c; + + for (n = 0; n < *p->num_mcaches_l; n++) + DML_LOG_VERBOSE("DML::%s: mcache_offsets_l[%u] = %u\n", __func__, n, p->mcache_offsets_l[n]); + + for (n = 0; n < *p->num_mcaches_c; n++) + DML_LOG_VERBOSE("DML::%s: mcache_offsets_c[%u] = %u\n", __func__, n, p->mcache_offsets_c[n]); + } + + *p->mcache_shift_granularity_l = l->mvmpg_access_width_l; + *p->mcache_shift_granularity_c = l->mvmpg_access_width_c; +} + +void dcn5_calculate_avg_bandwidth_required( + double *avg_bandwidth_required, + // input + unsigned int num_active_planes, + double ReadBandwidthLuma[], + double ReadBandwidthChroma[], + double cursor_bw[], + double dcc_dram_bw_nom_overhead_factor_p0[], + double dcc_dram_bw_nom_overhead_factor_p1[]) +{ + unsigned int k; + *avg_bandwidth_required = 0; + for (k = 0; k < num_active_planes; ++k) { + *avg_bandwidth_required += dcc_dram_bw_nom_overhead_factor_p0[k] * ReadBandwidthLuma[k] + + dcc_dram_bw_nom_overhead_factor_p1[k] * ReadBandwidthChroma[k] + + cursor_bw[k]; + } +} + +void dcn5_calculate_hostvm_inefficiency_factor( + double *HostVMInefficiencyFactor, + double *HostVMInefficiencyFactorPrefetch, + + bool gpuvm_enable, + bool hostvm_enable, + unsigned int remote_iommu_outstanding_translations, + unsigned int max_outstanding_reqs, + double urg_bandwidth_avail_active_pixel_and_vm, + double urg_bandwidth_avail_active_vm_only) +{ + *HostVMInefficiencyFactor = 1; + *HostVMInefficiencyFactorPrefetch = 1; + + if (gpuvm_enable && hostvm_enable) { + *HostVMInefficiencyFactor = urg_bandwidth_avail_active_pixel_and_vm / urg_bandwidth_avail_active_vm_only; + *HostVMInefficiencyFactorPrefetch = *HostVMInefficiencyFactor; + + if ((*HostVMInefficiencyFactorPrefetch < 4) && (remote_iommu_outstanding_translations < max_outstanding_reqs)) + *HostVMInefficiencyFactorPrefetch = 4; + DML_LOG_VERBOSE("DML::%s: urg_bandwidth_avail_active_pixel_and_vm = %f\n", __func__, urg_bandwidth_avail_active_pixel_and_vm); + DML_LOG_VERBOSE("DML::%s: urg_bandwidth_avail_active_vm_only = %f\n", __func__, urg_bandwidth_avail_active_vm_only); + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactor = %f\n", __func__, *HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactorPrefetch = %f\n", __func__, *HostVMInefficiencyFactorPrefetch); + } +} + +void dcn5_calculate_tdlut_setting( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_calculate_tdlut_setting_params *p) +{ + (void)scratch; + // locals + unsigned int tdlut_bpe = 8; + unsigned int tdlut_width; + unsigned int tdlut_pitch_bytes; + unsigned int tdlut_footprint_bytes; + unsigned int vmpg_bytes; + unsigned int tdlut_vmpg_per_frame; + unsigned int tdlut_pte_req_per_frame; + unsigned int tdlut_bytes_per_line; + double tdlut_drain_rate; + unsigned int tdlut_mpc_width; + unsigned int tdlut_bytes_per_group_simple; + + if (!p->setup_for_tdlut) { + *p->tdlut_groups_per_2row_ub = 0; + *p->tdlut_opt_time = 0; + *p->tdlut_drain_time = 0; + *p->tdlut_bytes_per_group = 0; + *p->tdlut_pte_bytes_per_frame = 0; + *p->tdlut_bytes_per_frame = 0; + return; + } + + if (p->tdlut_mpc_width_flag) { + tdlut_mpc_width = 33; + tdlut_bytes_per_group_simple = 39*256; + } else { + tdlut_mpc_width = 17; + tdlut_bytes_per_group_simple = 10*256; + } + + vmpg_bytes = p->gpuvm_page_size_kbytes * 1024; + + if (p->tdlut_addressing_mode == dml2_tdlut_simple_linear) { + if (p->tdlut_width_mode == dml2_tdlut_width_17_cube) + tdlut_width = 4916; + else + tdlut_width = 35940; + } else { + if (p->tdlut_width_mode == dml2_tdlut_width_17_cube) + tdlut_width = 17; + else // dml2_tdlut_width_33_cube + tdlut_width = 33; + } + + if (p->is_gfx11) + tdlut_pitch_bytes = (unsigned int)math_ceil2(tdlut_width * tdlut_bpe, 256); //256B alignment + else + tdlut_pitch_bytes = (unsigned int)math_ceil2(tdlut_width * tdlut_bpe, 128); //128B alignment + + if (p->tdlut_addressing_mode == dml2_tdlut_sw_linear) + tdlut_footprint_bytes = tdlut_pitch_bytes * tdlut_width * tdlut_width; + else + tdlut_footprint_bytes = tdlut_pitch_bytes; + + if (!p->gpuvm_enable) { + tdlut_vmpg_per_frame = 0; + tdlut_pte_req_per_frame = 0; + } else { + tdlut_vmpg_per_frame = (unsigned int)math_ceil2(tdlut_footprint_bytes - 1, vmpg_bytes) / vmpg_bytes + 1; + tdlut_pte_req_per_frame = (unsigned int)math_ceil2(tdlut_vmpg_per_frame - 1, 8) / 8 + 1; + } + tdlut_bytes_per_line = (unsigned int)math_ceil2(tdlut_width * tdlut_bpe, 64); //64b request + *p->tdlut_pte_bytes_per_frame = tdlut_pte_req_per_frame * 64; + + if (p->tdlut_addressing_mode == dml2_tdlut_sw_linear) { + //the tdlut_width is either 17 or 33 but the 33x33x33 is subsampled every other line/slice + *p->tdlut_bytes_per_frame = tdlut_bytes_per_line * tdlut_mpc_width * tdlut_mpc_width; + *p->tdlut_bytes_per_group = tdlut_bytes_per_line * tdlut_mpc_width; + //the delivery cycles is DispClk cycles per line * number of lines * number of slices + tdlut_drain_rate = tdlut_bytes_per_line * p->dispclk_mhz / math_ceil2(tdlut_mpc_width/2.0, 1); + } else { + //tdlut_addressing_mode = tdlut_simple_linear, 3dlut width should be 4*1229=4916 elements + *p->tdlut_bytes_per_frame = (unsigned int)math_ceil2(tdlut_width * tdlut_bpe, 256); + *p->tdlut_bytes_per_group = tdlut_bytes_per_group_simple; + tdlut_drain_rate = 2 * tdlut_bpe * p->dispclk_mhz; + } + + //the tdlut is fetched during the 2 row times of prefetch. + if (p->setup_for_tdlut) { + *p->tdlut_groups_per_2row_ub = (unsigned int)math_ceil2((double) *p->tdlut_bytes_per_frame / *p->tdlut_bytes_per_group, 1); + *p->tdlut_opt_time = (int) (*p->tdlut_bytes_per_frame - p->cursor_buffer_size * 1024) / tdlut_drain_rate; + *p->tdlut_drain_time = p->cursor_buffer_size * 1024 / tdlut_drain_rate; + } + + DML_LOG_VERBOSE("DML::%s: cursor_buffer_size = %d\n", __func__, p->cursor_buffer_size); + DML_LOG_VERBOSE("DML::%s: gpuvm_enable = %d\n", __func__, p->gpuvm_enable); + DML_LOG_VERBOSE("DML::%s: vmpg_bytes = %d\n", __func__, vmpg_bytes); + DML_LOG_VERBOSE("DML::%s: tdlut_vmpg_per_frame = %d\n", __func__, tdlut_vmpg_per_frame); + DML_LOG_VERBOSE("DML::%s: tdlut_pte_req_per_frame = %d\n", __func__, tdlut_pte_req_per_frame); + + DML_LOG_VERBOSE("DML::%s: dispclk_mhz = %f\n", __func__, p->dispclk_mhz); + DML_LOG_VERBOSE("DML::%s: tdlut_width = %u\n", __func__, tdlut_width); + DML_LOG_VERBOSE("DML::%s: tdlut_addressing_mode = %s\n", __func__, (p->tdlut_addressing_mode == dml2_tdlut_sw_linear) ? "sw_linear" : "simple_linear"); + DML_LOG_VERBOSE("DML::%s: tdlut_pitch_bytes = %u\n", __func__, tdlut_pitch_bytes); + DML_LOG_VERBOSE("DML::%s: tdlut_footprint_bytes = %u\n", __func__, tdlut_footprint_bytes); + DML_LOG_VERBOSE("DML::%s: tdlut_bytes_per_frame = %u\n", __func__, *p->tdlut_bytes_per_frame); + DML_LOG_VERBOSE("DML::%s: tdlut_bytes_per_line = %u\n", __func__, tdlut_bytes_per_line); + DML_LOG_VERBOSE("DML::%s: tdlut_bytes_per_group = %u\n", __func__, *p->tdlut_bytes_per_group); + DML_LOG_VERBOSE("DML::%s: tdlut_drain_rate = %f\n", __func__, tdlut_drain_rate); + DML_LOG_VERBOSE("DML::%s: tdlut_delivery_cycles = %u\n", __func__, p->tdlut_addressing_mode == dml2_tdlut_sw_linear ? (unsigned int)math_ceil2(tdlut_mpc_width/2.0, 1) * tdlut_mpc_width * tdlut_mpc_width : (unsigned int)math_ceil2(tdlut_width/2.0, 1)); + DML_LOG_VERBOSE("DML::%s: tdlut_opt_time = %f\n", __func__, *p->tdlut_opt_time); + DML_LOG_VERBOSE("DML::%s: tdlut_drain_time = %f\n", __func__, *p->tdlut_drain_time); + DML_LOG_VERBOSE("DML::%s: tdlut_groups_per_2row_ub = %d\n", __func__, *p->tdlut_groups_per_2row_ub); +} + +static void dcn5_calculate_tarb( + const struct dml2_display_cfg *display_cfg, + unsigned int PixelChunkSizeInKByte, + unsigned int NumberOfActiveSurfaces, + unsigned int NumberOfDPP[], + unsigned int dpte_group_bytes[], + unsigned int tdlut_bytes_per_group[], + double HostVMInefficiencyFactor, + double HostVMInefficiencyFactorPrefetch, + double ReturnBW, + unsigned int MetaChunkSize, + + // output + double *Tarb, + double *Tarb_prefetch) +{ + double extra_bytes = 0; + double extra_bytes_prefetch = 0; + double HostVMDynamicLevels; + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + extra_bytes = extra_bytes + (NumberOfDPP[k] * PixelChunkSizeInKByte * 1024); + + if (display_cfg->plane_descriptors[k].surface.dcc.enable) + extra_bytes = extra_bytes + (MetaChunkSize * 1024); + + if (display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut) + extra_bytes = extra_bytes + tdlut_bytes_per_group[k]; + } + + extra_bytes_prefetch = extra_bytes; + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + HostVMDynamicLevels = dcn5_calculate_host_vm_dynamic_levels(display_cfg->gpuvm_enable, display_cfg->hostvm_enable, display_cfg->plane_descriptors[k].overrides.hostvm_min_page_size_kbytes, display_cfg->hostvm_max_non_cached_page_table_levels); + + if (display_cfg->gpuvm_enable == true) { + extra_bytes = extra_bytes + NumberOfDPP[k] * dpte_group_bytes[k] * (1 + 8 * HostVMDynamicLevels) * HostVMInefficiencyFactor; + extra_bytes_prefetch = extra_bytes_prefetch + NumberOfDPP[k] * dpte_group_bytes[k] * (1 + 8 * HostVMDynamicLevels) * HostVMInefficiencyFactorPrefetch; + } + } + *Tarb = extra_bytes / ReturnBW; + *Tarb_prefetch = extra_bytes_prefetch / ReturnBW; + DML_LOG_VERBOSE("DML::%s: PixelChunkSizeInKByte = %d\n", __func__, PixelChunkSizeInKByte); + DML_LOG_VERBOSE("DML::%s: MetaChunkSize = %d\n", __func__, MetaChunkSize); + DML_LOG_VERBOSE("DML::%s: extra_bytes = %f\n", __func__, extra_bytes); + DML_LOG_VERBOSE("DML::%s: extra_bytes_prefetch = %f\n", __func__, extra_bytes_prefetch); +} + +void dcn5_calculate_extra_latency( + const struct dml2_display_cfg *display_cfg, + unsigned int ROBBufferSizeInKByte, + unsigned int RoundTripPingLatencyCycles, + unsigned int ReorderingBytes, + double DCFCLK, + double FabricClock, + unsigned int PixelChunkSizeInKByte, + double ReturnBW, + unsigned int NumberOfActiveSurfaces, + unsigned int NumberOfDPP[], + unsigned int dpte_group_bytes[], + unsigned int tdlut_bytes_per_group[], + double HostVMInefficiencyFactor, + double HostVMInefficiencyFactorPrefetch, + enum dml2_qos_param_type qos_type, + bool max_outstanding_when_urgent_expected, + unsigned int max_outstanding_requests, + unsigned int request_size_bytes_luma[], + unsigned int request_size_bytes_chroma[], + unsigned int MetaChunkSize, + unsigned int dchub_arb_to_ret_delay, + double Ttrip, + unsigned int hostvm_mode, + + // output + double *ExtraLatency, // Tex + double *ExtraLatency_sr, // Tex_sr + double *ExtraLatencyPrefetch) + +{ + double Tarb; + double Tarb_prefetch; + double Tex_trips; + unsigned int max_request_size_bytes = 0; + + dcn5_calculate_tarb( + display_cfg, + PixelChunkSizeInKByte, + NumberOfActiveSurfaces, + NumberOfDPP, + dpte_group_bytes, + tdlut_bytes_per_group, + HostVMInefficiencyFactor, + HostVMInefficiencyFactorPrefetch, + ReturnBW, + MetaChunkSize, + // output + &Tarb, + &Tarb_prefetch); + + Tex_trips = (display_cfg->hostvm_enable && hostvm_mode == 1) ? (2.0 * Ttrip) : 0.0; + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + if (request_size_bytes_luma[k] > max_request_size_bytes) + max_request_size_bytes = request_size_bytes_luma[k]; + if (request_size_bytes_chroma[k] > max_request_size_bytes) + max_request_size_bytes = request_size_bytes_chroma[k]; + } + + if (qos_type == dml2_qos_param_type_dcn4x) { + *ExtraLatency_sr = dchub_arb_to_ret_delay / DCFCLK; + *ExtraLatency = *ExtraLatency_sr; + if (max_outstanding_when_urgent_expected) + *ExtraLatency = *ExtraLatency + (ROBBufferSizeInKByte * 1024 - max_outstanding_requests * max_request_size_bytes) / ReturnBW; + } else { + *ExtraLatency_sr = dchub_arb_to_ret_delay / DCFCLK + RoundTripPingLatencyCycles / FabricClock + ReorderingBytes / ReturnBW; + *ExtraLatency = *ExtraLatency_sr; + } + *ExtraLatency = *ExtraLatency + Tex_trips; + *ExtraLatencyPrefetch = *ExtraLatency + Tarb_prefetch; + *ExtraLatency = *ExtraLatency + Tarb; + *ExtraLatency_sr = *ExtraLatency_sr + Tarb; + + DML_LOG_VERBOSE("DML::%s: qos_type=%u\n", __func__, qos_type); + DML_LOG_VERBOSE("DML::%s: hostvm_mode=%u\n", __func__, hostvm_mode); + DML_LOG_VERBOSE("DML::%s: Tex_trips=%f\n", __func__, Tex_trips); + DML_LOG_VERBOSE("DML::%s: DCFCLK=%f\n", __func__, DCFCLK); + DML_LOG_VERBOSE("DML::%s: ReturnBW=%f\n", __func__, ReturnBW); + if (qos_type == dml2_qos_param_type_dcn4x) { + DML_LOG_VERBOSE("DML::%s: max_outstanding_when_urgent_expected=%u\n", __func__, max_outstanding_when_urgent_expected); + DML_LOG_VERBOSE("DML::%s: max_outstanding_requests=%u\n", __func__, max_outstanding_requests); + DML_LOG_VERBOSE("DML::%s: max_request_size_bytes=%u\n", __func__, max_request_size_bytes); + DML_LOG_VERBOSE("DML::%s: ROBBufferSizeInKByte=%u\n", __func__, ROBBufferSizeInKByte); + } else { + DML_LOG_VERBOSE("DML::%s: FabricClock=%f\n", __func__, FabricClock); + DML_LOG_VERBOSE("DML::%s: RoundTripPingLatencyCycles=%u\n", __func__, RoundTripPingLatencyCycles); + DML_LOG_VERBOSE("DML::%s: ReorderingBytes=%u\n", __func__, ReorderingBytes); + } + DML_LOG_VERBOSE("DML::%s: Tarb=%f\n", __func__, Tarb); + DML_LOG_VERBOSE("DML::%s: ExtraLatency=%f\n", __func__, *ExtraLatency); + DML_LOG_VERBOSE("DML::%s: ExtraLatency_sr=%f\n", __func__, *ExtraLatency_sr); + DML_LOG_VERBOSE("DML::%s: ExtraLatencyPrefetch=%f\n", __func__, *ExtraLatencyPrefetch); +} + +double dcn5_calculate_t_wait( + long reserved_vblank_time_ns, + double UrgentLatency, + double Ttrip, + double temp_read_or_ppt_blackout_us, + bool drr_enabled + ) +{ + double TWait; + double t_urg_trip = math_max2(UrgentLatency, Ttrip); + TWait = math_max2(reserved_vblank_time_ns / 1000.0, drr_enabled ? temp_read_or_ppt_blackout_us : 0.0) + t_urg_trip; + + DML_LOG_VERBOSE("DML::%s: reserved_vblank_time_ns = %ld\n", __func__, reserved_vblank_time_ns); + DML_LOG_VERBOSE("DML::%s: UrgentLatency = %f\n", __func__, UrgentLatency); + DML_LOG_VERBOSE("DML::%s: Ttrip = %f\n", __func__, Ttrip); + DML_LOG_VERBOSE("DML::%s: TWait = %f\n", __func__, TWait); + return TWait; +} + +static void dcn5_calculate_v_update_and_dynamic_metadata_parameters( + unsigned int MaxInterDCNTileRepeaters, + double Dppclk, + double Dispclk, + double DCFClkDeepSleep, + double PixelClock, + unsigned int HTotal, + unsigned int VBlank, + unsigned int DynamicMetadataTransmittedBytes, + unsigned int DynamicMetadataLinesBeforeActiveRequired, + unsigned int InterlaceEnable, + bool ProgressiveToInterlaceUnitInOPP, + + // Output + double *TSetup, + double *Tdmbf, + double *Tdmec, + double *Tdmsks, + unsigned int *VUpdateOffsetPix, + unsigned int *VUpdateWidthPix, + unsigned int *VReadyOffsetPix) +{ + double TotalRepeaterDelayTime; + TotalRepeaterDelayTime = MaxInterDCNTileRepeaters * (2 / Dppclk + 3 / Dispclk); + *VUpdateWidthPix = (unsigned int)(math_ceil2((14.0 / DCFClkDeepSleep + 12.0 / Dppclk + TotalRepeaterDelayTime) * PixelClock, 1.0)); + *VReadyOffsetPix = (unsigned int)(math_ceil2(math_max2(150.0 / Dppclk, TotalRepeaterDelayTime + 20.0 / DCFClkDeepSleep + 10.0 / Dppclk) * PixelClock, 1.0)); + *VUpdateOffsetPix = (unsigned int)(math_ceil2(HTotal / 4.0, 1.0)); + *TSetup = (*VUpdateOffsetPix + *VUpdateWidthPix + *VReadyOffsetPix) / PixelClock; + *Tdmbf = DynamicMetadataTransmittedBytes / 4.0 / Dispclk; + *Tdmec = HTotal / PixelClock; + + if (DynamicMetadataLinesBeforeActiveRequired == 0) { + *Tdmsks = VBlank * HTotal / PixelClock / 2.0; + } else { + *Tdmsks = DynamicMetadataLinesBeforeActiveRequired * HTotal / PixelClock; + } + if (InterlaceEnable == 1 && ProgressiveToInterlaceUnitInOPP == false) { + *Tdmsks = *Tdmsks / 2; + } + DML_LOG_VERBOSE("DML::%s: DynamicMetadataLinesBeforeActiveRequired = %u\n", __func__, DynamicMetadataLinesBeforeActiveRequired); + DML_LOG_VERBOSE("DML::%s: VBlank = %u\n", __func__, VBlank); + DML_LOG_VERBOSE("DML::%s: HTotal = %u\n", __func__, HTotal); + DML_LOG_VERBOSE("DML::%s: PixelClock = %f\n", __func__, PixelClock); + DML_LOG_VERBOSE("DML::%s: Dppclk = %f\n", __func__, Dppclk); + DML_LOG_VERBOSE("DML::%s: DCFClkDeepSleep = %f\n", __func__, DCFClkDeepSleep); + DML_LOG_VERBOSE("DML::%s: MaxInterDCNTileRepeaters = %u\n", __func__, MaxInterDCNTileRepeaters); + DML_LOG_VERBOSE("DML::%s: TotalRepeaterDelayTime = %f\n", __func__, TotalRepeaterDelayTime); + DML_LOG_VERBOSE("DML::%s: VUpdateWidthPix = %u\n", __func__, *VUpdateWidthPix); + DML_LOG_VERBOSE("DML::%s: VReadyOffsetPix = %u\n", __func__, *VReadyOffsetPix); + DML_LOG_VERBOSE("DML::%s: VUpdateOffsetPix = %u\n", __func__, *VUpdateOffsetPix); + DML_LOG_VERBOSE("DML::%s: Tdmsks = %f\n", __func__, *Tdmsks); +} + +bool dcn5_calculate_prefetch_schedule(struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_CalculatePrefetchSchedule_params *p) +{ + struct dml2_core_calcs_CalculatePrefetchSchedule_locals *s = &scratch->CalculatePrefetchSchedule_locals; + bool dcc_mrq_enable; + + unsigned int vm_bytes; + unsigned int extra_tdpe_bytes; + unsigned int tdlut_row_bytes; + unsigned int Lo; + unsigned int cnvc_delay_subtotal; + + s->NoTimeToPrefetch = false; + s->DPPCycles = 0; + s->DISPCLKCycles = 0; + s->DSTTotalPixelsAfterScaler = 0.0; + s->LineTime = 0.0; + s->dst_y_prefetch_equ = 0.0; + s->prefetch_bw_oto = 0.0; + s->Tvm_oto = 0.0; + s->Tr0_oto = 0.0; + s->Tvm_oto_lines = 0.0; + s->Tr0_oto_lines = 0.0; + s->dst_y_prefetch_oto = 0.0; + s->TimeForFetchingVM = 0.0; + s->TimeForFetchingRowInVBlank = 0.0; + s->LinesToRequestPrefetchPixelData = 0.0; + s->HostVMDynamicLevelsTrips = 0; + s->trip_to_mem = 0.0; + *p->Tvm_trips = 0.0; + *p->Tr0_trips = 0.0; + s->Tvm_trips_rounded = 0.0; + s->Tr0_trips_rounded = 0.0; + s->max_Tsw = 0.0; + s->Lsw_oto = 0.0; + *p->Tpre_rounded = 0.0; + s->prefetch_bw_equ = 0.0; + s->Tvm_equ = 0.0; + s->Tr0_equ = 0.0; + s->Tdmbf = 0.0; + s->Tdmec = 0.0; + s->Tdmsks = 0.0; + *p->prefetch_sw_bytes = 0.0; + s->prefetch_bw_pr = 0.0; + s->bytes_pp = 0.0; + s->dep_bytes = 0.0; + s->min_Lsw_oto = 0.0; + s->min_Lsw_equ = 0.0; + s->Tsw_est1 = 0.0; + s->Tsw_est2 = 0.0; + s->Tsw_est3 = 0.0; + s->cursor_prefetch_bytes = 0; + *p->prefetch_cursor_bw = 0; + + dcc_mrq_enable = (p->dcc_enable && p->mrq_present); + + s->TWait_p = p->TWait - p->Ttrip; // TWait includes max(Turg, Ttrip) and Ttrip here is already max(Turg, Ttrip) + + if (p->display_cfg->gpuvm_enable == true && p->display_cfg->hostvm_enable == true) { + s->HostVMDynamicLevelsTrips = p->display_cfg->hostvm_max_non_cached_page_table_levels; + } else { + s->HostVMDynamicLevelsTrips = 0; + } + DML_LOG_VERBOSE("DML::%s: dcc_enable = %u\n", __func__, p->dcc_enable); + DML_LOG_VERBOSE("DML::%s: mrq_present = %u\n", __func__, p->mrq_present); + DML_LOG_VERBOSE("DML::%s: dcc_mrq_enable = %u\n", __func__, dcc_mrq_enable); + DML_LOG_VERBOSE("DML::%s: GPUVMEnable = %u\n", __func__, p->display_cfg->gpuvm_enable); + DML_LOG_VERBOSE("DML::%s: GPUVMPageTableLevels = %u\n", __func__, p->display_cfg->gpuvm_max_page_table_levels); + DML_LOG_VERBOSE("DML::%s: DCCEnable = %u\n", __func__, p->myPipe->DCCEnable); + DML_LOG_VERBOSE("DML::%s: VStartup = %u\n", __func__, p->VStartup); + DML_LOG_VERBOSE("DML::%s: HostVMEnable = %u\n", __func__, p->display_cfg->hostvm_enable); + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactor = %f\n", __func__, p->HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: TWait = %f\n", __func__, p->TWait); + DML_LOG_VERBOSE("DML::%s: TWait_p = %f\n", __func__, s->TWait_p); + DML_LOG_VERBOSE("DML::%s: Ttrip = %f\n", __func__, p->Ttrip); + DML_LOG_VERBOSE("DML::%s: myPipe->Dppclk = %f\n", __func__, p->myPipe->Dppclk); + DML_LOG_VERBOSE("DML::%s: myPipe->Dispclk = %f\n", __func__, p->myPipe->Dispclk); + dcn5_calculate_v_update_and_dynamic_metadata_parameters( + p->MaxInterDCNTileRepeaters, + p->myPipe->Dppclk, + p->myPipe->Dispclk, + p->myPipe->DCFClkDeepSleep, + p->myPipe->PixelClock, + p->myPipe->HTotal, + p->myPipe->VBlank, + p->DynamicMetadataTransmittedBytes, + p->DynamicMetadataLinesBeforeActiveRequired, + p->myPipe->InterlaceEnable, + p->myPipe->ProgressiveToInterlaceUnitInOPP, + p->TSetup, + + // Output + &s->Tdmbf, + &s->Tdmec, + &s->Tdmsks, + p->VUpdateOffsetPix, + p->VUpdateWidthPix, + p->VReadyOffsetPix); + + s->LineTime = p->myPipe->HTotal / p->myPipe->PixelClock; + s->trip_to_mem = p->Ttrip; + *p->Tvm_trips = p->ExtraLatencyPrefetch + math_max2(s->trip_to_mem * (p->display_cfg->gpuvm_max_page_table_levels * (s->HostVMDynamicLevelsTrips + 1)), p->Turg); + if (dcc_mrq_enable) + *p->Tvm_trips_flip = *p->Tvm_trips; + else + *p->Tvm_trips_flip = *p->Tvm_trips - s->trip_to_mem; + + *p->Tr0_trips_flip = s->trip_to_mem * (s->HostVMDynamicLevelsTrips + 1); + *p->Tr0_trips = math_max2(*p->Tr0_trips_flip, p->tdlut_opt_time / 2); + + if (p->DynamicMetadataVMEnabled == true) { + *p->Tdmdl_vm = s->TWait_p + *p->Tvm_trips; + *p->Tdmdl = *p->Tdmdl_vm + p->Ttrip; + } else { + *p->Tdmdl_vm = 0; + *p->Tdmdl = s->TWait_p + p->ExtraLatencyPrefetch + p->Ttrip; // Tex + } + + if (p->DynamicMetadataEnable == true) { + if (p->VStartup * s->LineTime < *p->TSetup + *p->Tdmdl + s->Tdmbf + s->Tdmec + s->Tdmsks) { + *p->NotEnoughTimeForDynamicMetadata = true; + DML_LOG_VERBOSE("DML::%s: Not Enough Time for Dynamic Meta!\n", __func__); + DML_LOG_VERBOSE("DML::%s: Tdmbf: %fus - time for dmd transfer from dchub to dio output buffer\n", __func__, s->Tdmbf); + DML_LOG_VERBOSE("DML::%s: Tdmec: %fus - time dio takes to transfer dmd\n", __func__, s->Tdmec); + DML_LOG_VERBOSE("DML::%s: Tdmsks: %fus - time before active dmd must complete transmission at dio\n", __func__, s->Tdmsks); + DML_LOG_VERBOSE("DML::%s: Tdmdl: %fus - time for fabric to become ready and fetch dmd \n", __func__, *p->Tdmdl); + } else { + *p->NotEnoughTimeForDynamicMetadata = false; + } + } else { + *p->NotEnoughTimeForDynamicMetadata = false; + } + + cnvc_delay_subtotal = (unsigned int)(p->DPPCLKDelaySubtotalPlusCNVCFormater); + if (p->display_cfg->plane_descriptors->composition.scaler_info.upsp_enabled && dml2_core_utils_is_420(p->display_cfg->plane_descriptors->pixel_format)) + cnvc_delay_subtotal += 15; + if (p->display_cfg->plane_descriptors->composition.scaler_info.upsp_enabled && + (dml2_core_utils_is_422_planar(p->display_cfg->plane_descriptors->pixel_format) || dml2_core_utils_is_422_packed(p->display_cfg->plane_descriptors->pixel_format))) + cnvc_delay_subtotal += 6; + + if (!p->myPipe->ScalerEnabled) + s->DPPCycles = cnvc_delay_subtotal + (unsigned int)(p->DPPCLKDelaySCLLBOnly); + else if (!p->display_cfg->plane_descriptors->composition.scaler_info.easf_enabled && !p->display_cfg->plane_descriptors->composition.scaler_info.isharp_enabled) + s->DPPCycles = cnvc_delay_subtotal + (unsigned int)(p->DPPCLKDelaySCL); + else if (p->display_cfg->plane_descriptors->composition.scaler_info.easf_enabled && p->display_cfg->plane_descriptors->composition.scaler_info.isharp_enabled) + s->DPPCycles = cnvc_delay_subtotal + 100; + else // easf only + s->DPPCycles = cnvc_delay_subtotal + 80; + + s->DPPCycles = (unsigned int)(s->DPPCycles + p->myPipe->NumberOfCursors * p->DPPCLKDelayCNVCCursor); + + s->DISPCLKCycles = (unsigned int)p->DISPCLKDelaySubtotal; + + if (p->display_cfg->plane_descriptors->tdlut.setup_for_tdlut && p->display_cfg->plane_descriptors->tdlut.tdlut_width_mode == dml2_tdlut_width_33_cube) + s->DISPCLKCycles += 34; + + s->DISPCLKCycles += (p->myPipe->ODMMode != dml2_odm_mode_bypass ? 18 : 0); + + if (p->myPipe->Dppclk == 0.0 || p->myPipe->Dispclk == 0.0) + return true; + + *p->DSTXAfterScaler = (unsigned int)math_round(s->DPPCycles * p->myPipe->PixelClock / p->myPipe->Dppclk + s->DISPCLKCycles * p->myPipe->PixelClock / p->myPipe->Dispclk + p->DSCDelay); + + if (p->myPipe->ODMMode == dml2_odm_mode_split_1to2 || p->myPipe->ODMMode == dml2_odm_mode_mso_1to2) + *p->DSTXAfterScaler += p->myPipe->HActive / 2; + else if (p->myPipe->ODMMode == dml2_odm_mode_mso_1to4) + *p->DSTXAfterScaler += (p->myPipe->HActive * 3) / 4; + else + *p->DSTXAfterScaler += (p->myPipe->DPPPerSurface - 1) * p->DPP_RECOUT_WIDTH; + + DML_LOG_VERBOSE("DML::%s: DynamicMetadataVMEnabled = %u\n", __func__, p->DynamicMetadataVMEnabled); + DML_LOG_VERBOSE("DML::%s: DPPCycles = %u\n", __func__, s->DPPCycles); + DML_LOG_VERBOSE("DML::%s: PixelClock = %f\n", __func__, p->myPipe->PixelClock); + DML_LOG_VERBOSE("DML::%s: Dppclk = %f\n", __func__, p->myPipe->Dppclk); + DML_LOG_VERBOSE("DML::%s: DISPCLKCycles = %u\n", __func__, s->DISPCLKCycles); + DML_LOG_VERBOSE("DML::%s: DISPCLK = %f\n", __func__, p->myPipe->Dispclk); + DML_LOG_VERBOSE("DML::%s: DSCDelay = %u\n", __func__, p->DSCDelay); + DML_LOG_VERBOSE("DML::%s: ODMMode = %u\n", __func__, p->myPipe->ODMMode); + DML_LOG_VERBOSE("DML::%s: DPP_RECOUT_WIDTH = %u\n", __func__, p->DPP_RECOUT_WIDTH); + DML_LOG_VERBOSE("DML::%s: DSTXAfterScaler = %u\n", __func__, *p->DSTXAfterScaler); + + DML_LOG_VERBOSE("DML::%s: setup_for_tdlut = %u\n", __func__, p->setup_for_tdlut); + DML_LOG_VERBOSE("DML::%s: tdlut_opt_time = %f\n", __func__, p->tdlut_opt_time); + DML_LOG_VERBOSE("DML::%s: tdlut_pte_bytes_per_frame = %u\n", __func__, p->tdlut_pte_bytes_per_frame); + + if (p->OutputFormat == dml2_420 || (p->myPipe->InterlaceEnable && p->myPipe->ProgressiveToInterlaceUnitInOPP)) + *p->DSTYAfterScaler = 1; + else + *p->DSTYAfterScaler = 0; + + s->DSTTotalPixelsAfterScaler = *p->DSTYAfterScaler * p->myPipe->HTotal + *p->DSTXAfterScaler; + *p->DSTYAfterScaler = (unsigned int)(math_floor2(s->DSTTotalPixelsAfterScaler / p->myPipe->HTotal, 1)); + *p->DSTXAfterScaler = (unsigned int)(s->DSTTotalPixelsAfterScaler - ((double)(*p->DSTYAfterScaler * p->myPipe->HTotal))); + DML_LOG_VERBOSE("DML::%s: DSTXAfterScaler = %u (final)\n", __func__, *p->DSTXAfterScaler); + DML_LOG_VERBOSE("DML::%s: DSTYAfterScaler = %u (final)\n", __func__, *p->DSTYAfterScaler); + + s->NoTimeToPrefetch = false; + DML_LOG_VERBOSE("DML::%s: Tr0_trips = %f\n", __func__, *p->Tr0_trips); + DML_LOG_VERBOSE("DML::%s: Tvm_trips = %f\n", __func__, *p->Tvm_trips); + DML_LOG_VERBOSE("DML::%s: trip_to_mem = %f\n", __func__, s->trip_to_mem); + DML_LOG_VERBOSE("DML::%s: ExtraLatencyPrefetch = %f\n", __func__, p->ExtraLatencyPrefetch); + DML_LOG_VERBOSE("DML::%s: GPUVMPageTableLevels = %u\n", __func__, p->display_cfg->gpuvm_max_page_table_levels); + DML_LOG_VERBOSE("DML::%s: HostVMDynamicLevelsTrips = %u\n", __func__, s->HostVMDynamicLevelsTrips); + if (p->display_cfg->gpuvm_enable) { + s->Tvm_trips_rounded = math_ceil2(4.0 * *p->Tvm_trips / s->LineTime, 1.0) / 4.0 * s->LineTime; + *p->Tvm_trips_flip_rounded = math_ceil2(4.0 * *p->Tvm_trips_flip / s->LineTime, 1.0) / 4.0 * s->LineTime; + } else { + if (p->DynamicMetadataEnable || dcc_mrq_enable || p->setup_for_tdlut) + s->Tvm_trips_rounded = math_max2(s->LineTime * math_ceil2(4.0*math_max3(p->ExtraLatencyPrefetch, p->Turg, s->trip_to_mem)/s->LineTime, 1)/4, s->LineTime/4.0); + else + s->Tvm_trips_rounded = s->LineTime / 4.0; + *p->Tvm_trips_flip_rounded = s->LineTime / 4.0; + } + + s->Tvm_trips_rounded = math_max2(s->Tvm_trips_rounded, s->LineTime / 4.0); + *p->Tvm_trips_flip_rounded = math_max2(*p->Tvm_trips_flip_rounded, s->LineTime / 4.0); + + if (p->display_cfg->gpuvm_enable == true || p->setup_for_tdlut || dcc_mrq_enable) { + s->Tr0_trips_rounded = math_ceil2(4.0 * *p->Tr0_trips / s->LineTime, 1.0) / 4.0 * s->LineTime; + *p->Tr0_trips_flip_rounded = math_ceil2(4.0 * *p->Tr0_trips_flip / s->LineTime, 1.0) / 4.0 * s->LineTime; + } else { + s->Tr0_trips_rounded = s->LineTime / 4.0; + *p->Tr0_trips_flip_rounded = s->LineTime / 4.0; + } + s->Tr0_trips_rounded = math_max2(s->Tr0_trips_rounded, s->LineTime / 4.0); + *p->Tr0_trips_flip_rounded = math_max2(*p->Tr0_trips_flip_rounded, s->LineTime / 4.0); + + if (p->display_cfg->gpuvm_enable == true) { + if (p->display_cfg->gpuvm_max_page_table_levels >= 3) { + *p->Tno_bw = p->ExtraLatencyPrefetch + s->trip_to_mem * (double)((p->display_cfg->gpuvm_max_page_table_levels - 2) * (s->HostVMDynamicLevelsTrips + 1)); + } else if (p->display_cfg->gpuvm_max_page_table_levels == 1 && !dcc_mrq_enable && !p->setup_for_tdlut) { + *p->Tno_bw = p->ExtraLatencyPrefetch; + } else { + *p->Tno_bw = 0; + } + } else { + *p->Tno_bw = 0; + } + + if (p->mrq_present || p->display_cfg->gpuvm_max_page_table_levels >= 3) + *p->Tno_bw_flip = *p->Tno_bw; + else + *p->Tno_bw_flip = 0; //because there is no 3DLUT for iFlip + + if (dml2_core_utils_is_420(p->myPipe->SourcePixelFormat)) { + s->bytes_pp = p->myPipe->BytePerPixelY + p->myPipe->BytePerPixelC / 4.0; + } else if (dml2_core_utils_is_422_planar(p->myPipe->SourcePixelFormat)) { + s->bytes_pp = p->myPipe->BytePerPixelY + p->myPipe->BytePerPixelC / 2.0; + } else { + s->bytes_pp = p->myPipe->BytePerPixelY + p->myPipe->BytePerPixelC; + } + + s->prefetch_bw_pr = s->bytes_pp * p->myPipe->PixelClock / (double)p->myPipe->DPPPerSurface; + if (p->myPipe->VRatio < 1.0) + s->prefetch_bw_pr = p->myPipe->VRatio * s->prefetch_bw_pr; + s->max_Tsw = (math_max2(p->PrefetchSourceLinesY, p->PrefetchSourceLinesC) * s->LineTime); + + *p->prefetch_sw_bytes = p->PrefetchSourceLinesY * p->swath_width_luma_ub * p->myPipe->BytePerPixelY + p->PrefetchSourceLinesC * p->swath_width_chroma_ub * p->myPipe->BytePerPixelC; + s->prefetch_bw_pr = s->prefetch_bw_pr; + *p->prefetch_sw_bytes = *p->prefetch_sw_bytes; + s->prefetch_bw_oto = math_max2(s->prefetch_bw_pr, *p->prefetch_sw_bytes / s->max_Tsw); + + s->min_Lsw_oto = math_max2(p->PrefetchSourceLinesY, p->PrefetchSourceLinesC) / __DML2_CALCS_MAX_VRATIO_PRE_OTO__; + s->min_Lsw_oto = math_max2(s->min_Lsw_oto, 2.0); + s->min_Lsw_oto = math_max2(s->min_Lsw_oto, p->tdlut_drain_time / s->LineTime); + + s->min_Lsw_equ = math_max2(p->PrefetchSourceLinesY, p->PrefetchSourceLinesC) / __DML2_CALCS_MAX_VRATIO_PRE_EQU__; + s->min_Lsw_equ = math_max2(s->min_Lsw_equ, 2.0); + s->min_Lsw_equ = math_max2(s->min_Lsw_equ, p->tdlut_drain_time / s->LineTime); + + vm_bytes = p->vm_bytes; // vm_bytes is dpde0_bytes_per_frame_ub_l + dpde0_bytes_per_frame_ub_c + 2*extra_dpde_bytes; + extra_tdpe_bytes = (unsigned int)math_max2(0, (p->display_cfg->gpuvm_max_page_table_levels - 1) * 128); + + if (p->setup_for_tdlut) + vm_bytes = vm_bytes + p->tdlut_pte_bytes_per_frame + (p->display_cfg->gpuvm_enable ? extra_tdpe_bytes : 0); + + tdlut_row_bytes = (unsigned long) math_ceil2(p->tdlut_bytes_per_frame/2.0, 1.0); + + s->prefetch_bw_oto = math_min2(s->prefetch_bw_oto, *p->prefetch_sw_bytes / (s->min_Lsw_oto * s->LineTime)); + + s->Lsw_oto = math_ceil2(4.0 * *p->prefetch_sw_bytes / s->prefetch_bw_oto / s->LineTime, 1.0) / 4.0; + s->prefetch_bw_oto = math_max3(s->prefetch_bw_oto, + p->vm_bytes * p->HostVMInefficiencyFactor / (31 * s->LineTime) - *p->Tno_bw, + (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) / (15 * s->LineTime)); + + if (p->display_cfg->gpuvm_enable == true) { + s->Tvm_oto = math_max3( + *p->Tvm_trips, + *p->Tno_bw + vm_bytes * p->HostVMInefficiencyFactor / s->prefetch_bw_oto, + s->LineTime / 4.0); + DML_LOG_VERBOSE("DML::%s: Tvm_oto max0 = %f\n", __func__, *p->Tvm_trips); + DML_LOG_VERBOSE("DML::%s: Tvm_oto max1 = %f\n", __func__, *p->Tno_bw + vm_bytes * p->HostVMInefficiencyFactor / s->prefetch_bw_oto); + DML_LOG_VERBOSE("DML::%s: Tvm_oto max2 = %f\n", __func__, s->LineTime / 4.0); + } else { + s->Tvm_oto = s->Tvm_trips_rounded; + } + + if ((p->display_cfg->gpuvm_enable == true || p->setup_for_tdlut || dcc_mrq_enable)) { + s->Tr0_oto = math_max3( + *p->Tr0_trips, + (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) / s->prefetch_bw_oto, + s->LineTime / 4.0); + DML_LOG_VERBOSE("DML::%s: Tr0_oto max0 = %f\n", __func__, *p->Tr0_trips); + DML_LOG_VERBOSE("DML::%s: Tr0_oto max1 = %f\n", __func__, (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) / s->prefetch_bw_oto); + DML_LOG_VERBOSE("DML::%s: Tr0_oto max2 = %f\n", __func__, s->LineTime / 4); + } else + s->Tr0_oto = s->LineTime / 4.0; + + s->Tvm_oto_lines = math_ceil2(4.0 * s->Tvm_oto / s->LineTime, 1) / 4.0; + s->Tr0_oto_lines = math_ceil2(4.0 * s->Tr0_oto / s->LineTime, 1) / 4.0; + s->dst_y_prefetch_oto = s->Tvm_oto_lines + 2 * s->Tr0_oto_lines + s->Lsw_oto; + + //To (time for delay after scaler) in line time + Lo = (unsigned int)(*p->DSTYAfterScaler + (double)*p->DSTXAfterScaler / (double)p->myPipe->HTotal); + + //Tpre_equ in line time + if (p->DynamicMetadataVMEnabled && p->DynamicMetadataEnable) + s->dst_y_prefetch_equ = p->VStartup - (*p->TSetup + math_max2(p->TCalc, *p->Tvm_trips) + s->TWait_p) / s->LineTime - Lo; + else + s->dst_y_prefetch_equ = p->VStartup - (*p->TSetup + math_max2(p->TCalc, p->ExtraLatencyPrefetch) + s->TWait_p) / s->LineTime - Lo; + s->dst_y_prefetch_equ = math_min2(s->dst_y_prefetch_equ, 63.75); // limit to the reg limit of U6.2 for DST_Y_PREFETCH + + DML_LOG_VERBOSE("DML::%s: HTotal = %u\n", __func__, p->myPipe->HTotal); + DML_LOG_VERBOSE("DML::%s: min_Lsw_oto = %f\n", __func__, s->min_Lsw_oto); + DML_LOG_VERBOSE("DML::%s: min_Lsw_equ = %f\n", __func__, s->min_Lsw_equ); + DML_LOG_VERBOSE("DML::%s: Tno_bw = %f\n", __func__, *p->Tno_bw); + DML_LOG_VERBOSE("DML::%s: Tno_bw_flip = %f\n", __func__, *p->Tno_bw_flip); + DML_LOG_VERBOSE("DML::%s: ExtraLatencyPrefetch = %f\n", __func__, p->ExtraLatencyPrefetch); + DML_LOG_VERBOSE("DML::%s: trip_to_mem = %f\n", __func__, s->trip_to_mem); + DML_LOG_VERBOSE("DML::%s: BytePerPixelY = %u\n", __func__, p->myPipe->BytePerPixelY); + DML_LOG_VERBOSE("DML::%s: PrefetchSourceLinesY = %f\n", __func__, p->PrefetchSourceLinesY); + DML_LOG_VERBOSE("DML::%s: swath_width_luma_ub = %u\n", __func__, p->swath_width_luma_ub); + DML_LOG_VERBOSE("DML::%s: BytePerPixelC = %u\n", __func__, p->myPipe->BytePerPixelC); + DML_LOG_VERBOSE("DML::%s: PrefetchSourceLinesC = %f\n", __func__, p->PrefetchSourceLinesC); + DML_LOG_VERBOSE("DML::%s: swath_width_chroma_ub = %u\n", __func__, p->swath_width_chroma_ub); + DML_LOG_VERBOSE("DML::%s: prefetch_sw_bytes = %f\n", __func__, *p->prefetch_sw_bytes); + DML_LOG_VERBOSE("DML::%s: max_Tsw = %f\n", __func__, s->max_Tsw); + DML_LOG_VERBOSE("DML::%s: bytes_pp = %f\n", __func__, s->bytes_pp); + DML_LOG_VERBOSE("DML::%s: vm_bytes = %u\n", __func__, vm_bytes); + DML_LOG_VERBOSE("DML::%s: PixelPTEBytesPerRow = %u\n", __func__, p->PixelPTEBytesPerRow); + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactor = %f\n", __func__, p->HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: Tvm_trips = %f\n", __func__, *p->Tvm_trips); + DML_LOG_VERBOSE("DML::%s: Tr0_trips = %f\n", __func__, *p->Tr0_trips); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip = %f\n", __func__, *p->Tvm_trips_flip); + DML_LOG_VERBOSE("DML::%s: Tr0_trips_flip = %f\n", __func__, *p->Tr0_trips_flip); + DML_LOG_VERBOSE("DML::%s: prefetch_bw_pr = %f\n", __func__, s->prefetch_bw_pr); + DML_LOG_VERBOSE("DML::%s: prefetch_bw_oto = %f\n", __func__, s->prefetch_bw_oto); + DML_LOG_VERBOSE("DML::%s: Tr0_oto = %f\n", __func__, s->Tr0_oto); + DML_LOG_VERBOSE("DML::%s: Tvm_oto = %f\n", __func__, s->Tvm_oto); + DML_LOG_VERBOSE("DML::%s: Tvm_oto_lines = %f\n", __func__, s->Tvm_oto_lines); + DML_LOG_VERBOSE("DML::%s: Tr0_oto_lines = %f\n", __func__, s->Tr0_oto_lines); + DML_LOG_VERBOSE("DML::%s: Lsw_oto = %f\n", __func__, s->Lsw_oto); + DML_LOG_VERBOSE("DML::%s: dst_y_prefetch_oto = %f\n", __func__, s->dst_y_prefetch_oto); + DML_LOG_VERBOSE("DML::%s: dst_y_prefetch_equ = %f\n", __func__, s->dst_y_prefetch_equ); + DML_LOG_VERBOSE("DML::%s: tdlut_row_bytes = %d\n", __func__, tdlut_row_bytes); + DML_LOG_VERBOSE("DML::%s: meta_row_bytes = %d\n", __func__, p->meta_row_bytes); + s->dst_y_prefetch_equ = math_floor2(4.0 * (s->dst_y_prefetch_equ + 0.125), 1) / 4.0; + *p->Tpre_rounded = s->dst_y_prefetch_equ * s->LineTime; + + DML_LOG_VERBOSE("DML::%s: dst_y_prefetch_equ: %f (after round)\n", __func__, s->dst_y_prefetch_equ); + DML_LOG_VERBOSE("DML::%s: LineTime: %f\n", __func__, s->LineTime); + DML_LOG_VERBOSE("DML::%s: VStartup: %u\n", __func__, p->VStartup); + DML_LOG_VERBOSE("DML::%s: Tvstartup: %fus - time between vstartup and first pixel of active\n", __func__, p->VStartup * s->LineTime); + DML_LOG_VERBOSE("DML::%s: TSetup: %fus - time from vstartup to vready\n", __func__, *p->TSetup); + DML_LOG_VERBOSE("DML::%s: TCalc: %fus - time for calculations in dchub starting at vready\n", __func__, p->TCalc); + DML_LOG_VERBOSE("DML::%s: TWait: %fus - time for fabric to become ready max(pstate exit,cstate enter/exit, urgent latency) after TCalc\n", __func__, p->TWait); + DML_LOG_VERBOSE("DML::%s: Tdmbf: %fus - time for dmd transfer from dchub to dio output buffer\n", __func__, s->Tdmbf); + DML_LOG_VERBOSE("DML::%s: Tdmec: %fus - time dio takes to transfer dmd\n", __func__, s->Tdmec); + DML_LOG_VERBOSE("DML::%s: Tdmsks: %fus - time before active dmd must complete transmission at dio\n", __func__, s->Tdmsks); + DML_LOG_VERBOSE("DML::%s: TWait = %f\n", __func__, p->TWait); + DML_LOG_VERBOSE("DML::%s: TWait_p = %f\n", __func__, s->TWait_p); + DML_LOG_VERBOSE("DML::%s: Ttrip = %f\n", __func__, p->Ttrip); + DML_LOG_VERBOSE("DML::%s: Tex = %f\n", __func__, p->ExtraLatencyPrefetch); + DML_LOG_VERBOSE("DML::%s: Tdmdl_vm: %fus - time for vm stages of dmd \n", __func__, *p->Tdmdl_vm); + DML_LOG_VERBOSE("DML::%s: Tdmdl: %fus - time for fabric to become ready and fetch dmd \n", __func__, *p->Tdmdl); + DML_LOG_VERBOSE("DML::%s: TWait_p: %fus\n", __func__, s->TWait_p); + DML_LOG_VERBOSE("DML::%s: Ttrip: %fus\n", __func__, p->Ttrip); + DML_LOG_VERBOSE("DML::%s: DSTXAfterScaler: %u pixels - number of pixel clocks pipeline and buffer delay after scaler \n", __func__, *p->DSTXAfterScaler); + DML_LOG_VERBOSE("DML::%s: DSTYAfterScaler: %u lines - number of lines of pipeline and buffer delay after scaler \n", __func__, *p->DSTYAfterScaler); + DML_LOG_VERBOSE("DML::%s: vm_bytes: %f (hvm inefficiency scaled)\n", __func__, vm_bytes*p->HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: row_bytes: %f (hvm inefficiency scaled, 1 row)\n", __func__, p->PixelPTEBytesPerRow*p->HostVMInefficiencyFactor+p->meta_row_bytes+tdlut_row_bytes); + DML_LOG_VERBOSE("DML::%s: Tno_bw: %f\n", __func__, *p->Tno_bw); + DML_LOG_VERBOSE("DML::%s: Tpre_rounded: %f\n", __func__, *p->Tpre_rounded); + DML_LOG_VERBOSE("DML::%s: Tvm_trips=%f Tvm_trips_rounded: %f, delta=%f\n", __func__, *p->Tvm_trips, s->Tvm_trips_rounded, (s->Tvm_trips_rounded - *p->Tvm_trips)); + + *p->dst_y_per_vm_vblank = 0; + *p->dst_y_per_row_vblank = 0; + *p->VRatioPrefetchY = 0; + *p->VRatioPrefetchC = 0; + *p->RequiredPrefetchPixelDataBWLuma = 0; + + // Derive bandwidth by finding how much data to move within the time constraint + // Tpre_rounded is Tpre rounding to 2-bit fraction + // Tvm_trips_rounded is Tvm_trips ceiling to 1/4 line time + // Tr0_trips_rounded is Tr0_trips ceiling to 1/4 line time + // So that means prefetch bw calculated can be higher since the total time availabe for prefetch is less + bool min_Lsw_equ_ok = *p->Tpre_rounded >= s->Tvm_trips_rounded + 2.0*s->Tr0_trips_rounded + s->min_Lsw_equ*s->LineTime; + + if (s->dst_y_prefetch_equ > 1 && min_Lsw_equ_ok) { + s->prefetch_bw1 = 0.; + s->prefetch_bw2 = 0.; + s->prefetch_bw3 = 0.; + s->prefetch_bw4 = 0.; + + // prefetch_bw1: VM + 2*R0 + SW + if (*p->Tpre_rounded - *p->Tno_bw > 0) { + s->prefetch_bw1 = (vm_bytes * p->HostVMInefficiencyFactor + + 2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) + + *p->prefetch_sw_bytes) + / (*p->Tpre_rounded - *p->Tno_bw); + s->Tsw_est1 = *p->prefetch_sw_bytes / s->prefetch_bw1; + } else + s->prefetch_bw1 = 0; + + DML_LOG_VERBOSE("DML::%s: prefetch_bw1: %f\n", __func__, s->prefetch_bw1); + if ((s->Tsw_est1 < s->min_Lsw_equ * s->LineTime) && (*p->Tpre_rounded - s->min_Lsw_equ * s->LineTime - 0.75 * s->LineTime - *p->Tno_bw > 0)) { + s->prefetch_bw1 = (vm_bytes * p->HostVMInefficiencyFactor + 2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes)) / + (*p->Tpre_rounded - s->min_Lsw_equ * s->LineTime - 0.75 * s->LineTime - *p->Tno_bw); + DML_LOG_VERBOSE("DML::%s: vm and 2 rows bytes = %f\n", __func__, (vm_bytes * p->HostVMInefficiencyFactor + 2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes))); + DML_LOG_VERBOSE("DML::%s: Tpre_rounded = %f\n", __func__, *p->Tpre_rounded); + DML_LOG_VERBOSE("DML::%s: minus term = %f\n", __func__, s->min_Lsw_equ * s->LineTime + 0.75 * s->LineTime + *p->Tno_bw); + DML_LOG_VERBOSE("DML::%s: min_Lsw_equ = %f\n", __func__, s->min_Lsw_equ); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, s->LineTime); + DML_LOG_VERBOSE("DML::%s: Tno_bw = %f\n", __func__, *p->Tno_bw); + DML_LOG_VERBOSE("DML::%s: Time to fetch vm and 2 rows = %f\n", __func__, (*p->Tpre_rounded - s->min_Lsw_equ * s->LineTime - 0.75 * s->LineTime - *p->Tno_bw)); + DML_LOG_VERBOSE("DML::%s: prefetch_bw1: %f (updated)\n", __func__, s->prefetch_bw1); + } + + // prefetch_bw2: VM + SW + if (*p->Tpre_rounded - *p->Tno_bw - 2.0 * s->Tr0_trips_rounded > 0) { + s->prefetch_bw2 = (vm_bytes * p->HostVMInefficiencyFactor + *p->prefetch_sw_bytes) / + (*p->Tpre_rounded - *p->Tno_bw - 2.0 * s->Tr0_trips_rounded); + s->Tsw_est2 = *p->prefetch_sw_bytes / s->prefetch_bw2; + } else + s->prefetch_bw2 = 0; + + DML_LOG_VERBOSE("DML::%s: prefetch_bw2: %f\n", __func__, s->prefetch_bw2); + if ((s->Tsw_est2 < s->min_Lsw_equ * s->LineTime) && ((*p->Tpre_rounded - *p->Tno_bw - 2.0 * s->Tr0_trips_rounded - s->min_Lsw_equ * s->LineTime - 0.25 * s->LineTime) > 0)) { + s->prefetch_bw2 = vm_bytes * p->HostVMInefficiencyFactor / (*p->Tpre_rounded - *p->Tno_bw - 2.0 * s->Tr0_trips_rounded - s->min_Lsw_equ * s->LineTime - 0.25 * s->LineTime); + DML_LOG_VERBOSE("DML::%s: prefetch_bw2: %f (updated)\n", __func__, s->prefetch_bw2); + } + + // prefetch_bw3: 2*R0 + SW + if (*p->Tpre_rounded - s->Tvm_trips_rounded > 0) { + s->prefetch_bw3 = (2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) + *p->prefetch_sw_bytes) / + (*p->Tpre_rounded - s->Tvm_trips_rounded); + s->Tsw_est3 = *p->prefetch_sw_bytes / s->prefetch_bw3; + } else + s->prefetch_bw3 = 0; + + DML_LOG_VERBOSE("DML::%s: prefetch_bw3: %f\n", __func__, s->prefetch_bw3); + if ((s->Tsw_est3 < s->min_Lsw_equ * s->LineTime) && ((*p->Tpre_rounded - s->min_Lsw_equ * s->LineTime - 0.5 * s->LineTime - s->Tvm_trips_rounded) > 0)) { + s->prefetch_bw3 = (2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes)) / (*p->Tpre_rounded - s->min_Lsw_equ * s->LineTime - 0.5 * s->LineTime - s->Tvm_trips_rounded); + DML_LOG_VERBOSE("DML::%s: prefetch_bw3: %f (updated)\n", __func__, s->prefetch_bw3); + } + + // prefetch_bw4: SW + if (*p->Tpre_rounded - s->Tvm_trips_rounded - 2 * s->Tr0_trips_rounded > 0) + s->prefetch_bw4 = *p->prefetch_sw_bytes / (*p->Tpre_rounded - s->Tvm_trips_rounded - 2 * s->Tr0_trips_rounded); + else + s->prefetch_bw4 = 0; + + DML_LOG_VERBOSE("DML::%s: Tno_bw: %f\n", __func__, *p->Tno_bw); + DML_LOG_VERBOSE("DML::%s: Tpre_rounded: %f\n", __func__, *p->Tpre_rounded); + DML_LOG_VERBOSE("DML::%s: Tvm_trips=%f Tvm_trips_rounded: %f, delta=%f\n", __func__, *p->Tvm_trips, s->Tvm_trips_rounded, (s->Tvm_trips_rounded - *p->Tvm_trips)); + DML_LOG_VERBOSE("DML::%s: Tr0_trips=%f Tr0_trips_rounded: %f, delta=%f\n", __func__, *p->Tr0_trips, s->Tr0_trips_rounded, (s->Tr0_trips_rounded - *p->Tr0_trips)); + DML_LOG_VERBOSE("DML::%s: Tsw_est1: %f\n", __func__, s->Tsw_est1); + DML_LOG_VERBOSE("DML::%s: Tsw_est2: %f\n", __func__, s->Tsw_est2); + DML_LOG_VERBOSE("DML::%s: Tsw_est3: %f\n", __func__, s->Tsw_est3); + DML_LOG_VERBOSE("DML::%s: prefetch_bw1: %f (final)\n", __func__, s->prefetch_bw1); + DML_LOG_VERBOSE("DML::%s: prefetch_bw2: %f (final)\n", __func__, s->prefetch_bw2); + DML_LOG_VERBOSE("DML::%s: prefetch_bw3: %f (final)\n", __func__, s->prefetch_bw3); + DML_LOG_VERBOSE("DML::%s: prefetch_bw4: %f (final)\n", __func__, s->prefetch_bw4); + { + bool Case1OK = false; + bool Case2OK = false; + bool Case3OK = false; + + // get "equalized" bw among all stages (vm, r0, sw), so based is all 3 stages are just above the latency-based requirement + // so it is not too dis-portionally favor a particular stage, next is either r0 more agressive and next is vm more agressive, the worst is all are agressive + // vs the latency based number + + // prefetch_bw1: VM + 2*R0 + SW + // so prefetch_bw1 will have enough bw to transfer the necessary data within Tpre_rounded - Tno_bw (Tpre is the the worst-case latency based time to fetch the data) + // here is to make sure equ bw wont be more agressive than the latency-based requirement. + // check vm time >= vm_trips + // check r0 time >= r0_trips + + double total_row_bytes = (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes); + + DML_LOG_VERBOSE("DML::%s: Tvm_trips_rounded = %f\n", __func__, s->Tvm_trips_rounded); + DML_LOG_VERBOSE("DML::%s: Tr0_trips_rounded = %f\n", __func__, s->Tr0_trips_rounded); + + if (s->prefetch_bw1 > 0) { + double vm_transfer_time = *p->Tno_bw + vm_bytes * p->HostVMInefficiencyFactor / s->prefetch_bw1; + double row_transfer_time = total_row_bytes / s->prefetch_bw1; + DML_LOG_VERBOSE("DML::%s: Case1: vm_transfer_time = %f\n", __func__, vm_transfer_time); + DML_LOG_VERBOSE("DML::%s: Case1: row_transfer_time = %f\n", __func__, row_transfer_time); + if (vm_transfer_time >= s->Tvm_trips_rounded && row_transfer_time >= s->Tr0_trips_rounded) { + Case1OK = true; + } + } + + // prefetch_bw2: VM + SW + // prefetch_bw2 will be enough bw to transfer VM and SW data within (Tpre_rounded - Tr0_trips_rounded - Tno_bw) + // check vm time >= vm_trips + // check r0 time < r0_trips + if (s->prefetch_bw2 > 0) { + double vm_transfer_time = *p->Tno_bw + vm_bytes * p->HostVMInefficiencyFactor / s->prefetch_bw2; + double row_transfer_time = total_row_bytes / s->prefetch_bw2; + DML_LOG_VERBOSE("DML::%s: Case2: vm_transfer_time = %f\n", __func__, vm_transfer_time); + DML_LOG_VERBOSE("DML::%s: Case2: row_transfer_time = %f\n", __func__, row_transfer_time); + if (vm_transfer_time >= s->Tvm_trips_rounded && row_transfer_time < s->Tr0_trips_rounded) { + Case2OK = true; + } + } + + // prefetch_bw3: VM + 2*R0 + // check vm time < vm_trips + // check r0 time >= r0_trips + if (s->prefetch_bw3 > 0) { + double vm_transfer_time = *p->Tno_bw + vm_bytes * p->HostVMInefficiencyFactor / s->prefetch_bw3; + double row_transfer_time = total_row_bytes / s->prefetch_bw3; + DML_LOG_VERBOSE("DML::%s: Case3: vm_transfer_time = %f\n", __func__, vm_transfer_time); + DML_LOG_VERBOSE("DML::%s: Case3: row_transfer_time = %f\n", __func__, row_transfer_time); + if (vm_transfer_time < s->Tvm_trips_rounded && row_transfer_time >= s->Tr0_trips_rounded) { + Case3OK = true; + } + } + + if (Case1OK) { + s->prefetch_bw_equ = s->prefetch_bw1; + } else if (Case2OK) { + s->prefetch_bw_equ = s->prefetch_bw2; + } else if (Case3OK) { + s->prefetch_bw_equ = s->prefetch_bw3; + } else { + s->prefetch_bw_equ = s->prefetch_bw4; + } + + s->prefetch_bw_equ = math_max3(s->prefetch_bw_equ, + p->vm_bytes * p->HostVMInefficiencyFactor / (31 * s->LineTime) - *p->Tno_bw, + (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) / (15 * s->LineTime)); + DML_LOG_VERBOSE("DML::%s: Case1OK: %u\n", __func__, Case1OK); + DML_LOG_VERBOSE("DML::%s: Case2OK: %u\n", __func__, Case2OK); + DML_LOG_VERBOSE("DML::%s: Case3OK: %u\n", __func__, Case3OK); + DML_LOG_VERBOSE("DML::%s: prefetch_bw_equ: %f\n", __func__, s->prefetch_bw_equ); + + if (s->prefetch_bw_equ > 0) { + if (p->display_cfg->gpuvm_enable == true) { + s->Tvm_equ = math_max3(*p->Tno_bw + vm_bytes * p->HostVMInefficiencyFactor / s->prefetch_bw_equ, *p->Tvm_trips, s->LineTime / 4); + } else { + s->Tvm_equ = s->LineTime / 4; + } + + if (p->display_cfg->gpuvm_enable == true || dcc_mrq_enable || p->setup_for_tdlut) { + s->Tr0_equ = math_max3((p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) / s->prefetch_bw_equ, // PixelPTEBytesPerRow is dpte_row_bytes + *p->Tr0_trips, + s->LineTime / 4); + } else { + s->Tr0_equ = s->LineTime / 4; + } + } else { + s->Tvm_equ = 0; + s->Tr0_equ = 0; + DML_LOG_VERBOSE("DML::%s: prefetch_bw_equ equals 0!\n", __func__); + } + } + DML_LOG_VERBOSE("DML::%s: Tvm_equ = %f\n", __func__, s->Tvm_equ); + DML_LOG_VERBOSE("DML::%s: Tr0_equ = %f\n", __func__, s->Tr0_equ); + + s->LinesToRequestPrefetchPixelData = s->dst_y_prefetch_equ - + (math_ceil2(4.0 * s->Tvm_equ / s->LineTime, 1.0) / 4.0) - + 2 * (math_ceil2(4.0 * s->Tr0_equ / s->LineTime, 1.0) / 4.0); + if (s->dst_y_prefetch_oto < s->dst_y_prefetch_equ && !(p->use_max_lsw && s->min_Lsw_oto < s->LinesToRequestPrefetchPixelData)) { + *p->dst_y_prefetch = s->dst_y_prefetch_oto; + s->TimeForFetchingVM = s->Tvm_oto; + s->TimeForFetchingRowInVBlank = s->Tr0_oto; + DML_LOG_VERBOSE("DML::%s: Using oto scheduling for prefetch\n", __func__); + } else { + *p->dst_y_prefetch = s->dst_y_prefetch_equ; + s->TimeForFetchingVM = s->Tvm_equ; + s->TimeForFetchingRowInVBlank = s->Tr0_equ; + DML_LOG_VERBOSE("DML::%s: Using equ scheduling for prefetch\n", __func__); + } + + *p->dst_y_per_vm_vblank = math_ceil2(4.0 * s->TimeForFetchingVM / s->LineTime, 1.0) / 4.0; + *p->dst_y_per_row_vblank = math_ceil2(4.0 * s->TimeForFetchingRowInVBlank / s->LineTime, 1.0) / 4.0; + + s->LinesToRequestPrefetchPixelData = *p->dst_y_prefetch - *p->dst_y_per_vm_vblank - 2 * *p->dst_y_per_row_vblank; + + s->cursor_prefetch_bytes = (unsigned int)math_max2(p->cursor_bytes_per_chunk, 4 * p->cursor_bytes_per_line); + *p->prefetch_cursor_bw = p->num_cursors * s->cursor_prefetch_bytes / (s->LinesToRequestPrefetchPixelData * s->LineTime); + + DML_LOG_VERBOSE("DML::%s: TimeForFetchingVM = %f\n", __func__, s->TimeForFetchingVM); + DML_LOG_VERBOSE("DML::%s: TimeForFetchingRowInVBlank = %f\n", __func__, s->TimeForFetchingRowInVBlank); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, s->LineTime); + DML_LOG_VERBOSE("DML::%s: dst_y_prefetch = %f\n", __func__, *p->dst_y_prefetch); + DML_LOG_VERBOSE("DML::%s: dst_y_per_vm_vblank = %f\n", __func__, *p->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML::%s: dst_y_per_row_vblank = %f\n", __func__, *p->dst_y_per_row_vblank); + DML_LOG_VERBOSE("DML::%s: LinesToRequestPrefetchPixelData = %f\n", __func__, s->LinesToRequestPrefetchPixelData); + DML_LOG_VERBOSE("DML::%s: PrefetchSourceLinesY = %f\n", __func__, p->PrefetchSourceLinesY); + + DML_LOG_VERBOSE("DML::%s: cursor_bytes_per_chunk = %d\n", __func__, p->cursor_bytes_per_chunk); + DML_LOG_VERBOSE("DML::%s: cursor_bytes_per_line = %d\n", __func__, p->cursor_bytes_per_line); + DML_LOG_VERBOSE("DML::%s: cursor_prefetch_bytes = %d\n", __func__, s->cursor_prefetch_bytes); + DML_LOG_VERBOSE("DML::%s: prefetch_cursor_bw = %f\n", __func__, *p->prefetch_cursor_bw); + DML_ASSERT(*p->dst_y_prefetch < 64); + + unsigned int min_lsw_required = (unsigned int)math_max2(2, p->tdlut_drain_time / s->LineTime); + if (s->LinesToRequestPrefetchPixelData >= min_lsw_required && s->prefetch_bw_equ > 0) { + *p->VRatioPrefetchY = (double)p->PrefetchSourceLinesY / s->LinesToRequestPrefetchPixelData; + *p->VRatioPrefetchY = math_max2(*p->VRatioPrefetchY, 1.0); + DML_LOG_VERBOSE("DML::%s: VRatioPrefetchY = %f\n", __func__, *p->VRatioPrefetchY); + DML_LOG_VERBOSE("DML::%s: SwathHeightY = %u\n", __func__, p->SwathHeightY); + DML_LOG_VERBOSE("DML::%s: VInitPreFillY = %u\n", __func__, p->VInitPreFillY); + if ((p->SwathHeightY > 4) && (p->VInitPreFillY > 3)) { + if (s->LinesToRequestPrefetchPixelData > (p->VInitPreFillY - 3.0) / 2.0) { + *p->VRatioPrefetchY = math_max2(*p->VRatioPrefetchY, + (double)p->MaxNumSwathY * p->SwathHeightY / (s->LinesToRequestPrefetchPixelData - (p->VInitPreFillY - 3.0) / 2.0)); + } else { + s->NoTimeToPrefetch = true; + DML_LOG_VERBOSE("DML::%s: No time to prefetch!. LinesToRequestPrefetchPixelData=%f VinitPreFillY=%u\n", __func__, s->LinesToRequestPrefetchPixelData, p->VInitPreFillY); + *p->VRatioPrefetchY = 0; + } + DML_LOG_VERBOSE("DML::%s: VRatioPrefetchY = %f\n", __func__, *p->VRatioPrefetchY); + DML_LOG_VERBOSE("DML::%s: PrefetchSourceLinesY = %f\n", __func__, p->PrefetchSourceLinesY); + DML_LOG_VERBOSE("DML::%s: MaxNumSwathY = %u\n", __func__, p->MaxNumSwathY); + } + + *p->VRatioPrefetchC = (double)p->PrefetchSourceLinesC / s->LinesToRequestPrefetchPixelData; + *p->VRatioPrefetchC = math_max2(*p->VRatioPrefetchC, 1.0); + + DML_LOG_VERBOSE("DML::%s: VRatioPrefetchC = %f\n", __func__, *p->VRatioPrefetchC); + DML_LOG_VERBOSE("DML::%s: SwathHeightC = %u\n", __func__, p->SwathHeightC); + DML_LOG_VERBOSE("DML::%s: VInitPreFillC = %u\n", __func__, p->VInitPreFillC); + if ((p->SwathHeightC > 4) && (p->VInitPreFillC > 3)) { + if (s->LinesToRequestPrefetchPixelData > (p->VInitPreFillC - 3.0) / 2.0) { + *p->VRatioPrefetchC = math_max2(*p->VRatioPrefetchC, (double)p->MaxNumSwathC * p->SwathHeightC / (s->LinesToRequestPrefetchPixelData - (p->VInitPreFillC - 3.0) / 2.0)); + } else { + s->NoTimeToPrefetch = true; + DML_LOG_VERBOSE("DML::%s: No time to prefetch!. LinesToRequestPrefetchPixelData=%f VInitPreFillC=%u\n", __func__, s->LinesToRequestPrefetchPixelData, p->VInitPreFillC); + *p->VRatioPrefetchC = 0; + } + DML_LOG_VERBOSE("DML::%s: VRatioPrefetchC = %f\n", __func__, *p->VRatioPrefetchC); + DML_LOG_VERBOSE("DML::%s: PrefetchSourceLinesC = %f\n", __func__, p->PrefetchSourceLinesC); + DML_LOG_VERBOSE("DML::%s: MaxNumSwathC = %u\n", __func__, p->MaxNumSwathC); + } + + *p->RequiredPrefetchPixelDataBWLuma = (double)p->PrefetchSourceLinesY / s->LinesToRequestPrefetchPixelData * p->myPipe->BytePerPixelY * p->swath_width_luma_ub / s->LineTime; + *p->RequiredPrefetchPixelDataBWChroma = (double)p->PrefetchSourceLinesC / s->LinesToRequestPrefetchPixelData * p->myPipe->BytePerPixelC * p->swath_width_chroma_ub / s->LineTime; + + DML_LOG_VERBOSE("DML::%s: BytePerPixelY = %u\n", __func__, p->myPipe->BytePerPixelY); + DML_LOG_VERBOSE("DML::%s: swath_width_luma_ub = %u\n", __func__, p->swath_width_luma_ub); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, s->LineTime); + DML_LOG_VERBOSE("DML::%s: RequiredPrefetchPixelDataBWLuma = %f\n", __func__, *p->RequiredPrefetchPixelDataBWLuma); + DML_LOG_VERBOSE("DML::%s: RequiredPrefetchPixelDataBWChroma = %f\n", __func__, *p->RequiredPrefetchPixelDataBWChroma); + } else { + s->NoTimeToPrefetch = true; + DML_LOG_VERBOSE("DML::%s: No time to prefetch!, LinesToRequestPrefetchPixelData: %f, should be >= %d\n", __func__, s->LinesToRequestPrefetchPixelData, min_lsw_required); + DML_LOG_VERBOSE("DML::%s: No time to prefetch!, prefetch_bw_equ: %f, should be > 0\n", __func__, s->prefetch_bw_equ); + *p->VRatioPrefetchY = 0; + *p->VRatioPrefetchC = 0; + *p->RequiredPrefetchPixelDataBWLuma = 0; + *p->RequiredPrefetchPixelDataBWChroma = 0; + } + + DML_LOG_VERBOSE("DML: Tpre: %fus - sum of time to request 2 x data pte, swaths\n", (double)s->LinesToRequestPrefetchPixelData * s->LineTime + 2.0 * s->TimeForFetchingRowInVBlank + s->TimeForFetchingVM); + DML_LOG_VERBOSE("DML: Tvm: %fus - time to fetch vm\n", s->TimeForFetchingVM); + DML_LOG_VERBOSE("DML: Tr0: %fus - time to fetch first row of data pagetables\n", s->TimeForFetchingRowInVBlank); + DML_LOG_VERBOSE("DML: Tsw: %fus = time to fetch enough pixel data and cursor data to feed the scalers init position and detile\n", (double)s->LinesToRequestPrefetchPixelData * s->LineTime); + DML_LOG_VERBOSE("DML: To: %fus - time for propagation from scaler to optc\n", (*p->DSTYAfterScaler + ((double)(*p->DSTXAfterScaler) / (double)p->myPipe->HTotal)) * s->LineTime); + DML_LOG_VERBOSE("DML: Tvstartup - TSetup - Tcalc - TWait - Tpre - To > 0\n"); + DML_LOG_VERBOSE("DML: Tslack(pre): %fus - time left over in schedule\n", p->VStartup * s->LineTime - s->TimeForFetchingVM - 2 * s->TimeForFetchingRowInVBlank - (*p->DSTYAfterScaler + ((double)(*p->DSTXAfterScaler) / (double)p->myPipe->HTotal)) * s->LineTime - p->TWait - p->TCalc - *p->TSetup); + DML_LOG_VERBOSE("DML: row_bytes = dpte_row_bytes (per_pipe) = PixelPTEBytesPerRow = : %u\n", p->PixelPTEBytesPerRow); + + } else { + DML_LOG_VERBOSE("DML::%s: No time to prefetch! dst_y_prefetch_equ = %f (should be > 1)\n", __func__, s->dst_y_prefetch_equ); + DML_LOG_VERBOSE("DML::%s: No time to prefetch! Tpre_rounded (%f) should be >= Tvm_trips_rounded (%f) + 2.0*Tr0_trips_rounded (%f) + min_Tsw_equ (%f)\n", + __func__, *p->Tpre_rounded, s->Tvm_trips_rounded, 2.0*s->Tr0_trips_rounded, s->min_Lsw_equ*s->LineTime); + s->NoTimeToPrefetch = true; + s->TimeForFetchingVM = 0; + s->TimeForFetchingRowInVBlank = 0; + *p->dst_y_per_vm_vblank = 0; + *p->dst_y_per_row_vblank = 0; + s->LinesToRequestPrefetchPixelData = 0; + *p->VRatioPrefetchY = 0; + *p->VRatioPrefetchC = 0; + *p->RequiredPrefetchPixelDataBWLuma = 0; + *p->RequiredPrefetchPixelDataBWChroma = 0; + } + + { + double prefetch_vm_bw; + double prefetch_row_bw; + + if (vm_bytes == 0) { + prefetch_vm_bw = 0; + } else if (*p->dst_y_per_vm_vblank > 0) { + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactor = %f\n", __func__, p->HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: dst_y_per_vm_vblank = %f\n", __func__, *p->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, s->LineTime); + prefetch_vm_bw = vm_bytes * p->HostVMInefficiencyFactor / (*p->dst_y_per_vm_vblank * s->LineTime); + DML_LOG_VERBOSE("DML::%s: prefetch_vm_bw = %f\n", __func__, prefetch_vm_bw); + } else { + prefetch_vm_bw = 0; + s->NoTimeToPrefetch = true; + DML_LOG_VERBOSE("DML::%s: No time to prefetch!. dst_y_per_vm_vblank=%f (should be > 0)\n", __func__, *p->dst_y_per_vm_vblank); + } + + if (p->PixelPTEBytesPerRow == 0 && tdlut_row_bytes == 0) { + prefetch_row_bw = 0; + } else if (*p->dst_y_per_row_vblank > 0) { + prefetch_row_bw = (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + tdlut_row_bytes) / (*p->dst_y_per_row_vblank * s->LineTime); + + DML_LOG_VERBOSE("DML::%s: PixelPTEBytesPerRow = %u\n", __func__, p->PixelPTEBytesPerRow); + DML_LOG_VERBOSE("DML::%s: dst_y_per_row_vblank = %f\n", __func__, *p->dst_y_per_row_vblank); + DML_LOG_VERBOSE("DML::%s: prefetch_row_bw = %f\n", __func__, prefetch_row_bw); + } else { + prefetch_row_bw = 0; + s->NoTimeToPrefetch = true; + DML_LOG_VERBOSE("DML::%s: No time to prefetch!. dst_y_per_row_vblank=%f (should be > 0)\n", __func__, *p->dst_y_per_row_vblank); + } + + *p->prefetch_vmrow_bw = math_max2(prefetch_vm_bw, prefetch_row_bw); + } + + if (s->NoTimeToPrefetch) { + s->TimeForFetchingVM = 0; + s->TimeForFetchingRowInVBlank = 0; + *p->dst_y_per_vm_vblank = 0; + *p->dst_y_per_row_vblank = 0; + *p->dst_y_prefetch = 0; + s->LinesToRequestPrefetchPixelData = 0; + *p->VRatioPrefetchY = 0; + *p->VRatioPrefetchC = 0; + *p->RequiredPrefetchPixelDataBWLuma = 0; + *p->RequiredPrefetchPixelDataBWChroma = 0; + *p->prefetch_vmrow_bw = 0; + } + + DML_LOG_VERBOSE("DML::%s: dst_y_per_vm_vblank = %f (final)\n", __func__, *p->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML::%s: dst_y_per_row_vblank = %f (final)\n", __func__, *p->dst_y_per_row_vblank); + DML_LOG_VERBOSE("DML::%s: prefetch_vmrow_bw = %f (final)\n", __func__, *p->prefetch_vmrow_bw); + DML_LOG_VERBOSE("DML::%s: RequiredPrefetchPixelDataBWLuma = %f (final)\n", __func__, *p->RequiredPrefetchPixelDataBWLuma); + DML_LOG_VERBOSE("DML::%s: RequiredPrefetchPixelDataBWChroma = %f (final)\n", __func__, *p->RequiredPrefetchPixelDataBWChroma); + DML_LOG_VERBOSE("DML::%s: NoTimeToPrefetch=%d\n", __func__, s->NoTimeToPrefetch); + return s->NoTimeToPrefetch; +} + +static double dcn5_calculate_urgent_bandwidth_required( + struct dml2_core_shared_get_urgent_bandwidth_required_locals *l, + const struct dml2_display_cfg *display_cfg, + bool inc_flip_bw, // including flip bw + bool use_qual_row_bw, + unsigned int NumberOfActiveSurfaces, + unsigned int NumberOfDPP[], + double dcc_dram_bw_nom_overhead_factor_p0[], + double dcc_dram_bw_nom_overhead_factor_p1[], + double dcc_dram_bw_pref_overhead_factor_p0[], + double dcc_dram_bw_pref_overhead_factor_p1[], + double ReadBandwidthLuma[], + double ReadBandwidthChroma[], + double PrefetchBandwidthLuma[], + double PrefetchBandwidthChroma[], + double excess_vactive_fill_bw_l[], + double excess_vactive_fill_bw_c[], + double cursor_bw[], + double dpte_row_bw[], + double meta_row_bw[], + double prefetch_cursor_bw[], + double prefetch_vmrow_bw[], + double flip_bw[], + double UrgentBurstFactorLuma[], + double UrgentBurstFactorChroma[], + double UrgentBurstFactorCursor[], + double UrgentBurstFactorLumaPre[], + double UrgentBurstFactorChromaPre[], + double UrgentBurstFactorCursorPre[], + /* outputs */ + double surface_required_bw[], + double surface_peak_required_bw[]) +{ + // set inc_flip_bw = 0 for total_dchub_urgent_read_bw_noflip calculation, 1 for total_dchub_urgent_read_bw as described in the MAS + // set use_qual_row_bw = 1 to calculate using qualified row bandwidth, used for total_flip_bw calculation + + memset(l, 0, sizeof(struct dml2_core_shared_get_urgent_bandwidth_required_locals)); + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + l->adj_factor_p0 = UrgentBurstFactorLuma[k] * dcc_dram_bw_nom_overhead_factor_p0[k]; + l->adj_factor_p1 = UrgentBurstFactorChroma[k] * dcc_dram_bw_nom_overhead_factor_p1[k]; + l->adj_factor_cur = UrgentBurstFactorCursor[k]; + l->adj_factor_p0_pre = UrgentBurstFactorLumaPre[k] * dcc_dram_bw_pref_overhead_factor_p0[k]; + l->adj_factor_p1_pre = UrgentBurstFactorChromaPre[k] * dcc_dram_bw_pref_overhead_factor_p1[k]; + l->adj_factor_cur_pre = UrgentBurstFactorCursorPre[k]; + + // The qualified row bandwidth, qual_row_bw, accounts for the regular non-flip row bandwidth when there is no possible immediate flip or HostVM invalidation flip. + // The qual_row_bw is zero if HostVM is possible and only non-zero and equal to row_bw(i) if immediate flip is not allowed for that pipe. + if (use_qual_row_bw) { + if (display_cfg->hostvm_enable) + l->per_plane_flip_bw[k] = 0; // qual_row_bw + else if (!display_cfg->plane_descriptors[k].immediate_flip) + l->per_plane_flip_bw[k] = NumberOfDPP[k] * (dpte_row_bw[k] + meta_row_bw[k]); + } else { + // the final_flip_bw includes the regular row_bw when immediate flip is disallowed (and no HostVM) + if ((!display_cfg->plane_descriptors[k].immediate_flip && !display_cfg->hostvm_enable) || !inc_flip_bw) + l->per_plane_flip_bw[k] = NumberOfDPP[k] * (dpte_row_bw[k] + meta_row_bw[k]); + else + l->per_plane_flip_bw[k] = NumberOfDPP[k] * flip_bw[k]; + } + + l->vm_row_bw = NumberOfDPP[k] * prefetch_vmrow_bw[k]; + l->flip_and_active_bw = l->per_plane_flip_bw[k] + + ReadBandwidthLuma[k] * l->adj_factor_p0 + + ReadBandwidthChroma[k] * l->adj_factor_p1 + + cursor_bw[k] * l->adj_factor_cur; + l->flip_and_prefetch_bw = l->per_plane_flip_bw[k] + + NumberOfDPP[k] * (PrefetchBandwidthLuma[k] * l->adj_factor_p0_pre + PrefetchBandwidthChroma[k] * l->adj_factor_p1_pre) + + prefetch_cursor_bw[k] * l->adj_factor_cur_pre; + l->active_and_excess_bw = (ReadBandwidthLuma[k] + excess_vactive_fill_bw_l[k]) * dcc_dram_bw_nom_overhead_factor_p0[k] + + (ReadBandwidthChroma[k] + excess_vactive_fill_bw_c[k]) * dcc_dram_bw_nom_overhead_factor_p1[k] + + dpte_row_bw[k] + meta_row_bw[k]; + + surface_required_bw[k] = math_max4(l->vm_row_bw, l->flip_and_active_bw, l->flip_and_prefetch_bw, l->active_and_excess_bw); + + /* export peak required bandwidth for the surface */ + surface_peak_required_bw[k] = math_max2(surface_required_bw[k], surface_peak_required_bw[k]); + + DML_LOG_VERBOSE("DML::%s: k=%d, max1: vm_row_bw=%f\n", __func__, k, l->vm_row_bw); + DML_LOG_VERBOSE("DML::%s: k=%d, max2: flip_and_active_bw=%f\n", __func__, k, l->flip_and_active_bw); + DML_LOG_VERBOSE("DML::%s: k=%d, max3: flip_and_prefetch_bw=%f\n", __func__, k, l->flip_and_prefetch_bw); + DML_LOG_VERBOSE("DML::%s: k=%d, max4: active_and_excess_bw=%f\n", __func__, k, l->active_and_excess_bw); + DML_LOG_VERBOSE("DML::%s: k=%d, surface_required_bw=%f\n", __func__, k, surface_required_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, surface_peak_required_bw=%f\n", __func__, k, surface_peak_required_bw[k]); + + l->required_bandwidth_mbps += surface_required_bw[k]; + + DML_LOG_VERBOSE("DML::%s: k=%d, NumberOfDPP=%d\n", __func__, k, NumberOfDPP[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, use_qual_row_bw=%d\n", __func__, k, use_qual_row_bw); + DML_LOG_VERBOSE("DML::%s: k=%d, immediate_flip=%d\n", __func__, k, display_cfg->plane_descriptors[k].immediate_flip); + DML_LOG_VERBOSE("DML::%s: k=%d, adj_factor_p0=%f\n", __func__, k, l->adj_factor_p0); + DML_LOG_VERBOSE("DML::%s: k=%d, adj_factor_p1=%f\n", __func__, k, l->adj_factor_p1); + DML_LOG_VERBOSE("DML::%s: k=%d, adj_factor_cur=%f\n", __func__, k, l->adj_factor_cur); + + DML_LOG_VERBOSE("DML::%s: k=%d, adj_factor_p0_pre=%f\n", __func__, k, l->adj_factor_p0_pre); + DML_LOG_VERBOSE("DML::%s: k=%d, adj_factor_p1_pre=%f\n", __func__, k, l->adj_factor_p1_pre); + DML_LOG_VERBOSE("DML::%s: k=%d, adj_factor_cur_pre=%f\n", __func__, k, l->adj_factor_cur_pre); + + DML_LOG_VERBOSE("DML::%s: k=%d, per_plane_flip_bw=%f\n", __func__, k, l->per_plane_flip_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, prefetch_vmrow_bw=%f\n", __func__, k, prefetch_vmrow_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, ReadBandwidthLuma=%f\n", __func__, k, ReadBandwidthLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, ReadBandwidthChroma=%f\n", __func__, k, ReadBandwidthChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, excess_vactive_fill_bw_l=%f\n", __func__, k, excess_vactive_fill_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, excess_vactive_fill_bw_c=%f\n", __func__, k, excess_vactive_fill_bw_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, cursor_bw=%f\n", __func__, k, cursor_bw[k]); + + DML_LOG_VERBOSE("DML::%s: k=%d, meta_row_bw=%f\n", __func__, k, meta_row_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, dpte_row_bw=%f\n", __func__, k, dpte_row_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, PrefetchBandwidthLuma=%f\n", __func__, k, PrefetchBandwidthLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, PrefetchBandwidthChroma=%f\n", __func__, k, PrefetchBandwidthChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, prefetch_cursor_bw=%f\n", __func__, k, prefetch_cursor_bw[k]); + } + + return l->required_bandwidth_mbps; +} + +void dcn5_calculate_peak_bandwidth_required( + struct dml2_core_internal_scratch *s, + struct dml2_core_calcs_calculate_peak_bandwidth_required_params *p) +{ + struct dml2_core_shared_calculate_peak_bandwidth_required_locals *l = &s->calculate_peak_bandwidth_required_locals; + + memset(l, 0, sizeof(struct dml2_core_shared_calculate_peak_bandwidth_required_locals)); + + DML_LOG_VERBOSE("DML::%s: inc_flip_bw = %d\n", __func__, p->inc_flip_bw); + DML_LOG_VERBOSE("DML::%s: NumberOfActiveSurfaces = %d\n", __func__, p->num_active_planes); + + for (unsigned int k = 0; k < p->num_active_planes; ++k) { + l->unity_array[k] = 1.0; + l->zero_array[k] = 0.0; + } + + dcn5_calculate_urgent_bandwidth_required( + &s->get_urgent_bandwidth_required_locals, + p->display_cfg, + 0, //inc_flip_bw, + 0, //use_qual_row_bw + p->num_active_planes, + p->num_of_dpp, + p->dcc_dram_bw_nom_overhead_factor_p0, + p->dcc_dram_bw_nom_overhead_factor_p1, + p->dcc_dram_bw_pref_overhead_factor_p0, + p->dcc_dram_bw_pref_overhead_factor_p1, + p->surface_read_bandwidth_l, + p->surface_read_bandwidth_c, + l->zero_array, //PrefetchBandwidthLuma, + l->zero_array, //PrefetchBandwidthChroma, + l->zero_array, + l->zero_array, + l->zero_array, + p->dpte_row_bw, + p->meta_row_bw, + l->zero_array, //prefetch_cursor_bw, + l->zero_array, //prefetch_vmrow_bw, + l->zero_array, //flip_bw, + l->zero_array, + l->zero_array, + l->zero_array, + l->zero_array, + l->zero_array, + l->zero_array, + **p->surface_avg_vactive_required_bw, + l->surface_dummy_bw); + + **p->urg_bandwidth_required = dcn5_calculate_urgent_bandwidth_required( + &s->get_urgent_bandwidth_required_locals, + p->display_cfg, + p->inc_flip_bw, + 0, //use_qual_row_bw + p->num_active_planes, + p->num_of_dpp, + p->dcc_dram_bw_nom_overhead_factor_p0, + p->dcc_dram_bw_nom_overhead_factor_p1, + p->dcc_dram_bw_pref_overhead_factor_p0, + p->dcc_dram_bw_pref_overhead_factor_p1, + p->surface_read_bandwidth_l, + p->surface_read_bandwidth_c, + p->prefetch_bandwidth_l, + p->prefetch_bandwidth_c, + p->excess_vactive_fill_bw_l, + p->excess_vactive_fill_bw_c, + p->cursor_bw, + p->dpte_row_bw, + p->meta_row_bw, + p->prefetch_cursor_bw, + p->prefetch_vmrow_bw, + p->flip_bw, + p->urgent_burst_factor_l, + p->urgent_burst_factor_c, + p->urgent_burst_factor_cursor, + p->urgent_burst_factor_prefetch_l, + p->urgent_burst_factor_prefetch_c, + p->urgent_burst_factor_prefetch_cursor, + l->surface_dummy_bw, + l->surface_dummy_bw); + + **p->urg_bandwidth_required_qual = dcn5_calculate_urgent_bandwidth_required( + &s->get_urgent_bandwidth_required_locals, + p->display_cfg, + 0, //inc_flip_bw + 1, //use_qual_row_bw + p->num_active_planes, + p->num_of_dpp, + p->dcc_dram_bw_nom_overhead_factor_p0, + p->dcc_dram_bw_nom_overhead_factor_p1, + p->dcc_dram_bw_pref_overhead_factor_p0, + p->dcc_dram_bw_pref_overhead_factor_p1, + p->surface_read_bandwidth_l, + p->surface_read_bandwidth_c, + p->prefetch_bandwidth_l, + p->prefetch_bandwidth_c, + p->excess_vactive_fill_bw_l, + p->excess_vactive_fill_bw_c, + p->cursor_bw, + p->dpte_row_bw, + p->meta_row_bw, + p->prefetch_cursor_bw, + p->prefetch_vmrow_bw, + p->flip_bw, + p->urgent_burst_factor_l, + p->urgent_burst_factor_c, + p->urgent_burst_factor_cursor, + p->urgent_burst_factor_prefetch_l, + p->urgent_burst_factor_prefetch_c, + p->urgent_burst_factor_prefetch_cursor, + l->surface_dummy_bw, + l->surface_dummy_bw); + + **p->non_urg_bandwidth_required = dcn5_calculate_urgent_bandwidth_required( + &s->get_urgent_bandwidth_required_locals, + p->display_cfg, + p->inc_flip_bw, + 0, //use_qual_row_bw + p->num_active_planes, + p->num_of_dpp, + p->dcc_dram_bw_nom_overhead_factor_p0, + p->dcc_dram_bw_nom_overhead_factor_p1, + p->dcc_dram_bw_pref_overhead_factor_p0, + p->dcc_dram_bw_pref_overhead_factor_p1, + p->surface_read_bandwidth_l, + p->surface_read_bandwidth_c, + p->prefetch_bandwidth_l, + p->prefetch_bandwidth_c, + p->excess_vactive_fill_bw_l, + p->excess_vactive_fill_bw_c, + p->cursor_bw, + p->dpte_row_bw, + p->meta_row_bw, + p->prefetch_cursor_bw, + p->prefetch_vmrow_bw, + p->flip_bw, + l->unity_array, + l->unity_array, + l->unity_array, + l->unity_array, + l->unity_array, + l->unity_array, + l->surface_dummy_bw, + **p->surface_peak_required_bw); + + DML_LOG_VERBOSE("DML::%s: urg_bandwidth_required%s=%f\n", __func__, (p->inc_flip_bw ? "_flip" : ""), **p->urg_bandwidth_required); + DML_LOG_VERBOSE("DML::%s: urg_bandwidth_required_qual=%f\n", __func__, **p->urg_bandwidth_required); + DML_LOG_VERBOSE("DML::%s: non_urg_bandwidth_required%s=%f\n", __func__, (p->inc_flip_bw ? "_flip" : ""), **p->non_urg_bandwidth_required); + DML_ASSERT(**p->urg_bandwidth_required >= **p->non_urg_bandwidth_required); + +} + +void dcn5_calculate_dcc_configuration( + bool DCCEnabled, + bool DCCProgrammingAssumesScanDirectionUnknown, + enum dml2_source_format_class SourcePixelFormat, + unsigned int SurfaceWidthLuma, + unsigned int SurfaceWidthChroma, + unsigned int SurfaceHeightLuma, + unsigned int SurfaceHeightChroma, + unsigned int nomDETInKByte, + unsigned int RequestHeight256ByteLuma, + unsigned int RequestHeight256ByteChroma, + enum dml2_swizzle_mode TilingFormat, + unsigned int BytePerPixelY, + unsigned int BytePerPixelC, + double BytePerPixelDETY, + double BytePerPixelDETC, + enum dml2_rotation_angle RotationAngle, + + // Output + enum dml2_core_internal_request_type *RequestLuma, + enum dml2_core_internal_request_type *RequestChroma, + unsigned int *MaxUncompressedBlockLuma, + unsigned int *MaxUncompressedBlockChroma, + unsigned int *MaxCompressedBlockLuma, + unsigned int *MaxCompressedBlockChroma, + unsigned int *IndependentBlockLuma, + unsigned int *IndependentBlockChroma) +{ + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)TilingFormat; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; + unsigned int DETBufferSizeForDCC = nomDETInKByte * 1024; + + unsigned int segment_order_horz_contiguous_luma; + unsigned int segment_order_horz_contiguous_chroma; + unsigned int segment_order_vert_contiguous_luma; + unsigned int segment_order_vert_contiguous_chroma; + + unsigned int req128_horz_wc_l; + unsigned int req128_horz_wc_c; + unsigned int req128_vert_wc_l; + unsigned int req128_vert_wc_c; + + bool yuv420_planar; + bool yuv422_planar; + unsigned int horz_subsample; + unsigned int vert_subsample; + unsigned int horz_div_l; + unsigned int horz_div_c; + unsigned int vert_div_l; + unsigned int vert_div_c; + + unsigned int swath_buf_size; + double detile_buf_vp_horz_limit; + double detile_buf_vp_vert_limit; + + unsigned int MAS_vp_horz_limit; + unsigned int MAS_vp_vert_limit; + unsigned int max_vp_horz_width; + unsigned int max_vp_vert_height; + unsigned int eff_surf_width_l; + unsigned int eff_surf_width_c; + unsigned int eff_surf_height_l; + unsigned int eff_surf_height_c; + + unsigned int full_swath_bytes_horz_wc_l; + unsigned int full_swath_bytes_horz_wc_c; + unsigned int full_swath_bytes_vert_wc_l; + unsigned int full_swath_bytes_vert_wc_c; + + yuv420_planar = dml2_core_utils_is_420(SourcePixelFormat); + yuv422_planar = dml2_core_utils_is_422_planar(SourcePixelFormat); + horz_subsample = (yuv420_planar || yuv422_planar) ? 1 : 0; + vert_subsample = yuv420_planar ? 1 : 0; + horz_div_l = 1; + horz_div_c = 1; + vert_div_l = 1; + vert_div_c = 1; + + if (BytePerPixelY == 1) + vert_div_l = 0; + if (BytePerPixelC == 1) + vert_div_c = 0; + + if (BytePerPixelC == 0) { + swath_buf_size = DETBufferSizeForDCC / 2 - 2 * 256; + detile_buf_vp_horz_limit = (double)swath_buf_size / ((double)RequestHeight256ByteLuma * BytePerPixelY / (1 + horz_div_l)); + detile_buf_vp_vert_limit = (double)swath_buf_size / (256.0 / RequestHeight256ByteLuma / (1 + vert_div_l)); + } else { + swath_buf_size = DETBufferSizeForDCC / 2 - 2 * 2 * 256; + detile_buf_vp_horz_limit = (double)swath_buf_size / ((double)RequestHeight256ByteLuma * BytePerPixelY / (1 + horz_div_l) + (double)RequestHeight256ByteChroma * BytePerPixelC / (1 + horz_div_c) / (1 + horz_subsample)); + detile_buf_vp_vert_limit = (double)swath_buf_size / (256.0 / RequestHeight256ByteLuma / (1 + vert_div_l) + 256.0 / RequestHeight256ByteChroma / (1 + vert_div_c) / (1 + vert_subsample)); + } + + if (SourcePixelFormat == dml2_420_10 || SourcePixelFormat == dml2_422_planar_10 || SourcePixelFormat == dml2_422_packed_10) { + detile_buf_vp_horz_limit = 1.5 * detile_buf_vp_horz_limit; + detile_buf_vp_vert_limit = 1.5 * detile_buf_vp_vert_limit; + } + + detile_buf_vp_horz_limit = math_floor2(detile_buf_vp_horz_limit - 1, 16); + detile_buf_vp_vert_limit = math_floor2(detile_buf_vp_vert_limit - 1, 16); + + MAS_vp_horz_limit = SourcePixelFormat == dml2_rgbe_alpha ? 3840 : 6144; + MAS_vp_vert_limit = SourcePixelFormat == dml2_rgbe_alpha ? 3840 : (BytePerPixelY == 8 ? 3072 : 6144); + max_vp_horz_width = (unsigned int)(math_min2((double)MAS_vp_horz_limit, detile_buf_vp_horz_limit)); + max_vp_vert_height = (unsigned int)(math_min2((double)MAS_vp_vert_limit, detile_buf_vp_vert_limit)); + eff_surf_width_l = (SurfaceWidthLuma > max_vp_horz_width ? max_vp_horz_width : SurfaceWidthLuma); + eff_surf_width_c = eff_surf_width_l / (1 + horz_subsample); + eff_surf_height_l = (SurfaceHeightLuma > max_vp_vert_height ? max_vp_vert_height : SurfaceHeightLuma); + eff_surf_height_c = eff_surf_height_l / (1 + vert_subsample); + + full_swath_bytes_horz_wc_l = eff_surf_width_l * RequestHeight256ByteLuma * BytePerPixelY; + full_swath_bytes_vert_wc_l = eff_surf_height_l * 256 / RequestHeight256ByteLuma; + if (BytePerPixelC > 0) { + full_swath_bytes_horz_wc_c = eff_surf_width_c * RequestHeight256ByteChroma * BytePerPixelC; + full_swath_bytes_vert_wc_c = eff_surf_height_c * 256 / RequestHeight256ByteChroma; + } else { + full_swath_bytes_horz_wc_c = 0; + full_swath_bytes_vert_wc_c = 0; + } + + if (SourcePixelFormat == dml2_420_10 || SourcePixelFormat == dml2_422_planar_10 || SourcePixelFormat == dml2_422_packed_10) { + full_swath_bytes_horz_wc_l = (unsigned int)(math_ceil2((double)full_swath_bytes_horz_wc_l * 2.0 / 3.0, 256.0)); + full_swath_bytes_horz_wc_c = (unsigned int)(math_ceil2((double)full_swath_bytes_horz_wc_c * 2.0 / 3.0, 256.0)); + full_swath_bytes_vert_wc_l = (unsigned int)(math_ceil2((double)full_swath_bytes_vert_wc_l * 2.0 / 3.0, 256.0)); + full_swath_bytes_vert_wc_c = (unsigned int)(math_ceil2((double)full_swath_bytes_vert_wc_c * 2.0 / 3.0, 256.0)); + } + + if (2 * full_swath_bytes_horz_wc_l + 2 * full_swath_bytes_horz_wc_c <= DETBufferSizeForDCC) { + req128_horz_wc_l = 0; + req128_horz_wc_c = 0; + } else if (full_swath_bytes_horz_wc_l < 1.5 * full_swath_bytes_horz_wc_c && 2 * full_swath_bytes_horz_wc_l + full_swath_bytes_horz_wc_c <= DETBufferSizeForDCC) { + req128_horz_wc_l = 0; + req128_horz_wc_c = 1; + } else if (full_swath_bytes_horz_wc_l >= 1.5 * full_swath_bytes_horz_wc_c && full_swath_bytes_horz_wc_l + 2 * full_swath_bytes_horz_wc_c <= DETBufferSizeForDCC) { + req128_horz_wc_l = 1; + req128_horz_wc_c = 0; + } else { + req128_horz_wc_l = 1; + req128_horz_wc_c = 1; + } + + if (2 * full_swath_bytes_vert_wc_l + 2 * full_swath_bytes_vert_wc_c <= DETBufferSizeForDCC) { + req128_vert_wc_l = 0; + req128_vert_wc_c = 0; + } else if (full_swath_bytes_vert_wc_l < 1.5 * full_swath_bytes_vert_wc_c && 2 * full_swath_bytes_vert_wc_l + full_swath_bytes_vert_wc_c <= DETBufferSizeForDCC) { + req128_vert_wc_l = 0; + req128_vert_wc_c = 1; + } else if (full_swath_bytes_vert_wc_l >= 1.5 * full_swath_bytes_vert_wc_c && full_swath_bytes_vert_wc_l + 2 * full_swath_bytes_vert_wc_c <= DETBufferSizeForDCC) { + req128_vert_wc_l = 1; + req128_vert_wc_c = 0; + } else { + req128_vert_wc_l = 1; + req128_vert_wc_c = 1; + } + + if (BytePerPixelY == 2) { + segment_order_horz_contiguous_luma = 0; + segment_order_vert_contiguous_luma = 1; + } else { + segment_order_horz_contiguous_luma = 1; + segment_order_vert_contiguous_luma = 0; + } + + if (BytePerPixelC == 2) { + segment_order_horz_contiguous_chroma = 0; + segment_order_vert_contiguous_chroma = 1; + } else { + segment_order_horz_contiguous_chroma = 1; + segment_order_vert_contiguous_chroma = 0; + } + DML_LOG_VERBOSE("DML::%s: DCCEnabled = %u\n", __func__, DCCEnabled); + DML_LOG_VERBOSE("DML::%s: nomDETInKByte = %u\n", __func__, nomDETInKByte); + DML_LOG_VERBOSE("DML::%s: DETBufferSizeForDCC = %u\n", __func__, DETBufferSizeForDCC); + DML_LOG_VERBOSE("DML::%s: req128_horz_wc_l = %u\n", __func__, req128_horz_wc_l); + DML_LOG_VERBOSE("DML::%s: req128_horz_wc_c = %u\n", __func__, req128_horz_wc_c); + DML_LOG_VERBOSE("DML::%s: full_swath_bytes_horz_wc_l = %u\n", __func__, full_swath_bytes_horz_wc_l); + DML_LOG_VERBOSE("DML::%s: full_swath_bytes_vert_wc_c = %u\n", __func__, full_swath_bytes_vert_wc_c); + DML_LOG_VERBOSE("DML::%s: segment_order_horz_contiguous_luma = %u\n", __func__, segment_order_horz_contiguous_luma); + DML_LOG_VERBOSE("DML::%s: segment_order_horz_contiguous_chroma = %u\n", __func__, segment_order_horz_contiguous_chroma); + if (DCCProgrammingAssumesScanDirectionUnknown == true) { + if (req128_horz_wc_l == 0 && req128_vert_wc_l == 0) { + *RequestLuma = dml2_core_internal_request_type_256_bytes; + } else if ((req128_horz_wc_l == 1 && segment_order_horz_contiguous_luma == 0) || (req128_vert_wc_l == 1 && segment_order_vert_contiguous_luma == 0)) { + *RequestLuma = dml2_core_internal_request_type_128_bytes_non_contiguous; + } else { + *RequestLuma = dml2_core_internal_request_type_128_bytes_contiguous; + } + if (req128_horz_wc_c == 0 && req128_vert_wc_c == 0) { + *RequestChroma = dml2_core_internal_request_type_256_bytes; + } else if ((req128_horz_wc_c == 1 && segment_order_horz_contiguous_chroma == 0) || (req128_vert_wc_c == 1 && segment_order_vert_contiguous_chroma == 0)) { + *RequestChroma = dml2_core_internal_request_type_128_bytes_non_contiguous; + } else { + *RequestChroma = dml2_core_internal_request_type_128_bytes_contiguous; + } + } else if (!dml2_core_utils_is_vertical_rotation(RotationAngle)) { + if (req128_horz_wc_l == 0) { + *RequestLuma = dml2_core_internal_request_type_256_bytes; + } else if (segment_order_horz_contiguous_luma == 0) { + *RequestLuma = dml2_core_internal_request_type_128_bytes_non_contiguous; + } else { + *RequestLuma = dml2_core_internal_request_type_128_bytes_contiguous; + } + if (req128_horz_wc_c == 0) { + *RequestChroma = dml2_core_internal_request_type_256_bytes; + } else if (segment_order_horz_contiguous_chroma == 0) { + *RequestChroma = dml2_core_internal_request_type_128_bytes_non_contiguous; + } else { + *RequestChroma = dml2_core_internal_request_type_128_bytes_contiguous; + } + } else { + if (req128_vert_wc_l == 0) { + *RequestLuma = dml2_core_internal_request_type_256_bytes; + } else if (segment_order_vert_contiguous_luma == 0) { + *RequestLuma = dml2_core_internal_request_type_128_bytes_non_contiguous; + } else { + *RequestLuma = dml2_core_internal_request_type_128_bytes_contiguous; + } + if (req128_vert_wc_c == 0) { + *RequestChroma = dml2_core_internal_request_type_256_bytes; + } else if (segment_order_vert_contiguous_chroma == 0) { + *RequestChroma = dml2_core_internal_request_type_128_bytes_non_contiguous; + } else { + *RequestChroma = dml2_core_internal_request_type_128_bytes_contiguous; + } + } + + if (*RequestLuma == dml2_core_internal_request_type_256_bytes) { + *MaxUncompressedBlockLuma = 256; + *MaxCompressedBlockLuma = 256; + *IndependentBlockLuma = 0; + } else if (*RequestLuma == dml2_core_internal_request_type_128_bytes_contiguous) { + *MaxUncompressedBlockLuma = 256; + *MaxCompressedBlockLuma = 128; + *IndependentBlockLuma = 128; + } else { + *MaxUncompressedBlockLuma = 256; + *MaxCompressedBlockLuma = 64; + *IndependentBlockLuma = 64; + } + + if (*RequestChroma == dml2_core_internal_request_type_256_bytes) { + *MaxUncompressedBlockChroma = 256; + *MaxCompressedBlockChroma = 256; + *IndependentBlockChroma = 0; + } else if (*RequestChroma == dml2_core_internal_request_type_128_bytes_contiguous) { + *MaxUncompressedBlockChroma = 256; + *MaxCompressedBlockChroma = 128; + *IndependentBlockChroma = 128; + } else { + *MaxUncompressedBlockChroma = 256; + *MaxCompressedBlockChroma = 64; + *IndependentBlockChroma = 64; + } + + if (DCCEnabled != true || BytePerPixelC == 0) { + *MaxUncompressedBlockChroma = 0; + *MaxCompressedBlockChroma = 0; + *IndependentBlockChroma = 0; + } + + if (DCCEnabled != true) { + *MaxUncompressedBlockLuma = 0; + *MaxCompressedBlockLuma = 0; + *IndependentBlockLuma = 0; + } + + DML_LOG_VERBOSE("DML::%s: MaxUncompressedBlockLuma = %u\n", __func__, *MaxUncompressedBlockLuma); + DML_LOG_VERBOSE("DML::%s: MaxCompressedBlockLuma = %u\n", __func__, *MaxCompressedBlockLuma); + DML_LOG_VERBOSE("DML::%s: IndependentBlockLuma = %u\n", __func__, *IndependentBlockLuma); + DML_LOG_VERBOSE("DML::%s: MaxUncompressedBlockChroma = %u\n", __func__, *MaxUncompressedBlockChroma); + DML_LOG_VERBOSE("DML::%s: MaxCompressedBlockChroma = %u\n", __func__, *MaxCompressedBlockChroma); + DML_LOG_VERBOSE("DML::%s: IndependentBlockChroma = %u\n", __func__, *IndependentBlockChroma); +} + +void dcn5_calculate_flip_schedule( + struct dml2_core_internal_scratch *s, + bool iflip_enable, + bool use_lb_flip_bw, + double HostVMInefficiencyFactor, + double Tvm_trips_flip, + double Tr0_trips_flip, + double Tvm_trips_flip_rounded, + double Tr0_trips_flip_rounded, + bool GPUVMEnable, + double vm_bytes, // vm_bytes + double DPTEBytesPerRow, // dpte_row_bytes + double BandwidthAvailableForImmediateFlip, + unsigned int TotImmediateFlipBytes, + enum dml2_source_format_class SourcePixelFormat, + double LineTime, + double VRatio, + double VRatioChroma, + double Tno_bw_flip, + unsigned int dpte_row_height, + unsigned int dpte_row_height_chroma, + bool use_one_row_for_frame_flip, + unsigned int max_flip_time_us, + unsigned int max_flip_time_lines, + unsigned int per_pipe_flip_bytes, + unsigned int meta_row_bytes, + unsigned int meta_row_height, + unsigned int meta_row_height_chroma, + bool dcc_mrq_enable, + + // Output + double *dst_y_per_vm_flip, + double *dst_y_per_row_flip, + double *final_flip_bw, + bool *ImmediateFlipSupportedForPipe) +{ + (void)use_one_row_for_frame_flip; + struct dml2_core_shared_CalculateFlipSchedule_locals *l = &s->CalculateFlipSchedule_locals; + + l->dual_plane = dml2_core_utils_is_420(SourcePixelFormat) || dml2_core_utils_is_422_planar(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha; + l->dpte_row_bytes = DPTEBytesPerRow; + + DML_LOG_VERBOSE("DML::%s: GPUVMEnable = %u\n", __func__, GPUVMEnable); + DML_LOG_VERBOSE("DML::%s: ip.max_flip_time_us = %d\n", __func__, max_flip_time_us); + DML_LOG_VERBOSE("DML::%s: ip.max_flip_time_lines = %d\n", __func__, max_flip_time_lines); + DML_LOG_VERBOSE("DML::%s: BandwidthAvailableForImmediateFlip = %f\n", __func__, BandwidthAvailableForImmediateFlip); + DML_LOG_VERBOSE("DML::%s: TotImmediateFlipBytes = %u\n", __func__, TotImmediateFlipBytes); + DML_LOG_VERBOSE("DML::%s: use_lb_flip_bw = %u\n", __func__, use_lb_flip_bw); + DML_LOG_VERBOSE("DML::%s: iflip_enable = %u\n", __func__, iflip_enable); + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactor = %f\n", __func__, HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, LineTime); + DML_LOG_VERBOSE("DML::%s: Tno_bw_flip = %f\n", __func__, Tno_bw_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip = %f\n", __func__, Tvm_trips_flip); + DML_LOG_VERBOSE("DML::%s: Tr0_trips_flip = %f\n", __func__, Tr0_trips_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip_rounded = %f\n", __func__, Tvm_trips_flip_rounded); + DML_LOG_VERBOSE("DML::%s: Tr0_trips_flip_rounded = %f\n", __func__, Tr0_trips_flip_rounded); + DML_LOG_VERBOSE("DML::%s: vm_bytes = %f\n", __func__, vm_bytes); + DML_LOG_VERBOSE("DML::%s: DPTEBytesPerRow = %f\n", __func__, DPTEBytesPerRow); + DML_LOG_VERBOSE("DML::%s: meta_row_bytes = %d\n", __func__, meta_row_bytes); + DML_LOG_VERBOSE("DML::%s: dpte_row_bytes = %f\n", __func__, l->dpte_row_bytes); + DML_LOG_VERBOSE("DML::%s: dpte_row_height = %d\n", __func__, dpte_row_height); + DML_LOG_VERBOSE("DML::%s: meta_row_height = %d\n", __func__, meta_row_height); + DML_LOG_VERBOSE("DML::%s: VRatio = %f\n", __func__, VRatio); + + if (TotImmediateFlipBytes > 0 && (GPUVMEnable || dcc_mrq_enable)) { + if (l->dual_plane) { + if (dcc_mrq_enable & GPUVMEnable) { + l->min_row_height = math_min2(dpte_row_height, meta_row_height); + l->min_row_height_chroma = math_min2(dpte_row_height_chroma, meta_row_height_chroma); + } else if (GPUVMEnable) { + l->min_row_height = dpte_row_height; + l->min_row_height_chroma = dpte_row_height_chroma; + } else { + l->min_row_height = meta_row_height; + l->min_row_height_chroma = meta_row_height_chroma; + } + l->min_row_time = math_min2(l->min_row_height * LineTime / VRatio, l->min_row_height_chroma * LineTime / VRatioChroma); + } else { + if (dcc_mrq_enable & GPUVMEnable) + l->min_row_height = math_min2(dpte_row_height, meta_row_height); + else if (GPUVMEnable) + l->min_row_height = dpte_row_height; + else + l->min_row_height = meta_row_height; + + l->min_row_time = l->min_row_height * LineTime / VRatio; + } + DML_LOG_VERBOSE("DML::%s: min_row_time = %f\n", __func__, l->min_row_time); + DML_ASSERT(l->min_row_time > 0); + + if (use_lb_flip_bw) { + // For mode check, calculation the flip bw requirement with worst case flip time + l->max_flip_time = math_min2(math_min2(l->min_row_time, (double)max_flip_time_lines * LineTime / VRatio), + math_max2(Tvm_trips_flip_rounded + 2 * Tr0_trips_flip_rounded, (double)max_flip_time_us)); + + //The lower bound on flip bandwidth + // Note: The get_urgent_bandwidth_required already consider dpte_row_bw and meta_row_bw in bandwidth calculation, so leave final_flip_bw = 0 if iflip not required + l->lb_flip_bw = 0; + + if (iflip_enable) { + l->hvm_scaled_vm_bytes = vm_bytes * HostVMInefficiencyFactor; + l->num_rows = 2; + l->hvm_scaled_row_bytes = (l->num_rows * l->dpte_row_bytes * HostVMInefficiencyFactor + l->num_rows * meta_row_bytes); + l->hvm_scaled_vm_row_bytes = l->hvm_scaled_vm_bytes + l->hvm_scaled_row_bytes; + l->lb_flip_bw = math_max3( + l->hvm_scaled_vm_row_bytes / (l->max_flip_time - Tno_bw_flip), + l->hvm_scaled_vm_bytes / (l->max_flip_time - Tno_bw_flip - 2 * Tr0_trips_flip_rounded), + l->hvm_scaled_row_bytes / (l->max_flip_time - Tvm_trips_flip_rounded)); + DML_LOG_VERBOSE("DML::%s: max_flip_time = %f\n", __func__, l->max_flip_time); + DML_LOG_VERBOSE("DML::%s: total vm bytes (hvm ineff scaled) = %f\n", __func__, l->hvm_scaled_vm_bytes); + DML_LOG_VERBOSE("DML::%s: total row bytes (%f row, hvm ineff scaled) = %f\n", __func__, l->num_rows, l->hvm_scaled_row_bytes); + DML_LOG_VERBOSE("DML::%s: total vm+row bytes (hvm ineff scaled) = %f\n", __func__, l->hvm_scaled_vm_row_bytes); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for vm and row = %f\n", __func__, l->hvm_scaled_vm_row_bytes / (l->max_flip_time - Tno_bw_flip)); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for vm = %f\n", __func__, l->hvm_scaled_vm_bytes / (l->max_flip_time - Tno_bw_flip - 2 * Tr0_trips_flip_rounded)); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for row = %f\n", __func__, l->hvm_scaled_row_bytes / (l->max_flip_time - Tvm_trips_flip_rounded)); + + if (l->lb_flip_bw > 0) { + DML_LOG_VERBOSE("DML::%s: mode_support est Tvm_flip = %f (bw-based)\n", __func__, Tno_bw_flip + l->hvm_scaled_vm_bytes / l->lb_flip_bw); + DML_LOG_VERBOSE("DML::%s: mode_support est Tr0_flip = %f (bw-based)\n", __func__, l->hvm_scaled_row_bytes / l->lb_flip_bw / l->num_rows); + DML_LOG_VERBOSE("DML::%s: mode_support est dst_y_per_vm_flip = %f (bw-based)\n", __func__, Tno_bw_flip + l->hvm_scaled_vm_bytes / l->lb_flip_bw / LineTime); + DML_LOG_VERBOSE("DML::%s: mode_support est dst_y_per_row_flip = %f (bw-based)\n", __func__, l->hvm_scaled_row_bytes / l->lb_flip_bw / LineTime / l->num_rows); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip_rounded + 2*Tr0_trips_flip_rounded = %f\n", __func__, (Tvm_trips_flip_rounded + 2 * Tr0_trips_flip_rounded)); + } + l->lb_flip_bw = math_max3(l->lb_flip_bw, + l->hvm_scaled_vm_bytes / (31 * LineTime) - Tno_bw_flip, + (l->dpte_row_bytes * HostVMInefficiencyFactor + meta_row_bytes) / (15 * LineTime)); + + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for vm reg limit = %f\n", __func__, l->hvm_scaled_vm_bytes / (31 * LineTime) - Tno_bw_flip); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for row reg limit = %f\n", __func__, (l->dpte_row_bytes * HostVMInefficiencyFactor + meta_row_bytes) / (15 * LineTime)); + } + + *final_flip_bw = l->lb_flip_bw; + + *dst_y_per_vm_flip = 1; // not used + *dst_y_per_row_flip = 1; // not used + *ImmediateFlipSupportedForPipe = l->min_row_time >= (Tvm_trips_flip_rounded + 2 * Tr0_trips_flip_rounded); + } else { + if (iflip_enable) { + l->ImmediateFlipBW = (double)per_pipe_flip_bytes * BandwidthAvailableForImmediateFlip / (double)TotImmediateFlipBytes; // flip_bw(i) + DML_LOG_VERBOSE("DML::%s: per_pipe_flip_bytes = %d\n", __func__, per_pipe_flip_bytes); + DML_LOG_VERBOSE("DML::%s: BandwidthAvailableForImmediateFlip = %f\n", __func__, BandwidthAvailableForImmediateFlip); + DML_LOG_VERBOSE("DML::%s: ImmediateFlipBW = %f\n", __func__, l->ImmediateFlipBW); + DML_LOG_VERBOSE("DML::%s: portion of flip bw = %f\n", __func__, (double)per_pipe_flip_bytes / (double)TotImmediateFlipBytes); + if (l->ImmediateFlipBW == 0) { + l->Tvm_flip = 0; + l->Tr0_flip = 0; + } else { + l->Tvm_flip = math_max3(Tvm_trips_flip, + Tno_bw_flip + vm_bytes * HostVMInefficiencyFactor / l->ImmediateFlipBW, + LineTime / 4.0); + + l->Tr0_flip = math_max3(Tr0_trips_flip, + (l->dpte_row_bytes * HostVMInefficiencyFactor + meta_row_bytes) / l->ImmediateFlipBW, + LineTime / 4.0); + } + DML_LOG_VERBOSE("DML::%s: total vm bytes (hvm ineff scaled) = %f\n", __func__, vm_bytes * HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: total row bytes (hvm ineff scaled, one row) = %f\n", __func__, (l->dpte_row_bytes * HostVMInefficiencyFactor + meta_row_bytes)); + DML_LOG_VERBOSE("DML::%s: Tvm_flip = %f (bw-based), Tvm_trips_flip = %f (latency-based)\n", __func__, Tno_bw_flip + vm_bytes * HostVMInefficiencyFactor / l->ImmediateFlipBW, Tvm_trips_flip); + DML_LOG_VERBOSE("DML::%s: Tr0_flip = %f (bw-based), Tr0_trips_flip = %f (latency-based)\n", __func__, (l->dpte_row_bytes * HostVMInefficiencyFactor + meta_row_bytes) / l->ImmediateFlipBW, Tr0_trips_flip); + *dst_y_per_vm_flip = math_ceil2(4.0 * (l->Tvm_flip / LineTime), 1.0) / 4.0; + *dst_y_per_row_flip = math_ceil2(4.0 * (l->Tr0_flip / LineTime), 1.0) / 4.0; + + *final_flip_bw = math_max2(vm_bytes * HostVMInefficiencyFactor / (*dst_y_per_vm_flip * LineTime), + (l->dpte_row_bytes * HostVMInefficiencyFactor + meta_row_bytes) / (*dst_y_per_row_flip * LineTime)); + + if (*dst_y_per_vm_flip >= 32 || *dst_y_per_row_flip >= 16 || l->Tvm_flip + 2 * l->Tr0_flip > l->min_row_time) { + *ImmediateFlipSupportedForPipe = false; + } else { + *ImmediateFlipSupportedForPipe = iflip_enable; + } + } else { + l->Tvm_flip = 0; + l->Tr0_flip = 0; + *dst_y_per_vm_flip = 0; + *dst_y_per_row_flip = 0; + *final_flip_bw = 0; + *ImmediateFlipSupportedForPipe = iflip_enable; + } + } + } else { + l->Tvm_flip = 0; + l->Tr0_flip = 0; + *dst_y_per_vm_flip = 0; + *dst_y_per_row_flip = 0; + *final_flip_bw = 0; + *ImmediateFlipSupportedForPipe = iflip_enable; + } + + if (!use_lb_flip_bw) { + DML_LOG_VERBOSE("DML::%s: dst_y_per_vm_flip = %f (should be < 32)\n", __func__, *dst_y_per_vm_flip); + DML_LOG_VERBOSE("DML::%s: dst_y_per_row_flip = %f (should be < 16)\n", __func__, *dst_y_per_row_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_flip = %f (final)\n", __func__, l->Tvm_flip); + DML_LOG_VERBOSE("DML::%s: Tr0_flip = %f (final)\n", __func__, l->Tr0_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_flip + 2*Tr0_flip = %f (should be <= min_row_time=%f)\n", __func__, l->Tvm_flip + 2 * l->Tr0_flip, l->min_row_time); + } + DML_LOG_VERBOSE("DML::%s: final_flip_bw = %f\n", __func__, *final_flip_bw); + DML_LOG_VERBOSE("DML::%s: ImmediateFlipSupportedForPipe = %u\n", __func__, *ImmediateFlipSupportedForPipe); +} + +bool dcn5_calculate_pstate_support_method( + enum dml2_pstate_method method, + double vactive_margin_us, + double reserved_vblank_us, + double blackout_us, + bool all_streams_blanked, + /* output */ + enum dml2_pstate_change_support *surface_pstate_change_support) +{ + *surface_pstate_change_support = dml2_pstate_change_unsupported; + if (method == dml2_pstate_method_na) { + /* automatic */ + if (all_streams_blanked || + (vactive_margin_us > 0 && reserved_vblank_us >= blackout_us)) + *surface_pstate_change_support = dml2_pstate_change_vblank_and_vactive; + else if (vactive_margin_us > 0) + *surface_pstate_change_support = dml2_pstate_change_vactive; + else if (reserved_vblank_us >= blackout_us) + *surface_pstate_change_support = dml2_pstate_change_vblank; + } else if (method == dml2_pstate_method_vactive || method == dml2_pstate_method_fw_vactive_drr) { + /* vactive */ + if (all_streams_blanked || + (vactive_margin_us > 0 && reserved_vblank_us >= blackout_us)) + *surface_pstate_change_support = dml2_pstate_change_vblank_and_vactive; + else if (vactive_margin_us > 0) + *surface_pstate_change_support = dml2_pstate_change_vactive; + } else if ((method == dml2_pstate_method_vblank || method == dml2_pstate_method_fw_vblank_drr) && + reserved_vblank_us >= blackout_us) { + /* vblank */ + *surface_pstate_change_support = dml2_pstate_change_vblank; + } else if (method == dml2_pstate_method_fw_drr) { + /* drr */ + *surface_pstate_change_support = dml2_pstate_change_drr; + } else if (method == dml2_pstate_method_alternate) { + /* TODO - alternate */ + *surface_pstate_change_support = dml2_pstate_change_mall_svp; + } + + return *surface_pstate_change_support != dml2_pstate_change_unsupported; +} + +static double dcn5_calculate_writeback_latency_hiding_us( + const struct dml2_display_cfg *display_cfg, + unsigned int writeback_buffer_size_bytes, + unsigned int stream_index, + unsigned int dwb_index) +{ + double byte_per_pixel_luma_in_buffer = 1.0; + double buffer_for_luma_bytes = (double)writeback_buffer_size_bytes * 1024.0; + double line_time_us = (double)display_cfg->stream_descriptors[stream_index].timing.h_total / + (double)display_cfg->stream_descriptors[stream_index].timing.pixel_clock_khz / 1000.0; + + if (display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_444_64) { + byte_per_pixel_luma_in_buffer = 8.0; + } else if (display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_444_32) { + byte_per_pixel_luma_in_buffer = 4.0; + } else if (display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_422_packed_8 + || display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_420_8) { + byte_per_pixel_luma_in_buffer = 1.0; + buffer_for_luma_bytes = buffer_for_luma_bytes / 2.0; + } else if (display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_422_packed_10 + || display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_420_10) { + byte_per_pixel_luma_in_buffer = 10.0 / 8.0; + } + + return (double)buffer_for_luma_bytes / + ((double)display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].output_height * + (double)display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].output_width / + ((double)display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].input_height * + line_time_us) * byte_per_pixel_luma_in_buffer); +} + +void dcn5_calculate_watermarks_and_dram_speed_change_support( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p) +{ + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_locals *s = &scratch->CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_locals; + + double reserved_vblank_time_us; + bool FoundCriticalSurface = false; + + s->TotalActiveWriteback = 0; + p->Watermark->UrgentWatermark = p->mmSOCParameters.UrgentLatency + p->mmSOCParameters.ExtraLatency; + + DML_LOG_VERBOSE("DML::%s: UrgentLatency = %f\n", __func__, p->mmSOCParameters.UrgentLatency); + DML_LOG_VERBOSE("DML::%s: ExtraLatency = %f\n", __func__, p->mmSOCParameters.ExtraLatency); + DML_LOG_VERBOSE("DML::%s: UrgentWatermark = %f\n", __func__, p->Watermark->UrgentWatermark); + + p->Watermark->USRRetrainingWatermark = p->mmSOCParameters.UrgentLatency + p->mmSOCParameters.ExtraLatency + p->mmSOCParameters.USRRetrainingLatency + p->mmSOCParameters.SMNLatency; + p->Watermark->DRAMClockChangeWatermark = p->mmSOCParameters.DRAMClockChangeLatency + p->Watermark->UrgentWatermark; + p->Watermark->FCLKChangeWatermark = p->mmSOCParameters.FCLKChangeLatency + p->Watermark->UrgentWatermark; + p->Watermark->StutterExitWatermark = p->mmSOCParameters.SRExitTime + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->StutterEnterPlusExitWatermark = p->mmSOCParameters.SREnterPlusExitTime + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->Z8StutterExitWatermark = p->mmSOCParameters.SRExitZ8Time + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->Z8StutterEnterPlusExitWatermark = p->mmSOCParameters.SREnterPlusExitZ8Time + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + if (p->mmSOCParameters.qos_type == dml2_qos_param_type_dcn4x) { + p->Watermark->StutterExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->StutterEnterPlusExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->Z8StutterExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->Z8StutterEnterPlusExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + } + p->Watermark->temp_read_or_ppt_watermark_us = p->mmSOCParameters.temp_read_or_ppt_blackout_us + p->Watermark->UrgentWatermark; + + DML_LOG_VERBOSE("DML::%s: UrgentLatency = %f\n", __func__, p->mmSOCParameters.UrgentLatency); + DML_LOG_VERBOSE("DML::%s: ExtraLatency = %f\n", __func__, p->mmSOCParameters.ExtraLatency); + DML_LOG_VERBOSE("DML::%s: DRAMClockChangeLatency = %f\n", __func__, p->mmSOCParameters.DRAMClockChangeLatency); + DML_LOG_VERBOSE("DML::%s: SREnterPlusExitZ8Time = %f\n", __func__, p->mmSOCParameters.SREnterPlusExitZ8Time); + DML_LOG_VERBOSE("DML::%s: SREnterPlusExitTime = %f\n", __func__, p->mmSOCParameters.SREnterPlusExitTime); + DML_LOG_VERBOSE("DML::%s: UrgentWatermark = %f\n", __func__, p->Watermark->UrgentWatermark); + DML_LOG_VERBOSE("DML::%s: USRRetrainingWatermark = %f\n", __func__, p->Watermark->USRRetrainingWatermark); + DML_LOG_VERBOSE("DML::%s: DRAMClockChangeWatermark = %f\n", __func__, p->Watermark->DRAMClockChangeWatermark); + DML_LOG_VERBOSE("DML::%s: FCLKChangeWatermark = %f\n", __func__, p->Watermark->FCLKChangeWatermark); + DML_LOG_VERBOSE("DML::%s: StutterExitWatermark = %f\n", __func__, p->Watermark->StutterExitWatermark); + DML_LOG_VERBOSE("DML::%s: StutterEnterPlusExitWatermark = %f\n", __func__, p->Watermark->StutterEnterPlusExitWatermark); + DML_LOG_VERBOSE("DML::%s: Z8StutterExitWatermark = %f\n", __func__, p->Watermark->Z8StutterExitWatermark); + DML_LOG_VERBOSE("DML::%s: Z8StutterEnterPlusExitWatermark = %f\n", __func__, p->Watermark->Z8StutterEnterPlusExitWatermark); + DML_LOG_VERBOSE("DML::%s: temp_read_or_ppt_watermark_us = %f\n", __func__, p->Watermark->temp_read_or_ppt_watermark_us); + + s->TotalActiveWriteback = 0; + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) + if (p->display_cfg->plane_descriptors[k].stream_index == k) + s->TotalActiveWriteback = s->TotalActiveWriteback + p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream; + + if (s->TotalActiveWriteback <= 1) { + p->Watermark->WritebackUrgentWatermark = p->mmSOCParameters.WritebackLatency; + } else { + p->Watermark->WritebackUrgentWatermark = p->mmSOCParameters.WritebackLatency + (s->TotalActiveWriteback - 1) * p->WritebackChunkSize * 1024.0 / 32.0 / p->SOCCLK; + } + if (p->USRRetrainingRequired) + p->Watermark->WritebackUrgentWatermark = p->Watermark->WritebackUrgentWatermark + p->mmSOCParameters.USRRetrainingLatency; + + if (s->TotalActiveWriteback <= 1) { + p->Watermark->WritebackDRAMClockChangeWatermark = p->mmSOCParameters.DRAMClockChangeLatency + p->mmSOCParameters.WritebackLatency; + p->Watermark->WritebackFCLKChangeWatermark = p->mmSOCParameters.FCLKChangeLatency + p->mmSOCParameters.WritebackLatency; + } else { + p->Watermark->WritebackDRAMClockChangeWatermark = p->mmSOCParameters.DRAMClockChangeLatency + p->mmSOCParameters.WritebackLatency + (s->TotalActiveWriteback - 1) * p->WritebackChunkSize * 1024.0 / 32.0 / p->SOCCLK; + p->Watermark->WritebackFCLKChangeWatermark = p->mmSOCParameters.FCLKChangeLatency + p->mmSOCParameters.WritebackLatency + (s->TotalActiveWriteback - 1) * p->WritebackChunkSize * 1024.0 / 32.0 / p->SOCCLK; + } + + if (p->USRRetrainingRequired) + p->Watermark->WritebackDRAMClockChangeWatermark = p->Watermark->WritebackDRAMClockChangeWatermark + p->mmSOCParameters.USRRetrainingLatency; + + if (p->USRRetrainingRequired) + p->Watermark->WritebackFCLKChangeWatermark = p->Watermark->WritebackFCLKChangeWatermark + p->mmSOCParameters.USRRetrainingLatency; + + if (s->TotalActiveWriteback <= 1) { + p->Watermark->writeback_temp_read_or_ppt_watermark_us = p->mmSOCParameters.temp_read_or_ppt_blackout_us; + } else { + p->Watermark->writeback_temp_read_or_ppt_watermark_us = p->mmSOCParameters.temp_read_or_ppt_blackout_us + (s->TotalActiveWriteback - 1) * p->WritebackChunkSize * 1024.0 / 32.0 / p->SOCCLK; + } + + DML_LOG_VERBOSE("DML::%s: WritebackDRAMClockChangeWatermark = %f\n", __func__, p->Watermark->WritebackDRAMClockChangeWatermark); + DML_LOG_VERBOSE("DML::%s: WritebackFCLKChangeWatermark = %f\n", __func__, p->Watermark->WritebackFCLKChangeWatermark); + DML_LOG_VERBOSE("DML::%s: writeback_temp_read_or_ppt_watermark_us = %f\n", __func__, p->Watermark->writeback_temp_read_or_ppt_watermark_us); + DML_LOG_VERBOSE("DML::%s: WritebackUrgentWatermark = %f\n", __func__, p->Watermark->WritebackUrgentWatermark); + DML_LOG_VERBOSE("DML::%s: USRRetrainingRequired = %u\n", __func__, p->USRRetrainingRequired); + DML_LOG_VERBOSE("DML::%s: USRRetrainingLatency = %f\n", __func__, p->mmSOCParameters.USRRetrainingLatency); + + s->TotalPixelBW = 0.0; + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + double h_total = (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total; + double pixel_clock_mhz = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000.0; + double v_ratio = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + double v_ratio_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + s->TotalPixelBW = s->TotalPixelBW + p->DPPPerSurface[k] + * (p->SwathWidthY[k] * p->BytePerPixelDETY[k] * v_ratio + p->SwathWidthC[k] * p->BytePerPixelDETC[k] * v_ratio_c) / (h_total / pixel_clock_mhz); + } + + *p->global_fclk_change_supported = true; + *p->global_dram_clock_change_supported = true; + *p->global_temp_read_or_ppt_supported = true; + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + double h_total = (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total; + double pixel_clock_mhz = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000.0; + double v_ratio = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + double v_ratio_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + double v_taps = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_taps; + double v_taps_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_taps; + double h_ratio = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio; + double h_ratio_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio; + double LBBitPerPixel = 57; + + s->LBLatencyHidingSourceLinesY[k] = (unsigned int)(math_min2((double)p->MaxLineBufferLines, math_floor2((double)p->LineBufferSize / LBBitPerPixel / ((double)p->SwathWidthY[k] / math_max2(h_ratio, 1.0)), 1)) - (v_taps - 1)); + s->LBLatencyHidingSourceLinesC[k] = (unsigned int)(math_min2((double)p->MaxLineBufferLines, math_floor2((double)p->LineBufferSize / LBBitPerPixel / ((double)p->SwathWidthC[k] / math_max2(h_ratio_c, 1.0)), 1)) - (v_taps_c - 1)); + + DML_LOG_VERBOSE("DML::%s: k=%u, MaxLineBufferLines= %u\n", __func__, k, p->MaxLineBufferLines); + DML_LOG_VERBOSE("DML::%s: k=%u, LineBufferSize = %u\n", __func__, k, p->LineBufferSize); + DML_LOG_VERBOSE("DML::%s: k=%u, LBBitPerPixel = %f\n", __func__, k, LBBitPerPixel); + DML_LOG_VERBOSE("DML::%s: k=%u, HRatio = %f\n", __func__, k, h_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u, VTaps = %f\n", __func__, k, v_taps); + + s->EffectiveLBLatencyHidingY = s->LBLatencyHidingSourceLinesY[k] / v_ratio * (h_total / pixel_clock_mhz); + s->EffectiveLBLatencyHidingC = s->LBLatencyHidingSourceLinesC[k] / v_ratio_c * (h_total / pixel_clock_mhz); + + s->EffectiveDETBufferSizeY = p->DETBufferSizeY[k]; + if (p->UnboundedRequestEnabled) { + s->EffectiveDETBufferSizeY = s->EffectiveDETBufferSizeY + p->CompressedBufferSizeInkByte * 1024 * (p->SwathWidthY[k] * p->BytePerPixelDETY[k] * v_ratio) / (h_total / pixel_clock_mhz) / s->TotalPixelBW; + } + + s->LinesInDETY[k] = (double)s->EffectiveDETBufferSizeY / p->BytePerPixelDETY[k] / p->SwathWidthY[k]; + s->LinesInDETYRoundedDownToSwath[k] = (unsigned int)(math_floor2(s->LinesInDETY[k], p->SwathHeightY[k])); + s->FullDETBufferingTimeY = s->LinesInDETYRoundedDownToSwath[k] * (h_total / pixel_clock_mhz) / v_ratio; + + s->ActiveClockChangeLatencyHidingY = s->EffectiveLBLatencyHidingY + s->FullDETBufferingTimeY - ((double)p->DSTXAfterScaler[k] / h_total + (double)p->DSTYAfterScaler[k]) * h_total / pixel_clock_mhz; + + if (p->NumberOfActiveSurfaces > 1) { + s->ActiveClockChangeLatencyHidingY = s->ActiveClockChangeLatencyHidingY - (1.0 - 1.0 / (double)p->NumberOfActiveSurfaces) * (double)p->SwathHeightY[k] * (double)h_total / pixel_clock_mhz / v_ratio; + } + + if (p->BytePerPixelDETC[k] > 0) { + s->LinesInDETC[k] = p->DETBufferSizeC[k] / p->BytePerPixelDETC[k] / p->SwathWidthC[k]; + s->LinesInDETCRoundedDownToSwath[k] = (unsigned int)(math_floor2(s->LinesInDETC[k], p->SwathHeightC[k])); + s->FullDETBufferingTimeC = s->LinesInDETCRoundedDownToSwath[k] * (h_total / pixel_clock_mhz) / v_ratio_c; + s->ActiveClockChangeLatencyHidingC = s->EffectiveLBLatencyHidingC + s->FullDETBufferingTimeC - ((double)p->DSTXAfterScaler[k] / (double)h_total + (double)p->DSTYAfterScaler[k]) * (double)h_total / pixel_clock_mhz; + if (p->NumberOfActiveSurfaces > 1) { + s->ActiveClockChangeLatencyHidingC = s->ActiveClockChangeLatencyHidingC - (1.0 - 1.0 / (double)p->NumberOfActiveSurfaces) * (double)p->SwathHeightC[k] * (double)h_total / pixel_clock_mhz / v_ratio_c; + } + s->ActiveClockChangeLatencyHiding = math_min2(s->ActiveClockChangeLatencyHidingY, s->ActiveClockChangeLatencyHidingC); + } else { + s->ActiveClockChangeLatencyHiding = s->ActiveClockChangeLatencyHidingY; + } + + DML_LOG_VERBOSE("DML::%s: ActiveClockChangeLatencyHidingY = %f\n", __func__, s->ActiveClockChangeLatencyHidingY); + DML_LOG_VERBOSE("DML::%s: ActiveClockChangeLatencyHidingC = %f\n", __func__, s->ActiveClockChangeLatencyHidingC); + DML_LOG_VERBOSE("DML::%s: ActiveClockChangeLatencyHiding = %f\n", __func__, s->ActiveClockChangeLatencyHiding); + + s->ActiveDRAMClockChangeLatencyMargin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->DRAMClockChangeWatermark; + s->ActiveFCLKChangeLatencyMargin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->FCLKChangeWatermark; + s->USRRetrainingLatencyMargin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->USRRetrainingWatermark; + s->temp_read_or_ppt_latency_margin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->temp_read_or_ppt_watermark_us; + + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveFCLKChangeLatencyMargin = %f\n", __func__, k, s->ActiveFCLKChangeLatencyMargin[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveDRAMClockChangeLatencyMargin = %f\n", __func__, k, s->ActiveDRAMClockChangeLatencyMargin[k]); + + if (p->VActiveLatencyHidingMargin) { + p->VActiveLatencyHidingMargin[k] = s->ActiveDRAMClockChangeLatencyMargin[k]; + DML_LOG_VERBOSE("DML::%s: k=%u, VActiveLatencyHidingMargin = %f\n", __func__, k, p->VActiveLatencyHidingMargin[k]); + } + + if (p->VActiveLatencyHidingUs) { + p->VActiveLatencyHidingUs[k] = s->ActiveClockChangeLatencyHiding; + DML_LOG_VERBOSE("DML::%s: k=%u, VActiveLatencyHidingUs = %f\n", __func__, k, p->VActiveLatencyHidingUs[k]); + } + + for (unsigned int j = 0; j < p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream; ++j) { + s->WritebackLatencyHiding = dcn5_calculate_writeback_latency_hiding_us(p->display_cfg, + p->WritebackInterfaceBufferSize * 1024, + p->display_cfg->plane_descriptors[k].stream_index, + j); + + s->WritebackDRAMClockChangeLatencyMargin = s->WritebackLatencyHiding - p->Watermark->WritebackDRAMClockChangeWatermark; + s->WritebackFCLKChangeLatencyMargin = s->WritebackLatencyHiding - p->Watermark->WritebackFCLKChangeWatermark; + s->WritebackTempReadOrPptLatencyMargin = s->WritebackLatencyHiding - p->Watermark->writeback_temp_read_or_ppt_watermark_us; + s->ActiveDRAMClockChangeLatencyMargin[k] = math_min2(s->ActiveDRAMClockChangeLatencyMargin[k], s->WritebackDRAMClockChangeLatencyMargin); + s->ActiveFCLKChangeLatencyMargin[k] = math_min2(s->ActiveFCLKChangeLatencyMargin[k], s->WritebackFCLKChangeLatencyMargin); + s->temp_read_or_ppt_latency_margin[k] = math_min2(s->temp_read_or_ppt_latency_margin[k], s->WritebackTempReadOrPptLatencyMargin); + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveFCLKChangeLatencyMargin = %f (WB)\n", __func__, k, s->ActiveFCLKChangeLatencyMargin[k]); + } + + p->MaxActiveDRAMClockChangeLatencySupported[k] = s->ActiveDRAMClockChangeLatencyMargin[k] + p->mmSOCParameters.DRAMClockChangeLatency; + + reserved_vblank_time_us = (double)p->display_cfg->plane_descriptors[k].overrides.reserved_vblank_time_ns / 1000; + + *p->global_fclk_change_supported &= dcn5_calculate_pstate_support_method( + dml2_pstate_method_vactive, + s->ActiveFCLKChangeLatencyMargin[k], + reserved_vblank_time_us, + p->mmSOCParameters.FCLKChangeLatency, + p->display_cfg->overrides.all_streams_blanked, + /* output */ + &p->FCLKChangeSupport[k]); + + *p->global_temp_read_or_ppt_supported &= dcn5_calculate_pstate_support_method( + dml2_pstate_method_vactive, + s->temp_read_or_ppt_latency_margin[k], + reserved_vblank_time_us, + p->mmSOCParameters.temp_read_or_ppt_blackout_us, + p->display_cfg->overrides.all_streams_blanked, + /* output */ + &p->temp_read_or_ppt_support[k]); + + *p->global_dram_clock_change_support_required |= p->uclk_pstate_switch_modes[k] != dml2_pstate_method_na; + *p->global_dram_clock_change_supported &= dcn5_calculate_pstate_support_method( + p->uclk_pstate_switch_modes[k], + s->ActiveDRAMClockChangeLatencyMargin[k], + reserved_vblank_time_us, + p->mmSOCParameters.DRAMClockChangeLatency, + p->display_cfg->overrides.all_streams_blanked, + /* output */ + &p->DRAMClockChangeSupport[k]); + + s->dst_y_pstate = (unsigned int)(math_ceil2((p->mmSOCParameters.DRAMClockChangeLatency + p->mmSOCParameters.UrgentLatency) / (h_total / pixel_clock_mhz), 1)); + s->src_y_pstate_l = (unsigned int)(math_ceil2(s->dst_y_pstate * v_ratio, p->SwathHeightY[k])); + s->src_y_ahead_l = (unsigned int)(math_floor2(p->DETBufferSizeY[k] / p->BytePerPixelDETY[k] / p->SwathWidthY[k], p->SwathHeightY[k]) + s->LBLatencyHidingSourceLinesY[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, DETBufferSizeY = %u\n", __func__, k, p->DETBufferSizeY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, BytePerPixelDETY = %f\n", __func__, k, p->BytePerPixelDETY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathWidthY = %u\n", __func__, k, p->SwathWidthY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathHeightY = %u\n", __func__, k, p->SwathHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, LBLatencyHidingSourceLinesY = %u\n", __func__, k, s->LBLatencyHidingSourceLinesY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dst_y_pstate = %u\n", __func__, k, s->dst_y_pstate); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_pstate_l = %u\n", __func__, k, s->src_y_pstate_l); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_ahead_l = %u\n", __func__, k, s->src_y_ahead_l); + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_height_l = %u\n", __func__, k, p->meta_row_height_l[k]); + + if (p->BytePerPixelDETC[k] > 0) { + s->src_y_pstate_c = (unsigned int)(math_ceil2(s->dst_y_pstate * v_ratio_c, p->SwathHeightC[k])); + s->src_y_ahead_c = (unsigned int)(math_floor2(p->DETBufferSizeC[k] / p->BytePerPixelDETC[k] / p->SwathWidthC[k], p->SwathHeightC[k]) + s->LBLatencyHidingSourceLinesC[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_height_c = %u\n", __func__, k, p->meta_row_height_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_pstate_c = %u\n", __func__, k, s->src_y_pstate_c); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_ahead_c = %u\n", __func__, k, s->src_y_ahead_c); + DML_LOG_VERBOSE("DML::%s: k=%u, sub_vp_lines_c = %u\n", __func__, k, s->sub_vp_lines_c); + } + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveFCLKChangeLatencyMargin=%f\n", __func__, k, s->ActiveFCLKChangeLatencyMargin[k]); + if (((!FoundCriticalSurface) || ((s->ActiveFCLKChangeLatencyMargin[k] + p->mmSOCParameters.FCLKChangeLatency) < *p->MaxActiveFCLKChangeLatencySupported))) { + FoundCriticalSurface = true; + *p->MaxActiveFCLKChangeLatencySupported = s->ActiveFCLKChangeLatencyMargin[k] + p->mmSOCParameters.FCLKChangeLatency; + } + } + + DML_LOG_VERBOSE("DML::%s: DRAMClockChangeSupport = %u\n", __func__, *p->global_dram_clock_change_supported); + DML_LOG_VERBOSE("DML::%s: FCLKChangeSupport = %u\n", __func__, *p->global_fclk_change_supported); + DML_LOG_VERBOSE("DML::%s: MaxActiveFCLKChangeLatencySupported = %f\n", __func__, *p->MaxActiveFCLKChangeLatencySupported); + DML_LOG_VERBOSE("DML::%s: USRRetrainingSupport = %u\n", __func__, *p->USRRetrainingSupport); +} + +void dcn5_calculate_pstate_keepout_dst_lines( + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_watermarks *watermarks, + unsigned int pstate_keepout_dst_lines[]) +{ + const struct dml2_stream_parameters *stream_descriptor; + unsigned int i; + + for (i = 0; i < display_cfg->num_planes; i++) { + (void)display_cfg; + stream_descriptor = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[i].stream_index]; + + pstate_keepout_dst_lines[i] = + (unsigned int)math_ceil(watermarks->DRAMClockChangeWatermark / ((double)stream_descriptor->timing.h_total * 1000.0 / (double)stream_descriptor->timing.pixel_clock_khz)); + + if (pstate_keepout_dst_lines[i] > stream_descriptor->timing.v_total - 1) { + pstate_keepout_dst_lines[i] = stream_descriptor->timing.v_total - 1; + } + } +} + +void dcn5_calculate_vactive_det_fill_latency( + const struct dml2_display_cfg *display_cfg, + unsigned int num_active_planes, + unsigned int bytes_required_l[], + unsigned int bytes_required_c[], + double dcc_dram_bw_nom_overhead_factor_p0[], + double dcc_dram_bw_nom_overhead_factor_p1[], + double surface_read_bw_l[], + double surface_read_bw_c[], + double surface_avg_vactive_required_bw[], + double surface_peak_required_bw[], + /* output */ + double vactive_det_fill_delay_us[]) +{ + (void)display_cfg; + double effective_excess_bandwidth; + double effective_excess_bandwidth_l; + double effective_excess_bandwidth_c; + unsigned int plane_index; + + for (plane_index = 0; plane_index < num_active_planes; plane_index++) { + if (bytes_required_l[plane_index] <= 0 && bytes_required_c[plane_index] <= 0) { + continue; + } + + vactive_det_fill_delay_us[plane_index] = 0.0; + effective_excess_bandwidth = (surface_peak_required_bw[plane_index] - surface_avg_vactive_required_bw[plane_index]); + + effective_excess_bandwidth_l = effective_excess_bandwidth * surface_read_bw_l[plane_index] + / (surface_read_bw_l[plane_index] + surface_read_bw_c[plane_index]) / dcc_dram_bw_nom_overhead_factor_p0[plane_index]; + if (effective_excess_bandwidth_l > 0.0) { + vactive_det_fill_delay_us[plane_index] = math_max2(vactive_det_fill_delay_us[plane_index], bytes_required_l[plane_index] / effective_excess_bandwidth_l); + } + + effective_excess_bandwidth_c = effective_excess_bandwidth * surface_read_bw_c[plane_index] + / (surface_read_bw_l[plane_index] + surface_read_bw_c[plane_index]) / dcc_dram_bw_nom_overhead_factor_p1[plane_index]; + if (effective_excess_bandwidth_c > 0.0) { + vactive_det_fill_delay_us[plane_index] = math_max2(vactive_det_fill_delay_us[plane_index], bytes_required_c[plane_index] / effective_excess_bandwidth_c); + } + } +} + +double dcn5_calculate_write_back_delay( + enum dml2_source_format_class WritebackPixelFormat, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackVTaps, + unsigned int WritebackVTapsChroma, + unsigned int WritebackDestinationWidth, + unsigned int WritebackDestinationHeight, + unsigned int WritebackSourceWidth, + unsigned int WritebackSourceHeight, + unsigned int HTotal) +{ + (void)WritebackHRatio; + double CalculateWriteBackDelay; + double Line_length; + double Output_lines_last_notclamped; + double WritebackVInit; + + WritebackVInit = (WritebackVRatio + WritebackVTaps + 1) / 2; + Line_length = math_max2((double)WritebackDestinationWidth, math_ceil2((double)WritebackDestinationWidth / 6.0, 1.0) * WritebackVTaps); + Output_lines_last_notclamped = WritebackDestinationHeight - 1 - math_ceil2(((double)WritebackSourceHeight - (double)WritebackVInit) / (double)WritebackVRatio, 1.0); + if (Output_lines_last_notclamped < 0) + CalculateWriteBackDelay = 0; + else + CalculateWriteBackDelay = Output_lines_last_notclamped * Line_length + (HTotal - WritebackSourceWidth) + 80; + + double v_ratio_chroma; + double output_width_chroma; + double output_height_chroma; + + if (WritebackPixelFormat == dml2_420_8 || WritebackPixelFormat == dml2_422_packed_8 + || WritebackPixelFormat == dml2_420_10 || WritebackPixelFormat == dml2_422_packed_10) + output_width_chroma = 0.5 * WritebackDestinationWidth; + else + output_width_chroma = WritebackDestinationWidth; + + if (WritebackPixelFormat == dml2_420_8 || WritebackPixelFormat == dml2_420_10) { + v_ratio_chroma = 2.0 * WritebackVRatio; + output_height_chroma = 0.5 * WritebackDestinationHeight; + } else { + v_ratio_chroma = WritebackVRatio; + output_height_chroma = WritebackDestinationHeight; + } + + double CalculateWriteBackDelay_chroma; + double Line_length_chroma; + double Output_lines_last_notclamped_chroma; + double WritebackVInit_chroma; + + WritebackVInit_chroma = (v_ratio_chroma + WritebackVTapsChroma + 1) / 2; + Line_length_chroma = math_max2((double)output_height_chroma, math_ceil2((double)output_width_chroma / 6.0, 1.0) * WritebackVTapsChroma); + Output_lines_last_notclamped_chroma = output_height_chroma - 1 - math_ceil2(((double)WritebackSourceHeight - (double)WritebackVInit_chroma) / (double)v_ratio_chroma, 1.0); + if (Output_lines_last_notclamped_chroma < 0) + CalculateWriteBackDelay_chroma = 0; + else + CalculateWriteBackDelay_chroma = Output_lines_last_notclamped_chroma * Line_length_chroma + (HTotal - WritebackSourceWidth) + 80; + + return math_max2(CalculateWriteBackDelay, CalculateWriteBackDelay_chroma); +} + +void dcn5_calculate_meta_and_pte_times(struct dml2_core_shared_CalculateMetaAndPTETimes_params *p) +{ + unsigned int meta_chunk_width; + unsigned int min_meta_chunk_width; + unsigned int meta_chunk_per_row_int; + unsigned int meta_row_remainder; + unsigned int meta_chunk_threshold; + unsigned int meta_chunks_per_row_ub; + unsigned int meta_chunk_width_chroma; + unsigned int min_meta_chunk_width_chroma; + unsigned int meta_chunk_per_row_int_chroma; + unsigned int meta_row_remainder_chroma; + unsigned int meta_chunk_threshold_chroma; + unsigned int meta_chunks_per_row_ub_chroma; + unsigned int dpte_group_width_luma; + unsigned int dpte_groups_per_row_luma_ub; + unsigned int dpte_group_width_chroma; + unsigned int dpte_groups_per_row_chroma_ub; + double pixel_clock_mhz; + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + p->DST_Y_PER_PTE_ROW_NOM_L[k] = p->dpte_row_height[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + if (p->BytePerPixelC[k] == 0) { + p->DST_Y_PER_PTE_ROW_NOM_C[k] = 0; + } else { + p->DST_Y_PER_PTE_ROW_NOM_C[k] = p->dpte_row_height_chroma[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + } + p->DST_Y_PER_META_ROW_NOM_L[k] = p->meta_row_height[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + if (p->BytePerPixelC[k] == 0) { + p->DST_Y_PER_META_ROW_NOM_C[k] = 0; + } else { + p->DST_Y_PER_META_ROW_NOM_C[k] = p->meta_row_height_chroma[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + } + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (p->display_cfg->plane_descriptors[k].surface.dcc.enable == true && p->mrq_present) { + meta_chunk_width = p->MetaChunkSize * 1024 * 256 / p->BytePerPixelY[k] / p->meta_row_height[k]; + min_meta_chunk_width = p->MinMetaChunkSizeBytes * 256 / p->BytePerPixelY[k] / p->meta_row_height[k]; + meta_chunk_per_row_int = p->meta_row_width[k] / meta_chunk_width; + meta_row_remainder = p->meta_row_width[k] % meta_chunk_width; + if (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle)) { + meta_chunk_threshold = 2 * min_meta_chunk_width - p->meta_req_width[k]; + } else { + meta_chunk_threshold = 2 * min_meta_chunk_width - p->meta_req_height[k]; + } + if (meta_row_remainder <= meta_chunk_threshold) { + meta_chunks_per_row_ub = meta_chunk_per_row_int + 1; + } else { + meta_chunks_per_row_ub = meta_chunk_per_row_int + 2; + } + p->TimePerMetaChunkNominal[k] = p->meta_row_height[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio * + p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / + (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) / meta_chunks_per_row_ub; + p->TimePerMetaChunkVBlank[k] = p->dst_y_per_row_vblank[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / + (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) / meta_chunks_per_row_ub; + p->TimePerMetaChunkFlip[k] = p->dst_y_per_row_flip[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / + (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) / meta_chunks_per_row_ub; + if (p->BytePerPixelC[k] == 0) { + p->TimePerChromaMetaChunkNominal[k] = 0; + p->TimePerChromaMetaChunkVBlank[k] = 0; + p->TimePerChromaMetaChunkFlip[k] = 0; + } else { + meta_chunk_width_chroma = p->MetaChunkSize * 1024 * 256 / p->BytePerPixelC[k] / p->meta_row_height_chroma[k]; + min_meta_chunk_width_chroma = p->MinMetaChunkSizeBytes * 256 / p->BytePerPixelC[k] / p->meta_row_height_chroma[k]; + meta_chunk_per_row_int_chroma = (unsigned int)((double)p->meta_row_width_chroma[k] / meta_chunk_width_chroma); + meta_row_remainder_chroma = p->meta_row_width_chroma[k] % meta_chunk_width_chroma; + if (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle)) { + meta_chunk_threshold_chroma = 2 * min_meta_chunk_width_chroma - p->meta_req_width_chroma[k]; + } else { + meta_chunk_threshold_chroma = 2 * min_meta_chunk_width_chroma - p->meta_req_height_chroma[k]; + } + if (meta_row_remainder_chroma <= meta_chunk_threshold_chroma) { + meta_chunks_per_row_ub_chroma = meta_chunk_per_row_int_chroma + 1; + } else { + meta_chunks_per_row_ub_chroma = meta_chunk_per_row_int_chroma + 2; + } + p->TimePerChromaMetaChunkNominal[k] = p->meta_row_height_chroma[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) / meta_chunks_per_row_ub_chroma; + p->TimePerChromaMetaChunkVBlank[k] = p->dst_y_per_row_vblank[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) / meta_chunks_per_row_ub_chroma; + p->TimePerChromaMetaChunkFlip[k] = p->dst_y_per_row_flip[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) / meta_chunks_per_row_ub_chroma; + } + } else { + p->TimePerMetaChunkNominal[k] = 0; + p->TimePerMetaChunkVBlank[k] = 0; + p->TimePerMetaChunkFlip[k] = 0; + p->TimePerChromaMetaChunkNominal[k] = 0; + p->TimePerChromaMetaChunkVBlank[k] = 0; + p->TimePerChromaMetaChunkFlip[k] = 0; + } + + DML_LOG_VERBOSE("DML::%s: k=%d, DST_Y_PER_META_ROW_NOM_L = %f\n", __func__, k, p->DST_Y_PER_META_ROW_NOM_L[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, DST_Y_PER_META_ROW_NOM_C = %f\n", __func__, k, p->DST_Y_PER_META_ROW_NOM_C[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, TimePerMetaChunkNominal = %f\n", __func__, k, p->TimePerMetaChunkNominal[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, TimePerMetaChunkVBlank = %f\n", __func__, k, p->TimePerMetaChunkVBlank[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, TimePerMetaChunkFlip = %f\n", __func__, k, p->TimePerMetaChunkFlip[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, TimePerChromaMetaChunkNominal= %f\n", __func__, k, p->TimePerChromaMetaChunkNominal[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, TimePerChromaMetaChunkVBlank = %f\n", __func__, k, p->TimePerChromaMetaChunkVBlank[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, TimePerChromaMetaChunkFlip = %f\n", __func__, k, p->TimePerChromaMetaChunkFlip[k]); + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + p->DST_Y_PER_PTE_ROW_NOM_L[k] = p->dpte_row_height[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + if (p->BytePerPixelC[k] == 0) { + p->DST_Y_PER_PTE_ROW_NOM_C[k] = 0; + } else { + p->DST_Y_PER_PTE_ROW_NOM_C[k] = p->dpte_row_height_chroma[k] / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + } + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + pixel_clock_mhz = ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + + if (p->display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut) + p->time_per_tdlut_group[k] = 2 * p->dst_y_per_row_vblank[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / p->tdlut_groups_per_2row_ub[k]; + else + p->time_per_tdlut_group[k] = 0; + + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_tdlut_group = %f\n", __func__, k, p->time_per_tdlut_group[k]); + + if (p->display_cfg->gpuvm_enable == true) { + if (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle)) { + dpte_group_width_luma = (unsigned int)((double)p->dpte_group_bytes[k] / (double)p->PTERequestSizeY[k] * p->PixelPTEReqWidthY[k]); + } else { + dpte_group_width_luma = (unsigned int)((double)p->dpte_group_bytes[k] / (double)p->PTERequestSizeY[k] * p->PixelPTEReqHeightY[k]); + } + if (p->use_one_row_for_frame[k]) { + dpte_groups_per_row_luma_ub = (unsigned int)(math_ceil2((double)p->dpte_row_width_luma_ub[k] / (double)dpte_group_width_luma / 2.0, 1.0)); + } else { + dpte_groups_per_row_luma_ub = (unsigned int)(math_ceil2((double)p->dpte_row_width_luma_ub[k] / (double)dpte_group_width_luma, 1.0)); + } + if (dpte_groups_per_row_luma_ub <= 2) { + dpte_groups_per_row_luma_ub = dpte_groups_per_row_luma_ub + 1; + } + DML_LOG_VERBOSE("DML::%s: k=%u, use_one_row_for_frame = %u\n", __func__, k, p->use_one_row_for_frame[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_group_bytes = %u\n", __func__, k, p->dpte_group_bytes[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PTERequestSizeY = %u\n", __func__, k, p->PTERequestSizeY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEReqWidthY = %u\n", __func__, k, p->PixelPTEReqWidthY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, PixelPTEReqHeightY = %u\n", __func__, k, p->PixelPTEReqHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_row_width_luma_ub = %u\n", __func__, k, p->dpte_row_width_luma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_group_width_luma = %u\n", __func__, k, dpte_group_width_luma); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_groups_per_row_luma_ub = %u\n", __func__, k, dpte_groups_per_row_luma_ub); + + p->time_per_pte_group_nom_luma[k] = p->DST_Y_PER_PTE_ROW_NOM_L[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / dpte_groups_per_row_luma_ub; + p->time_per_pte_group_vblank_luma[k] = p->dst_y_per_row_vblank[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / dpte_groups_per_row_luma_ub; + p->time_per_pte_group_flip_luma[k] = p->dst_y_per_row_flip[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / dpte_groups_per_row_luma_ub; + if (p->BytePerPixelC[k] == 0) { + p->time_per_pte_group_nom_chroma[k] = 0; + p->time_per_pte_group_vblank_chroma[k] = 0; + p->time_per_pte_group_flip_chroma[k] = 0; + } else { + if (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle)) { + dpte_group_width_chroma = (unsigned int)((double)p->dpte_group_bytes[k] / (double)p->PTERequestSizeC[k] * p->PixelPTEReqWidthC[k]); + } else { + dpte_group_width_chroma = (unsigned int)((double)p->dpte_group_bytes[k] / (double)p->PTERequestSizeC[k] * p->PixelPTEReqHeightC[k]); + } + + if (p->use_one_row_for_frame[k]) { + dpte_groups_per_row_chroma_ub = (unsigned int)(math_ceil2((double)p->dpte_row_width_chroma_ub[k] / (double)dpte_group_width_chroma / 2.0, 1.0)); + } else { + dpte_groups_per_row_chroma_ub = (unsigned int)(math_ceil2((double)p->dpte_row_width_chroma_ub[k] / (double)dpte_group_width_chroma, 1.0)); + } + if (dpte_groups_per_row_chroma_ub <= 2) { + dpte_groups_per_row_chroma_ub = dpte_groups_per_row_chroma_ub + 1; + } + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_row_width_chroma_ub = %u\n", __func__, k, p->dpte_row_width_chroma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_group_width_chroma = %u\n", __func__, k, dpte_group_width_chroma); + DML_LOG_VERBOSE("DML::%s: k=%u, dpte_groups_per_row_chroma_ub = %u\n", __func__, k, dpte_groups_per_row_chroma_ub); + + p->time_per_pte_group_nom_chroma[k] = p->DST_Y_PER_PTE_ROW_NOM_C[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / dpte_groups_per_row_chroma_ub; + p->time_per_pte_group_vblank_chroma[k] = p->dst_y_per_row_vblank[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / dpte_groups_per_row_chroma_ub; + p->time_per_pte_group_flip_chroma[k] = p->dst_y_per_row_flip[k] * p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz / dpte_groups_per_row_chroma_ub; + } + } else { + p->time_per_pte_group_nom_luma[k] = 0; + p->time_per_pte_group_vblank_luma[k] = 0; + p->time_per_pte_group_flip_luma[k] = 0; + p->time_per_pte_group_nom_chroma[k] = 0; + p->time_per_pte_group_vblank_chroma[k] = 0; + p->time_per_pte_group_flip_chroma[k] = 0; + } + DML_LOG_VERBOSE("DML::%s: k=%u, dst_y_per_row_vblank = %f\n", __func__, k, p->dst_y_per_row_vblank[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dst_y_per_row_flip = %f\n", __func__, k, p->dst_y_per_row_flip[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, DST_Y_PER_PTE_ROW_NOM_L = %f\n", __func__, k, p->DST_Y_PER_PTE_ROW_NOM_L[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, DST_Y_PER_PTE_ROW_NOM_C = %f\n", __func__, k, p->DST_Y_PER_PTE_ROW_NOM_C[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_pte_group_nom_luma = %f\n", __func__, k, p->time_per_pte_group_nom_luma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_pte_group_vblank_luma = %f\n", __func__, k, p->time_per_pte_group_vblank_luma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_pte_group_flip_luma = %f\n", __func__, k, p->time_per_pte_group_flip_luma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_pte_group_nom_chroma = %f\n", __func__, k, p->time_per_pte_group_nom_chroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_pte_group_vblank_chroma = %f\n", __func__, k, p->time_per_pte_group_vblank_chroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, time_per_pte_group_flip_chroma = %f\n", __func__, k, p->time_per_pte_group_flip_chroma[k]); + } +} + +void dcn5_calculate_vm_group_and_request_times( + const struct dml2_display_cfg *display_cfg, + unsigned int NumberOfActiveSurfaces, + unsigned int BytePerPixelC[], + double dst_y_per_vm_vblank[], + double dst_y_per_vm_flip[], + unsigned int dpte_row_width_luma_ub[], + unsigned int dpte_row_width_chroma_ub[], + unsigned int vm_group_bytes[], + unsigned int dpde0_bytes_per_frame_ub_l[], + unsigned int dpde0_bytes_per_frame_ub_c[], + unsigned int tdlut_pte_bytes_per_frame[], + unsigned int meta_pte_bytes_per_frame_ub_l[], + unsigned int meta_pte_bytes_per_frame_ub_c[], + bool mrq_present, + + // Output + double TimePerVMGroupVBlank[], + double TimePerVMGroupFlip[], + double TimePerVMRequestVBlank[], + double TimePerVMRequestFlip[]) +{ + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; + unsigned int num_group_per_lower_vm_stage = 0; + unsigned int num_req_per_lower_vm_stage = 0; + unsigned int num_group_per_lower_vm_stage_flip; + unsigned int num_group_per_lower_vm_stage_pref; + unsigned int num_req_per_lower_vm_stage_flip; + unsigned int num_req_per_lower_vm_stage_pref; + double line_time; + + DML_LOG_VERBOSE("DML::%s: NumberOfActiveSurfaces = %u\n", __func__, NumberOfActiveSurfaces); + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + double pixel_clock_mhz = ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + bool dcc_mrq_enable = display_cfg->plane_descriptors[k].surface.dcc.enable && mrq_present; + DML_LOG_VERBOSE("DML::%s: k=%u, dcc_mrq_enable = %u\n", __func__, k, dcc_mrq_enable); + DML_LOG_VERBOSE("DML::%s: k=%u, vm_group_bytes = %u\n", __func__, k, vm_group_bytes[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpde0_bytes_per_frame_ub_l = %u\n", __func__, k, dpde0_bytes_per_frame_ub_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dpde0_bytes_per_frame_ub_c = %u\n", __func__, k, dpde0_bytes_per_frame_ub_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, meta_pte_bytes_per_frame_ub_l = %d\n", __func__, k, meta_pte_bytes_per_frame_ub_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, meta_pte_bytes_per_frame_ub_c = %d\n", __func__, k, meta_pte_bytes_per_frame_ub_c[k]); + + if (display_cfg->gpuvm_enable) { + if (display_cfg->gpuvm_max_page_table_levels >= 2) { + num_group_per_lower_vm_stage += (unsigned int) math_ceil2((double) (dpde0_bytes_per_frame_ub_l[k]) / (double) (vm_group_bytes[k]), 1); + + if (BytePerPixelC[k] > 0) + num_group_per_lower_vm_stage += (unsigned int) math_ceil2((double) (dpde0_bytes_per_frame_ub_c[k]) / (double) (vm_group_bytes[k]), 1); + } + + if (dcc_mrq_enable) { + if (BytePerPixelC[k] > 0) { + num_group_per_lower_vm_stage += (unsigned int)(2.0 /*for each mpde0 group*/ + math_ceil2((double) (meta_pte_bytes_per_frame_ub_l[k]) / (double) (vm_group_bytes[k]), 1) + + math_ceil2((double) (meta_pte_bytes_per_frame_ub_c[k]) / (double) (vm_group_bytes[k]), 1)); + } else { + num_group_per_lower_vm_stage += (unsigned int)(1.0 + math_ceil2((double) (meta_pte_bytes_per_frame_ub_l[k]) / (double) (vm_group_bytes[k]), 1)); + } + } + + num_group_per_lower_vm_stage_flip = num_group_per_lower_vm_stage; + num_group_per_lower_vm_stage_pref = num_group_per_lower_vm_stage; + + if (display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut && display_cfg->gpuvm_enable) { + num_group_per_lower_vm_stage_pref += (unsigned int) math_ceil2(tdlut_pte_bytes_per_frame[k] / vm_group_bytes[k], 1); + if (display_cfg->gpuvm_max_page_table_levels >= 2) + num_group_per_lower_vm_stage_pref += 1; // tdpe0 group + } + + if (display_cfg->gpuvm_max_page_table_levels >= 2) { + num_req_per_lower_vm_stage += dpde0_bytes_per_frame_ub_l[k] / 64; + if (BytePerPixelC[k] > 0) + num_req_per_lower_vm_stage += dpde0_bytes_per_frame_ub_c[k]; + } + + if (dcc_mrq_enable) { + num_req_per_lower_vm_stage += meta_pte_bytes_per_frame_ub_l[k] / 64; + if (BytePerPixelC[k] > 0) + num_req_per_lower_vm_stage += meta_pte_bytes_per_frame_ub_c[k] / 64; + } + + num_req_per_lower_vm_stage_flip = num_req_per_lower_vm_stage; + num_req_per_lower_vm_stage_pref = num_req_per_lower_vm_stage; + + if (display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut && display_cfg->gpuvm_enable) { + num_req_per_lower_vm_stage_pref += tdlut_pte_bytes_per_frame[k] / 64; + } + + line_time = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / pixel_clock_mhz; + + if (num_group_per_lower_vm_stage_pref > 0) + TimePerVMGroupVBlank[k] = dst_y_per_vm_vblank[k] * line_time / num_group_per_lower_vm_stage_pref; + else + TimePerVMGroupVBlank[k] = 0; + + if (num_group_per_lower_vm_stage_flip > 0) + TimePerVMGroupFlip[k] = dst_y_per_vm_flip[k] * line_time / num_group_per_lower_vm_stage_flip; + else + TimePerVMGroupFlip[k] = 0; + + if (num_req_per_lower_vm_stage_pref > 0) + TimePerVMRequestVBlank[k] = dst_y_per_vm_vblank[k] * line_time / num_req_per_lower_vm_stage_pref; + else + TimePerVMRequestVBlank[k] = 0.0; + if (num_req_per_lower_vm_stage_flip > 0) + TimePerVMRequestFlip[k] = dst_y_per_vm_flip[k] * line_time / num_req_per_lower_vm_stage_flip; + else + TimePerVMRequestFlip[k] = 0.0; + + DML_LOG_VERBOSE("DML::%s: k=%u, dst_y_per_vm_vblank = %f\n", __func__, k, dst_y_per_vm_vblank[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dst_y_per_vm_flip = %f\n", __func__, k, dst_y_per_vm_flip[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, line_time = %f\n", __func__, k, line_time); + DML_LOG_VERBOSE("DML::%s: k=%u, num_group_per_lower_vm_stage_pref = %d\n", __func__, k, num_group_per_lower_vm_stage_pref); + DML_LOG_VERBOSE("DML::%s: k=%u, num_group_per_lower_vm_stage_flip = %d\n", __func__, k, num_group_per_lower_vm_stage_flip); + DML_LOG_VERBOSE("DML::%s: k=%u, num_req_per_lower_vm_stage_pref = %d\n", __func__, k, num_req_per_lower_vm_stage_pref); + DML_LOG_VERBOSE("DML::%s: k=%u, num_req_per_lower_vm_stage_flip = %d\n", __func__, k, num_req_per_lower_vm_stage_flip); + + if (display_cfg->gpuvm_max_page_table_levels > 2) { + TimePerVMGroupVBlank[k] = TimePerVMGroupVBlank[k] / 2; + TimePerVMGroupFlip[k] = TimePerVMGroupFlip[k] / 2; + TimePerVMRequestVBlank[k] = TimePerVMRequestVBlank[k] / 2; + TimePerVMRequestFlip[k] = TimePerVMRequestFlip[k] / 2; + } + + } else { + TimePerVMGroupVBlank[k] = 0; + TimePerVMGroupFlip[k] = 0; + TimePerVMRequestVBlank[k] = 0; + TimePerVMRequestFlip[k] = 0; + } + DML_LOG_VERBOSE("DML::%s: k=%u, TimePerVMGroupVBlank = %f\n", __func__, k, TimePerVMGroupVBlank[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, TimePerVMGroupFlip = %f\n", __func__, k, TimePerVMGroupFlip[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, TimePerVMRequestVBlank = %f\n", __func__, k, TimePerVMRequestVBlank[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, TimePerVMRequestFlip = %f\n", __func__, k, TimePerVMRequestFlip[k]); + } +} + +void dcn5_calculate_stutter_efficiency(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateStutterEfficiency_params *p) +{ + struct dml2_core_calcs_CalculateStutterEfficiency_locals *l = &scratch->CalculateStutterEfficiency_locals; + + unsigned int TotalNumberOfActiveOTG = 0; + double SinglePixelClock = 0; + unsigned int SingleHTotal = 0; + unsigned int SingleVTotal = 0; + bool SameTiming = true; + bool at_least_one_single_pipe_single_plane_surface = false; + bool FoundCriticalSurface = false; + + memset(l, 0, sizeof(struct dml2_core_calcs_CalculateStutterEfficiency_locals)); + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (p->display_cfg->plane_descriptors[k].surface.dcc.enable == true) { + if ((dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockWidth256BytesY[k] > p->SwathHeightY[k]) || (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockHeight256BytesY[k] > p->SwathHeightY[k]) || p->DCCYMaxUncompressedBlock[k] < 256) { + l->MaximumEffectiveCompressionLuma = 2; + } else { + l->MaximumEffectiveCompressionLuma = 4; + } + l->TotalCompressedReadBandwidth = l->TotalCompressedReadBandwidth + p->ReadBandwidthSurfaceLuma[k] / math_min2(p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane0, l->MaximumEffectiveCompressionLuma); + DML_LOG_VERBOSE("DML::%s: k=%u, ReadBandwidthSurfaceLuma = %f\n", __func__, k, p->ReadBandwidthSurfaceLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, NetDCCRateLuma = %f\n", __func__, k, p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane0); + DML_LOG_VERBOSE("DML::%s: k=%u, MaximumEffectiveCompressionLuma = %f\n", __func__, k, l->MaximumEffectiveCompressionLuma); + l->TotalZeroSizeRequestReadBandwidth = l->TotalZeroSizeRequestReadBandwidth + p->ReadBandwidthSurfaceLuma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane0; + l->TotalZeroSizeCompressedReadBandwidth = l->TotalZeroSizeCompressedReadBandwidth + p->ReadBandwidthSurfaceLuma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane0 / l->MaximumEffectiveCompressionLuma; + + if (p->ReadBandwidthSurfaceChroma[k] > 0) { + if ((dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockWidth256BytesC[k] > p->SwathHeightC[k]) || (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockHeight256BytesC[k] > p->SwathHeightC[k]) || p->DCCCMaxUncompressedBlock[k] < 256) { + l->MaximumEffectiveCompressionChroma = 2; + } else { + l->MaximumEffectiveCompressionChroma = 4; + } + l->TotalCompressedReadBandwidth = l->TotalCompressedReadBandwidth + p->ReadBandwidthSurfaceChroma[k] / math_min2(p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane1, l->MaximumEffectiveCompressionChroma); + DML_LOG_VERBOSE("DML::%s: k=%u, ReadBandwidthSurfaceChroma = %f\n", __func__, k, p->ReadBandwidthSurfaceChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, NetDCCRateChroma = %f\n", __func__, k, p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane1); + DML_LOG_VERBOSE("DML::%s: k=%u, MaximumEffectiveCompressionChroma = %f\n", __func__, k, l->MaximumEffectiveCompressionChroma); + l->TotalZeroSizeRequestReadBandwidth = l->TotalZeroSizeRequestReadBandwidth + p->ReadBandwidthSurfaceChroma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane1; + l->TotalZeroSizeCompressedReadBandwidth = l->TotalZeroSizeCompressedReadBandwidth + p->ReadBandwidthSurfaceChroma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane1 / l->MaximumEffectiveCompressionChroma; + } + } else { + l->TotalCompressedReadBandwidth = l->TotalCompressedReadBandwidth + p->ReadBandwidthSurfaceLuma[k] + p->ReadBandwidthSurfaceChroma[k]; + } + l->TotalRowReadBandwidth = l->TotalRowReadBandwidth + p->DPPPerSurface[k] * (p->meta_row_bw[k] + p->dpte_row_bw[k]); + } + + l->AverageDCCCompressionRate = p->TotalDataReadBandwidth / l->TotalCompressedReadBandwidth; + l->AverageDCCZeroSizeFraction = l->TotalZeroSizeRequestReadBandwidth / p->TotalDataReadBandwidth; + + DML_LOG_VERBOSE("DML::%s: UnboundedRequestEnabled = %u\n", __func__, p->UnboundedRequestEnabled); + DML_LOG_VERBOSE("DML::%s: TotalCompressedReadBandwidth = %f\n", __func__, l->TotalCompressedReadBandwidth); + DML_LOG_VERBOSE("DML::%s: TotalZeroSizeRequestReadBandwidth = %f\n", __func__, l->TotalZeroSizeRequestReadBandwidth); + DML_LOG_VERBOSE("DML::%s: TotalZeroSizeCompressedReadBandwidth = %f\n", __func__, l->TotalZeroSizeCompressedReadBandwidth); + DML_LOG_VERBOSE("DML::%s: MaximumEffectiveCompressionLuma = %f\n", __func__, l->MaximumEffectiveCompressionLuma); + DML_LOG_VERBOSE("DML::%s: MaximumEffectiveCompressionChroma = %f\n", __func__, l->MaximumEffectiveCompressionChroma); + DML_LOG_VERBOSE("DML::%s: AverageDCCCompressionRate = %f\n", __func__, l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: AverageDCCZeroSizeFraction = %f\n", __func__, l->AverageDCCZeroSizeFraction); + + DML_LOG_VERBOSE("DML::%s: CompbufReservedSpace64B = %u (%f kbytes)\n", __func__, p->CompbufReservedSpace64B, p->CompbufReservedSpace64B * 64 / 1024.0); + DML_LOG_VERBOSE("DML::%s: CompbufReservedSpaceZs = %u\n", __func__, p->CompbufReservedSpaceZs); + DML_LOG_VERBOSE("DML::%s: CompressedBufferSizeInkByte = %u kbytes\n", __func__, p->CompressedBufferSizeInkByte); + DML_LOG_VERBOSE("DML::%s: ROBBufferSizeInKByte = %u kbytes\n", __func__, p->ROBBufferSizeInKByte); + if (l->AverageDCCZeroSizeFraction == 1) { + l->AverageZeroSizeCompressionRate = l->TotalZeroSizeRequestReadBandwidth / l->TotalZeroSizeCompressedReadBandwidth; + l->EffectiveCompressedBufferSize = (double)p->MetaFIFOSizeInKEntries * 1024 * 64 * l->AverageZeroSizeCompressionRate + ((double)p->ZeroSizeBufferEntries - p->CompbufReservedSpaceZs) * 64 * l->AverageZeroSizeCompressionRate; + + + } else if (l->AverageDCCZeroSizeFraction > 0) { + l->AverageZeroSizeCompressionRate = l->TotalZeroSizeRequestReadBandwidth / l->TotalZeroSizeCompressedReadBandwidth; + l->EffectiveCompressedBufferSize = math_min2((double)p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate, + (double)p->MetaFIFOSizeInKEntries * 1024 * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate + 1 / l->AverageDCCCompressionRate)) + + (p->rob_alloc_compressed ? math_min2(((double)p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64) * l->AverageDCCCompressionRate, + ((double)p->ZeroSizeBufferEntries - p->CompbufReservedSpaceZs) * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate)) + : ((double)p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64)); + + + DML_LOG_VERBOSE("DML::%s: min 1 = %f\n", __func__, p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: min 2 = %f\n", __func__, p->MetaFIFOSizeInKEntries * 1024 * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate + 1 / l->AverageDCCCompressionRate)); + DML_LOG_VERBOSE("DML::%s: min 3 = %d\n", __func__, (p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64)); + DML_LOG_VERBOSE("DML::%s: min 4 = %f\n", __func__, (p->ZeroSizeBufferEntries - p->CompbufReservedSpaceZs) * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate)); + } else { + l->EffectiveCompressedBufferSize = math_min2((double)p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate, + (double)p->MetaFIFOSizeInKEntries * 1024 * 64 * l->AverageDCCCompressionRate) + + ((double)p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64) * (p->rob_alloc_compressed ? l->AverageDCCCompressionRate : 1.0); + + DML_LOG_VERBOSE("DML::%s: min 1 = %f\n", __func__, p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: min 2 = %f\n", __func__, p->MetaFIFOSizeInKEntries * 1024 * 64 * l->AverageDCCCompressionRate); + } + + DML_LOG_VERBOSE("DML::%s: MetaFIFOSizeInKEntries = %u\n", __func__, p->MetaFIFOSizeInKEntries); + DML_LOG_VERBOSE("DML::%s: ZeroSizeBufferEntries = %u\n", __func__, p->ZeroSizeBufferEntries); + DML_LOG_VERBOSE("DML::%s: AverageZeroSizeCompressionRate = %f\n", __func__, l->AverageZeroSizeCompressionRate); + DML_LOG_VERBOSE("DML::%s: EffectiveCompressedBufferSize = %f (%f kbytes)\n", __func__, l->EffectiveCompressedBufferSize, l->EffectiveCompressedBufferSize / 1024.0); + + *p->StutterPeriod = 0; + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + l->LinesInDETY = ((double)p->DETBufferSizeY[k] + (p->UnboundedRequestEnabled == true ? l->EffectiveCompressedBufferSize : 0) * p->ReadBandwidthSurfaceLuma[k] / p->TotalDataReadBandwidth) / p->BytePerPixelDETY[k] / p->SwathWidthY[k]; + l->LinesInDETYRoundedDownToSwath = math_floor2(l->LinesInDETY, p->SwathHeightY[k]); + l->DETBufferingTimeY = l->LinesInDETYRoundedDownToSwath * ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)) / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + at_least_one_single_pipe_single_plane_surface |= (p->DPPPerSurface[k] == 1) && (p->ReadBandwidthSurfaceChroma[k] == 0); + DML_LOG_VERBOSE("DML::%s: k=%u, DETBufferSizeY = %u (%u kbytes)\n", __func__, k, p->DETBufferSizeY[k], p->DETBufferSizeY[k] / 1024); + DML_LOG_VERBOSE("DML::%s: k=%u, BytePerPixelDETY = %f\n", __func__, k, p->BytePerPixelDETY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathWidthY = %u\n", __func__, k, p->SwathWidthY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, ReadBandwidthSurfaceLuma = %f\n", __func__, k, p->ReadBandwidthSurfaceLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, TotalDataReadBandwidth = %f\n", __func__, k, p->TotalDataReadBandwidth); + DML_LOG_VERBOSE("DML::%s: k=%u, LinesInDETY = %f\n", __func__, k, l->LinesInDETY); + DML_LOG_VERBOSE("DML::%s: k=%u, LinesInDETYRoundedDownToSwath = %f\n", __func__, k, l->LinesInDETYRoundedDownToSwath); + DML_LOG_VERBOSE("DML::%s: k=%u, VRatio = %f\n", __func__, k, p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u, DETBufferingTimeY = %f\n", __func__, k, l->DETBufferingTimeY); + + if (!FoundCriticalSurface || l->DETBufferingTimeY < *p->StutterPeriod) { + bool isInterlaceTiming = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.interlaced && !p->ProgressiveToInterlaceUnitInOPP; + + FoundCriticalSurface = true; + *p->StutterPeriod = l->DETBufferingTimeY; + l->FrameTimeCriticalSurface = (isInterlaceTiming ? math_floor2((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total / 2.0, 1.0) : p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total) * (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + l->VActiveTimeCriticalSurface = (isInterlaceTiming ? math_floor2((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_active / 2.0, 1.0) : p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_active) * (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + l->BytePerPixelYCriticalSurface = p->BytePerPixelY[k]; + l->SwathWidthYCriticalSurface = p->SwathWidthY[k]; + l->SwathHeightYCriticalSurface = p->SwathHeightY[k]; + l->BlockWidth256BytesYCriticalSurface = p->BlockWidth256BytesY[k]; + l->DETBufferSizeYCriticalSurface = p->DETBufferSizeY[k]; + l->MinTTUVBlankCriticalSurface = p->MinTTUVBlank[k]; + l->SinglePlaneCriticalSurface = (p->ReadBandwidthSurfaceChroma[k] == 0); + l->SinglePipeCriticalSurface = (p->DPPPerSurface[k] == 1); + + DML_LOG_VERBOSE("DML::%s: k=%u, FoundCriticalSurface = %u\n", __func__, k, FoundCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, StutterPeriod = %f\n", __func__, k, *p->StutterPeriod); + DML_LOG_VERBOSE("DML::%s: k=%u, MinTTUVBlankCriticalSurface = %f\n", __func__, k, l->MinTTUVBlankCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, FrameTimeCriticalSurface= %f\n", __func__, k, l->FrameTimeCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, VActiveTimeCriticalSurface = %f\n", __func__, k, l->VActiveTimeCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, BytePerPixelYCriticalSurface = %u\n", __func__, k, l->BytePerPixelYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathWidthYCriticalSurface = %f\n", __func__, k, l->SwathWidthYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathHeightYCriticalSurface = %f\n", __func__, k, l->SwathHeightYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, BlockWidth256BytesYCriticalSurface = %u\n", __func__, k, l->BlockWidth256BytesYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SinglePlaneCriticalSurface = %u\n", __func__, k, l->SinglePlaneCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SinglePipeCriticalSurface = %u\n", __func__, k, l->SinglePipeCriticalSurface); + } + } + + // for bounded req, the stutter period is calculated only based on DET size, but during burst there can be some return inside ROB/compressed buffer + // stutter period is calculated only on the det sizing + // if (cdb + rob >= det) the stutter burst will be absorbed by the cdb + rob which is before decompress + // else + // the cdb + rob part will be in compressed rate with urg bw (idea bw) + // the det part will be return at uncompressed rate with 64B/dcfclk + // + // for unbounded req, the stutter period should be calculated as total of CDB+ROB+DET, so the term "PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer" + // should be == EffectiveCompressedBufferSize which will returned a compressed rate, the rest of stutter period is from the DET will be returned at uncompressed rate with 64B/dcfclk + + l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer = math_min2(*p->StutterPeriod * p->TotalDataReadBandwidth, l->EffectiveCompressedBufferSize); + DML_LOG_VERBOSE("DML::%s: AverageDCCCompressionRate = %f\n", __func__, l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: StutterPeriod*TotalDataReadBandwidth = %f (%f kbytes)\n", __func__, *p->StutterPeriod * p->TotalDataReadBandwidth, (*p->StutterPeriod * p->TotalDataReadBandwidth) / 1024.0); + DML_LOG_VERBOSE("DML::%s: EffectiveCompressedBufferSize = %f (%f kbytes)\n", __func__, l->EffectiveCompressedBufferSize, l->EffectiveCompressedBufferSize / 1024.0); + DML_LOG_VERBOSE("DML::%s: PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer = %f (%f kbytes)\n", __func__, l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer, l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer / 1024); + DML_LOG_VERBOSE("DML::%s: ReturnBW = %f\n", __func__, p->ReturnBW); + DML_LOG_VERBOSE("DML::%s: TotalDataReadBandwidth = %f\n", __func__, p->TotalDataReadBandwidth); + DML_LOG_VERBOSE("DML::%s: TotalRowReadBandwidth = %f\n", __func__, l->TotalRowReadBandwidth); + DML_LOG_VERBOSE("DML::%s: DCFCLK = %f\n", __func__, p->DCFCLK); + + l->StutterBurstTime = l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer + / (p->ReturnBW * (p->hw_debug5 ? 1 : l->AverageDCCCompressionRate)) + + (*p->StutterPeriod * p->TotalDataReadBandwidth - l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer) + / math_min2(p->DCFCLK * 64, p->ReturnBW * (p->hw_debug5 ? 1 : l->AverageDCCCompressionRate)) + + *p->StutterPeriod * l->TotalRowReadBandwidth / p->ReturnBW; + DML_LOG_VERBOSE("DML::%s: Part 1 = %f\n", __func__, l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer / p->ReturnBW / (p->hw_debug5 ? 1 : l->AverageDCCCompressionRate)); + DML_LOG_VERBOSE("DML::%s: Part 2 = %f\n", __func__, (*p->StutterPeriod * p->TotalDataReadBandwidth - l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer) / (p->DCFCLK * 64)); + DML_LOG_VERBOSE("DML::%s: Part 3 = %f\n", __func__, *p->StutterPeriod * l->TotalRowReadBandwidth / p->ReturnBW); + DML_LOG_VERBOSE("DML::%s: StutterBurstTime = %f\n", __func__, l->StutterBurstTime); + l->TotalActiveWriteback = 0; + memset(l->stream_visited, 0, DML2_MAX_PLANES * sizeof(bool)); + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (!l->stream_visited[p->display_cfg->plane_descriptors[k].stream_index]) { + + for (unsigned int j = 0; j < p->display_cfg->stream_descriptors[k].writeback.active_writebacks_per_stream; j++) + l->TotalActiveWriteback = l->TotalActiveWriteback + 1; + + if (TotalNumberOfActiveOTG == 0) { // first otg + SinglePixelClock = ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + SingleHTotal = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total; + SingleVTotal = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total; + } else if (SinglePixelClock != ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) || + SingleHTotal != p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total || + SingleVTotal != p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total) { + SameTiming = false; + } + TotalNumberOfActiveOTG = TotalNumberOfActiveOTG + 1; + l->stream_visited[p->display_cfg->plane_descriptors[k].stream_index] = 1; + } + } + + if (l->TotalActiveWriteback == 0) { + DML_LOG_VERBOSE("DML::%s: SRExitTime = %f\n", __func__, p->SRExitTime); + DML_LOG_VERBOSE("DML::%s: SRExitZ8Time = %f\n", __func__, p->SRExitZ8Time); + DML_LOG_VERBOSE("DML::%s: StutterPeriod = %f\n", __func__, *p->StutterPeriod); + *p->StutterEfficiencyNotIncludingVBlank = math_max2(0., 1 - (p->SRExitTime + l->StutterBurstTime) / *p->StutterPeriod) * 100; + *p->Z8StutterEfficiencyNotIncludingVBlank = math_max2(0., 1 - (p->SRExitZ8Time + l->StutterBurstTime) / *p->StutterPeriod) * 100; + *p->NumberOfStutterBurstsPerFrame = (*p->StutterEfficiencyNotIncludingVBlank > 0 ? (unsigned int)(math_ceil2(l->VActiveTimeCriticalSurface / *p->StutterPeriod, 1)) : 0); + *p->Z8NumberOfStutterBurstsPerFrame = (*p->Z8StutterEfficiencyNotIncludingVBlank > 0 ? (unsigned int)(math_ceil2(l->VActiveTimeCriticalSurface / *p->StutterPeriod, 1)) : 0); + } else { + *p->StutterEfficiencyNotIncludingVBlank = 0.; + *p->Z8StutterEfficiencyNotIncludingVBlank = 0.; + *p->NumberOfStutterBurstsPerFrame = 0; + *p->Z8NumberOfStutterBurstsPerFrame = 0; + } + DML_LOG_VERBOSE("DML::%s: VActiveTimeCriticalSurface = %f\n", __func__, l->VActiveTimeCriticalSurface); + DML_LOG_VERBOSE("DML::%s: StutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->StutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: Z8StutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->Z8StutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: NumberOfStutterBurstsPerFrame = %u\n", __func__, *p->NumberOfStutterBurstsPerFrame); + DML_LOG_VERBOSE("DML::%s: Z8NumberOfStutterBurstsPerFrame = %u\n", __func__, *p->Z8NumberOfStutterBurstsPerFrame); + + if (*p->StutterEfficiencyNotIncludingVBlank > 0) { + if (!((p->SynchronizeTimings || TotalNumberOfActiveOTG == 1) && SameTiming)) { + *p->StutterEfficiency = *p->StutterEfficiencyNotIncludingVBlank; + } else { + *p->StutterEfficiency = (1 - (*p->NumberOfStutterBurstsPerFrame * p->SRExitTime + l->StutterBurstTime * l->VActiveTimeCriticalSurface / *p->StutterPeriod) / l->FrameTimeCriticalSurface) * 100; + } + } else { + *p->StutterEfficiency = 0; + *p->NumberOfStutterBurstsPerFrame = 0; + } + + if (*p->Z8StutterEfficiencyNotIncludingVBlank > 0) { + if (!((p->SynchronizeTimings || TotalNumberOfActiveOTG == 1) && SameTiming)) { + *p->Z8StutterEfficiency = *p->Z8StutterEfficiencyNotIncludingVBlank; + } else { + *p->Z8StutterEfficiency = (1 - (*p->Z8NumberOfStutterBurstsPerFrame * p->SRExitZ8Time + l->StutterBurstTime * l->VActiveTimeCriticalSurface / *p->StutterPeriod) / l->FrameTimeCriticalSurface) * 100; + } + } else { + *p->Z8StutterEfficiency = 0.; + *p->Z8NumberOfStutterBurstsPerFrame = 0; + } + + DML_LOG_VERBOSE("DML::%s: TotalNumberOfActiveOTG = %u\n", __func__, TotalNumberOfActiveOTG); + DML_LOG_VERBOSE("DML::%s: SameTiming = %u\n", __func__, SameTiming); + DML_LOG_VERBOSE("DML::%s: SynchronizeTimings = %u\n", __func__, p->SynchronizeTimings); + DML_LOG_VERBOSE("DML::%s: LastZ8StutterPeriod = %f\n", __func__, *p->Z8StutterEfficiencyNotIncludingVBlank > 0 ? l->VActiveTimeCriticalSurface - (*p->Z8NumberOfStutterBurstsPerFrame - 1) * *p->StutterPeriod : 0); + DML_LOG_VERBOSE("DML::%s: Z8StutterEnterPlusExitWatermark = %f\n", __func__, p->Z8StutterEnterPlusExitWatermark); + DML_LOG_VERBOSE("DML::%s: StutterBurstTime = %f\n", __func__, l->StutterBurstTime); + DML_LOG_VERBOSE("DML::%s: StutterPeriod = %f\n", __func__, *p->StutterPeriod); + DML_LOG_VERBOSE("DML::%s: StutterEfficiency = %f\n", __func__, *p->StutterEfficiency); + DML_LOG_VERBOSE("DML::%s: Z8StutterEfficiency = %f\n", __func__, *p->Z8StutterEfficiency); + DML_LOG_VERBOSE("DML::%s: StutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->StutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: Z8NumberOfStutterBurstsPerFrame = %u\n", __func__, *p->Z8NumberOfStutterBurstsPerFrame); + + *p->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE = !(!p->UnboundedRequestEnabled && (TotalNumberOfActiveOTG == 1) && at_least_one_single_pipe_single_plane_surface); + + DML_LOG_VERBOSE("DML::%s: DETBufferSizeYCriticalSurface = %u\n", __func__, l->DETBufferSizeYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: PixelChunkSizeInKByte = %u\n", __func__, p->PixelChunkSizeInKByte); + DML_LOG_VERBOSE("DML::%s: DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE = %u\n", __func__, *p->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE); +} + +void dcn5_check_urgent_bandwidth_support( + double *frac_urg_bandwidth_nom, + bool *bandwidth_support_ok, // max of vm, prefetch, vactive all ok + + double non_urg_bandwidth_required, + double urg_bandwidth_required, + double urg_bandwidth_available) +{ + *bandwidth_support_ok = urg_bandwidth_required <= urg_bandwidth_available; + *frac_urg_bandwidth_nom = non_urg_bandwidth_required / urg_bandwidth_available; + *bandwidth_support_ok &= (*frac_urg_bandwidth_nom <= 1.0); + + DML_LOG_VERBOSE("DML::%s: frac_urg_bandwidth_nom = %f\n", __func__, *frac_urg_bandwidth_nom); + DML_LOG_VERBOSE("DML::%s: bandwidth_support_ok = %d\n", __func__, *bandwidth_support_ok); +} + +double dcn5_get_bandwidth_available_for_immediate_flip( + double urg_bandwidth_required, // no flip + double urg_bandwidth_available) +{ + double flip_bw_available_mbps = urg_bandwidth_available - urg_bandwidth_required; + + DML_LOG_VERBOSE("DML::%s: flip_bw_available_mbps = %f\n", __func__, flip_bw_available_mbps); + + return flip_bw_available_mbps; +} + +void dcn5_check_immediate_flip_bandwidth_support( + // Output + double *frac_urg_bandwidth_flip, + bool *flip_bandwidth_support_ok, + + // Input + double urg_bandwidth_required_flip, + double non_urg_bandwidth_required_flip, + double urg_bandwidth_available) +{ + *frac_urg_bandwidth_flip = non_urg_bandwidth_required_flip / urg_bandwidth_available; + *flip_bandwidth_support_ok = urg_bandwidth_available >= urg_bandwidth_required_flip; + *flip_bandwidth_support_ok &= (*frac_urg_bandwidth_flip <= 1.0); + + DML_LOG_VERBOSE("DML::%s: frac_urg_bandwidth_flip = %f\n", __func__, *frac_urg_bandwidth_flip); + DML_LOG_VERBOSE("DML::%s: flip_bandwidth_support_ok = %d\n", __func__, *flip_bandwidth_support_ok); + DML_LOG_VERBOSE("DML::%s: urg_bandwidth_available=%f %s urg_bandwidth_required=%f\n", + __func__, urg_bandwidth_available, (urg_bandwidth_available < urg_bandwidth_required_flip) ? "<" : ">=", urg_bandwidth_required_flip); +} + +unsigned int dcn5_get_pipe_flip_bytes( + double hostvm_inefficiency_factor, + unsigned int vm_bytes, + unsigned int dpte_row_bytes, + unsigned int meta_row_bytes) +{ + unsigned int flip_bytes = 0; + + flip_bytes += (unsigned int) ((vm_bytes * hostvm_inefficiency_factor) + 2*meta_row_bytes); + flip_bytes += (unsigned int) (2*dpte_row_bytes * hostvm_inefficiency_factor); + + return flip_bytes; +} + +struct dml2_core_internal_g6_temp_read_blackouts_table { + struct { + unsigned int uclk_khz; + unsigned int blackout_us; + } entries[DML_MAX_CLK_TABLE_SIZE]; +}; + +struct dml2_core_internal_g6_temp_read_blackouts_table core_dcn5_g6_temp_read_blackout_table = { + .entries = { + { + .uclk_khz = 96000, + .blackout_us = 23, + }, + { + .uclk_khz = 435000, + .blackout_us = 10, + }, + { + .uclk_khz = 521000, + .blackout_us = 10, + }, + { + .uclk_khz = 731000, + .blackout_us = 8, + }, + { + .uclk_khz = 822000, + .blackout_us = 8, + }, + { + .uclk_khz = 962000, + .blackout_us = 5, + }, + { + .uclk_khz = 1069000, + .blackout_us = 5, + }, + { + .uclk_khz = 1187000, + .blackout_us = 5, + }, + }, +}; + +void dcn5_adjust_pixel_clock_for_progressive_to_interlace_unit(const struct dml2_display_cfg *display_cfg, bool ptoi_supported, double *PixelClockBackEnd) +{ + //unsigned int num_active_planes = display_cfg->num_planes; + + //Progressive To Interlace Unit Effect + for (unsigned int k = 0; k < display_cfg->num_planes; ++k) { + PixelClockBackEnd[k] = ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.interlaced == 1 && ptoi_supported == true) { + // FIXME_STAGE2... can sw pass the pixel rate for interlaced directly + //display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz = 2 * display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz; + } + } +} + +static void dcn5_rq_dlg_get_wm_regs(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_dchub_watermark_regs *wm_regs) +{ + double refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : utm_soc_bb->dchub_refclk_mhz; + + wm_regs->fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz); + wm_regs->sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz); + wm_regs->temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz); + wm_regs->uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz); + wm_regs->urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + wm_regs->usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz); + wm_regs->refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.UrgentLatency * refclk_freq_in_mhz); + wm_regs->refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.MetaTripToMemory * refclk_freq_in_mhz); + wm_regs->frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000); + wm_regs->frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000); +} + +void dcn5_get_mcif_arb_params(const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_mcif_global_register_set *out) +{ + out->wm_regs[0].fclk_pstate = (unsigned int)(mode_lib->mp.Watermark.WritebackFCLKChangeWatermark * 1000.0); + out->wm_regs[0].uclk_pstate = (unsigned int)(mode_lib->mp.Watermark.WritebackDRAMClockChangeWatermark * 1000.0); + out->wm_regs[0].urgent = (unsigned int)(mode_lib->mp.Watermark.WritebackUrgentWatermark * 1000.0); + out->wm_regs[0].temp_read_or_ppt = (unsigned int)(mode_lib->mp.Watermark.writeback_temp_read_or_ppt_watermark_us * 1000.0); +} + +void dml2_core_dcn5_calcs_cursor_dlg_reg(struct dml2_cursor_dlg_regs *cursor_dlg_regs, const struct dml2_get_cursor_dlg_reg *p) +{ + int dst_x_offset = (int) ((p->cursor_x_position + (p->cursor_stereo_en == 0 ? 0 : math_max2(p->cursor_primary_offset, p->cursor_secondary_offset)) - + (p->cursor_hotspot_x * (p->cursor_2x_magnify == 0 ? 1 : 2))) * p->dlg_refclk_mhz / p->pixel_rate_mhz / p->hratio); + cursor_dlg_regs->dst_x_offset = (unsigned int) ((dst_x_offset > 0) ? dst_x_offset : 0); + + DML_LOG_VERBOSE("DML_DLG::%s: cursor_x_position=%d\n", __func__, p->cursor_x_position); + DML_LOG_VERBOSE("DML_DLG::%s: dlg_refclk_mhz=%f\n", __func__, p->dlg_refclk_mhz); + DML_LOG_VERBOSE("DML_DLG::%s: pixel_rate_mhz=%f\n", __func__, p->pixel_rate_mhz); + DML_LOG_VERBOSE("DML_DLG::%s: dst_x_offset=%d\n", __func__, dst_x_offset); + DML_LOG_VERBOSE("DML_DLG::%s: dst_x_offset=%d (reg)\n", __func__, cursor_dlg_regs->dst_x_offset); + + cursor_dlg_regs->chunk_hdl_adjust = 3; + cursor_dlg_regs->dst_y_offset = 0; + + cursor_dlg_regs->qos_level_fixed = 8; + cursor_dlg_regs->qos_ramp_disable = 0; +} + +void dcn5_rq_dlg_get_rq_reg(struct dml2_display_rq_regs *rq_regs, + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + unsigned int pipe_idx) +{ + unsigned int plane_idx = mode_lib->mp.pipe_plane[pipe_idx]; + enum dml2_source_format_class source_format = display_cfg->plane_descriptors[plane_idx].pixel_format; + enum dml2_swizzle_mode sw_mode = display_cfg->plane_descriptors[plane_idx].surface.tiling; + bool dual_plane = dml2_core_utils_is_dual_plane((enum dml2_source_format_class)(source_format)); + + unsigned int pixel_chunk_bytes = 0; + unsigned int min_pixel_chunk_bytes = 0; + unsigned int dpte_group_bytes = 0; + unsigned int mpte_group_bytes = 0; + + unsigned int p1_pixel_chunk_bytes = 0; + unsigned int p1_min_pixel_chunk_bytes = 0; + unsigned int p1_dpte_group_bytes = 0; + unsigned int p1_mpte_group_bytes = 0; + + unsigned int detile_buf_plane1_addr = 0; + unsigned int detile_buf_size_in_bytes; + double stored_swath_l_bytes; + double stored_swath_c_bytes; + + DML_LOG_VERBOSE("DML_DLG::%s: Calculation for pipe[%d] start\n", __func__, pipe_idx); + + pixel_chunk_bytes = (unsigned int)(mode_lib->ip.pixel_chunk_size_kbytes * 1024); + min_pixel_chunk_bytes = (unsigned int)(mode_lib->ip.min_pixel_chunk_size_bytes); + + if (pixel_chunk_bytes == 64 * 1024) + min_pixel_chunk_bytes = 0; + + dpte_group_bytes = (unsigned int)(mode_lib->mp.dpte_group_bytes[mode_lib->mp.pipe_plane[pipe_idx]]); + mpte_group_bytes = (unsigned int)(mode_lib->mp.vm_group_bytes[mode_lib->mp.pipe_plane[pipe_idx]]); + + p1_pixel_chunk_bytes = pixel_chunk_bytes; + p1_min_pixel_chunk_bytes = min_pixel_chunk_bytes; + p1_dpte_group_bytes = dpte_group_bytes; + p1_mpte_group_bytes = mpte_group_bytes; + + if (source_format == dml2_rgbe_alpha) + p1_pixel_chunk_bytes = (unsigned int)(mode_lib->ip.alpha_pixel_chunk_size_kbytes * 1024); + + rq_regs->unbounded_request_enabled = mode_lib->mp.UnboundedRequestEnabled; + rq_regs->pte_buffer_mode = mode_lib->mp.PTE_BUFFER_MODE[mode_lib->mp.pipe_plane[pipe_idx]]; + rq_regs->force_one_row_for_frame = mode_lib->mp.use_one_row_for_frame[mode_lib->mp.pipe_plane[pipe_idx]]; + rq_regs->rq_regs_l.chunk_size = dml2_core_utils_log_and_substract_if_non_zero(pixel_chunk_bytes, 10); + rq_regs->rq_regs_c.chunk_size = dml2_core_utils_log_and_substract_if_non_zero(p1_pixel_chunk_bytes, 10); + + DML_LOG_VERBOSE("DML_DLG: %s: pte_buffer_mode = %u\n", __func__, rq_regs->pte_buffer_mode); + DML_LOG_VERBOSE("DML_DLG: %s: force_one_row_for_frame = %u\n", __func__, rq_regs->force_one_row_for_frame); + + if (min_pixel_chunk_bytes == 0) + rq_regs->rq_regs_l.min_chunk_size = 0; + else + rq_regs->rq_regs_l.min_chunk_size = dml2_core_utils_log_and_substract_if_non_zero(min_pixel_chunk_bytes, 8 - 1); + + if (p1_min_pixel_chunk_bytes == 0) + rq_regs->rq_regs_c.min_chunk_size = 0; + else + rq_regs->rq_regs_c.min_chunk_size = dml2_core_utils_log_and_substract_if_non_zero(p1_min_pixel_chunk_bytes, 8 - 1); + + rq_regs->rq_regs_l.dpte_group_size = dml2_core_utils_log_and_substract_if_non_zero(dpte_group_bytes, 6); + rq_regs->rq_regs_l.mpte_group_size = dml2_core_utils_log_and_substract_if_non_zero(mpte_group_bytes, 6); + rq_regs->rq_regs_c.dpte_group_size = dml2_core_utils_log_and_substract_if_non_zero(p1_dpte_group_bytes, 6); + rq_regs->rq_regs_c.mpte_group_size = dml2_core_utils_log_and_substract_if_non_zero(p1_mpte_group_bytes, 6); + + detile_buf_size_in_bytes = mode_lib->mp.DETBufferSizeInKByte[mode_lib->mp.pipe_plane[pipe_idx]] * 1024; + + if (dml2_core_utils_is_linear(sw_mode) && display_cfg->gpuvm_enable) { + unsigned int p0_pte_row_height_linear = mode_lib->mp.dpte_row_height_linear[mode_lib->mp.pipe_plane[pipe_idx]]; + DML_LOG_VERBOSE("DML_DLG: %s: p0_pte_row_height_linear = %u\n", __func__, p0_pte_row_height_linear); + DML_ASSERT(p0_pte_row_height_linear >= 8); + + rq_regs->rq_regs_l.pte_row_height_linear = math_log2_approx(p0_pte_row_height_linear) - 3; + if (dual_plane) { + unsigned int p1_pte_row_height_linear = mode_lib->mp.dpte_row_height_linear_chroma[mode_lib->mp.pipe_plane[pipe_idx]]; + DML_LOG_VERBOSE("DML_DLG: %s: p1_pte_row_height_linear = %u\n", __func__, p1_pte_row_height_linear); + if (sw_mode == dml2_sw_linear) { + DML_ASSERT(p1_pte_row_height_linear >= 8); + } + rq_regs->rq_regs_c.pte_row_height_linear = math_log2_approx(p1_pte_row_height_linear) - 3; + } + } else { + rq_regs->rq_regs_l.pte_row_height_linear = 0; + rq_regs->rq_regs_c.pte_row_height_linear = 0; + } + + rq_regs->rq_regs_l.swath_height = dml2_core_utils_log_and_substract_if_non_zero(mode_lib->mp.SwathHeightY[mode_lib->mp.pipe_plane[pipe_idx]], 0); + rq_regs->rq_regs_c.swath_height = dml2_core_utils_log_and_substract_if_non_zero(mode_lib->mp.SwathHeightC[mode_lib->mp.pipe_plane[pipe_idx]], 0); + + // FIXME_DCN4, programming guide has dGPU condition + if (pixel_chunk_bytes >= 32 * 1024 || (dual_plane && p1_pixel_chunk_bytes >= 32 * 1024)) { //32kb + rq_regs->drq_expansion_mode = 0; + } else { + rq_regs->drq_expansion_mode = 2; + } + rq_regs->prq_expansion_mode = 1; + rq_regs->crq_expansion_mode = 1; + rq_regs->mrq_expansion_mode = 1; + + stored_swath_l_bytes = mode_lib->mp.DETBufferSizeY[mode_lib->mp.pipe_plane[pipe_idx]]; + stored_swath_c_bytes = mode_lib->mp.DETBufferSizeC[mode_lib->mp.pipe_plane[pipe_idx]]; + + // Note: detile_buf_plane1_addr is in unit of 1KB + if (dual_plane) { + if (stored_swath_l_bytes / stored_swath_c_bytes <= 1.5) { + detile_buf_plane1_addr = (unsigned int)(detile_buf_size_in_bytes / 2.0 / 1024.0); // half to chroma + DML_LOG_VERBOSE("DML_DLG: %s: detile_buf_plane1_addr = %d (1/2 to chroma)\n", __func__, detile_buf_plane1_addr); + } else { + detile_buf_plane1_addr = (unsigned int)(dml2_core_utils_round_to_multiple((unsigned int)((2.0 * detile_buf_size_in_bytes) / 3.0), 1024, 0) / 1024.0); // 2/3 to luma + DML_LOG_VERBOSE("DML_DLG: %s: detile_buf_plane1_addr = %d (1/3 chroma)\n", __func__, detile_buf_plane1_addr); + } + } + rq_regs->plane1_base_address = detile_buf_plane1_addr; + + DML_LOG_VERBOSE("DML_DLG: %s: stored_swath_l_bytes = %f\n", __func__, stored_swath_l_bytes); + DML_LOG_VERBOSE("DML_DLG: %s: stored_swath_c_bytes = %f\n", __func__, stored_swath_c_bytes); + DML_LOG_VERBOSE("DML_DLG: %s: detile_buf_size_in_bytes = %d\n", __func__, detile_buf_size_in_bytes); + DML_LOG_VERBOSE("DML_DLG: %s: detile_buf_plane1_addr = %d\n", __func__, detile_buf_plane1_addr); + DML_LOG_VERBOSE("DML_DLG: %s: plane1_base_address = %d\n", __func__, rq_regs->plane1_base_address); + DML_LOG_VERBOSE("DML_DLG::%s: Calculation for pipe[%d] done\n", __func__, pipe_idx); +} + +static void dcn5_rq_dlg_get_dlg_reg( + struct dml2_core_internal_scratch *s, + struct dml2_display_dlg_regs *disp_dlg_regs, + struct dml2_display_ttu_regs *disp_ttu_regs, + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + const unsigned int pipe_idx, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + struct dml2_core_shared_rq_dlg_get_dlg_reg_locals *l = &s->rq_dlg_get_dlg_reg_locals; + + memset(l, 0, sizeof(struct dml2_core_shared_rq_dlg_get_dlg_reg_locals)); + + DML_LOG_VERBOSE("DML_DLG::%s: Calculation for pipe_idx=%d\n", __func__, pipe_idx); + + l->plane_idx = mode_lib->mp.pipe_plane[pipe_idx]; + DML_ASSERT(l->plane_idx < DML2_MAX_PLANES); + + l->source_format = dml2_444_8; + l->odm_mode = dml2_odm_mode_bypass; + l->dual_plane = false; + l->htotal = 0; + l->hactive = 0; + l->hblank_end = 0; + l->vblank_end = 0; + l->interlaced = false; + l->pclk_freq_in_mhz = 0.0; + l->refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : utm_soc_bb->dchub_refclk_mhz; + l->ref_freq_to_pix_freq = 0.0; + + if (l->plane_idx < DML2_MAX_PLANES) { + + l->timing = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[l->plane_idx].stream_index].timing; + l->source_format = display_cfg->plane_descriptors[l->plane_idx].pixel_format; + l->odm_mode = mode_lib->mp.ODMMode[l->plane_idx]; + + l->dual_plane = dml2_core_utils_is_dual_plane(l->source_format); + + l->htotal = l->timing->h_total; + l->hactive = l->timing->h_active; + l->hblank_end = l->timing->h_blank_end; + l->vblank_end = l->timing->v_blank_end; + l->interlaced = l->timing->interlaced; + l->pclk_freq_in_mhz = (double)l->timing->pixel_clock_khz / 1000; + l->ref_freq_to_pix_freq = l->refclk_freq_in_mhz / l->pclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG::%s: plane_idx = %d\n", __func__, l->plane_idx); + DML_LOG_VERBOSE("DML_DLG: %s: htotal = %d\n", __func__, l->htotal); + DML_LOG_VERBOSE("DML_DLG: %s: refclk_freq_in_mhz = %3.2f\n", __func__, l->refclk_freq_in_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: dlg_ref_clk_mhz = %3.2f\n", __func__, display_cfg->overrides.hw.dlg_ref_clk_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: soc.refclk_mhz = %u\n", __func__, utm_soc_bb->dchub_refclk_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: pclk_freq_in_mhz = %3.2f\n", __func__, l->pclk_freq_in_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: ref_freq_to_pix_freq = %3.2f\n", __func__, l->ref_freq_to_pix_freq); + DML_LOG_VERBOSE("DML_DLG: %s: interlaced = %d\n", __func__, l->interlaced); + + DML_ASSERT(l->refclk_freq_in_mhz != 0); + DML_ASSERT(l->pclk_freq_in_mhz != 0); + DML_ASSERT(l->ref_freq_to_pix_freq < 4.0); + + // Need to figure out which side of odm combine we're in + // Assume the pipe instance under the same plane is in order + + if (l->odm_mode == dml2_odm_mode_bypass) { + disp_dlg_regs->refcyc_h_blank_end = (unsigned int)((double)l->hblank_end * l->ref_freq_to_pix_freq); + } else if (l->odm_mode == dml2_odm_mode_combine_2to1 || l->odm_mode == dml2_odm_mode_combine_3to1 || l->odm_mode == dml2_odm_mode_combine_4to1) { + // find out how many pipe are in this plane + l->num_active_pipes = mode_lib->mp.num_active_pipes; + l->first_pipe_idx_in_plane = DML2_MAX_PLANES; + l->pipe_idx_in_combine = 0; // pipe index within the plane + l->odm_combine_factor = 2; + + if (l->odm_mode == dml2_odm_mode_combine_3to1) + l->odm_combine_factor = 3; + else if (l->odm_mode == dml2_odm_mode_combine_4to1) + l->odm_combine_factor = 4; + + for (unsigned int i = 0; i < l->num_active_pipes; i++) { + if (mode_lib->mp.pipe_plane[i] == l->plane_idx) { + if (i < l->first_pipe_idx_in_plane) { + l->first_pipe_idx_in_plane = i; + } + } + } + l->pipe_idx_in_combine = pipe_idx - l->first_pipe_idx_in_plane; // DML assumes the pipes in the same plane will have continuous indexing (i.e. plane 0 use pipe 0, 1, and plane 1 uses pipe 2, 3, etc.) + + disp_dlg_regs->refcyc_h_blank_end = (unsigned int)(((double)l->hblank_end + (double)l->pipe_idx_in_combine * (double)l->hactive / (double)l->odm_combine_factor) * l->ref_freq_to_pix_freq); + DML_LOG_VERBOSE("DML_DLG: %s: pipe_idx = %d\n", __func__, pipe_idx); + DML_LOG_VERBOSE("DML_DLG: %s: first_pipe_idx_in_plane = %d\n", __func__, l->first_pipe_idx_in_plane); + DML_LOG_VERBOSE("DML_DLG: %s: pipe_idx_in_combine = %d\n", __func__, l->pipe_idx_in_combine); + DML_LOG_VERBOSE("DML_DLG: %s: odm_combine_factor = %d\n", __func__, l->odm_combine_factor); + } + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_h_blank_end = %d\n", __func__, disp_dlg_regs->refcyc_h_blank_end); + + DML_ASSERT(disp_dlg_regs->refcyc_h_blank_end < (unsigned int)math_pow(2, 13)); + + disp_dlg_regs->ref_freq_to_pix_freq = (unsigned int)(l->ref_freq_to_pix_freq * math_pow(2, 19)); + disp_dlg_regs->refcyc_per_htotal = (unsigned int)(l->ref_freq_to_pix_freq * (double)l->htotal * math_pow(2, 8)); + disp_dlg_regs->dlg_vblank_end = l->interlaced ? (l->vblank_end / 2) : l->vblank_end; // 15 bits + + l->min_ttu_vblank = mode_lib->mp.MinTTUVBlank[mode_lib->mp.pipe_plane[pipe_idx]]; + l->min_dst_y_next_start = (unsigned int)(mode_lib->mp.MIN_DST_Y_NEXT_START[mode_lib->mp.pipe_plane[pipe_idx]]); + + DML_LOG_VERBOSE("DML_DLG: %s: min_ttu_vblank (us) = %3.2f\n", __func__, l->min_ttu_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: min_dst_y_next_start = %d\n", __func__, l->min_dst_y_next_start); + DML_LOG_VERBOSE("DML_DLG: %s: ref_freq_to_pix_freq = %3.2f\n", __func__, l->ref_freq_to_pix_freq); + + l->vready_after_vcount0 = (unsigned int)(mode_lib->mp.VREADY_AT_OR_AFTER_VSYNC[mode_lib->mp.pipe_plane[pipe_idx]]); + disp_dlg_regs->vready_after_vcount0 = l->vready_after_vcount0; + + DML_LOG_VERBOSE("DML_DLG: %s: vready_after_vcount0 = %d\n", __func__, disp_dlg_regs->vready_after_vcount0); + + l->dst_x_after_scaler = (unsigned int)(mode_lib->mp.DSTXAfterScaler[mode_lib->mp.pipe_plane[pipe_idx]]); + l->dst_y_after_scaler = (unsigned int)(mode_lib->mp.DSTYAfterScaler[mode_lib->mp.pipe_plane[pipe_idx]]); + + DML_LOG_VERBOSE("DML_DLG: %s: dst_x_after_scaler = %d\n", __func__, l->dst_x_after_scaler); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_after_scaler = %d\n", __func__, l->dst_y_after_scaler); + + l->dst_y_prefetch = mode_lib->mp.dst_y_prefetch[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_vm_vblank = mode_lib->mp.dst_y_per_vm_vblank[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_row_vblank = mode_lib->mp.dst_y_per_row_vblank[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_vm_flip = mode_lib->mp.dst_y_per_vm_flip[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_row_flip = mode_lib->mp.dst_y_per_row_flip[mode_lib->mp.pipe_plane[pipe_idx]]; + + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_prefetch (after rnd) = %3.2f\n", __func__, l->dst_y_prefetch); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_vm_flip = %3.2f\n", __func__, l->dst_y_per_vm_flip); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_row_flip = %3.2f\n", __func__, l->dst_y_per_row_flip); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_vm_vblank = %3.2f\n", __func__, l->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_row_vblank = %3.2f\n", __func__, l->dst_y_per_row_vblank); + + if (l->dst_y_prefetch > 0 && l->dst_y_per_vm_vblank > 0 && l->dst_y_per_row_vblank > 0) { + DML_ASSERT(l->dst_y_prefetch > (l->dst_y_per_vm_vblank + l->dst_y_per_row_vblank)); + } + + l->vratio_pre_l = mode_lib->mp.VRatioPrefetchY[mode_lib->mp.pipe_plane[pipe_idx]]; + l->vratio_pre_c = mode_lib->mp.VRatioPrefetchC[mode_lib->mp.pipe_plane[pipe_idx]]; + + DML_LOG_VERBOSE("DML_DLG: %s: vratio_pre_l = %3.2f\n", __func__, l->vratio_pre_l); + DML_LOG_VERBOSE("DML_DLG: %s: vratio_pre_c = %3.2f\n", __func__, l->vratio_pre_c); + + // Active + l->refcyc_per_line_delivery_pre_l = mode_lib->mp.DisplayPipeLineDeliveryTimeLumaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_line_delivery_l = mode_lib->mp.DisplayPipeLineDeliveryTimeLuma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_pre_l = %3.2f\n", __func__, l->refcyc_per_line_delivery_pre_l); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_l = %3.2f\n", __func__, l->refcyc_per_line_delivery_l); + + l->refcyc_per_line_delivery_pre_c = 0.0; + l->refcyc_per_line_delivery_c = 0.0; + + if (l->dual_plane) { + l->refcyc_per_line_delivery_pre_c = mode_lib->mp.DisplayPipeLineDeliveryTimeChromaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_line_delivery_c = mode_lib->mp.DisplayPipeLineDeliveryTimeChroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_pre_c = %3.2f\n", __func__, l->refcyc_per_line_delivery_pre_c); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_c = %3.2f\n", __func__, l->refcyc_per_line_delivery_c); + } + + disp_dlg_regs->refcyc_per_vm_dmdata = (unsigned int)(mode_lib->mp.Tdmdl_vm[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + disp_dlg_regs->dmdata_dl_delta = (unsigned int)(mode_lib->mp.Tdmdl[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + + l->refcyc_per_req_delivery_pre_l = mode_lib->mp.DisplayPipeRequestDeliveryTimeLumaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_req_delivery_l = mode_lib->mp.DisplayPipeRequestDeliveryTimeLuma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_pre_l = %3.2f\n", __func__, l->refcyc_per_req_delivery_pre_l); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_l = %3.2f\n", __func__, l->refcyc_per_req_delivery_l); + + l->refcyc_per_req_delivery_pre_c = 0.0; + l->refcyc_per_req_delivery_c = 0.0; + if (l->dual_plane) { + l->refcyc_per_req_delivery_pre_c = mode_lib->mp.DisplayPipeRequestDeliveryTimeChromaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_req_delivery_c = mode_lib->mp.DisplayPipeRequestDeliveryTimeChroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_pre_c = %3.2f\n", __func__, l->refcyc_per_req_delivery_pre_c); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_c = %3.2f\n", __func__, l->refcyc_per_req_delivery_c); + } + + // TTU - Cursor + DML_ASSERT(display_cfg->plane_descriptors[l->plane_idx].cursor.num_cursors <= 1); + + // Assign to register structures + disp_dlg_regs->min_dst_y_next_start = (unsigned int)((double)l->min_dst_y_next_start * math_pow(2, 2)); + DML_ASSERT(disp_dlg_regs->min_dst_y_next_start < (unsigned int)math_pow(2, 18)); + + disp_dlg_regs->dst_y_after_scaler = l->dst_y_after_scaler; // in terms of line + disp_dlg_regs->refcyc_x_after_scaler = (unsigned int)((double)l->dst_x_after_scaler * l->ref_freq_to_pix_freq); // in terms of refclk + disp_dlg_regs->dst_y_prefetch = (unsigned int)(l->dst_y_prefetch * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_vm_vblank = (unsigned int)(l->dst_y_per_vm_vblank * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_row_vblank = (unsigned int)(l->dst_y_per_row_vblank * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_vm_flip = (unsigned int)(l->dst_y_per_vm_flip * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_row_flip = (unsigned int)(l->dst_y_per_row_flip * math_pow(2, 2)); + + disp_dlg_regs->vratio_prefetch = (unsigned int)(l->vratio_pre_l * math_pow(2, 19)); + disp_dlg_regs->vratio_prefetch_c = (unsigned int)(l->vratio_pre_c * math_pow(2, 19)); + + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_vm_vblank = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_row_vblank = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_row_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_vm_flip = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_vm_flip); + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_row_flip = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_row_flip); + + disp_dlg_regs->refcyc_per_vm_group_vblank = (unsigned int)(mode_lib->mp.TimePerVMGroupVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + disp_dlg_regs->refcyc_per_vm_group_flip = (unsigned int)(mode_lib->mp.TimePerVMGroupFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + disp_dlg_regs->refcyc_per_vm_req_vblank = (unsigned int)(mode_lib->mp.TimePerVMRequestVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz * math_pow(2, 10)); + disp_dlg_regs->refcyc_per_vm_req_flip = (unsigned int)(mode_lib->mp.TimePerVMRequestFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz * math_pow(2, 10)); + + l->dst_y_per_pte_row_nom_l = mode_lib->mp.DST_Y_PER_PTE_ROW_NOM_L[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_pte_row_nom_c = mode_lib->mp.DST_Y_PER_PTE_ROW_NOM_C[mode_lib->mp.pipe_plane[pipe_idx]]; + l->refcyc_per_pte_group_nom_l = mode_lib->mp.time_per_pte_group_nom_luma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_nom_c = mode_lib->mp.time_per_pte_group_nom_chroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_vblank_l = mode_lib->mp.time_per_pte_group_vblank_luma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_vblank_c = mode_lib->mp.time_per_pte_group_vblank_chroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_flip_l = mode_lib->mp.time_per_pte_group_flip_luma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_flip_c = mode_lib->mp.time_per_pte_group_flip_chroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_tdlut_group = mode_lib->mp.time_per_tdlut_group[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + disp_dlg_regs->dst_y_per_pte_row_nom_l = (unsigned int)(l->dst_y_per_pte_row_nom_l * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_pte_row_nom_c = (unsigned int)(l->dst_y_per_pte_row_nom_c * math_pow(2, 2)); + + disp_dlg_regs->refcyc_per_pte_group_nom_l = (unsigned int)(l->refcyc_per_pte_group_nom_l); + disp_dlg_regs->refcyc_per_pte_group_nom_c = (unsigned int)(l->refcyc_per_pte_group_nom_c); + disp_dlg_regs->refcyc_per_pte_group_vblank_l = (unsigned int)(l->refcyc_per_pte_group_vblank_l); + disp_dlg_regs->refcyc_per_pte_group_vblank_c = (unsigned int)(l->refcyc_per_pte_group_vblank_c); + disp_dlg_regs->refcyc_per_pte_group_flip_l = (unsigned int)(l->refcyc_per_pte_group_flip_l); + disp_dlg_regs->refcyc_per_pte_group_flip_c = (unsigned int)(l->refcyc_per_pte_group_flip_c); + disp_dlg_regs->refcyc_per_line_delivery_pre_l = (unsigned int)math_floor2(l->refcyc_per_line_delivery_pre_l, 1); + disp_dlg_regs->refcyc_per_line_delivery_l = (unsigned int)math_floor2(l->refcyc_per_line_delivery_l, 1); + disp_dlg_regs->refcyc_per_line_delivery_pre_c = (unsigned int)math_floor2(l->refcyc_per_line_delivery_pre_c, 1); + disp_dlg_regs->refcyc_per_line_delivery_c = (unsigned int)math_floor2(l->refcyc_per_line_delivery_c, 1); + + l->dst_y_per_meta_row_nom_l = mode_lib->mp.DST_Y_PER_META_ROW_NOM_L[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_meta_row_nom_c = mode_lib->mp.DST_Y_PER_META_ROW_NOM_C[mode_lib->mp.pipe_plane[pipe_idx]]; + l->refcyc_per_meta_chunk_nom_l = mode_lib->mp.TimePerMetaChunkNominal[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_nom_c = mode_lib->mp.TimePerChromaMetaChunkNominal[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_vblank_l = mode_lib->mp.TimePerMetaChunkVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_vblank_c = mode_lib->mp.TimePerChromaMetaChunkVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_flip_l = mode_lib->mp.TimePerMetaChunkFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_flip_c = mode_lib->mp.TimePerChromaMetaChunkFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + disp_dlg_regs->dst_y_per_meta_row_nom_l = (unsigned int)(l->dst_y_per_meta_row_nom_l * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_meta_row_nom_c = (unsigned int)(l->dst_y_per_meta_row_nom_c * math_pow(2, 2)); + disp_dlg_regs->refcyc_per_meta_chunk_nom_l = (unsigned int)(l->refcyc_per_meta_chunk_nom_l); + disp_dlg_regs->refcyc_per_meta_chunk_nom_c = (unsigned int)(l->refcyc_per_meta_chunk_nom_c); + disp_dlg_regs->refcyc_per_meta_chunk_vblank_l = (unsigned int)(l->refcyc_per_meta_chunk_vblank_l); + disp_dlg_regs->refcyc_per_meta_chunk_vblank_c = (unsigned int)(l->refcyc_per_meta_chunk_vblank_c); + disp_dlg_regs->refcyc_per_meta_chunk_flip_l = (unsigned int)(l->refcyc_per_meta_chunk_flip_l); + disp_dlg_regs->refcyc_per_meta_chunk_flip_c = (unsigned int)(l->refcyc_per_meta_chunk_flip_c); + + disp_dlg_regs->refcyc_per_tdlut_group = (unsigned int)(l->refcyc_per_tdlut_group); + disp_dlg_regs->dst_y_delta_drq_limit = 0x7fff; // off + + disp_ttu_regs->refcyc_per_req_delivery_pre_l = (unsigned int)(l->refcyc_per_req_delivery_pre_l * math_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_l = (unsigned int)(l->refcyc_per_req_delivery_l * math_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_pre_c = (unsigned int)(l->refcyc_per_req_delivery_pre_c * math_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_c = (unsigned int)(l->refcyc_per_req_delivery_c * math_pow(2, 10)); + disp_ttu_regs->qos_level_low_wm = 0; + + disp_ttu_regs->qos_level_high_wm = (unsigned int)(4.0 * (double)l->htotal * l->ref_freq_to_pix_freq); + + disp_ttu_regs->qos_level_flip = 14; + disp_ttu_regs->qos_level_fixed_l = 8; + disp_ttu_regs->qos_level_fixed_c = 8; + disp_ttu_regs->qos_ramp_disable_l = 0; + disp_ttu_regs->qos_ramp_disable_c = 0; + disp_ttu_regs->min_ttu_vblank = (unsigned int)(l->min_ttu_vblank * l->refclk_freq_in_mhz); + + // CHECK for HW registers' range, DML_ASSERT or clamp + DML_ASSERT(l->refcyc_per_req_delivery_pre_l < math_pow(2, 13)); + DML_ASSERT(l->refcyc_per_req_delivery_l < math_pow(2, 13)); + DML_ASSERT(l->refcyc_per_req_delivery_pre_c < math_pow(2, 13)); + DML_ASSERT(l->refcyc_per_req_delivery_c < math_pow(2, 13)); + if (disp_dlg_regs->refcyc_per_vm_group_vblank >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_group_vblank = (unsigned int)(math_pow(2, 23) - 1); + + if (disp_dlg_regs->refcyc_per_vm_group_flip >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_group_flip = (unsigned int)(math_pow(2, 23) - 1); + + if (disp_dlg_regs->refcyc_per_vm_req_vblank >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_req_vblank = (unsigned int)(math_pow(2, 23) - 1); + + if (disp_dlg_regs->refcyc_per_vm_req_flip >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_req_flip = (unsigned int)(math_pow(2, 23) - 1); + + + DML_ASSERT(disp_dlg_regs->dst_y_after_scaler < (unsigned int)8); + DML_ASSERT(disp_dlg_regs->refcyc_x_after_scaler < (unsigned int)math_pow(2, 13)); + + if (disp_dlg_regs->dst_y_per_pte_row_nom_l >= (unsigned int)math_pow(2, 17)) { + DML_LOG_VERBOSE("DML_DLG: %s: Warning DST_Y_PER_PTE_ROW_NOM_L %u > register max U15.2 %u, clamp to max\n", __func__, disp_dlg_regs->dst_y_per_pte_row_nom_l, (unsigned int)math_pow(2, 17) - 1); + l->dst_y_per_pte_row_nom_l = (unsigned int)math_pow(2, 17) - 1; + } + if (l->dual_plane) { + if (disp_dlg_regs->dst_y_per_pte_row_nom_c >= (unsigned int)math_pow(2, 17)) { + DML_LOG_VERBOSE("DML_DLG: %s: Warning DST_Y_PER_PTE_ROW_NOM_C %u > register max U15.2 %u, clamp to max\n", __func__, disp_dlg_regs->dst_y_per_pte_row_nom_c, (unsigned int)math_pow(2, 17) - 1); + l->dst_y_per_pte_row_nom_c = (unsigned int)math_pow(2, 17) - 1; + } + } + + if (disp_dlg_regs->refcyc_per_pte_group_nom_l >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_l = (unsigned int)(math_pow(2, 23) - 1); + if (l->dual_plane) { + if (disp_dlg_regs->refcyc_per_pte_group_nom_c >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_c = (unsigned int)(math_pow(2, 23) - 1); + } + DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_l < (unsigned int)math_pow(2, 13)); + if (l->dual_plane) { + DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_c < (unsigned int)math_pow(2, 13)); + } + + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_l < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_l < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_c < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_c < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_ttu_regs->qos_level_low_wm < (unsigned int)math_pow(2, 14)); + DML_ASSERT(disp_ttu_regs->qos_level_high_wm < (unsigned int)math_pow(2, 14)); + DML_ASSERT(disp_ttu_regs->min_ttu_vblank < (unsigned int)math_pow(2, 24)); + DML_LOG_VERBOSE("DML_DLG::%s: Calculation for pipe[%d] done\n", __func__, pipe_idx); + } +} + +static void dcn5_rq_dlg_get_arb_params(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_display_arb_regs *arb_param) +{ + double refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : utm_soc_bb->dchub_refclk_mhz; + + arb_param->max_req_outstanding = utm_soc_bb->max_outstanding_reqs; + arb_param->min_req_outstanding = utm_soc_bb->max_outstanding_reqs; // turn off the sat level feature if this set to max + arb_param->sdpif_request_rate_limit = (3 * mode_lib->ip.words_per_channel * utm_soc_bb->dram_config.channel_count) / 4; + arb_param->sdpif_request_rate_limit = arb_param->sdpif_request_rate_limit < 96 ? 96 : arb_param->sdpif_request_rate_limit; + arb_param->sat_level_us = 60; + arb_param->hvm_max_qos_commit_threshold = 0xf; + arb_param->hvm_min_req_outstand_commit_threshold = 0xa; + arb_param->compbuf_reserved_space_kbytes = mode_lib->mp.compbuf_reserved_space_64b * 64 / 1024; + arb_param->compbuf_size = mode_lib->mp.CompressedBufferSizeInkByte / mode_lib->ip.compressed_buffer_segment_size_in_kbytes; + arb_param->allow_sdpif_rate_limit_when_cstate_req = mode_lib->mp.hw_debug5; + arb_param->dcfclk_deep_sleep_hysteresis = mode_lib->mp.dcfclk_deep_sleep_hysteresis; + arb_param->pstate_stall_threshold = (unsigned int)(mode_lib->ip_caps.fams2.max_allow_delay_us * refclk_freq_in_mhz); + + DML_LOG_VERBOSE("DML::%s: max_req_outstanding = %d\n", __func__, arb_param->max_req_outstanding); + DML_LOG_VERBOSE("DML::%s: sdpif_request_rate_limit = %d\n", __func__, arb_param->sdpif_request_rate_limit); + DML_LOG_VERBOSE("DML::%s: compbuf_reserved_space_kbytes = %d\n", __func__, arb_param->compbuf_reserved_space_kbytes); + DML_LOG_VERBOSE("DML::%s: allow_sdpif_rate_limit_when_cstate_req = %d\n", __func__, arb_param->allow_sdpif_rate_limit_when_cstate_req); + DML_LOG_VERBOSE("DML::%s: dcfclk_deep_sleep_hysteresis = %d\n", __func__, arb_param->dcfclk_deep_sleep_hysteresis); +} + +void dcn5_get_watermarks(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_dchub_watermark_regs *out) +{ + dcn5_rq_dlg_get_wm_regs(display_cfg, mode_lib, utm_soc_bb, out); +} + +void dcn5_get_arb_params(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_display_arb_regs *out) +{ + dcn5_rq_dlg_get_arb_params(display_cfg, mode_lib, utm_soc_bb, out); +} + +void dcn5_get_pipe_regs(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_dchub_per_pipe_register_set *out, int pipe_index, + const struct dml2_utm_soc_bb *utm_soc_bb, + struct dml2_core_internal_scratch *s) +{ + dcn5_rq_dlg_get_rq_reg(&out->rq_regs, display_cfg, mode_lib, pipe_index); + dcn5_rq_dlg_get_dlg_reg(s, &out->dlg_regs, &out->ttu_regs, display_cfg, mode_lib, pipe_index, utm_soc_bb); + out->det_size = mode_lib->mp.DETBufferSizeInKByte[mode_lib->mp.pipe_plane[pipe_index]] / mode_lib->ip.config_return_buffer_segment_size_in_kbytes; +} + +void dcn5_get_per_dwb_params(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_mcif_per_pipe_register_set *out, + int stream_index, + int dwb_index) +{ + double writeback_latency_hiding_us = dcn5_calculate_writeback_latency_hiding_us(display_cfg, + mode_lib->ip.writeback_interface_buffer_size_kbytes * 1024, + stream_index, + dwb_index); + + out->max_scaled_time_ns = (unsigned int)math_max2( + (writeback_latency_hiding_us - mode_lib->mp.Watermark.WritebackUrgentWatermark) * 1000.0, + 0.0); + + /* 1024ps units in U6.6 format */ + out->time_per_pixel = (unsigned int)((1000000.0 * math_pow(2, 6)) / + (double)display_cfg->stream_descriptors[stream_index].timing.pixel_clock_khz); + + out->slice_lines = 31; + out->arbitration_slice = 2; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.h new file mode 100644 index 000000000000..d4869571db9d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.h @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN5_CALCS_DCHUB_H__ +#define __DML2_CORE_DCN5_CALCS_DCHUB_H__ +#include "dml2_internal_shared_types.h" + +void dcn5_calculate_max_det_and_min_compressed_buffer_size( + unsigned int ConfigReturnBufferSizeInKByte, + unsigned int ConfigReturnBufferSegmentSizeInKByte, + unsigned int ROBBufferSizeInKByte, + unsigned int MaxNumDPP, + unsigned int nomDETInKByteOverrideEnable, // VBA_DELTA, allow DV to override default DET size + unsigned int nomDETInKByteOverrideValue, // VBA_DELTA + bool is_mrq_present, + + // Output + unsigned int *MaxTotalDETInKByte, + unsigned int *nomDETInKByte, + unsigned int *MinCompressedBufferSizeInKByte); + +// ??? +void dcn5_adjust_pixel_clock_for_progressive_to_interlace_unit(const struct dml2_display_cfg *display_cfg, bool ptoi_supported, double *PixelClockBackEnd); + +void dcn5_calculate_swath_width( + const struct dml2_display_cfg *display_cfg, + bool ForceSingleDPP, + unsigned int NumberOfActiveSurfaces, + enum dml2_odm_mode ODMMode[], + unsigned int BytePerPixY[], + unsigned int BytePerPixC[], + unsigned int Read256BytesBlockHeightY[], + unsigned int Read256BytesBlockHeightC[], + unsigned int Read256BytesBlockWidthY[], + unsigned int Read256BytesBlockWidthC[], + bool surf_linear128_l[], + bool surf_linear128_c[], + unsigned int DPPPerSurface[], + + // Output + unsigned int req_per_swath_ub_l[], + unsigned int req_per_swath_ub_c[], + unsigned int SwathWidthSingleDPPY[], + unsigned int SwathWidthSingleDPPC[], + unsigned int SwathWidthY[], // per-pipe + unsigned int SwathWidthC[], // per-pipe + unsigned int MaximumSwathHeightY[], + unsigned int MaximumSwathHeightC[], + unsigned int swath_width_luma_ub[], // per-pipe + unsigned int swath_width_chroma_ub[], // per-pipe + unsigned int swath_width_luma_ub_single_dpp[], + unsigned int swath_width_chroma_ub_single_dpp[]); + +void dcn5_calculate_swath_and_det_configuration(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *p); + +void dcn5_calculate_vm_row_and_swath(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateVMRowAndSwath_params *p); + +void dcn5_calculate_bytes_to_fetch_required_to_hide_latency( + struct dml2_core_calcs_calculate_bytes_to_fetch_required_to_hide_latency_params *p); + +void dcn5_calculate_excess_vactive_bandwidth_required( + const struct dml2_display_cfg *display_cfg, + unsigned int num_active_planes, + unsigned int bytes_required_l[], + unsigned int bytes_required_c[], + /* outputs */ + double excess_vactive_fill_bw_l[], + double excess_vactive_fill_bw_c[]); + +void dcn5_calculate_cursor_req_attributes( + unsigned int cursor_width, + unsigned int cursor_bpp, + + // output + unsigned int *cursor_lines_per_chunk, + unsigned int *cursor_bytes_per_line, + unsigned int *cursor_bytes_per_chunk, + unsigned int *cursor_bytes); + +void dcn5_calculate_cursor_urgent_burst_factor( + unsigned int CursorBufferSize, + unsigned int CursorWidth, + unsigned int cursor_bytes_per_chunk, + unsigned int cursor_lines_per_chunk, + double LineTime, + double UrgentLatency, + + double *UrgentBurstFactorCursor, + bool *NotEnoughUrgentLatencyHiding); + +void dcn5_calculate_urgent_burst_factor( + const struct dml2_plane_parameters *plane_cfg, + unsigned int swath_width_luma_ub, + unsigned int swath_width_chroma_ub, + unsigned int SwathHeightY, + unsigned int SwathHeightC, + double LineTime, + double UrgentLatency, + double VRatio, + double VRatioC, + double BytePerPixelInDETY, + double BytePerPixelInDETC, + unsigned int DETBufferSizeY, + unsigned int DETBufferSizeC, + // Output + double *UrgentBurstFactorLuma, + double *UrgentBurstFactorChroma, + bool *NotEnoughUrgentLatencyHiding); + +void dcn5_calculate_dcfclk_deep_sleep( + const struct dml2_display_cfg *display_cfg, + unsigned int NumberOfActiveSurfaces, + unsigned int BytePerPixelY[], + unsigned int BytePerPixelC[], + unsigned int SwathWidthY[], + unsigned int SwathWidthC[], + unsigned int DPPPerSurface[], + double PSCL_THROUGHPUT[], + double PSCL_THROUGHPUT_CHROMA[], + double Dppclk[], + double ReadBandwidthLuma[], + double ReadBandwidthChroma[], + unsigned int ReturnBusWidth, + + // Output + double *DCFClkDeepSleep); + +unsigned int dcn5_calculate_max_vstartup( + bool ptoi_supported, + unsigned int vblank_nom_default_us, + const struct dml2_timing_cfg *timing, + double write_back_delay_us); + +void dcn5_calculate_mcache_setting( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_calculate_mcache_setting_params *p); + +void dcn5_calculate_avg_bandwidth_required( + double *avg_bandwidth_required, + + // input + unsigned int num_active_planes, + double ReadBandwidthLuma[], + double ReadBandwidthChroma[], + double cursor_bw[], + double dcc_dram_bw_nom_overhead_factor_p0[], + double dcc_dram_bw_nom_overhead_factor_p1[]); + +void dcn5_calculate_hostvm_inefficiency_factor( + double *HostVMInefficiencyFactor, + double *HostVMInefficiencyFactorPrefetch, + + bool gpuvm_enable, + bool hostvm_enable, + unsigned int remote_iommu_outstanding_translations, + unsigned int max_outstanding_reqs, + double urg_bandwidth_avail_active_pixel_and_vm, + double urg_bandwidth_avail_active_vm_only); + +void dcn5_calculate_tdlut_setting( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_calculate_tdlut_setting_params *p); + +void dcn5_calculate_extra_latency( + const struct dml2_display_cfg *display_cfg, + unsigned int ROBBufferSizeInKByte, + unsigned int RoundTripPingLatencyCycles, + unsigned int ReorderingBytes, + double DCFCLK, + double FabricClock, + unsigned int PixelChunkSizeInKByte, + double ReturnBW, + unsigned int NumberOfActiveSurfaces, + unsigned int NumberOfDPP[], + unsigned int dpte_group_bytes[], + unsigned int tdlut_bytes_per_group[], + double HostVMInefficiencyFactor, + double HostVMInefficiencyFactorPrefetch, + enum dml2_qos_param_type qos_type, + bool max_outstanding_when_urgent_expected, + unsigned int max_outstanding_requests, + unsigned int request_size_bytes_luma[], + unsigned int request_size_bytes_chroma[], + unsigned int MetaChunkSize, + unsigned int dchub_arb_to_ret_delay, + double Ttrip, + unsigned int hostvm_mode, + + // output + double *ExtraLatency, // Tex + double *ExtraLatency_sr, // Tex_sr + double *ExtraLatencyPrefetch); + +double dcn5_calculate_t_wait( + long reserved_vblank_time_ns, + double UrgentLatency, + double Ttrip, + double temp_read_or_ppt_blackout_us, + bool drr_enabled); + +bool dcn5_calculate_prefetch_schedule(struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_CalculatePrefetchSchedule_params *p); + +void dcn5_calculate_peak_bandwidth_required( + struct dml2_core_internal_scratch *s, + struct dml2_core_calcs_calculate_peak_bandwidth_required_params *p); + +// ??? +void dcn5_check_urgent_bandwidth_support( + double *frac_urg_bandwidth_nom, + bool *bandwidth_support_ok, // max of vm, prefetch, vactive all ok + + double non_urg_bandwidth_required, + double urg_bandwidth_required, + double urg_bandwidth_available); + +// ??? +double dcn5_get_bandwidth_available_for_immediate_flip( + double urg_bandwidth_required, // no flip + double urg_bandwidth_available); + +// ??? +unsigned int dcn5_get_pipe_flip_bytes( + double hostvm_inefficiency_factor, + unsigned int vm_bytes, + unsigned int dpte_row_bytes, + unsigned int meta_row_bytes); + +// ??? +void dcn5_check_immediate_flip_bandwidth_support( + // Output + double *frac_urg_bandwidth_flip, + bool *flip_bandwidth_support_ok, + + // Input + double urg_bandwidth_required_flip, + double non_urg_bandwidth_required_flip, + double urg_bandwidth_available); + +void dcn5_calculate_dcc_configuration( + bool DCCEnabled, + bool DCCProgrammingAssumesScanDirectionUnknown, + enum dml2_source_format_class SourcePixelFormat, + unsigned int SurfaceWidthLuma, + unsigned int SurfaceWidthChroma, + unsigned int SurfaceHeightLuma, + unsigned int SurfaceHeightChroma, + unsigned int nomDETInKByte, + unsigned int RequestHeight256ByteLuma, + unsigned int RequestHeight256ByteChroma, + enum dml2_swizzle_mode TilingFormat, + unsigned int BytePerPixelY, + unsigned int BytePerPixelC, + double BytePerPixelDETY, + double BytePerPixelDETC, + enum dml2_rotation_angle RotationAngle, + + // Output + enum dml2_core_internal_request_type *RequestLuma, + enum dml2_core_internal_request_type *RequestChroma, + unsigned int *MaxUncompressedBlockLuma, + unsigned int *MaxUncompressedBlockChroma, + unsigned int *MaxCompressedBlockLuma, + unsigned int *MaxCompressedBlockChroma, + unsigned int *IndependentBlockLuma, + unsigned int *IndependentBlockChroma); + +void dcn5_calculate_flip_schedule( + struct dml2_core_internal_scratch *s, + bool iflip_enable, + bool use_lb_flip_bw, + double HostVMInefficiencyFactor, + double Tvm_trips_flip, + double Tr0_trips_flip, + double Tvm_trips_flip_rounded, + double Tr0_trips_flip_rounded, + bool GPUVMEnable, + double vm_bytes, // vm_bytes + double DPTEBytesPerRow, // dpte_row_bytes + double BandwidthAvailableForImmediateFlip, + unsigned int TotImmediateFlipBytes, + enum dml2_source_format_class SourcePixelFormat, + double LineTime, + double VRatio, + double VRatioChroma, + double Tno_bw_flip, + unsigned int dpte_row_height, + unsigned int dpte_row_height_chroma, + bool use_one_row_for_frame_flip, + unsigned int max_flip_time_us, + unsigned int max_flip_time_lines, + unsigned int per_pipe_flip_bytes, + unsigned int meta_row_bytes, + unsigned int meta_row_height, + unsigned int meta_row_height_chroma, + bool dcc_mrq_enable, + + // Output + double *dst_y_per_vm_flip, + double *dst_y_per_row_flip, + double *final_flip_bw, + bool *ImmediateFlipSupportedForPipe); + +void dcn5_calculate_watermarks_and_dram_speed_change_support( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p); + +bool dcn5_calculate_pstate_support_method( + enum dml2_pstate_method method, + double vactive_margin_us, + double reserved_vblank_us, + double blackout_us, + bool all_streams_blanked, + /* output */ + enum dml2_pstate_change_support *surface_pstate_change_support); + +void dcn5_calculate_pstate_keepout_dst_lines( + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_watermarks *watermarks, + unsigned int pstate_keepout_dst_lines[]); + +void dcn5_calculate_vactive_det_fill_latency( + const struct dml2_display_cfg *display_cfg, + unsigned int num_active_planes, + unsigned int bytes_required_l[], + unsigned int bytes_required_c[], + double dcc_dram_bw_nom_overhead_factor_p0[], + double dcc_dram_bw_nom_overhead_factor_p1[], + double surface_read_bw_l[], + double surface_read_bw_c[], + double surface_avg_vactive_required_bw[], + double surface_peak_required_bw[], + /* output */ + double vactive_det_fill_delay_us[]); + +double dcn5_calculate_write_back_delay( + enum dml2_source_format_class WritebackPixelFormat, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackVTaps, + unsigned int WritebackVTapsChroma, + unsigned int WritebackDestinationWidth, + unsigned int WritebackDestinationHeight, + unsigned int WritebackSourceWidth, + unsigned int WritebackSourceHeight, + unsigned int HTotal); + +void dcn5_calculate_meta_and_pte_times(struct dml2_core_shared_CalculateMetaAndPTETimes_params *p); + +void dcn5_calculate_vm_group_and_request_times( + const struct dml2_display_cfg *display_cfg, + unsigned int NumberOfActiveSurfaces, + unsigned int BytePerPixelC[], + double dst_y_per_vm_vblank[], + double dst_y_per_vm_flip[], + unsigned int dpte_row_width_luma_ub[], + unsigned int dpte_row_width_chroma_ub[], + unsigned int vm_group_bytes[], + unsigned int dpde0_bytes_per_frame_ub_l[], + unsigned int dpde0_bytes_per_frame_ub_c[], + unsigned int tdlut_pte_bytes_per_frame[], + unsigned int meta_pte_bytes_per_frame_ub_l[], + unsigned int meta_pte_bytes_per_frame_ub_c[], + bool mrq_present, + + // Output + double TimePerVMGroupVBlank[], + double TimePerVMGroupFlip[], + double TimePerVMRequestVBlank[], + double TimePerVMRequestFlip[]); + +void dcn5_calculate_stutter_efficiency(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateStutterEfficiency_params *p); + +void dcn5_calculate_byte_per_pixel_and_block_sizes( + enum dml2_source_format_class SourcePixelFormat, + enum dml2_swizzle_mode SurfaceTiling, + unsigned int pitch_y, + unsigned int pitch_c, + + // Output + unsigned int *BytePerPixelY, + unsigned int *BytePerPixelC, + double *BytePerPixelDETY, + double *BytePerPixelDETC, + unsigned int *BlockHeight256BytesY, + unsigned int *BlockHeight256BytesC, + unsigned int *BlockWidth256BytesY, + unsigned int *BlockWidth256BytesC, + unsigned int *MacroTileHeightY, + unsigned int *MacroTileHeightC, + unsigned int *MacroTileWidthY, + unsigned int *MacroTileWidthC, + bool *surf_linear128_l, + bool *surf_linear128_c); + +void dml2_core_dcn5_calcs_cursor_dlg_reg(struct dml2_cursor_dlg_regs *cursor_dlg_regs, const struct dml2_get_cursor_dlg_reg *p); + +unsigned int dcn5_calculate_vm_and_row_bytes(struct dml2_core_shared_calculate_vm_and_row_bytes_params *p); + +void dcn5_get_pipe_regs(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_dchub_per_pipe_register_set *out, int pipe_index, const struct dml2_utm_soc_bb *utm_soc_bb, + struct dml2_core_internal_scratch *s); + +void dcn5_get_arb_params(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_display_arb_regs *out); + +void dcn5_get_watermarks(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_dchub_watermark_regs *out); + +void dcn5_rq_dlg_get_rq_reg(struct dml2_display_rq_regs *rq_regs, + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + unsigned int pipe_idx); + +void dcn5_get_mcif_arb_params(const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_mcif_global_register_set *out); + +void dcn5_get_per_dwb_params(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_mcif_per_pipe_register_set *out, + int stream_index, + int dwb_index); + +#endif /* __DML2_CORE_DCN5_CALCS_DCHUB_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.c new file mode 100644 index 000000000000..83098c1fa50b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.c @@ -0,0 +1,1029 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn5_calcs_display_pipe.h" +#include "dml2_core_utils.h" +#include "dml2_core_dcn5_calcs_dsc_latency.h" + +#define DML2_MAX_FMT_420_BUFFER_WIDTH 4096 + +static double dcn5_trunc_to_valid_bpp( + struct dml2_core_shared_TruncToValidBPP_locals *l, + double LinkBitRate, + unsigned int Lanes, + unsigned int HTotal, + unsigned int HActive, + double PixelClock, + double DesiredBPP, + bool DSCEnable, + enum dml2_output_encoder_class Output, + enum dml2_output_format_class Format, + unsigned int DSCInputBitPerComponent, + unsigned int DSCSlices, + unsigned int AudioRate, + unsigned int AudioLayout, + enum dml2_odm_mode ODMModeNoDSC, + enum dml2_odm_mode ODMModeDSC, + + // Output + unsigned int *RequiredSlots) +{ + (void)DSCInputBitPerComponent; + (void)RequiredSlots; + double MaxLinkBPP; + unsigned int MinDSCBPP; + double MaxDSCBPP; + unsigned int NonDSCBPP0; + unsigned int NonDSCBPP1; + unsigned int NonDSCBPP2; + enum dml2_odm_mode ODMMode; + unsigned int slice_width = (int)math_ceil2((double)HActive / DSCSlices, 1.0); + + 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 = slice_width; + 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; + NonDSCBPP1 = 24; + NonDSCBPP2 = 24; + } else { + NonDSCBPP0 = 16; + NonDSCBPP1 = 20; + NonDSCBPP2 = 24; + } + if (Format == dml2_n422 || Output == dml2_hdmifrl) { + MinDSCBPP = 7; + MaxDSCBPP = 16; + } else { + MinDSCBPP = 8; + MaxDSCBPP = 16; + } + } + + 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 (DSCEnable && Output == dml2_dp2p0) { + MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128.0 / 132.0 * 383.0 / 384.0 * 65536.0 / 65540.0; + MaxLinkBPP = math_floor2(MaxLinkBPP * slice_width - 128.0, 128.0) / slice_width; + } 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); + MaxLinkBPP = math_floor2(MaxLinkBPP * slice_width - 8.0 * Lanes, 8.0 * Lanes) / slice_width; + } else { + MaxLinkBPP = LinkBitRate / 10.0 * 8.0 * Lanes / PixelClock; + } + + ODMMode = DSCEnable ? ODMModeDSC : ODMModeNoDSC; + + if (ODMMode == dml2_odm_mode_split_1to2) { + MaxLinkBPP = 2 * MaxLinkBPP; + } + + if (DesiredBPP == 0) { + if (DSCEnable) { + if (MaxLinkBPP < MinDSCBPP) { + return __DML2_CALCS_DPP_INVALID__; + } else if (MaxLinkBPP >= MaxDSCBPP) { + return MaxDSCBPP; + } else { + return math_floor2(16.0 * MaxLinkBPP, 1.0) / 16.0; + } + } else { + if (MaxLinkBPP >= NonDSCBPP2) { + return NonDSCBPP2; + } else if (MaxLinkBPP >= NonDSCBPP1) { + return NonDSCBPP1; + } else if (MaxLinkBPP >= NonDSCBPP0) { + return NonDSCBPP0; + } else { + return __DML2_CALCS_DPP_INVALID__; + } + } + } else { + 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; + } + } +} + +void dcn5_calculate_output_link( + struct dml2_core_internal_scratch *s, + double PHYCLK, + double PHYCLKD18, + double PHYCLKD32, + double Downspreading, + enum dml2_output_encoder_class Output, + enum dml2_output_format_class OutputFormat, + unsigned int HTotal, + unsigned int HActive, + double PixelClockBackEnd, + double ForcedOutputLinkBPP, + unsigned int DSCInputBitPerComponent, + unsigned int NumberOfDSCSlices, + double AudioSampleRate, + unsigned int AudioSampleLayout, + enum dml2_odm_mode ODMModeNoDSC, + enum dml2_odm_mode ODMModeDSC, + enum dml2_dsc_enable_option DSCEnable, + unsigned int OutputLinkDPLanes, + enum dml2_output_link_dp_rate OutputLinkDPRate, + + // Output + bool *RequiresDSC, + bool *RequiresFEC, + double *OutBpp, + enum dml2_core_internal_output_type *OutputType, + enum dml2_core_internal_output_type_rate *OutputRate, + unsigned int *RequiredSlots) +{ + bool LinkDSCEnable; + unsigned int dummy; + *RequiresDSC = false; + *RequiresFEC = false; + *OutBpp = 0; + + *OutputType = dml2_core_internal_output_type_unknown; + *OutputRate = dml2_core_internal_output_rate_unknown; + +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: DSCEnable = %u (dis, en, en_if_necessary)\n", __func__, DSCEnable); + DML_LOG_VERBOSE("DML::%s: PHYCLK = %f\n", __func__, PHYCLK); + DML_LOG_VERBOSE("DML::%s: PixelClockBackEnd = %f\n", __func__, PixelClockBackEnd); + DML_LOG_VERBOSE("DML::%s: AudioSampleRate = %f\n", __func__, AudioSampleRate); + DML_LOG_VERBOSE("DML::%s: HActive = %u\n", __func__, HActive); + DML_LOG_VERBOSE("DML::%s: HTotal = %u\n", __func__, HTotal); + DML_LOG_VERBOSE("DML::%s: ODMModeNoDSC = %u\n", __func__, ODMModeNoDSC); + DML_LOG_VERBOSE("DML::%s: ODMModeDSC = %u\n", __func__, ODMModeDSC); + DML_LOG_VERBOSE("DML::%s: ForcedOutputLinkBPP = %f\n", __func__, ForcedOutputLinkBPP); + DML_LOG_VERBOSE("DML::%s: Output (encoder) = %u\n", __func__, Output); + DML_LOG_VERBOSE("DML::%s: OutputLinkDPRate = %u\n", __func__, OutputLinkDPRate); +#endif + { + if (Output == dml2_hdmi) { + *RequiresDSC = false; + *RequiresFEC = false; + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, math_min2(600, PHYCLK) * 10, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, false, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = "HDMI"; + *OutputType = dml2_core_internal_output_type_hdmi; + } else if (Output == dml2_dp || Output == dml2_dp2p0 || Output == dml2_edp) { + if (DSCEnable == dml2_dsc_enable) { + *RequiresDSC = true; + LinkDSCEnable = true; + if (Output == dml2_dp || Output == dml2_dp2p0) { + *RequiresFEC = true; + } else { + *RequiresFEC = false; + } + } else { + *RequiresDSC = false; + LinkDSCEnable = false; + if (Output == dml2_dp2p0) { + *RequiresFEC = true; + } else { + *RequiresFEC = false; + } + } + if (Output == dml2_dp2p0) { + *OutBpp = 0; + if ((OutputLinkDPRate == dml2_dp_rate_na || OutputLinkDPRate == dml2_dp_rate_uhbr10) && PHYCLKD32 >= 10000.0 / 32) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 10000, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + if (*OutBpp == 0 && PHYCLKD32 < 13500.0 / 32 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 10000, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + } + //OutputTypeAndRate = Output & " UHBR10"; + *OutputType = dml2_core_internal_output_type_dp2p0; + *OutputRate = dml2_core_internal_output_rate_dp_rate_uhbr10; + } + if ((OutputLinkDPRate == dml2_dp_rate_na || OutputLinkDPRate == dml2_dp_rate_uhbr13p5) && *OutBpp == 0 && PHYCLKD32 >= 13500.0 / 32) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 13500, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + + if (*OutBpp == 0 && PHYCLKD32 < 20000.0 / 32 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 13500, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + } + //OutputTypeAndRate = Output & " UHBR13p5"; + *OutputType = dml2_core_internal_output_type_dp2p0; + *OutputRate = dml2_core_internal_output_rate_dp_rate_uhbr13p5; + } + if ((OutputLinkDPRate == dml2_dp_rate_na || OutputLinkDPRate == dml2_dp_rate_uhbr20) && *OutBpp == 0 && PHYCLKD32 >= 20000.0 / 32) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 20000, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + if (*OutBpp == 0 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 20000, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + } + //OutputTypeAndRate = Output & " UHBR20"; + *OutputType = dml2_core_internal_output_type_dp2p0; + *OutputRate = dml2_core_internal_output_rate_dp_rate_uhbr20; + } + } else { // output is dp or edp + *OutBpp = 0; + if ((OutputLinkDPRate == dml2_dp_rate_na || OutputLinkDPRate == dml2_dp_rate_hbr) && PHYCLK >= 270) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 2700, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + if (*OutBpp == 0 && PHYCLK < 540 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + if (Output == dml2_dp) { + *RequiresFEC = true; + } + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 2700, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + } + //OutputTypeAndRate = Output & " HBR"; + *OutputType = (Output == dml2_dp) ? dml2_core_internal_output_type_dp : dml2_core_internal_output_type_edp; + *OutputRate = dml2_core_internal_output_rate_dp_rate_hbr; + } + if ((OutputLinkDPRate == dml2_dp_rate_na || OutputLinkDPRate == dml2_dp_rate_hbr2) && *OutBpp == 0 && PHYCLK >= 540) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 5400, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + + if (*OutBpp == 0 && PHYCLK < 810 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + if (Output == dml2_dp) { + *RequiresFEC = true; + } + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 5400, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + } + //OutputTypeAndRate = Output & " HBR2"; + *OutputType = (Output == dml2_dp) ? dml2_core_internal_output_type_dp : dml2_core_internal_output_type_edp; + *OutputRate = dml2_core_internal_output_rate_dp_rate_hbr2; + } + if ((OutputLinkDPRate == dml2_dp_rate_na || OutputLinkDPRate == dml2_dp_rate_hbr3) && *OutBpp == 0 && PHYCLK >= 810) { // VBA_ERROR, vba code doesn't have hbr3 check + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 8100, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + + if (*OutBpp == 0 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + if (Output == dml2_dp) { + *RequiresFEC = true; + } + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, (1 - Downspreading / 100) * 8100, OutputLinkDPLanes, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, + OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, RequiredSlots); + } + //OutputTypeAndRate = Output & " HBR3"; + *OutputType = (Output == dml2_dp) ? dml2_core_internal_output_type_dp : dml2_core_internal_output_type_edp; + *OutputRate = dml2_core_internal_output_rate_dp_rate_hbr3; + } + } + } else if (Output == dml2_hdmifrl) { + if (DSCEnable == dml2_dsc_enable) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + } else { + *RequiresDSC = false; + LinkDSCEnable = false; + *RequiresFEC = false; + } + *OutBpp = 0; + if (PHYCLKD18 >= 3000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 3000, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "3x3"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_3x3; + } + if (*OutBpp == 0 && PHYCLKD18 >= 6000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 6000, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "6x3"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_6x3; + } + if (*OutBpp == 0 && PHYCLKD18 >= 6000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 6000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "6x4"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_6x4; + } + if (*OutBpp == 0 && PHYCLKD18 >= 8000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 8000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "8x4"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_8x4; + } + if (*OutBpp == 0 && PHYCLKD18 >= 10000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 10000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "10x4"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_10x4; + } + if (*OutBpp == 0 && PHYCLKD18 >= 12000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 12000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "12x4"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_12x4; + } + if (*OutBpp == 0 && PHYCLKD18 >= 16000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 16000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "16x4"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_16x4; + } + if (*OutBpp == 0 && PHYCLKD18 >= 20000.0 / 18) { + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 20000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + if (*OutBpp == 0 && DSCEnable == dml2_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + *OutBpp = dcn5_trunc_to_valid_bpp(&s->TruncToValidBPP_locals, 20000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + } + //OutputTypeAndRate = Output & "20x4"; + *OutputType = dml2_core_internal_output_type_hdmifrl; + *OutputRate = dml2_core_internal_output_rate_hdmi_rate_20x4; + } + } + } +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: RequiresDSC = %u\n", __func__, *RequiresDSC); + DML_LOG_VERBOSE("DML::%s: RequiresFEC = %u\n", __func__, *RequiresFEC); + DML_LOG_VERBOSE("DML::%s: OutBpp = %f\n", __func__, *OutBpp); +#endif +} + + +static enum dml2_odm_mode dcn5_decide_odm_mode(unsigned int HActive, + double MaxDispclk, + unsigned int MaximumPixelsPerLinePerDSCUnit, + enum dml2_output_format_class OutFormat, + bool UseDSC, + unsigned int MaximumSlicesPerDSCUnit, + unsigned int NumberOfDSCSlices, + double SurfaceRequiredDISPCLKWithoutODMCombine, + double SurfaceRequiredDISPCLKWithODMCombineTwoToOne, + double SurfaceRequiredDISPCLKWithODMCombineThreeToOne, + double SurfaceRequiredDISPCLKWithODMCombineFourToOne) +{ + (void)SurfaceRequiredDISPCLKWithODMCombineFourToOne; + enum dml2_odm_mode MinimumRequiredODMModeForMaxDispClock; + enum dml2_odm_mode MinimumRequiredODMModeForMaxDSCHActive; + enum dml2_odm_mode MinimumRequiredODMModeForMax420HActive; + enum dml2_odm_mode ODMMode = dml2_odm_mode_bypass; + + MinimumRequiredODMModeForMaxDispClock = + (SurfaceRequiredDISPCLKWithoutODMCombine <= MaxDispclk) ? dml2_odm_mode_bypass : + (SurfaceRequiredDISPCLKWithODMCombineTwoToOne <= MaxDispclk) ? dml2_odm_mode_combine_2to1 : + (SurfaceRequiredDISPCLKWithODMCombineThreeToOne <= MaxDispclk) ? dml2_odm_mode_combine_3to1 : dml2_odm_mode_combine_4to1; + if (ODMMode < MinimumRequiredODMModeForMaxDispClock) + ODMMode = MinimumRequiredODMModeForMaxDispClock; + + if (UseDSC) { + MinimumRequiredODMModeForMaxDSCHActive = + (HActive <= 1 * MaximumPixelsPerLinePerDSCUnit) ? dml2_odm_mode_bypass : + (HActive <= 2 * MaximumPixelsPerLinePerDSCUnit) ? dml2_odm_mode_combine_2to1 : + (HActive <= 3 * MaximumPixelsPerLinePerDSCUnit) ? dml2_odm_mode_combine_3to1 : dml2_odm_mode_combine_4to1; + if (ODMMode < MinimumRequiredODMModeForMaxDSCHActive) + ODMMode = MinimumRequiredODMModeForMaxDSCHActive; + } + + if (OutFormat == dml2_420) { + MinimumRequiredODMModeForMax420HActive = + (HActive <= 1 * DML2_MAX_FMT_420_BUFFER_WIDTH) ? dml2_odm_mode_bypass : + (HActive <= 2 * DML2_MAX_FMT_420_BUFFER_WIDTH) ? dml2_odm_mode_combine_2to1 : + (HActive <= 3 * DML2_MAX_FMT_420_BUFFER_WIDTH) ? dml2_odm_mode_combine_3to1 : dml2_odm_mode_combine_4to1; + if (ODMMode < MinimumRequiredODMModeForMax420HActive) + ODMMode = MinimumRequiredODMModeForMax420HActive; + } + + if (UseDSC) { + if (ODMMode == dml2_odm_mode_bypass && NumberOfDSCSlices > MaximumSlicesPerDSCUnit) + ODMMode = dml2_odm_mode_combine_2to1; + if (ODMMode == dml2_odm_mode_combine_2to1 && NumberOfDSCSlices > MaximumSlicesPerDSCUnit * 2) + ODMMode = dml2_odm_mode_combine_3to1; + if (ODMMode == dml2_odm_mode_combine_3to1 && NumberOfDSCSlices > MaximumSlicesPerDSCUnit * 3) + ODMMode = dml2_odm_mode_combine_4to1; + if (ODMMode == dml2_odm_mode_combine_3to1 && (NumberOfDSCSlices % 2 == 0) && (NumberOfDSCSlices % 3 != 0)) + ODMMode = dml2_odm_mode_combine_4to1; + } + + return ODMMode; +} + +static void dcn5_calculate_odm_constraints( + enum dml2_odm_mode ODMUse, + double SurfaceRequiredDISPCLKWithoutODMCombine, + double SurfaceRequiredDISPCLKWithODMCombineTwoToOne, + double SurfaceRequiredDISPCLKWithODMCombineThreeToOne, + double SurfaceRequiredDISPCLKWithODMCombineFourToOne, + unsigned int MaximumPixelsPerLinePerDSCUnit, + unsigned int MaximumSlicesPerDSCUnit, + /* Output */ + double *DISPCLKRequired, + unsigned int *NumberOfDPPRequired, + unsigned int *MaxHActiveForDSC, + unsigned int *MaxDSCSlices, + unsigned int *MaxHActiveFor420) +{ + switch (ODMUse) { + case dml2_odm_mode_combine_2to1: + *DISPCLKRequired = SurfaceRequiredDISPCLKWithODMCombineTwoToOne; + *NumberOfDPPRequired = 2; + break; + case dml2_odm_mode_combine_3to1: + *DISPCLKRequired = SurfaceRequiredDISPCLKWithODMCombineThreeToOne; + *NumberOfDPPRequired = 3; + break; + case dml2_odm_mode_combine_4to1: + *DISPCLKRequired = SurfaceRequiredDISPCLKWithODMCombineFourToOne; + *NumberOfDPPRequired = 4; + break; + case dml2_odm_mode_auto: + case dml2_odm_mode_split_1to2: + case dml2_odm_mode_mso_1to2: + case dml2_odm_mode_mso_1to4: + case dml2_odm_mode_bypass: + default: + *DISPCLKRequired = SurfaceRequiredDISPCLKWithoutODMCombine; + *NumberOfDPPRequired = 1; + break; + } + *MaxDSCSlices = *NumberOfDPPRequired * MaximumSlicesPerDSCUnit; + *MaxHActiveForDSC = *NumberOfDPPRequired * MaximumPixelsPerLinePerDSCUnit; + *MaxHActiveFor420 = *NumberOfDPPRequired * DML2_MAX_FMT_420_BUFFER_WIDTH; +} + +static bool dcn5_validate_odm_mode(enum dml2_odm_mode ODMMode, + double MaxDispclk, + unsigned int HActive, + enum dml2_output_format_class OutFormat, + bool UseDSC, + unsigned int NumberOfDSCSlices, + unsigned int TotalNumberOfActiveDPP, + unsigned int MaxNumDPP, + double DISPCLKRequired, + unsigned int NumberOfDPPRequired, + unsigned int MaxHActiveForDSC, + unsigned int MaxDSCSlices, + unsigned int MaxHActiveFor420, + unsigned int odm_combine_support_mask) +{ + bool are_odm_segments_symmetrical = (ODMMode == dml2_odm_mode_combine_3to1) ? UseDSC : true; + unsigned int pixels_per_clock_cycle = (OutFormat == dml2_420 || OutFormat == dml2_n422) ? 2 : 1; + unsigned int h_timing_div_mode = + (ODMMode == dml2_odm_mode_combine_4to1 || ODMMode == dml2_odm_mode_combine_3to1) ? 4 : + (ODMMode == dml2_odm_mode_combine_2to1) ? 2 : pixels_per_clock_cycle; + + if (DISPCLKRequired > MaxDispclk) + return false; + if ((TotalNumberOfActiveDPP + NumberOfDPPRequired) > MaxNumDPP) + return false; + if (are_odm_segments_symmetrical) { + if (HActive % (NumberOfDPPRequired * pixels_per_clock_cycle)) + return false; + } + if (HActive % h_timing_div_mode) + /* + * TODO - OTG_H_TOTAL, OTG_H_BLANK_START/END and + * OTG_H_SYNC_A_START/END all need to be visible by h timing div + * mode. This logic only checks H active. + */ + return false; + + if (UseDSC) { + if (HActive > MaxHActiveForDSC) + return false; + if (NumberOfDSCSlices > MaxDSCSlices) + return false; + if (HActive % NumberOfDSCSlices) + return false; + if (NumberOfDSCSlices % NumberOfDPPRequired) + return false; + if (ODMMode == dml2_odm_mode_combine_3to1) { + if ((NumberOfDSCSlices % 2 == 0) && (NumberOfDSCSlices % 3 != 0)) + return false; + } + } + + if (OutFormat == dml2_420) { + if (HActive > MaxHActiveFor420) + return false; + } + + return !!(odm_combine_support_mask & ODMMode); +} + +void dcn5_calculate_odm_mode( + unsigned int MaximumPixelsPerLinePerDSCUnit, + unsigned int HActive, + enum dml2_output_format_class OutFormat, + enum dml2_output_encoder_class Output, + enum dml2_odm_mode ODMUse, + double MaxDispclk, + bool DSCEnable, + unsigned int TotalNumberOfActiveDPP, + unsigned int MaxNumDPP, + double PixelClock, + unsigned int MaximumSlicesPerDSCUnit, + unsigned int NumberOfDSCSlices, + unsigned int odm_combine_support_mask, + + // Output + bool *TotalAvailablePipesSupport, + unsigned int *NumberOfDPP, + enum dml2_odm_mode *ODMMode, + double *RequiredDISPCLKPerSurface) +{ + double SurfaceRequiredDISPCLKWithoutODMCombine; + double SurfaceRequiredDISPCLKWithODMCombineTwoToOne; + double SurfaceRequiredDISPCLKWithODMCombineThreeToOne; + double SurfaceRequiredDISPCLKWithODMCombineFourToOne; + double DISPCLKRequired; + unsigned int NumberOfDPPRequired; + unsigned int MaxHActiveForDSC; + unsigned int MaxDSCSlices; + unsigned int MaxHActiveFor420; + bool success; + bool UseDSC = DSCEnable && (NumberOfDSCSlices > 0); + enum dml2_odm_mode DecidedODMMode; + bool isTMDS420 = (OutFormat == dml2_420 && Output == dml2_hdmi); + + SurfaceRequiredDISPCLKWithoutODMCombine = dcn5_calculate_required_dispclk(dml2_odm_mode_bypass, PixelClock, isTMDS420); + SurfaceRequiredDISPCLKWithODMCombineTwoToOne = dcn5_calculate_required_dispclk(dml2_odm_mode_combine_2to1, PixelClock, isTMDS420); + SurfaceRequiredDISPCLKWithODMCombineThreeToOne = dcn5_calculate_required_dispclk(dml2_odm_mode_combine_3to1, PixelClock, isTMDS420); + SurfaceRequiredDISPCLKWithODMCombineFourToOne = dcn5_calculate_required_dispclk(dml2_odm_mode_combine_4to1, PixelClock, isTMDS420); +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: ODMUse = %d\n", __func__, ODMUse); + DML_LOG_VERBOSE("DML::%s: Output = %d\n", __func__, Output); + DML_LOG_VERBOSE("DML::%s: DSCEnable = %d\n", __func__, DSCEnable); + DML_LOG_VERBOSE("DML::%s: MaxDispclk = %f\n", __func__, MaxDispclk); + DML_LOG_VERBOSE("DML::%s: MaximumPixelsPerLinePerDSCUnit = %d\n", __func__, MaximumPixelsPerLinePerDSCUnit); + DML_LOG_VERBOSE("DML::%s: SurfaceRequiredDISPCLKWithoutODMCombine = %f\n", __func__, SurfaceRequiredDISPCLKWithoutODMCombine); + DML_LOG_VERBOSE("DML::%s: SurfaceRequiredDISPCLKWithODMCombineTwoToOne = %f\n", __func__, SurfaceRequiredDISPCLKWithODMCombineTwoToOne); + DML_LOG_VERBOSE("DML::%s: SurfaceRequiredDISPCLKWithODMCombineThreeToOne = %f\n", __func__, SurfaceRequiredDISPCLKWithODMCombineThreeToOne); + DML_LOG_VERBOSE("DML::%s: SurfaceRequiredDISPCLKWithODMCombineFourToOne = %f\n", __func__, SurfaceRequiredDISPCLKWithODMCombineFourToOne); +#endif + if (ODMUse == dml2_odm_mode_auto) + DecidedODMMode = dcn5_decide_odm_mode(HActive, + MaxDispclk, + MaximumPixelsPerLinePerDSCUnit, + OutFormat, + UseDSC, + MaximumSlicesPerDSCUnit, + NumberOfDSCSlices, + SurfaceRequiredDISPCLKWithoutODMCombine, + SurfaceRequiredDISPCLKWithODMCombineTwoToOne, + SurfaceRequiredDISPCLKWithODMCombineThreeToOne, + SurfaceRequiredDISPCLKWithODMCombineFourToOne); + else + DecidedODMMode = ODMUse; + dcn5_calculate_odm_constraints(DecidedODMMode, + SurfaceRequiredDISPCLKWithoutODMCombine, + SurfaceRequiredDISPCLKWithODMCombineTwoToOne, + SurfaceRequiredDISPCLKWithODMCombineThreeToOne, + SurfaceRequiredDISPCLKWithODMCombineFourToOne, + MaximumPixelsPerLinePerDSCUnit, + MaximumSlicesPerDSCUnit, + &DISPCLKRequired, + &NumberOfDPPRequired, + &MaxHActiveForDSC, + &MaxDSCSlices, + &MaxHActiveFor420); + success = dcn5_validate_odm_mode(DecidedODMMode, + MaxDispclk, + HActive, + OutFormat, + UseDSC, + NumberOfDSCSlices, + TotalNumberOfActiveDPP, + MaxNumDPP, + DISPCLKRequired, + NumberOfDPPRequired, + MaxHActiveForDSC, + MaxDSCSlices, + MaxHActiveFor420, + odm_combine_support_mask); + + *ODMMode = DecidedODMMode; + *TotalAvailablePipesSupport = success; + *NumberOfDPP = NumberOfDPPRequired; + *RequiredDISPCLKPerSurface = success ? DISPCLKRequired : 0; +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: ODMMode = %d\n", __func__, *ODMMode); + DML_LOG_VERBOSE("DML::%s: NumberOfDPP = %d\n", __func__, *NumberOfDPP); + DML_LOG_VERBOSE("DML::%s: TotalAvailablePipesSupport = %d\n", __func__, *TotalAvailablePipesSupport); + DML_LOG_VERBOSE("DML::%s: RequiredDISPCLKPerSurface = %f\n", __func__, *RequiredDISPCLKPerSurface); +#endif +} + +double dcn5_calculate_required_dtbclk( + bool DSCEnable, + double PixelClock, + enum dml2_output_format_class OutputFormat, + double OutputBpp, + unsigned int DSCSlices, + unsigned int HTotal, + unsigned int HActive, + unsigned int AudioRate, + unsigned int AudioLayout) +{ + if (DSCEnable != true) { + return math_max2(PixelClock / 4.0 * OutputBpp / 24.0, 25.0); + } else { + double PixelWordRate = PixelClock / (OutputFormat == dml2_444 ? 1 : 2); + double HCActive = math_ceil2(DSCSlices * math_ceil2(OutputBpp * math_ceil2(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1); + double HCBlank = 64 + 32 * math_ceil2(AudioRate * (AudioLayout == 1 ? 1 : 0.25) * HTotal / (PixelClock * 1000), 1); + double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal; + double HActiveTribyteRate = PixelWordRate * HCActive / HActive; + return math_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002; + } +} + +double dcn5_calculate_required_dispclk( + enum dml2_odm_mode ODMMode, + double PixelClock, + bool isTMDS420) +{ + double DispClk; + + if (ODMMode == dml2_odm_mode_combine_4to1) { + DispClk = PixelClock / 4.0; + } else if (ODMMode == dml2_odm_mode_combine_3to1) { + DispClk = PixelClock / 3.0; + } else if (ODMMode == dml2_odm_mode_combine_2to1) { + DispClk = PixelClock / 2.0; + } else { + DispClk = PixelClock; + } + + if (isTMDS420) { + double TMDS420MinPixClock = PixelClock / 2.0; + DispClk = math_max2(DispClk, TMDS420MinPixClock); + } + + return DispClk; +} + +double dcn5_calculate_write_back_dispclk( + enum dml2_source_format_class WritebackPixelFormat, + double PixelClock, + enum dml2_odm_mode ODMMode, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackHTaps, + unsigned int WritebackVTaps, + unsigned int WritebackHTapsChroma, + unsigned int WritebackVTapsChroma, + unsigned int WritebackSourceWidth, + unsigned int WritebackDestinationWidth, + unsigned int HTotal, + unsigned int WritebackLineBufferSize) +{ + (void)WritebackSourceWidth; + (void)WritebackLineBufferSize; + double DISPCLK_H, DISPCLK_V, THROUGHPUT, LoadVcoef; + double effectivePixelClock; + + if (ODMMode == dml2_odm_mode_combine_4to1) + effectivePixelClock = PixelClock / 4; + else if (ODMMode == dml2_odm_mode_combine_3to1) + effectivePixelClock = PixelClock / 3; + if (ODMMode == dml2_odm_mode_combine_2to1) + effectivePixelClock = PixelClock / 2; + else + effectivePixelClock = PixelClock; + + DISPCLK_H = effectivePixelClock * math_ceil2((double)WritebackHTaps / 4.0, 1) / WritebackHRatio; + LoadVcoef = math_ceil2(1 / WritebackVRatio, 1) * (math_ceil2(WritebackVTaps / 4.0, 1) + 4.0); + DISPCLK_V = effectivePixelClock * (math_ceil2(WritebackDestinationWidth / 4.0, 1) * (WritebackVTaps * math_ceil2(1 / WritebackVRatio, 1) + 1) / HTotal + LoadVcoef / HTotal); + THROUGHPUT = effectivePixelClock * math_ceil2(1 / WritebackVRatio, 1) * (double)WritebackDestinationWidth / (double)HTotal; + + double v_ratio_chroma; + double h_ratio_chroma; + double output_width_chroma; + + if (dml2_core_utils_is_420(WritebackPixelFormat)) { + v_ratio_chroma = 2.0 * WritebackVRatio; + h_ratio_chroma = 2.0 * WritebackHRatio; + output_width_chroma = 0.5 * WritebackDestinationWidth; + } else if (dml2_core_utils_is_422_packed(WritebackPixelFormat) || dml2_core_utils_is_422_planar(WritebackPixelFormat)) { + v_ratio_chroma = WritebackVRatio; + h_ratio_chroma = 2.0 * WritebackHRatio; + output_width_chroma = 0.5 * WritebackDestinationWidth; + } else { + v_ratio_chroma = WritebackVRatio; + h_ratio_chroma = WritebackHRatio; + output_width_chroma = WritebackDestinationWidth; + } + + double DISPCLK_H_CHROMA, DISPCLK_V_CHROMA, THROUGHPUT_CHROMA, LoadVcoef_chroma; + DISPCLK_H_CHROMA = effectivePixelClock * math_ceil2((double)WritebackHTapsChroma / 4.0, 1) / h_ratio_chroma; + LoadVcoef_chroma = math_ceil2(1 / v_ratio_chroma, 1) * (math_ceil2(WritebackVTapsChroma / 4.0, 1) + 4.0); + DISPCLK_V_CHROMA = effectivePixelClock * (math_ceil2(output_width_chroma / 4.0, 1) * (WritebackVTapsChroma * math_ceil2(1 / v_ratio_chroma, 1) + 1) / HTotal + LoadVcoef_chroma / HTotal); + THROUGHPUT_CHROMA = effectivePixelClock * math_ceil2(1 / v_ratio_chroma, 1) * output_width_chroma / (double)HTotal; + + return math_max2(math_max3(DISPCLK_H, DISPCLK_V, THROUGHPUT), math_max3(DISPCLK_H_CHROMA, DISPCLK_V_CHROMA, THROUGHPUT_CHROMA)); +} + +unsigned int dcn5_calculate_dsc_delay_requirement( + bool DSCEnabled, + enum dml2_odm_mode ODMMode, + unsigned int DSCInputBitPerComponent, + double OutputBpp, + unsigned int HActive, + unsigned int HTotal, + unsigned int NumberOfDSCSlices, + enum dml2_output_format_class OutputFormat, + enum dml2_output_encoder_class Output, + double PixelClock, + double PixelClockBackEnd, + bool use_legacy_dsc_delay_formula) +{ + (void)Output; + unsigned int DSCDelayRequirement_val = 0; + unsigned int NumberOfDSCSlicesFactor = 1; + + if (DSCEnabled == true && OutputBpp != 0) { + + if (ODMMode == dml2_odm_mode_combine_4to1) + NumberOfDSCSlicesFactor = 4; + else if (ODMMode == dml2_odm_mode_combine_3to1) + NumberOfDSCSlicesFactor = 3; + else if (ODMMode == dml2_odm_mode_combine_2to1) + NumberOfDSCSlicesFactor = 2; + + delay_uncertainty_t DscResult; + unsigned int EffectiveNumSlices = NumberOfDSCSlices / NumberOfDSCSlicesFactor; + int SliceWidth = (int)math_ceil2((double)HActive / (double)NumberOfDSCSlices, 1.0); + + // ASSUMPTION: Enabling dynamic gating for maximum delay calculation: + // - dscclk_dynamic_gating_en = 1 (enabled for maximum delay) + // - dispclk_dynamic_gating_en = 1 (enabled for maximum delay) + // - initial_xmit_delay_offset = 0 (no additional offset) + // - group_delay_after_initial_xmit_delay_override_en = 0 (use automatic calculation) + // - group_delay_after_initial_xmit_delay = 0 (not used when override disabled) + +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: use_legacy_dsc_delay_formula= %u\n", __func__, use_legacy_dsc_delay_formula); +#endif + if (use_legacy_dsc_delay_formula) { + // Legacy DCN5 DSC delay calculation + dcn5_dsc_compute_delay_legacy(&DscResult, + DSCInputBitPerComponent, // bpc + (float)OutputBpp, // bpp + SliceWidth, // slice_width + EffectiveNumSlices, // num_slices (adjusted for ODM) + OutputFormat, // pixel_format + 1, // dscclk_dynamic_gating_en (enabled for max delay) + 1, // dispclk_dynamic_gating_en (enabled for max delay) + 0, // initial_xmit_delay_offset (no offset) + 0, // group_delay_after_initial_xmit_delay_override_en (use auto calc) + 0); // group_delay_after_initial_xmit_delay (not used) + } else { + // DCN5.1 and DCN6 DSC delay calculation + dcn5_dsc_compute_delay(&DscResult, + DSCInputBitPerComponent, // bpc + (float)OutputBpp, // bpp + SliceWidth, // slice_width + EffectiveNumSlices, // num_slices (adjusted for ODM) + OutputFormat, // pixel_format + 1, // dscclk_dynamic_gating_en (enabled for max delay) + 1, // dispclk_dynamic_gating_en (enabled for max delay) + 0, // initial_xmit_delay_offset (no offset) + 0, // group_delay_after_initial_xmit_delay_override_en (use auto calc) + 0); // group_delay_after_initial_xmit_delay (not used) + } + + // Apply ODM factor multiplier and timing adjustments + DSCDelayRequirement_val = (unsigned int)(DscResult.delay * NumberOfDSCSlicesFactor); + + DSCDelayRequirement_val = (unsigned int)(DSCDelayRequirement_val + (HTotal - HActive) * math_ceil2((double)DSCDelayRequirement_val / (double)HActive, 1.0)); + DSCDelayRequirement_val = (unsigned int)(DSCDelayRequirement_val * PixelClock / PixelClockBackEnd); + + } else { + DSCDelayRequirement_val = 0; + } +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: DSCEnabled= %u\n", __func__, DSCEnabled); + DML_LOG_VERBOSE("DML::%s: ODMMode = %u\n", __func__, ODMMode); + DML_LOG_VERBOSE("DML::%s: OutputBpp = %f\n", __func__, OutputBpp); + DML_LOG_VERBOSE("DML::%s: HActive = %u\n", __func__, HActive); + DML_LOG_VERBOSE("DML::%s: HTotal = %u\n", __func__, HTotal); + DML_LOG_VERBOSE("DML::%s: PixelClock = %f\n", __func__, PixelClock); + DML_LOG_VERBOSE("DML::%s: PixelClockBackEnd = %f\n", __func__, PixelClockBackEnd); + DML_LOG_VERBOSE("DML::%s: OutputFormat = %u\n", __func__, OutputFormat); + DML_LOG_VERBOSE("DML::%s: DSCInputBitPerComponent = %u\n", __func__, DSCInputBitPerComponent); + DML_LOG_VERBOSE("DML::%s: NumberOfDSCSlices = %u\n", __func__, NumberOfDSCSlices); + DML_LOG_VERBOSE("DML::%s: DSCDelayRequirement_val = %u\n", __func__, DSCDelayRequirement_val); +#endif + + return DSCDelayRequirement_val; +} + +void dcn5_calculate_single_pipe_dppclk_and_scl_throughput( + double HRatio, + double HRatioChroma, + double VRatio, + double VRatioChroma, + double MaxDCHUBToPSCLThroughput, + double MaxPSCLToLBThroughput, + double PixelClock, + enum dml2_source_format_class SourcePixelFormat, + unsigned int HTaps, + unsigned int HTapsChroma, + unsigned int VTaps, + unsigned int VTapsChroma, + + // Output + double *PSCL_THROUGHPUT, + double *PSCL_THROUGHPUT_CHROMA, + double *DPPCLKUsingSingleDPP) +{ + double DPPCLKUsingSingleDPPLuma; + double DPPCLKUsingSingleDPPChroma; + + if (HRatio > 1) { + *PSCL_THROUGHPUT = math_min2(MaxDCHUBToPSCLThroughput, MaxPSCLToLBThroughput * HRatio / math_ceil2((double)HTaps / 6.0, 1.0)); + } else { + *PSCL_THROUGHPUT = math_min2(MaxDCHUBToPSCLThroughput, MaxPSCLToLBThroughput); + } + + DPPCLKUsingSingleDPPLuma = PixelClock * math_max3(VTaps / 6 * math_min2(1, HRatio), HRatio * VRatio / *PSCL_THROUGHPUT, 1); + + if ((HTaps > 6 || VTaps > 6) && DPPCLKUsingSingleDPPLuma < 2 * PixelClock) + DPPCLKUsingSingleDPPLuma = 2 * PixelClock; + + if (!dml2_core_utils_is_420(SourcePixelFormat) && !dml2_core_utils_is_422_planar(SourcePixelFormat) && SourcePixelFormat != dml2_rgbe_alpha) { + *PSCL_THROUGHPUT_CHROMA = 0; + *DPPCLKUsingSingleDPP = DPPCLKUsingSingleDPPLuma; + } else { + if (HRatioChroma > 1) { + *PSCL_THROUGHPUT_CHROMA = math_min2(MaxDCHUBToPSCLThroughput, MaxPSCLToLBThroughput * HRatioChroma / math_ceil2((double)HTapsChroma / 6.0, 1.0)); + } else { + *PSCL_THROUGHPUT_CHROMA = math_min2(MaxDCHUBToPSCLThroughput, MaxPSCLToLBThroughput); + } + DPPCLKUsingSingleDPPChroma = PixelClock * math_max3(VTapsChroma / 6 * math_min2(1, HRatioChroma), + HRatioChroma * VRatioChroma / *PSCL_THROUGHPUT_CHROMA, 1); + if ((HTapsChroma > 6 || VTapsChroma > 6) && DPPCLKUsingSingleDPPChroma < 2 * PixelClock) + DPPCLKUsingSingleDPPChroma = 2 * PixelClock; + *DPPCLKUsingSingleDPP = math_max2(DPPCLKUsingSingleDPPLuma, DPPCLKUsingSingleDPPChroma); + } +} + +void dcn5_calculate_pixel_delivery_times( + const struct dml2_display_cfg *display_cfg, + unsigned int NoOfDPP[DML2_MAX_PLANES], + unsigned int NumberOfActiveSurfaces, + double VRatioPrefetchY[], + double VRatioPrefetchC[], + unsigned int swath_width_luma_ub[], + unsigned int swath_width_chroma_ub[], + double PSCL_THROUGHPUT[], + double PSCL_THROUGHPUT_CHROMA[], + double Dppclk[], + double DCFCLKDeepSleep, + unsigned int BytePerPixelY[], + unsigned int BytePerPixelC[], + unsigned int req_per_swath_ub_l[], + unsigned int req_per_swath_ub_c[], + + // Output + double DisplayPipeLineDeliveryTimeLuma[], + double DisplayPipeLineDeliveryTimeChroma[], + double DisplayPipeLineDeliveryTimeLumaPrefetch[], + double DisplayPipeLineDeliveryTimeChromaPrefetch[], + double DisplayPipeRequestDeliveryTimeLuma[], + double DisplayPipeRequestDeliveryTimeChroma[], + double DisplayPipeRequestDeliveryTimeLumaPrefetch[], + double DisplayPipeRequestDeliveryTimeChromaPrefetch[]) +{ + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + double pixel_clock_mhz = ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: k=%u : HRatio = %f\n", __func__, k, display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u : VRatio = %f\n", __func__, k, display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u : HRatioChroma = %f\n", __func__, k, display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u : VRatioChroma = %f\n", __func__, k, display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u : VRatioPrefetchY = %f\n", __func__, k, VRatioPrefetchY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : VRatioPrefetchC = %f\n", __func__, k, VRatioPrefetchC[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : swath_width_luma_ub = %u\n", __func__, k, swath_width_luma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : swath_width_chroma_ub = %u\n", __func__, k, swath_width_chroma_ub[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : PSCL_THROUGHPUT = %f\n", __func__, k, PSCL_THROUGHPUT[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : PSCL_THROUGHPUT_CHROMA = %f\n", __func__, k, PSCL_THROUGHPUT_CHROMA[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DPPPerSurface = %u\n", __func__, k, NoOfDPP[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : pixel_clock_mhz = %f\n", __func__, k, pixel_clock_mhz); + DML_LOG_VERBOSE("DML::%s: k=%u : Dppclk = %f\n", __func__, k, Dppclk[k]); +#endif + if (display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio <= 1) { + DisplayPipeLineDeliveryTimeLuma[k] = swath_width_luma_ub[k] * NoOfDPP[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio / pixel_clock_mhz; + } else { + DisplayPipeLineDeliveryTimeLuma[k] = swath_width_luma_ub[k] / PSCL_THROUGHPUT[k] / Dppclk[k]; + } + + if (BytePerPixelC[k] == 0) { + DisplayPipeLineDeliveryTimeChroma[k] = 0; + } else { + if (display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio <= 1) { + DisplayPipeLineDeliveryTimeChroma[k] = swath_width_chroma_ub[k] * NoOfDPP[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio / pixel_clock_mhz; + } else { + DisplayPipeLineDeliveryTimeChroma[k] = swath_width_chroma_ub[k] / PSCL_THROUGHPUT_CHROMA[k] / Dppclk[k]; + } + } + + if (VRatioPrefetchY[k] <= 1) { + DisplayPipeLineDeliveryTimeLumaPrefetch[k] = swath_width_luma_ub[k] * NoOfDPP[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio / pixel_clock_mhz; + } else { + DisplayPipeLineDeliveryTimeLumaPrefetch[k] = swath_width_luma_ub[k] / PSCL_THROUGHPUT[k] / Dppclk[k]; + } + + if (BytePerPixelC[k] == 0) { + DisplayPipeLineDeliveryTimeChromaPrefetch[k] = 0; + } else { + if (VRatioPrefetchC[k] <= 1) { + DisplayPipeLineDeliveryTimeChromaPrefetch[k] = swath_width_chroma_ub[k] * NoOfDPP[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio / pixel_clock_mhz; + } else { + DisplayPipeLineDeliveryTimeChromaPrefetch[k] = swath_width_chroma_ub[k] / PSCL_THROUGHPUT_CHROMA[k] / Dppclk[k]; + } + } + + if (BytePerPixelC[k] > 0) { + DisplayPipeLineDeliveryTimeLuma[k] = math_max2(DisplayPipeLineDeliveryTimeLuma[k], swath_width_luma_ub[k] * BytePerPixelY[k] / 32.0 / DCFCLKDeepSleep); + DisplayPipeLineDeliveryTimeChroma[k] = math_max2(DisplayPipeLineDeliveryTimeChroma[k], swath_width_chroma_ub[k] * BytePerPixelC[k] / 32.0 / DCFCLKDeepSleep); + DisplayPipeLineDeliveryTimeLumaPrefetch[k] = math_max2(DisplayPipeLineDeliveryTimeLumaPrefetch[k], swath_width_luma_ub[k] * BytePerPixelY[k] / 32.0 / DCFCLKDeepSleep); + DisplayPipeLineDeliveryTimeChromaPrefetch[k] = math_max2(DisplayPipeLineDeliveryTimeChromaPrefetch[k], swath_width_chroma_ub[k] * BytePerPixelC[k] / 32.0 / DCFCLKDeepSleep); + } else { + DisplayPipeLineDeliveryTimeLuma[k] = math_max2(DisplayPipeLineDeliveryTimeLuma[k], swath_width_luma_ub[k] * BytePerPixelY[k] / 64.0 / DCFCLKDeepSleep); + DisplayPipeLineDeliveryTimeLumaPrefetch[k] = math_max2(DisplayPipeLineDeliveryTimeLumaPrefetch[k], swath_width_luma_ub[k] * BytePerPixelY[k] / 64.0 / DCFCLKDeepSleep); + } + +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeLineDeliveryTimeLuma = %f\n", __func__, k, DisplayPipeLineDeliveryTimeLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeLineDeliveryTimeLumaPrefetch = %f\n", __func__, k, DisplayPipeLineDeliveryTimeLumaPrefetch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeLineDeliveryTimeChroma = %f\n", __func__, k, DisplayPipeLineDeliveryTimeChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeLineDeliveryTimeChromaPrefetch = %f\n", __func__, k, DisplayPipeLineDeliveryTimeChromaPrefetch[k]); +#endif + } + + for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) { + + DisplayPipeRequestDeliveryTimeLuma[k] = DisplayPipeLineDeliveryTimeLuma[k] / req_per_swath_ub_l[k]; + DisplayPipeRequestDeliveryTimeLumaPrefetch[k] = DisplayPipeLineDeliveryTimeLumaPrefetch[k] / req_per_swath_ub_l[k]; + if (BytePerPixelC[k] == 0) { + DisplayPipeRequestDeliveryTimeChroma[k] = 0; + DisplayPipeRequestDeliveryTimeChromaPrefetch[k] = 0; + } else { + DisplayPipeRequestDeliveryTimeChroma[k] = DisplayPipeLineDeliveryTimeChroma[k] / req_per_swath_ub_c[k]; + DisplayPipeRequestDeliveryTimeChromaPrefetch[k] = DisplayPipeLineDeliveryTimeChromaPrefetch[k] / req_per_swath_ub_c[k]; + } +#ifdef __DML_VBA_DEBUG__ + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeRequestDeliveryTimeLuma = %f\n", __func__, k, DisplayPipeRequestDeliveryTimeLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeRequestDeliveryTimeLumaPrefetch = %f\n", __func__, k, DisplayPipeRequestDeliveryTimeLumaPrefetch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : req_per_swath_ub_l = %d\n", __func__, k, req_per_swath_ub_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeRequestDeliveryTimeChroma = %f\n", __func__, k, DisplayPipeRequestDeliveryTimeChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : DisplayPipeRequestDeliveryTimeChromaPrefetch = %f\n", __func__, k, DisplayPipeRequestDeliveryTimeChromaPrefetch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u : req_per_swath_ub_c = %d\n", __func__, k, req_per_swath_ub_c[k]); +#endif + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.h new file mode 100644 index 000000000000..25d7ca666a33 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_display_pipe.h @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN5_CALCS_DISPLAY_PIPE_H__ +#define __DML2_CORE_DCN5_CALCS_DISPLAY_PIPE_H__ +#include "dml2_internal_shared_types.h" + +void dcn5_calculate_output_link( + struct dml2_core_internal_scratch *s, + double PHYCLK, + double PHYCLKD18, + double PHYCLKD32, + double Downspreading, + enum dml2_output_encoder_class Output, + enum dml2_output_format_class OutputFormat, + unsigned int HTotal, + unsigned int HActive, + double PixelClockBackEnd, + double ForcedOutputLinkBPP, + unsigned int DSCInputBitPerComponent, + unsigned int NumberOfDSCSlices, + double AudioSampleRate, + unsigned int AudioSampleLayout, + enum dml2_odm_mode ODMModeNoDSC, + enum dml2_odm_mode ODMModeDSC, + enum dml2_dsc_enable_option DSCEnable, + unsigned int OutputLinkDPLanes, + enum dml2_output_link_dp_rate OutputLinkDPRate, + + // Output + bool *RequiresDSC, + bool *RequiresFEC, + double *OutBpp, + enum dml2_core_internal_output_type *OutputType, + enum dml2_core_internal_output_type_rate *OutputRate, + unsigned int *RequiredSlots); + +void dcn5_calculate_odm_mode( + unsigned int MaximumPixelsPerLinePerDSCUnit, + unsigned int HActive, + enum dml2_output_format_class OutFormat, + enum dml2_output_encoder_class Output, + enum dml2_odm_mode ODMUse, + double MaxDispclk, + bool DSCEnable, + unsigned int TotalNumberOfActiveDPP, + unsigned int MaxNumDPP, + double PixelClock, + unsigned int MaximumSlicesPerDSCUnit, + unsigned int NumberOfDSCSlices, + unsigned int odm_combine_support_mask, + + // Output + bool *TotalAvailablePipesSupport, + unsigned int *NumberOfDPP, + enum dml2_odm_mode *ODMMode, + double *RequiredDISPCLKPerSurface); + +double dcn5_calculate_required_dtbclk( + bool DSCEnable, + double PixelClock, + enum dml2_output_format_class OutputFormat, + double OutputBpp, + unsigned int DSCSlices, + unsigned int HTotal, + unsigned int HActive, + unsigned int AudioRate, + unsigned int AudioLayout); + +double dcn5_calculate_required_dispclk( + enum dml2_odm_mode ODMMode, + double PixelClock, + bool isTMDS420); + +double dcn5_calculate_write_back_dispclk( + enum dml2_source_format_class WritebackPixelFormat, + double PixelClock, + enum dml2_odm_mode ODMMode, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackHTaps, + unsigned int WritebackVTaps, + unsigned int WritebackHTapsChroma, + unsigned int WritebackVTapsChroma, + unsigned int WritebackSourceWidth, + unsigned int WritebackDestinationWidth, + unsigned int HTotal, + unsigned int WritebackLineBufferSize); + +unsigned int dcn5_calculate_dsc_delay_requirement( + bool DSCEnabled, + enum dml2_odm_mode ODMMode, + unsigned int DSCInputBitPerComponent, + double OutputBpp, + unsigned int HActive, + unsigned int HTotal, + unsigned int NumberOfDSCSlices, + enum dml2_output_format_class OutputFormat, + enum dml2_output_encoder_class Output, + double PixelClock, + double PixelClockBackEnd, + bool use_legacy_dsc_delay_formula); + +void dcn5_calculate_single_pipe_dppclk_and_scl_throughput( + double HRatio, + double HRatioChroma, + double VRatio, + double VRatioChroma, + double MaxDCHUBToPSCLThroughput, + double MaxPSCLToLBThroughput, + double PixelClock, + enum dml2_source_format_class SourcePixelFormat, + unsigned int HTaps, + unsigned int HTapsChroma, + unsigned int VTaps, + unsigned int VTapsChroma, + + // Output + double *PSCL_THROUGHPUT, + double *PSCL_THROUGHPUT_CHROMA, + double *DPPCLKUsingSingleDPP); + +void dcn5_calculate_pixel_delivery_times( + const struct dml2_display_cfg *display_cfg, + unsigned int NoOfDPP[DML2_MAX_PLANES], + unsigned int NumberOfActiveSurfaces, + double VRatioPrefetchY[], + double VRatioPrefetchC[], + unsigned int swath_width_luma_ub[], + unsigned int swath_width_chroma_ub[], + double PSCL_THROUGHPUT[], + double PSCL_THROUGHPUT_CHROMA[], + double Dppclk[], + double DCFCLKDeepSleep, + unsigned int BytePerPixelY[], + unsigned int BytePerPixelC[], + unsigned int req_per_swath_ub_l[], + unsigned int req_per_swath_ub_c[], + + // Output + double DisplayPipeLineDeliveryTimeLuma[], + double DisplayPipeLineDeliveryTimeChroma[], + double DisplayPipeLineDeliveryTimeLumaPrefetch[], + double DisplayPipeLineDeliveryTimeChromaPrefetch[], + double DisplayPipeRequestDeliveryTimeLuma[], + double DisplayPipeRequestDeliveryTimeChroma[], + double DisplayPipeRequestDeliveryTimeLumaPrefetch[], + double DisplayPipeRequestDeliveryTimeChromaPrefetch[]); + +#endif /* __DML2_CORE_DCN5_CALCS_DISPLAY_PIPE_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.c new file mode 100644 index 000000000000..d4201b019850 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + +#include "dml2_debug.h" +#include "dml2_core_dcn5_calcs_dsc_latency.h" +#include "lib_float_math.h" + +/* ------------------------------------------------------------------------- + * Shared DSC sub-functions (static) — identical for legacy and updated paths + * ------------------------------------------------------------------------- */ + +// function to compute the dscc_pcl (i.e., pixel compression layer) latency in terms of groups (output is actually in terms of dispclk) +static int dscc_pcl_compute_delay(enum dml2_output_format_class pixel_format, int num_slices) +{ + int dispclk_per_dscclk; + int delay; + + //1 slice configuration uses a single slice processor (/3 clock) + if (num_slices == 1) { + dispclk_per_dscclk = 3; + } else { //greater than 1 slice configuration uses two slice processor (/6 clock) + dispclk_per_dscclk = 6; + } + + // N422 and N420 process 2x pixels per clock + if (pixel_format == dml2_420 || pixel_format == dml2_n422) { + dispclk_per_dscclk *= 2; + } + + //fixed delay though dscc_pcl + delay = 7; + + delay *= dispclk_per_dscclk; + return delay; +} + +// function to compute dscc_bcl layer delay in terms of groups + pipeline delay and pixels (444 format pixels) +// returns the delay in groups of pixels, pipeline delay in cycles, and individual pixels +// valid bpc = source bits per component in the set of {8, 10, 12} +// valid bpp = increments of 1/16 of a bit +// min = 6/7/8 in N420/N422/444, respectively +// max = such that compression is 1:1 +// valid slice_width = number of pixels per slice line, must be less than or equal to `DSC_MAX_WIDTH/num_slices (or 4096/num_slices in 420 mode) +// valid num_slices = number of slices in the horiziontal direction per DSC engine in the set of {1, 2, 3, 4} +// valid pixel_format = pixel/color format in the set of {dml2_444, dml2_s422, dml2_n422, dml2_420} +// note: implementation takes 4 cycles to process a group in N422 mode or 3 cycles in all other pixel formats +static void dscc_bcl_compute_delay(latency_t *p, int bpc, float bpp, int slice_width, int num_slices, enum dml2_output_format_class pixel_format, int initial_xmit_delay_offset, int group_delay_after_initial_xmit_delay_override_en, int group_delay_after_initial_xmit_delay) +{ + //fixed value + int rc_model_size = 8192; + + //latency calculation variables + int pixels_per_clock; + int padding_pixels; + int ssm_group_priming_delay; + int ssm_pipeline_delay; + int obsm_pipeline_delay; + int slice_padded_pixels; + int ixd_plus_padding; + int ixd_plus_padding_groups; + int cycles_per_group; + int groups_per_bcl_cycle; + int syntax_elements_per_group; + int group_delay; + int pipeline_delay; + int pixel_delay; + int additional_group_delay; + int lines_to_reach_ixd_adjusted; + int groups_to_reach_ixd_adjusted; + int slice_width_groups; + int initial_xmit_delay; + int lines_to_reach_ixd; + int slice_width_modified; + + //N422/N420 operate at 2 pixels per clock + if (pixel_format == dml2_n422 || pixel_format == dml2_420) { + pixels_per_clock = 2; + } else { //all other modes operate at 1 pixel per clock + pixels_per_clock = 1; + } + + //initial transmit delay as per PPS. If debugging, ensure register's + //initial_xmit delay matches the final initial_xmit_delay below + initial_xmit_delay = (int)math_round((double)(rc_model_size / 2.0 / bpp / pixels_per_clock)); + + //slice width as seen by dscc_bcl in pixels or pixels pairs (depending on number of pixels per pixel container based on pixel format) + slice_width_modified = (pixel_format == dml2_n422 || pixel_format == dml2_420) ? slice_width/2 : slice_width; + + //initial_xmit_delay is increased by one under specific conditions as per pps spreadsheet + padding_pixels = ((slice_width_modified % 3) != 0) ? (3 - (slice_width_modified % 3)) * (initial_xmit_delay / slice_width_modified) : 0; + if ((3.0 * pixels_per_clock * bpp) >= (((initial_xmit_delay + 2) / 3) * (3 + (pixel_format == dml2_n422)))) { + if ((initial_xmit_delay + padding_pixels) % 3 == 1) + initial_xmit_delay++; + } + + //increase initial xmit delay by the offset value + initial_xmit_delay += initial_xmit_delay_offset; + + //sub-stream multiplexer balance fifo priming delay in groups as per dsc standard + switch (bpc) { + case 8: + ssm_group_priming_delay = 83; + break; + case 10: + ssm_group_priming_delay = 91; + break; + case 12: + ssm_group_priming_delay = 115; + break; + case 14: + ssm_group_priming_delay = 123; + break; + case 16: + ssm_group_priming_delay = 128; + break; + default: + DML_LOG_VERBOSE("ERROR: BPC is not a valid value. bpc = %d", bpc); + ssm_group_priming_delay = 83; // Default to 8bpc value + break; + } + + //slice width in groups is rounded up to the nearest group as DSC adds padded pixels such that there are an integer number of groups per slice + slice_width_groups = (slice_width_modified + 2) / 3; + + //determine number of padded pixels in the last group of a slice line, computed as + slice_padded_pixels = 3 * slice_width_groups - slice_width_modified; + + //determine integer number of complete slice lines required to reach initial transmit delay without ssm delay considered + lines_to_reach_ixd = initial_xmit_delay / slice_width_modified; + + //increase initial transmit delay by the number of padded pixels added to a slice line multiplied by the integer number of complete lines to reach initial transmit delay + //this step is necessary as each padded pixel added takes up a clock cycle but is not included in the initialXmitDelay and, therefore, adds to the overall delay + ixd_plus_padding = initial_xmit_delay + slice_padded_pixels * lines_to_reach_ixd; + + //convert the the padded initial transmit delay from pixels to groups by rounding up to the nearest group as DSC processes in groups of pixels + ixd_plus_padding_groups = (ixd_plus_padding + 2) / 3; + + //number of groups required for a slice to reach initial transmit delay is the sum of the padded initial transmit delay plus the ssm group priming delay + groups_to_reach_ixd_adjusted = ixd_plus_padding_groups + ssm_group_priming_delay; + + //number of lines required to reach padded initial transmit delay in groups in slices to the left of the last horizontal slice + //needs to be rounded up as a complete slice lines are buffered prior to initial transmit delay being reached in the last horizontal slice + lines_to_reach_ixd_adjusted = (groups_to_reach_ixd_adjusted + slice_width_groups - 1) / slice_width_groups; //round up lines to reach ixd to next + + //determine if there are non-zero number of pixels reached in the group where initial transmit delay is reached + //an additional group time (i.e., 3 pixel times) is required before the first output if there are no additional pixels beyond initial transmit delay + additional_group_delay = ((initial_xmit_delay - lines_to_reach_ixd * slice_width_modified) % 3) == 0 ? 1 : 0; + + //number of pipeline delay cycles in the ssm block (can be determined empirically or analytically by inspecting the ssm block), 1 cycle for mg_dm_ra flopping, 1 cycle for ssefc flopping, 2 cycles for memory read pipeline + ssm_pipeline_delay = 4; + + //number of pipe delay cycles in the obsm block (can be determined empirically or analytically by inspecting the obsm block), 1 cycle for flopping data out + obsm_pipeline_delay = 1; + + //a group of pixels is worth 6 pixels in N422/N420 mode or 3 pixels in all other modes + cycles_per_group = (pixel_format == dml2_n422 || pixel_format == dml2_420) ? 6 : 3; + + //number of groups processed per bcl cycle where a bcl cycle consists of processing the set of syntax elements at the input (1 syntax element in 1 slice config or 2 syntax elements in a 2 or more slice config) + groups_per_bcl_cycle = (num_slices > 1) ? 2 : 1; + + //number of syntax elements per group is 4 in N422 or 3 in all other pixel formats + syntax_elements_per_group = (pixel_format == dml2_n422) ? 4 : 3; + + //delay of the bit stream contruction layer in pixels is the sum of: + //1. number of pixel containers in a slice line multiplied by the number of lines required to reach initial transmit delay multiplied by number of slices to the left of the last horizontal slice + group_delay = (lines_to_reach_ixd_adjusted * slice_width_groups * (num_slices - 1)); + + //2. number of pixel containers required to reach initial transmit delay (specifically, in the last horizontal slice), value of groups_to_reach_ixd_adjusted is multiplied by groups_per_bcl_cycle (processing rate is 1/2 in 2 or more slice config) + group_delay += (lines_to_reach_ixd_adjusted - 1) * slice_width_groups; + group_delay += groups_per_bcl_cycle * (groups_to_reach_ixd_adjusted - ((lines_to_reach_ixd_adjusted - 1) * slice_width_groups)); + + //3. additional group of delay if initial transmit delay is reached exactly in a group + group_delay += additional_group_delay; + + //4. additional group delay if slice width is not evenly divisible by 2, applicable when initial xmit delay is met on the ssm output 1 + if (num_slices >= 2) { + if ((num_slices % 2) == 0 || lines_to_reach_ixd_adjusted % 2 == 0) { + group_delay += (slice_width_groups % 2) != 0; + } + } + + //5. additional 1 group if slice count is 3 or more, slice count is odd, and initial transmit delay is reached on ssm output 0 + if (group_delay_after_initial_xmit_delay_override_en == 0) { + if (num_slices >= 3) { + if ((num_slices % 2) == 1) { + if (lines_to_reach_ixd_adjusted % 2 == 1) { + group_delay += 1; + } + } + } + } else { //use programmed delay + group_delay += group_delay_after_initial_xmit_delay; + + //reduce group delay by 1 if initial xmit delay is reached on the first slice stream + if (num_slices >= 3) { + if ((num_slices % 2) == 1) { + if (lines_to_reach_ixd_adjusted % 2 == 1) { + group_delay -= -1; + } + } + } + } + + //6. ssm and obsm pipeline delay (i.e., clock cycles of delay) + pipeline_delay = ssm_pipeline_delay + obsm_pipeline_delay; + + //pixel delay is group_delay (converted to pixels) + pipeline, however, first group (or first pair) of groups in 1 slice config (or 2 or more slice config) is a special case since it is processed as soon as it arrives (i.e., in syntax_elements_per_group cycles * groups_per_bcl_cycle) + pixel_delay = ((group_delay - groups_per_bcl_cycle) * cycles_per_group) + (syntax_elements_per_group * groups_per_bcl_cycle) + pipeline_delay; + + //map delay values to return data structure + p->groups = group_delay; + p->pipeline = pipeline_delay; + p->pixels = pixel_delay; + + // Extra variables for functional coverage + p->additional_group_delay = additional_group_delay; + p->lines_to_reach_ixd = lines_to_reach_ixd_adjusted; + p->groups_to_reach_ixd = groups_to_reach_ixd_adjusted; + p->slice_width_groups = slice_width_groups; + p->initial_xmit_delay = initial_xmit_delay; + p->number_of_lines_to_reach_ixd = lines_to_reach_ixd; + p->slice_width_modified = slice_width_modified; + + return; +} + +// function to compute input delay (delay from DSC pixel input to DSCCIF output) +// returns the delay +// valid pixel_format = pixel/color format in the set of {dml2_444, dml2_s422, dml2_n422, dml2_420} +static int dsc_compute_input_pixel_delay(enum dml2_output_format_class pixel_format, int num_slices, int dispclk_dynamic_gating_en) +{ + // initialize latency value + int delay = 0; + + // sfr + delay += 2; + + // dscc - vblank clock control for dispclk + if (dispclk_dynamic_gating_en == 1) { + //in N420, pixel containers arrive every other cycle so the downstream ends up getting 2 pixel containers back to back for which the logic can absorb (i.e., hide the stall) + if (pixel_format != dml2_420) { + delay += 1; //1 cycle delay from first set of pixels until the clock turns on + } + } + + // dsccif, delay values for N422/S422 only, no delay for N444/N420 + if (pixel_format == dml2_n422) { + // extra delay required to collect a pair of pixels + delay += 1; + } else if (pixel_format == dml2_s422) { + // extra delay required to collect pixels for interpolation + delay += 4; + } + + //dsccif, base delay in derasterization block + delay += 1; + + //2 slices or more configuration, derasterization enabled + if (num_slices >= 2) { + delay += 4; // ram 1 cycle write, ram 2 cycle read, 1 cycle prefetch buffer + }; + + // return result + return delay; +} + +/* ------------------------------------------------------------------------- + * Output pixel delay — legacy and updated formulas differ here + * ------------------------------------------------------------------------- */ + +// function to compute output pixel delay (delay from DSCCIF output to DSC output) — legacy DCN5 formula +// returns the delay and uncertainty +// valid pixel_format = pixel/color format in the set of {dml2_444, dml2_s422, dml2_n422, dml2_420} +static delay_uncertainty_t legacy_dsc_compute_output_pixel_delay(enum dml2_output_format_class pixel_format, int num_slices, int dscclk_dynamic_gating_en) +{ + // initialize latency value + int dispclk_per_dscclk; + int delay = 0; + int uncertainty = 0; + delay_uncertainty_t delay_uncertainty; + + //1 slice configuration uses a single slice processor (/3 clock) + if (num_slices == 1) { + dispclk_per_dscclk = 3; + } else { //greater than 1 slice configuration uses two slice processor (/6 clock) + dispclk_per_dscclk = 6; + } + + // N422 and N420 process 2x pixels per clock + if (pixel_format == dml2_420 || pixel_format == dml2_n422) { + dispclk_per_dscclk *= 2; + } + + // dscc - input deserializer + //1 slice configuration has a single slice stream, 3 cycles (multiplied by pixels per container) to accumulate 1 group + if (num_slices == 1) { + delay += 3; + + //N422/N420 single slice configuration is a special case; pixel containers arrive every other cycle and data is output on the cycle after every 3rd data transfer + //this causes 2 extra cycles of delay (i.e., not 3) + if (pixel_format == dml2_420 || pixel_format == dml2_n422) { + delay += 2; + } + } else { //greater than 1 slice configration has two slice streams, 6 cycles to accumulate 2 groups + //no need to multiply by pixels per container as first half of first slice line is output faster by dscc_if as + //the second slice stream has no started yet so we do not need to wait for those pixels to appear at the input + delay += 6; + } + + // dscc - input cdc fifo begin + delay += 1; // flop data/update address + uncertainty += 2 * dispclk_per_dscclk; //(2 cycles of transport + metastability delay) * x dscclks per dispclk + uncertainty += 1 * dispclk_per_dscclk; // 1st stage of sync cell flopping has 1 cycle of uncertainty due to clock skew * dscclks per dispclk + delay += 2 * dispclk_per_dscclk; // 2nd and 3rd stage of sync cell flopping have 2 cycles * x dscclks per dispclk + delay += 1 * dispclk_per_dscclk; // 1 flop data out * x dscclks per dispclk + + // dscc - vblank clock control for dscclk + if (dscclk_dynamic_gating_en == 1) { + delay += 1 * dispclk_per_dscclk; //1 cycle delay from first set of pixels until the clock turns on + } + + // dscc_top - engine logic excluded, see other formula + + // dscc - syntax element cdc fifo begin + delay += 1 * dispclk_per_dscclk; // flop data/update address * 6 dscclks per dispclk + uncertainty += 2; // 2 cycles of transport + metastability delay + uncertainty += 1; // 1st stage of sync cell flopping has 1 cycle of uncertainty due to clock skew + delay += 2; // 2nd and 3rd stage of sync cell flopping have 2 cycles * x dscclks per dispclk + delay += 1; // 1 flop data out + + // dscc_bcl - bitstream construction layer logic exclued, see other formula + + // sft + delay += 1; + + // return delay and uncertainty + delay_uncertainty.delay = delay; + delay_uncertainty.uncertainty = uncertainty; + return delay_uncertainty; +} + +// function to compute output pixel delay (delay from DSCCIF output to DSC output) — updated formula (DCN5.1 and newer) +// returns the delay and uncertainty +// valid pixel_format = pixel/color format in the set of {N444, S422, N422, N420} +static delay_uncertainty_t dsc_compute_output_pixel_delay(enum dml2_output_format_class pixel_format, int num_slices, int dscclk_dynamic_gating_en) +{ + // initialize latency value + int dispclk_per_dscclk; + int delay = 0; + int uncertainty = 0; + delay_uncertainty_t delay_uncertainty; + + //1 slice configuration uses a single slice processor (/3 clock) + if (num_slices == 1) { + dispclk_per_dscclk = 3; + } else { //greater than 1 slice configuration uses two slice processor (/6 clock) + dispclk_per_dscclk = 6; + } + + // N422 and N420 process 2x pixels per clock + if (pixel_format == dml2_420 || pixel_format == dml2_n422) { + dispclk_per_dscclk *= 2; + } + + // dscc - input deserializer + //1 slice configuration has a single slice stream, 3 cycles (multiplied by pixels per container) to accumulate 1 group + if (num_slices == 1) { + delay += 3; + + //N422/N420 single slice configuration is a special case; pixel containers arrive every other cycle and data is output on the cycle after every 3rd data transfer + //this causes 2 extra cycles of delay (i.e., not 3) + if (pixel_format == dml2_420 || pixel_format == dml2_n422) { + delay += 2; + } + } else { //greater than 1 slice configration has two slice streams, 6 cycles to accumulate 2 groups + //no need to multiply by pixels per container as first half of first slice line is output faster by dscc_if as + //the second slice stream has no started yet so we do not need to wait for those pixels to appear at the input + //it takes 12 cycles for 6 pairs to arrive + if (pixel_format == dml2_n422) { + delay += 12; + } else { + delay += 6; + } + } + + // dscc - input cdc fifo begin + delay += 1; // flop data/update address + uncertainty += 1 * dispclk_per_dscclk; // 1st stage of sync cell flopping has 1 cycle of uncertainty due to clock skew * dscclks per dispclk + uncertainty += 1 * dispclk_per_dscclk; //(1 cycle metastability delay, src synchronous flop) * x dscclks per dispclk + delay += 2 * dispclk_per_dscclk; // 2nd and 3rd stage of sync cell flopping have 2 cycles * x dscclks per dispclk + + // dscc - vblank clock control for dscclk + if (dscclk_dynamic_gating_en == 1) { + uncertainty += 1 * dispclk_per_dscclk; //(1 cycle metastability delay, src synchronous flop) * x dscclks per dispclk + uncertainty += 1 * dispclk_per_dscclk; // 1st stage of sync cell flopping has 1 cycle of uncertainty due to clock skew * dscclks per dispclk + + //counting this real synchronizer delay as uncertainty because it happens in parallel with dsccif/pixel serializer logic + uncertainty += 2 * dispclk_per_dscclk; // 2nd and 3rd stage of sync cell flopping have 2 cycles * x dscclks per dispclk + uncertainty += 1 * dispclk_per_dscclk; //1 cycle delay from first set of pixels until the clock turns on + } + + // dscc_top - engine logic excluded, see other formula + + // dscc - syntax element cdc fifo begin + delay += 1 * dispclk_per_dscclk; // flop data/update address * 6 dscclks per dispclk + uncertainty += 1; // 1st stage of sync cell flopping has 1 cycle of uncertainty due to clock skew + uncertainty += 1; //1 cycle metastability delay, src synchronous flop + delay += 2; // 2nd and 3rd stage of sync cell flopping have 2 cycles + delay += 1; // 1 flop data out + + // dscc_bcl - bitstream construction layer logic exclued, see other formula + + // sft + delay += 1; + + // return delay and uncertainty + delay_uncertainty.delay = delay; + delay_uncertainty.uncertainty = uncertainty; + return delay_uncertainty; +} + +/* ------------------------------------------------------------------------- + * Public functions + * ------------------------------------------------------------------------- */ + +// dcn5_dsc_compute_delay_legacy - DSC delay using the original DCN5 formula +// valid bpc = source bits per component in the set of {8, 10, 12} +// valid bpp = increments of 1/16 of a bit +// min = 6/7/8 in N420/N422/444, respectively +// max = such that compression is 1:1 +// valid slice_width = number of pixels per slice line, must be less than or equal to `DSC_MAX_WIDTH/num_slices (or 4096/num_slices in 420 mode) +// valid num_slices = number of slices in the horiziontal direction per DSC engine in the set of {1, 2, 3, 4} +// valid pixel_format = pixel/color format in the set of {N444, S422, N422, N420} +void dcn5_dsc_compute_delay_legacy(delay_uncertainty_t *p, int bpc, float bpp, int slice_width, int num_slices, enum dml2_output_format_class pixel_format, int dscclk_dynamic_gating_en, int dispclk_dynamic_gating_en, int initial_xmit_delay_offset, int group_delay_after_initial_xmit_delay_override_en, int group_delay_after_initial_xmit_delay) +{ + // initialize + int total_delay = 0; + int total_uncertainty = 0; + + delay_uncertainty_t delay_uncertainty; + latency_t dscc_bcl_latency; + + // compute input pixel delay + total_delay += dsc_compute_input_pixel_delay(pixel_format, num_slices, dispclk_dynamic_gating_en); + + // dscc_pcl delay + total_delay += dscc_pcl_compute_delay(pixel_format, num_slices); + + // dscc_bcl delay + dscc_bcl_compute_delay(&dscc_bcl_latency, bpc, bpp, slice_width, num_slices, pixel_format, initial_xmit_delay_offset, group_delay_after_initial_xmit_delay_override_en, group_delay_after_initial_xmit_delay); + total_delay += dscc_bcl_latency.pixels; + + // compute output delay + delay_uncertainty = legacy_dsc_compute_output_pixel_delay(pixel_format, num_slices, dscclk_dynamic_gating_en); + + delay_uncertainty.delay += total_delay; + delay_uncertainty.uncertainty += total_uncertainty; + + p->delay = delay_uncertainty.delay; + p->uncertainty = delay_uncertainty.uncertainty; + return; +} + +// dcn5_dsc_compute_delay - DSC delay using the updated formula (DCN5.1 and newer) +// valid bpc = source bits per component in the set of {8, 10, 12} +// valid bpp = increments of 1/16 of a bit +// min = 6/7/8 in N420/N422/444, respectively +// max = such that compression is 1:1 +// valid slice_width = number of pixels per slice line, must be less than or equal to `DSC_MAX_WIDTH/num_slices (or 4096/num_slices in 420 mode) +// valid num_slices = number of slices in the horiziontal direction per DSC engine in the set of {1, 2, 3, 4} +// valid pixel_format = pixel/color format in the set of {N444, S422, N422, N420} +void dcn5_dsc_compute_delay(delay_uncertainty_t *p, int bpc, float bpp, int slice_width, int num_slices, enum dml2_output_format_class pixel_format, int dscclk_dynamic_gating_en, int dispclk_dynamic_gating_en, int initial_xmit_delay_offset, int group_delay_after_initial_xmit_delay_override_en, int group_delay_after_initial_xmit_delay) +{ + // initialize + int total_delay = 0; + int total_uncertainty = 0; + + delay_uncertainty_t delay_uncertainty; + latency_t dscc_bcl_latency; + + // compute input pixel delay + total_delay += dsc_compute_input_pixel_delay(pixel_format, num_slices, dispclk_dynamic_gating_en); + + // dscc_pcl delay + total_delay += dscc_pcl_compute_delay(pixel_format, num_slices); + + // dscc_bcl delay + dscc_bcl_compute_delay(&dscc_bcl_latency, bpc, bpp, slice_width, num_slices, pixel_format, initial_xmit_delay_offset, group_delay_after_initial_xmit_delay_override_en, group_delay_after_initial_xmit_delay); + total_delay += dscc_bcl_latency.pixels; + + // compute output delay + delay_uncertainty = dsc_compute_output_pixel_delay(pixel_format, num_slices, dscclk_dynamic_gating_en); + + delay_uncertainty.delay += total_delay; + delay_uncertainty.uncertainty += total_uncertainty; + + p->delay = delay_uncertainty.delay; + p->uncertainty = delay_uncertainty.uncertainty; + return; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.h new file mode 100644 index 000000000000..796d38cafa38 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN5_CALCS_DSC_LATENCY_H__ +#define __DML2_CORE_DCN5_CALCS_DSC_LATENCY_H__ + +#include "dml2_external_lib_deps.h" +#include "dml2_core_calcs_dsc_shared_types.h" + +// dcn5_dsc_compute_delay - DSC delay using the updated formula (DCN5.1 and newer) +void dcn5_dsc_compute_delay(delay_uncertainty_t *p, int bpc, float bpp, int slice_width, int num_slices, + enum dml2_output_format_class pixel_format, int dscclk_dynamic_gating_en, int dispclk_dynamic_gating_en, + int initial_xmit_delay_offset, int group_delay_after_initial_xmit_delay_override_en, + int group_delay_after_initial_xmit_delay); + +// dcn5_dsc_compute_delay_legacy - DSC delay using the original DCN5 formula +void dcn5_dsc_compute_delay_legacy(delay_uncertainty_t *p, int bpc, float bpp, int slice_width, int num_slices, + enum dml2_output_format_class pixel_format, int dscclk_dynamic_gating_en, int dispclk_dynamic_gating_en, + int initial_xmit_delay_offset, int group_delay_after_initial_xmit_delay_override_en, + int group_delay_after_initial_xmit_delay); + +#endif /* __DML2_CORE_DCN5_CALCS_DSC_LATENCY_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.c new file mode 100644 index 000000000000..31681c7f655e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn5_funcs_initialize.h" +#include "dml2_debug.h" + +static struct dml2_core_ip_params core_dcn5_ip_caps_base = { + 0 +}; + +static void patch_ip_caps_with_explicit_ip_params(struct dml2_ip_capabilities *ip_caps, const struct dml2_core_ip_params *ip_params) +{ + ip_caps->pipe_count = ip_params->max_num_dpp; + ip_caps->otg_count = ip_params->max_num_otg; + ip_caps->num_dsc = ip_params->num_dsc; + ip_caps->max_num_dp2p0_streams = ip_params->max_num_dp2p0_streams; + ip_caps->max_num_dp2p0_outputs = ip_params->max_num_dp2p0_outputs; + ip_caps->max_num_hdmi_frl_outputs = ip_params->max_num_hdmi_frl_outputs; + ip_caps->max_num_wb = ip_params->max_num_wb; + ip_caps->rob_buffer_size_kbytes = ip_params->rob_buffer_size_kbytes; + ip_caps->config_return_buffer_size_in_kbytes = ip_params->config_return_buffer_size_in_kbytes; + ip_caps->config_return_buffer_segment_size_in_kbytes = ip_params->config_return_buffer_segment_size_in_kbytes; + ip_caps->meta_fifo_size_in_kentries = ip_params->meta_fifo_size_in_kentries; + ip_caps->compressed_buffer_segment_size_in_kbytes = ip_params->compressed_buffer_segment_size_in_kbytes; + ip_caps->cursor_buffer_size = ip_params->cursor_buffer_size; + ip_caps->max_flip_time_us = ip_params->max_flip_time_us; + ip_caps->max_flip_time_lines = ip_params->max_flip_time_lines; + ip_caps->hostvm_mode = ip_params->hostvm_mode; + ip_caps->vblank_nom_default_us = ip_params->vblank_nom_default_us; +} + +static void patch_ip_params_with_ip_caps(struct dml2_core_ip_params *ip_params, const struct dml2_ip_capabilities *ip_caps) +{ + ip_params->max_num_dpp = ip_caps->pipe_count; + ip_params->max_num_opp = ip_caps->pipe_count; + ip_params->max_num_otg = ip_caps->otg_count; + ip_params->num_dsc = ip_caps->num_dsc; + ip_params->max_num_dp2p0_streams = ip_caps->max_num_dp2p0_streams; + ip_params->max_num_dp2p0_outputs = ip_caps->max_num_dp2p0_outputs; + ip_params->max_num_hdmi_frl_outputs = ip_caps->max_num_hdmi_frl_outputs; + ip_params->max_num_wb = ip_caps->max_num_wb; + ip_params->rob_buffer_size_kbytes = ip_caps->rob_buffer_size_kbytes; + ip_params->config_return_buffer_size_in_kbytes = ip_caps->config_return_buffer_size_in_kbytes; + ip_params->config_return_buffer_segment_size_in_kbytes = ip_caps->config_return_buffer_segment_size_in_kbytes; + ip_params->meta_fifo_size_in_kentries = ip_caps->meta_fifo_size_in_kentries; + ip_params->compressed_buffer_segment_size_in_kbytes = ip_caps->compressed_buffer_segment_size_in_kbytes; + ip_params->cursor_buffer_size = ip_caps->cursor_buffer_size; + ip_params->max_flip_time_us = ip_caps->max_flip_time_us; + ip_params->max_flip_time_lines = ip_caps->max_flip_time_lines; + ip_params->hostvm_mode = ip_caps->hostvm_mode; + ip_params->vblank_nom_default_us = ip_caps->vblank_nom_default_us; +} + +bool dml2_core_dcn5_funcs_initialize(struct dml2_core_initialize_in_out *in_out) +{ + struct dml2_core_instance *core = in_out->instance; + + DML_LOG_DEBUG("DML_CORE::%s enter\n", __func__); + if (in_out->explicit_ip_bb && in_out->explicit_ip_bb_size > 0) { + memcpy(&core->clean_me_up.mode_lib.ip, in_out->explicit_ip_bb, in_out->explicit_ip_bb_size); + + patch_ip_caps_with_explicit_ip_params(in_out->ip_caps, in_out->explicit_ip_bb); + } else { + memcpy(&core->clean_me_up.mode_lib.ip, &core_dcn5_ip_caps_base, sizeof(struct dml2_core_ip_params)); + patch_ip_params_with_ip_caps(&core->clean_me_up.mode_lib.ip, in_out->ip_caps); + + core->clean_me_up.mode_lib.ip.imall_supported = false; + } + + memcpy(&core->clean_me_up.mode_lib.ip_caps, in_out->ip_caps, sizeof(struct dml2_ip_capabilities)); + core->utm_soc_bb = in_out->utm_soc_bb; + + core->clean_me_up.mode_lib.ip.use_legacy_dsc_delay_formula = + (in_out->project_id != dml2_project_dcn5x_utm); + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.h new file mode 100644 index 000000000000..4a52fcf7222a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_initialize.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN5_FUNCS_INITIALIZE_H__ +#define __DML2_CORE_DCN5_FUNCS_INITIALIZE_H__ +#include "dml2_internal_shared_types.h" + +bool dml2_core_dcn5_funcs_initialize(struct dml2_core_initialize_in_out *in_out); + + +#endif /* __DML2_CORE_DCN5_FUNCS_INITIALIZE_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c new file mode 100644 index 000000000000..8497eaea012e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c @@ -0,0 +1,1791 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn5_funcs_mode_programming.h" +#include "dml2_core_dcn5_calcs_dchub.h" +#include "dml2_core_dcn5_calcs_display_pipe.h" +#include "dml2_core_utils.h" +#include "dml2_debug.h" + +static bool dcn5_mode_programming(struct dml2_core_calcs_mode_programming_ex *in_out_params) +{ + const struct dml2_display_cfg *display_cfg = in_out_params->in_display_cfg; + const struct dml2_utm_soc_bb *utm_soc_bb = in_out_params->utm_soc_bb; + const struct dml2_sop_table *sop_table = &in_out_params->utm_soc_bb->sop_table; + const struct core_display_cfg_support_info *cfg_support_info = in_out_params->cfg_support_info; + struct dml2_core_internal_display_mode_lib *mode_lib = in_out_params->mode_lib; + const struct dml2_display_cfg_programming *programming = in_out_params->programming; + struct dml2_core_calcs_mode_programming_locals *s = &mode_lib->scratch.dml_core_mode_programming_locals; + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *CalculateWatermarks_params = &mode_lib->scratch.CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params; + struct dml2_core_calcs_CalculateVMRowAndSwath_params *CalculateVMRowAndSwath_params = &mode_lib->scratch.CalculateVMRowAndSwath_params; + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *CalculateSwathAndDETConfiguration_params = &mode_lib->scratch.CalculateSwathAndDETConfiguration_params; + struct dml2_core_calcs_CalculateStutterEfficiency_params *CalculateStutterEfficiency_params = &mode_lib->scratch.CalculateStutterEfficiency_params; + struct dml2_core_calcs_CalculatePrefetchSchedule_params *CalculatePrefetchSchedule_params = &mode_lib->scratch.CalculatePrefetchSchedule_params; + struct dml2_core_calcs_calculate_mcache_setting_params *calculate_mcache_setting_params = &mode_lib->scratch.calculate_mcache_setting_params; + struct dml2_core_calcs_calculate_tdlut_setting_params *calculate_tdlut_setting_params = &mode_lib->scratch.calculate_tdlut_setting_params; + struct dml2_core_shared_CalculateMetaAndPTETimes_params *CalculateMetaAndPTETimes_params = &mode_lib->scratch.CalculateMetaAndPTETimes_params; + struct dml2_core_calcs_calculate_peak_bandwidth_required_params *calculate_peak_bandwidth_params = &mode_lib->scratch.calculate_peak_bandwidth_params; + struct dml2_core_calcs_calculate_bytes_to_fetch_required_to_hide_latency_params *calculate_bytes_to_fetch_required_to_hide_latency_params = &mode_lib->scratch.calculate_bytes_to_fetch_required_to_hide_latency_params; + + unsigned int k, j; + bool must_support_iflip; + const long min_return_uclk_cycles = 83; + const long min_return_fclk_cycles = 75; + struct dml2_soc_operating_point max_sop; + struct dml2_soc_operating_point min_sop; + double min_return_latency_in_DCFCLK_cycles = 0; + + DML_LOG_VERBOSE("DML::%s: --- START --- \n", __func__); + + s->num_active_planes = display_cfg->num_planes; + dml2_core_utils_get_stream_output_bpp(s->OutputBpp, display_cfg); + + mode_lib->mp.num_active_pipes = dml2_core_util_get_num_active_pipes(display_cfg->num_planes, cfg_support_info); + dml2_core_utils_pipe_plane_mapping(cfg_support_info, mode_lib->mp.pipe_plane); + + mode_lib->mp.GlobalDPPCLK = programming->min_clocks.dcn4x.dpprefclk_khz / 1000.0; + sop_table->get_max_sop(sop_table, &max_sop); + sop_table->get_min_sop(sop_table, &min_sop); + s->SOCCLK = min_sop.socclk_khz / 1000.0; + + for (k = 0; k < s->num_active_planes; ++k) { + unsigned int stream_index = display_cfg->plane_descriptors[k].stream_index; + DML_ASSERT(cfg_support_info->stream_support_info[stream_index].odms_used <= 4); + DML_ASSERT(cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 4 || + cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 2 || + cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 1); + + if (cfg_support_info->stream_support_info[stream_index].odms_used > 1) + DML_ASSERT(cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 1); + + switch (cfg_support_info->stream_support_info[stream_index].odms_used) { + case (4): + mode_lib->mp.ODMMode[k] = dml2_odm_mode_combine_4to1; + break; + case (3): + mode_lib->mp.ODMMode[k] = dml2_odm_mode_combine_3to1; + break; + case (2): + mode_lib->mp.ODMMode[k] = dml2_odm_mode_combine_2to1; + break; + default: + if (cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 4) + mode_lib->mp.ODMMode[k] = dml2_odm_mode_mso_1to4; + else if (cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 2) + mode_lib->mp.ODMMode[k] = dml2_odm_mode_mso_1to2; + else + mode_lib->mp.ODMMode[k] = dml2_odm_mode_bypass; + break; + } + } + + for (k = 0; k < s->num_active_planes; ++k) { + mode_lib->mp.NoOfDPP[k] = cfg_support_info->plane_support_info[k].dpps_used; + mode_lib->mp.Dppclk[k] = programming->plane_programming[k].min_clocks.dcn4x.dppclk_khz / 1000.0; + DML_ASSERT(mode_lib->mp.Dppclk[k] > 0); + } + + for (k = 0; k < s->num_active_planes; ++k) { + unsigned int stream_index = display_cfg->plane_descriptors[k].stream_index; + mode_lib->mp.DSCCLK[k] = programming->stream_programming[stream_index].min_clocks.dcn4x.dscclk_khz / 1000.0; + DML_LOG_VERBOSE("DML::%s: k=%d stream_index=%d, mode_lib->mp.DSCCLK = %f\n", __func__, k, stream_index, mode_lib->mp.DSCCLK[k]); + } + + mode_lib->mp.Dispclk = programming->min_clocks.dcn4x.dispclk_khz / 1000.0; + mode_lib->mp.DCFCLKDeepSleep = programming->min_clocks.dcn4x.deepsleep_dcfclk_khz / 1000.0; + + memcpy(mode_lib->mp.uclk_pstate_switch_modes, + in_out_params->uclk_params->pstate_switch_modes, + sizeof(in_out_params->uclk_params->pstate_switch_modes)); + + DML_ASSERT(mode_lib->mp.Dcfclk > 0); + DML_ASSERT(mode_lib->mp.FabricClock > 0); + DML_ASSERT(mode_lib->mp.uclk_freq_mhz > 0); + DML_ASSERT(mode_lib->mp.GlobalDPPCLK > 0); + DML_ASSERT(mode_lib->mp.Dispclk > 0); + DML_ASSERT(mode_lib->mp.DCFCLKDeepSleep > 0); + DML_ASSERT(s->SOCCLK > 0); + + DML_LOG_VERBOSE("DML::%s: num_active_planes = %u\n", __func__, s->num_active_planes); + DML_LOG_VERBOSE("DML::%s: num_active_pipes = %u\n", __func__, mode_lib->mp.num_active_pipes); + DML_LOG_VERBOSE("DML::%s: Dcfclk = %f\n", __func__, mode_lib->mp.Dcfclk); + DML_LOG_VERBOSE("DML::%s: FabricClock = %f\n", __func__, mode_lib->mp.FabricClock); + DML_LOG_VERBOSE("DML::%s: uclk_freq_mhz = %f\n", __func__, mode_lib->mp.uclk_freq_mhz); + DML_LOG_VERBOSE("DML::%s: Dispclk = %f\n", __func__, mode_lib->mp.Dispclk); + for (k = 0; k < s->num_active_planes; ++k) { + DML_LOG_VERBOSE("DML::%s: Dppclk[%0d] = %f\n", __func__, k, mode_lib->mp.Dppclk[k]); + } + DML_LOG_VERBOSE("DML::%s: GlobalDPPCLK = %f\n", __func__, mode_lib->mp.GlobalDPPCLK); + DML_LOG_VERBOSE("DML::%s: DCFCLKDeepSleep = %f\n", __func__, mode_lib->mp.DCFCLKDeepSleep); + DML_LOG_VERBOSE("DML::%s: SOCCLK = %f\n", __func__, s->SOCCLK); + for (k = 0; k < mode_lib->mp.num_active_pipes; ++k) { + DML_LOG_VERBOSE("DML::%s: pipe=%d is in plane=%d\n", __func__, k, mode_lib->mp.pipe_plane[k]); + DML_LOG_VERBOSE("DML::%s: Per-plane DPPPerSurface[%0d] = %d\n", __func__, k, mode_lib->mp.NoOfDPP[k]); + } + + for (k = 0; k < s->num_active_planes; k++) + DML_LOG_VERBOSE("DML::%s: plane_%d: reserved_vblank_time_ns = %lu\n", __func__, k, display_cfg->plane_descriptors[k].overrides.reserved_vblank_time_ns); + + dcn5_calculate_max_det_and_min_compressed_buffer_size( + mode_lib->ip.config_return_buffer_size_in_kbytes, + mode_lib->ip.config_return_buffer_segment_size_in_kbytes, + mode_lib->ip.rob_buffer_size_kbytes, + mode_lib->ip.max_num_dpp, + display_cfg->overrides.hw.force_nom_det_size_kbytes.enable, + display_cfg->overrides.hw.force_nom_det_size_kbytes.value, + mode_lib->ip.dcn_mrq_present, + + /* Output */ + &s->MaxTotalDETInKByte, + &s->NomDETInKByte, + &s->MinCompressedBufferSizeInKByte); + + + dcn5_adjust_pixel_clock_for_progressive_to_interlace_unit(display_cfg, mode_lib->ip.ptoi_supported, s->PixelClockBackEnd); + + for (k = 0; k < s->num_active_planes; ++k) { + dcn5_calculate_single_pipe_dppclk_and_scl_throughput( + display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio, + display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio, + display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio, + display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio, + mode_lib->ip.max_dchub_pscl_bw_pix_per_clk, + mode_lib->ip.max_pscl_lb_bw_pix_per_clk, + ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000), + display_cfg->plane_descriptors[k].pixel_format, + display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_taps, + display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_taps, + display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_taps, + display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_taps, + + /* Output */ + &mode_lib->mp.PSCL_THROUGHPUT[k], + &mode_lib->mp.PSCL_THROUGHPUT_CHROMA[k], + &mode_lib->mp.DPPCLKUsingSingleDPP[k]); + } + + for (k = 0; k < s->num_active_planes; ++k) { + dcn5_calculate_byte_per_pixel_and_block_sizes( + display_cfg->plane_descriptors[k].pixel_format, + display_cfg->plane_descriptors[k].surface.tiling, + display_cfg->plane_descriptors[k].surface.plane0.pitch, + display_cfg->plane_descriptors[k].surface.plane1.pitch, + + // Output + &mode_lib->mp.BytePerPixelY[k], + &mode_lib->mp.BytePerPixelC[k], + &mode_lib->mp.BytePerPixelInDETY[k], + &mode_lib->mp.BytePerPixelInDETC[k], + &mode_lib->mp.Read256BlockHeightY[k], + &mode_lib->mp.Read256BlockHeightC[k], + &mode_lib->mp.Read256BlockWidthY[k], + &mode_lib->mp.Read256BlockWidthC[k], + &mode_lib->mp.MacroTileHeightY[k], + &mode_lib->mp.MacroTileHeightC[k], + &mode_lib->mp.MacroTileWidthY[k], + &mode_lib->mp.MacroTileWidthC[k], + &mode_lib->mp.surf_linear128_l[k], + &mode_lib->mp.surf_linear128_c[k]); + } + + dcn5_calculate_swath_width( + display_cfg, + false, // ForceSingleDPP + s->num_active_planes, + mode_lib->mp.ODMMode, + mode_lib->mp.BytePerPixelY, + mode_lib->mp.BytePerPixelC, + mode_lib->mp.Read256BlockHeightY, + mode_lib->mp.Read256BlockHeightC, + mode_lib->mp.Read256BlockWidthY, + mode_lib->mp.Read256BlockWidthC, + mode_lib->mp.surf_linear128_l, + mode_lib->mp.surf_linear128_c, + mode_lib->mp.NoOfDPP, + + /* Output */ + mode_lib->mp.req_per_swath_ub_l, + mode_lib->mp.req_per_swath_ub_c, + mode_lib->mp.SwathWidthSingleDPPY, + mode_lib->mp.SwathWidthSingleDPPC, + mode_lib->mp.SwathWidthY, + mode_lib->mp.SwathWidthC, + s->dummy_integer_array[0], // unsigned int MaximumSwathHeightY[] + s->dummy_integer_array[1], // unsigned int MaximumSwathHeightC[] + mode_lib->mp.swath_width_luma_ub, + mode_lib->mp.swath_width_chroma_ub, + s->dummy_integer_array[2], + s->dummy_integer_array[3]); + + for (k = 0; k < s->num_active_planes; ++k) { + mode_lib->mp.cursor_bw[k] = display_cfg->plane_descriptors[k].cursor.num_cursors * display_cfg->plane_descriptors[k].cursor.cursor_width * display_cfg->plane_descriptors[k].cursor.cursor_bpp / 8.0 / + ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)); + mode_lib->mp.vactive_sw_bw_l[k] = mode_lib->mp.SwathWidthSingleDPPY[k] * mode_lib->mp.BytePerPixelY[k] / (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)) * display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + mode_lib->mp.vactive_sw_bw_c[k] = mode_lib->mp.SwathWidthSingleDPPC[k] * mode_lib->mp.BytePerPixelC[k] / (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)) * display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + DML_LOG_VERBOSE("DML::%s: vactive_sw_bw_l[%i] = %fBps\n", __func__, k, mode_lib->mp.vactive_sw_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: vactive_sw_bw_c[%i] = %fBps\n", __func__, k, mode_lib->mp.vactive_sw_bw_c[k]); + } + + CalculateSwathAndDETConfiguration_params->display_cfg = display_cfg; + CalculateSwathAndDETConfiguration_params->ConfigReturnBufferSizeInKByte = mode_lib->ip.config_return_buffer_size_in_kbytes; + CalculateSwathAndDETConfiguration_params->MaxTotalDETInKByte = s->MaxTotalDETInKByte; + CalculateSwathAndDETConfiguration_params->MinCompressedBufferSizeInKByte = s->MinCompressedBufferSizeInKByte; + CalculateSwathAndDETConfiguration_params->rob_buffer_size_kbytes = mode_lib->ip.rob_buffer_size_kbytes; + CalculateSwathAndDETConfiguration_params->pixel_chunk_size_kbytes = mode_lib->ip.pixel_chunk_size_kbytes; + CalculateSwathAndDETConfiguration_params->rob_buffer_size_kbytes = mode_lib->ip.rob_buffer_size_kbytes; + CalculateSwathAndDETConfiguration_params->pixel_chunk_size_kbytes = mode_lib->ip.pixel_chunk_size_kbytes; + CalculateSwathAndDETConfiguration_params->ForceSingleDPP = false; + CalculateSwathAndDETConfiguration_params->NumberOfActiveSurfaces = s->num_active_planes; + CalculateSwathAndDETConfiguration_params->nomDETInKByte = s->NomDETInKByte; + CalculateSwathAndDETConfiguration_params->ConfigReturnBufferSegmentSizeInkByte = mode_lib->ip.config_return_buffer_segment_size_in_kbytes; + CalculateSwathAndDETConfiguration_params->CompressedBufferSegmentSizeInkByte = mode_lib->ip.compressed_buffer_segment_size_in_kbytes; + CalculateSwathAndDETConfiguration_params->ReadBandwidthLuma = mode_lib->mp.vactive_sw_bw_l; + CalculateSwathAndDETConfiguration_params->ReadBandwidthChroma = mode_lib->mp.vactive_sw_bw_c; + CalculateSwathAndDETConfiguration_params->MaximumSwathWidthLuma = s->dummy_single_array[0]; + CalculateSwathAndDETConfiguration_params->MaximumSwathWidthChroma = s->dummy_single_array[1]; + CalculateSwathAndDETConfiguration_params->Read256BytesBlockHeightY = mode_lib->mp.Read256BlockHeightY; + CalculateSwathAndDETConfiguration_params->Read256BytesBlockHeightC = mode_lib->mp.Read256BlockHeightC; + CalculateSwathAndDETConfiguration_params->Read256BytesBlockWidthY = mode_lib->mp.Read256BlockWidthY; + CalculateSwathAndDETConfiguration_params->Read256BytesBlockWidthC = mode_lib->mp.Read256BlockWidthC; + CalculateSwathAndDETConfiguration_params->surf_linear128_l = mode_lib->mp.surf_linear128_l; + CalculateSwathAndDETConfiguration_params->surf_linear128_c = mode_lib->mp.surf_linear128_c; + CalculateSwathAndDETConfiguration_params->ODMMode = mode_lib->mp.ODMMode; + CalculateSwathAndDETConfiguration_params->DPPPerSurface = mode_lib->mp.NoOfDPP; + CalculateSwathAndDETConfiguration_params->BytePerPixY = mode_lib->mp.BytePerPixelY; + CalculateSwathAndDETConfiguration_params->BytePerPixC = mode_lib->mp.BytePerPixelC; + CalculateSwathAndDETConfiguration_params->BytePerPixDETY = mode_lib->mp.BytePerPixelInDETY; + CalculateSwathAndDETConfiguration_params->BytePerPixDETC = mode_lib->mp.BytePerPixelInDETC; + CalculateSwathAndDETConfiguration_params->mrq_present = mode_lib->ip.dcn_mrq_present; + + // output + CalculateSwathAndDETConfiguration_params->req_per_swath_ub_l = mode_lib->mp.req_per_swath_ub_l; + CalculateSwathAndDETConfiguration_params->req_per_swath_ub_c = mode_lib->mp.req_per_swath_ub_c; + CalculateSwathAndDETConfiguration_params->swath_width_luma_ub = s->dummy_long_array[0]; + CalculateSwathAndDETConfiguration_params->swath_width_chroma_ub = s->dummy_long_array[1]; + CalculateSwathAndDETConfiguration_params->SwathWidth = s->dummy_long_array[2]; + CalculateSwathAndDETConfiguration_params->SwathWidthChroma = s->dummy_long_array[3]; + CalculateSwathAndDETConfiguration_params->SwathHeightY = mode_lib->mp.SwathHeightY; + CalculateSwathAndDETConfiguration_params->SwathHeightC = mode_lib->mp.SwathHeightC; + CalculateSwathAndDETConfiguration_params->request_size_bytes_luma = mode_lib->mp.request_size_bytes_luma; + CalculateSwathAndDETConfiguration_params->request_size_bytes_chroma = mode_lib->mp.request_size_bytes_chroma; + CalculateSwathAndDETConfiguration_params->DETBufferSizeInKByte = mode_lib->mp.DETBufferSizeInKByte; + CalculateSwathAndDETConfiguration_params->DETBufferSizeY = mode_lib->mp.DETBufferSizeY; + CalculateSwathAndDETConfiguration_params->DETBufferSizeC = mode_lib->mp.DETBufferSizeC; + CalculateSwathAndDETConfiguration_params->full_swath_bytes_l = s->full_swath_bytes_l; + CalculateSwathAndDETConfiguration_params->full_swath_bytes_c = s->full_swath_bytes_c; + CalculateSwathAndDETConfiguration_params->full_swath_bytes_single_dpp_l = s->dummy_long_array[4]; + CalculateSwathAndDETConfiguration_params->full_swath_bytes_single_dpp_c = s->dummy_long_array[5]; + CalculateSwathAndDETConfiguration_params->UnboundedRequestEnabled = &mode_lib->mp.UnboundedRequestEnabled; + CalculateSwathAndDETConfiguration_params->compbuf_reserved_space_64b = &mode_lib->mp.compbuf_reserved_space_64b; + CalculateSwathAndDETConfiguration_params->hw_debug5 = &mode_lib->mp.hw_debug5; + CalculateSwathAndDETConfiguration_params->CompressedBufferSizeInkByte = &mode_lib->mp.CompressedBufferSizeInkByte; + CalculateSwathAndDETConfiguration_params->ViewportSizeSupportPerSurface = &s->dummy_boolean_array[0][0]; + CalculateSwathAndDETConfiguration_params->ViewportSizeSupport = &s->dummy_boolean[0]; + + // Calculate DET size, swath height here. + dcn5_calculate_swath_and_det_configuration(&mode_lib->scratch, CalculateSwathAndDETConfiguration_params); + + // DSC Delay + mode_lib->mp.use_legacy_dsc_delay_formula = mode_lib->ip.use_legacy_dsc_delay_formula; + for (k = 0; k < s->num_active_planes; ++k) { + mode_lib->mp.DSCDelay[k] = dcn5_calculate_dsc_delay_requirement(cfg_support_info->stream_support_info[display_cfg->plane_descriptors[k].stream_index].dsc_enable, + mode_lib->mp.ODMMode[k], + mode_lib->ip.maximum_dsc_bits_per_component, + s->OutputBpp[k], + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_active, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total, + cfg_support_info->stream_support_info[display_cfg->plane_descriptors[k].stream_index].num_dsc_slices, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_format, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_encoder, + ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000), + s->PixelClockBackEnd[k], + mode_lib->mp.use_legacy_dsc_delay_formula); + } + + for (k = 0; k < s->num_active_planes; ++k) { + s->SurfaceParameters[k].PixelClock = ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + s->SurfaceParameters[k].DPPPerSurface = mode_lib->mp.NoOfDPP[k]; + s->SurfaceParameters[k].RotationAngle = display_cfg->plane_descriptors[k].composition.rotation_angle; + s->SurfaceParameters[k].ViewportHeight = display_cfg->plane_descriptors[k].composition.viewport.plane0.height; + s->SurfaceParameters[k].ViewportHeightC = display_cfg->plane_descriptors[k].composition.viewport.plane1.height; + s->SurfaceParameters[k].BlockWidth256BytesY = mode_lib->mp.Read256BlockWidthY[k]; + s->SurfaceParameters[k].BlockHeight256BytesY = mode_lib->mp.Read256BlockHeightY[k]; + s->SurfaceParameters[k].BlockWidth256BytesC = mode_lib->mp.Read256BlockWidthC[k]; + s->SurfaceParameters[k].BlockHeight256BytesC = mode_lib->mp.Read256BlockHeightC[k]; + s->SurfaceParameters[k].BlockWidthY = mode_lib->mp.MacroTileWidthY[k]; + s->SurfaceParameters[k].BlockHeightY = mode_lib->mp.MacroTileHeightY[k]; + s->SurfaceParameters[k].BlockWidthC = mode_lib->mp.MacroTileWidthC[k]; + s->SurfaceParameters[k].BlockHeightC = mode_lib->mp.MacroTileHeightC[k]; + s->SurfaceParameters[k].InterlaceEnable = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.interlaced; + s->SurfaceParameters[k].HTotal = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total; + s->SurfaceParameters[k].DCCEnable = display_cfg->plane_descriptors[k].surface.dcc.enable; + s->SurfaceParameters[k].SourcePixelFormat = display_cfg->plane_descriptors[k].pixel_format; + s->SurfaceParameters[k].SurfaceTiling = display_cfg->plane_descriptors[k].surface.tiling; + s->SurfaceParameters[k].BytePerPixelY = mode_lib->mp.BytePerPixelY[k]; + s->SurfaceParameters[k].BytePerPixelC = mode_lib->mp.BytePerPixelC[k]; + s->SurfaceParameters[k].ProgressiveToInterlaceUnitInOPP = mode_lib->ip.ptoi_supported; + s->SurfaceParameters[k].VRatio = display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + s->SurfaceParameters[k].VRatioChroma = display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + s->SurfaceParameters[k].VTaps = display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_taps; + s->SurfaceParameters[k].VTapsChroma = display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_taps; + s->SurfaceParameters[k].PitchY = display_cfg->plane_descriptors[k].surface.plane0.pitch; + s->SurfaceParameters[k].PitchC = display_cfg->plane_descriptors[k].surface.plane1.pitch; + s->SurfaceParameters[k].ViewportStationary = display_cfg->plane_descriptors[k].composition.viewport.stationary; + s->SurfaceParameters[k].ViewportXStart = display_cfg->plane_descriptors[k].composition.viewport.plane0.x_start; + s->SurfaceParameters[k].ViewportYStart = display_cfg->plane_descriptors[k].composition.viewport.plane0.y_start; + s->SurfaceParameters[k].ViewportXStartC = display_cfg->plane_descriptors[k].composition.viewport.plane1.y_start; + s->SurfaceParameters[k].ViewportYStartC = display_cfg->plane_descriptors[k].composition.viewport.plane1.y_start; + s->SurfaceParameters[k].FORCE_ONE_ROW_FOR_FRAME = display_cfg->plane_descriptors[k].overrides.hw.force_one_row_for_frame; + s->SurfaceParameters[k].SwathHeightY = mode_lib->mp.SwathHeightY[k]; + s->SurfaceParameters[k].SwathHeightC = mode_lib->mp.SwathHeightC[k]; + s->SurfaceParameters[k].DCCMetaPitchY = display_cfg->plane_descriptors[k].surface.dcc.plane0.pitch; + s->SurfaceParameters[k].DCCMetaPitchC = display_cfg->plane_descriptors[k].surface.dcc.plane1.pitch; + s->SurfaceParameters[k].UPSPEnabled = display_cfg->plane_descriptors[k].composition.scaler_info.upsp_enabled; + } + + CalculateVMRowAndSwath_params->display_cfg = display_cfg; + CalculateVMRowAndSwath_params->uclk_pstate_switch_modes = mode_lib->ms.uclk_pstate_switch_modes; + CalculateVMRowAndSwath_params->NumberOfActiveSurfaces = s->num_active_planes; + CalculateVMRowAndSwath_params->myPipe = s->SurfaceParameters; + CalculateVMRowAndSwath_params->PTEBufferSizeInRequestsLuma = mode_lib->ip.dpte_buffer_size_in_pte_reqs_luma; + CalculateVMRowAndSwath_params->PTEBufferSizeInRequestsChroma = mode_lib->ip.dpte_buffer_size_in_pte_reqs_chroma; + CalculateVMRowAndSwath_params->SwathWidthY = mode_lib->mp.SwathWidthY; + CalculateVMRowAndSwath_params->SwathWidthC = mode_lib->mp.SwathWidthC; + CalculateVMRowAndSwath_params->DCCMetaBufferSizeBytes = mode_lib->ip.dcc_meta_buffer_size_bytes; + CalculateVMRowAndSwath_params->mrq_present = mode_lib->ip.dcn_mrq_present; + + // output + CalculateVMRowAndSwath_params->PTEBufferSizeNotExceeded = s->dummy_boolean_array[0]; + CalculateVMRowAndSwath_params->dpte_row_width_luma_ub = mode_lib->mp.dpte_row_width_luma_ub; + CalculateVMRowAndSwath_params->dpte_row_width_chroma_ub = mode_lib->mp.dpte_row_width_chroma_ub; + CalculateVMRowAndSwath_params->dpte_row_height_luma = mode_lib->mp.dpte_row_height; + CalculateVMRowAndSwath_params->dpte_row_height_chroma = mode_lib->mp.dpte_row_height_chroma; + CalculateVMRowAndSwath_params->dpte_row_height_linear_luma = mode_lib->mp.dpte_row_height_linear; + CalculateVMRowAndSwath_params->dpte_row_height_linear_chroma = mode_lib->mp.dpte_row_height_linear_chroma; + CalculateVMRowAndSwath_params->vm_group_bytes = mode_lib->mp.vm_group_bytes; + CalculateVMRowAndSwath_params->dpte_group_bytes = mode_lib->mp.dpte_group_bytes; + CalculateVMRowAndSwath_params->PixelPTEReqWidthY = mode_lib->mp.PixelPTEReqWidthY; + CalculateVMRowAndSwath_params->PixelPTEReqHeightY = mode_lib->mp.PixelPTEReqHeightY; + CalculateVMRowAndSwath_params->PTERequestSizeY = mode_lib->mp.PTERequestSizeY; + CalculateVMRowAndSwath_params->PixelPTEReqWidthC = mode_lib->mp.PixelPTEReqWidthC; + CalculateVMRowAndSwath_params->PixelPTEReqHeightC = mode_lib->mp.PixelPTEReqHeightC; + CalculateVMRowAndSwath_params->PTERequestSizeC = mode_lib->mp.PTERequestSizeC; + CalculateVMRowAndSwath_params->vmpg_width_y = s->vmpg_width_y; + CalculateVMRowAndSwath_params->vmpg_height_y = s->vmpg_height_y; + CalculateVMRowAndSwath_params->vmpg_width_c = s->vmpg_width_c; + CalculateVMRowAndSwath_params->vmpg_height_c = s->vmpg_height_c; + CalculateVMRowAndSwath_params->dpde0_bytes_per_frame_ub_l = mode_lib->mp.dpde0_bytes_per_frame_ub_l; + CalculateVMRowAndSwath_params->dpde0_bytes_per_frame_ub_c = mode_lib->mp.dpde0_bytes_per_frame_ub_c; + CalculateVMRowAndSwath_params->PrefetchSourceLinesY = mode_lib->mp.PrefetchSourceLinesY; + CalculateVMRowAndSwath_params->PrefetchSourceLinesC = mode_lib->mp.PrefetchSourceLinesC; + CalculateVMRowAndSwath_params->VInitPreFillY = mode_lib->mp.VInitPreFillY; + CalculateVMRowAndSwath_params->VInitPreFillC = mode_lib->mp.VInitPreFillC; + CalculateVMRowAndSwath_params->MaxNumSwathY = mode_lib->mp.MaxNumSwathY; + CalculateVMRowAndSwath_params->MaxNumSwathC = mode_lib->mp.MaxNumSwathC; + CalculateVMRowAndSwath_params->dpte_row_bw = mode_lib->mp.dpte_row_bw; + CalculateVMRowAndSwath_params->PixelPTEBytesPerRow = mode_lib->mp.PixelPTEBytesPerRow; + CalculateVMRowAndSwath_params->dpte_row_bytes_per_row_l = s->dpte_row_bytes_per_row_l; + CalculateVMRowAndSwath_params->dpte_row_bytes_per_row_c = s->dpte_row_bytes_per_row_c; + CalculateVMRowAndSwath_params->vm_bytes = mode_lib->mp.vm_bytes; + CalculateVMRowAndSwath_params->use_one_row_for_frame = mode_lib->mp.use_one_row_for_frame; + CalculateVMRowAndSwath_params->use_one_row_for_frame_flip = mode_lib->mp.use_one_row_for_frame_flip; + CalculateVMRowAndSwath_params->PTE_BUFFER_MODE = mode_lib->mp.PTE_BUFFER_MODE; + CalculateVMRowAndSwath_params->BIGK_FRAGMENT_SIZE = mode_lib->mp.BIGK_FRAGMENT_SIZE; + CalculateVMRowAndSwath_params->DCCMetaBufferSizeNotExceeded = s->dummy_boolean_array[1]; + CalculateVMRowAndSwath_params->meta_row_bw = mode_lib->mp.meta_row_bw; + CalculateVMRowAndSwath_params->meta_row_bytes = mode_lib->mp.meta_row_bytes; + CalculateVMRowAndSwath_params->meta_row_bytes_per_row_ub_l = s->meta_row_bytes_per_row_ub_l; + CalculateVMRowAndSwath_params->meta_row_bytes_per_row_ub_c = s->meta_row_bytes_per_row_ub_c; + CalculateVMRowAndSwath_params->meta_req_width_luma = mode_lib->mp.meta_req_width; + CalculateVMRowAndSwath_params->meta_req_height_luma = mode_lib->mp.meta_req_height; + CalculateVMRowAndSwath_params->meta_row_width_luma = mode_lib->mp.meta_row_width; + CalculateVMRowAndSwath_params->meta_row_height_luma = mode_lib->mp.meta_row_height; + CalculateVMRowAndSwath_params->meta_pte_bytes_per_frame_ub_l = mode_lib->mp.meta_pte_bytes_per_frame_ub_l; + CalculateVMRowAndSwath_params->meta_req_width_chroma = mode_lib->mp.meta_req_width_chroma; + CalculateVMRowAndSwath_params->meta_row_height_chroma = mode_lib->mp.meta_row_height_chroma; + CalculateVMRowAndSwath_params->meta_row_width_chroma = mode_lib->mp.meta_row_width_chroma; + CalculateVMRowAndSwath_params->meta_req_height_chroma = mode_lib->mp.meta_req_height_chroma; + CalculateVMRowAndSwath_params->meta_pte_bytes_per_frame_ub_c = mode_lib->mp.meta_pte_bytes_per_frame_ub_c; + + dcn5_calculate_vm_row_and_swath(&mode_lib->scratch, CalculateVMRowAndSwath_params); + + memset(calculate_mcache_setting_params, 0, sizeof(struct dml2_core_calcs_calculate_mcache_setting_params)); + for (k = 0; k < s->num_active_planes; k++) { + mode_lib->mp.dcc_dram_bw_nom_overhead_factor_p0[k] = 1.0; + mode_lib->mp.dcc_dram_bw_pref_overhead_factor_p0[k] = 1.0; + mode_lib->mp.dcc_dram_bw_nom_overhead_factor_p1[k] = 1.0; + mode_lib->mp.dcc_dram_bw_pref_overhead_factor_p1[k] = 1.0; + } + + dcn5_calculate_hostvm_inefficiency_factor( + &s->HostVMInefficiencyFactor, + &s->HostVMInefficiencyFactorPrefetch, + + display_cfg->gpuvm_enable, + display_cfg->hostvm_enable, + mode_lib->ip.remote_iommu_outstanding_translations, + utm_soc_bb->max_outstanding_reqs, + 1.0, + 0.5); + + s->TotalDCCActiveDPP = 0; + s->TotalActiveDPP = 0; + for (k = 0; k < s->num_active_planes; ++k) { + s->TotalActiveDPP = s->TotalActiveDPP + mode_lib->mp.NoOfDPP[k]; + if (display_cfg->plane_descriptors[k].surface.dcc.enable) + s->TotalDCCActiveDPP = s->TotalDCCActiveDPP + mode_lib->mp.NoOfDPP[k]; + } + // Calculate tdlut schedule related terms + for (k = 0; k <= s->num_active_planes - 1; k++) { + calculate_tdlut_setting_params->dispclk_mhz = mode_lib->mp.Dispclk; + calculate_tdlut_setting_params->setup_for_tdlut = display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut; + calculate_tdlut_setting_params->tdlut_width_mode = display_cfg->plane_descriptors[k].tdlut.tdlut_width_mode; + calculate_tdlut_setting_params->tdlut_addressing_mode = display_cfg->plane_descriptors[k].tdlut.tdlut_addressing_mode; + calculate_tdlut_setting_params->cursor_buffer_size = mode_lib->ip.cursor_buffer_size; + calculate_tdlut_setting_params->gpuvm_enable = display_cfg->gpuvm_enable; + calculate_tdlut_setting_params->gpuvm_page_size_kbytes = display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes; + + // output + calculate_tdlut_setting_params->tdlut_pte_bytes_per_frame = &s->tdlut_pte_bytes_per_frame[k]; + calculate_tdlut_setting_params->tdlut_bytes_per_frame = &s->tdlut_bytes_per_frame[k]; + calculate_tdlut_setting_params->tdlut_groups_per_2row_ub = &s->tdlut_groups_per_2row_ub[k]; + calculate_tdlut_setting_params->tdlut_opt_time = &s->tdlut_opt_time[k]; + calculate_tdlut_setting_params->tdlut_drain_time = &s->tdlut_drain_time[k]; + calculate_tdlut_setting_params->tdlut_bytes_per_group = &s->tdlut_bytes_per_group[k]; + + dcn5_calculate_tdlut_setting(&mode_lib->scratch, calculate_tdlut_setting_params); + } + + dcn5_calculate_extra_latency( + display_cfg, + mode_lib->ip.rob_buffer_size_kbytes, + 0, + s->ReorderingBytes, + mode_lib->mp.Dcfclk, + mode_lib->mp.FabricClock, + mode_lib->ip.pixel_chunk_size_kbytes, + mode_lib->mp.dram_bw_mbps, + s->num_active_planes, + mode_lib->mp.NoOfDPP, + mode_lib->mp.dpte_group_bytes, + s->tdlut_bytes_per_group, + s->HostVMInefficiencyFactor, + s->HostVMInefficiencyFactorPrefetch, + dml2_qos_param_type_dcn4x, + !(display_cfg->overrides.max_outstanding_when_urgent_expected_disable), + utm_soc_bb->max_outstanding_reqs, + mode_lib->mp.request_size_bytes_luma, + mode_lib->mp.request_size_bytes_chroma, + mode_lib->ip.meta_chunk_size_kbytes, + mode_lib->ip.dchub_arb_to_ret_delay, + mode_lib->mp.TripToMemory, + mode_lib->ip.hostvm_mode, + + // output + &mode_lib->mp.ExtraLatency, + &mode_lib->mp.ExtraLatency_sr, + &mode_lib->mp.ExtraLatencyPrefetch); + + mode_lib->mp.TCalc = 24.0 / mode_lib->mp.DCFCLKDeepSleep; + + for (k = 0; k < s->num_active_planes; ++k) { + mode_lib->mp.WritebackDelay[k] = 0.0; + for (j = 0; j < display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream; ++j) { + mode_lib->mp.WritebackDelay[k] = math_max2(mode_lib->mp.WritebackDelay[k], + utm_soc_bb->writeback_base_latency_us + + dcn5_calculate_write_back_delay( + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].h_ratio, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].v_ratio, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].v_taps, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].v_taps_chroma, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].output_width, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].output_height, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].input_width, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].input_height, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total) + / mode_lib->mp.Dispclk); + } + } + + /* VActive bytes to fetch for UCLK P-State */ + calculate_bytes_to_fetch_required_to_hide_latency_params->display_cfg = display_cfg; + calculate_bytes_to_fetch_required_to_hide_latency_params->mrq_present = mode_lib->ip.dcn_mrq_present; + + calculate_bytes_to_fetch_required_to_hide_latency_params->num_active_planes = s->num_active_planes; + calculate_bytes_to_fetch_required_to_hide_latency_params->num_of_dpp = mode_lib->mp.NoOfDPP; + calculate_bytes_to_fetch_required_to_hide_latency_params->meta_row_height_l = mode_lib->mp.meta_row_height; + calculate_bytes_to_fetch_required_to_hide_latency_params->meta_row_height_c = mode_lib->mp.meta_row_height_chroma; + calculate_bytes_to_fetch_required_to_hide_latency_params->meta_row_bytes_per_row_ub_l = s->meta_row_bytes_per_row_ub_l; + calculate_bytes_to_fetch_required_to_hide_latency_params->meta_row_bytes_per_row_ub_c = s->meta_row_bytes_per_row_ub_c; + calculate_bytes_to_fetch_required_to_hide_latency_params->dpte_row_height_l = mode_lib->mp.dpte_row_height; + calculate_bytes_to_fetch_required_to_hide_latency_params->dpte_row_height_c = mode_lib->mp.dpte_row_height_chroma; + calculate_bytes_to_fetch_required_to_hide_latency_params->dpte_bytes_per_row_l = s->dpte_row_bytes_per_row_l; + calculate_bytes_to_fetch_required_to_hide_latency_params->dpte_bytes_per_row_c = s->dpte_row_bytes_per_row_c; + calculate_bytes_to_fetch_required_to_hide_latency_params->byte_per_pix_l = mode_lib->mp.BytePerPixelY; + calculate_bytes_to_fetch_required_to_hide_latency_params->byte_per_pix_c = mode_lib->mp.BytePerPixelC; + calculate_bytes_to_fetch_required_to_hide_latency_params->swath_width_l = mode_lib->mp.SwathWidthY; + calculate_bytes_to_fetch_required_to_hide_latency_params->swath_width_c = mode_lib->mp.SwathWidthC; + calculate_bytes_to_fetch_required_to_hide_latency_params->swath_height_l = mode_lib->mp.SwathHeightY; + calculate_bytes_to_fetch_required_to_hide_latency_params->swath_height_c = mode_lib->mp.SwathHeightC; + for (k = 0; k < s->num_active_planes; ++k) { + calculate_bytes_to_fetch_required_to_hide_latency_params->latency_to_hide_us[k] = utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + } + + /* outputs */ + calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_l = s->pstate_bytes_required_l[dml2_pstate_type_uclk]; + calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_c = s->pstate_bytes_required_c[dml2_pstate_type_uclk]; + + dcn5_calculate_bytes_to_fetch_required_to_hide_latency(calculate_bytes_to_fetch_required_to_hide_latency_params); + + /* Excess VActive bandwidth required to fill DET */ + dcn5_calculate_excess_vactive_bandwidth_required( + display_cfg, + s->num_active_planes, + s->pstate_bytes_required_l[dml2_pstate_type_uclk], + s->pstate_bytes_required_c[dml2_pstate_type_uclk], + /* outputs */ + mode_lib->mp.excess_vactive_fill_bw_l, + mode_lib->mp.excess_vactive_fill_bw_c); + + mode_lib->mp.TripToMemory = math_max2(mode_lib->mp.UrgentLatency, mode_lib->mp.TripToMemory); + + for (k = 0; k < s->num_active_planes; ++k) { + bool cursor_not_enough_urgent_latency_hiding = 0; + double line_time_us; + + dcn5_calculate_cursor_req_attributes( + display_cfg->plane_descriptors[k].cursor.cursor_width, + display_cfg->plane_descriptors[k].cursor.cursor_bpp, + + // output + &s->cursor_lines_per_chunk[k], + &s->cursor_bytes_per_line[k], + &s->cursor_bytes_per_chunk[k], + &s->cursor_bytes[k]); + + line_time_us = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + + dcn5_calculate_cursor_urgent_burst_factor( + mode_lib->ip.cursor_buffer_size, + display_cfg->plane_descriptors[k].cursor.cursor_width, + s->cursor_bytes_per_chunk[k], + s->cursor_lines_per_chunk[k], + line_time_us, + mode_lib->mp.UrgentLatency, + + // output + &mode_lib->mp.UrgentBurstFactorCursor[k], + &cursor_not_enough_urgent_latency_hiding); + mode_lib->mp.UrgentBurstFactorCursorPre[k] = mode_lib->mp.UrgentBurstFactorCursor[k]; + + dcn5_calculate_urgent_burst_factor( + &display_cfg->plane_descriptors[k], + mode_lib->mp.swath_width_luma_ub[k], + mode_lib->mp.swath_width_chroma_ub[k], + mode_lib->mp.SwathHeightY[k], + mode_lib->mp.SwathHeightC[k], + line_time_us, + mode_lib->mp.UrgentLatency, + display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio, + display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio, + mode_lib->mp.BytePerPixelInDETY[k], + mode_lib->mp.BytePerPixelInDETC[k], + mode_lib->mp.DETBufferSizeY[k], + mode_lib->mp.DETBufferSizeC[k], + + /* output */ + &mode_lib->mp.UrgentBurstFactorLuma[k], + &mode_lib->mp.UrgentBurstFactorChroma[k], + &mode_lib->mp.NotEnoughUrgentLatencyHiding[k]); + + mode_lib->mp.NotEnoughUrgentLatencyHiding[k] = mode_lib->mp.NotEnoughUrgentLatencyHiding[k] || cursor_not_enough_urgent_latency_hiding; + } + + for (k = 0; k < s->num_active_planes; ++k) { + s->MaxVStartupLines[k] = dcn5_calculate_max_vstartup( + mode_lib->ip.ptoi_supported, + mode_lib->ip.vblank_nom_default_us, + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing, + mode_lib->mp.WritebackDelay[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaxVStartupLines = %u\n", __func__, k, s->MaxVStartupLines[k]); + DML_LOG_VERBOSE("DML::%s: k=%u WritebackDelay = %f\n", __func__, k, mode_lib->mp.WritebackDelay[k]); + } + + s->immediate_flip_required = false; + for (k = 0; k < s->num_active_planes; ++k) { + s->immediate_flip_required = s->immediate_flip_required || display_cfg->plane_descriptors[k].immediate_flip; + } + DML_LOG_VERBOSE("DML::%s: immediate_flip_required = %u\n", __func__, s->immediate_flip_required); + + { + s->DestinationLineTimesForPrefetchLessThan2 = false; + s->VRatioPrefetchMoreThanMax = false; + + DML_LOG_VERBOSE("DML::%s: Start one iteration of prefetch schedule evaluation\n", __func__); + + for (k = 0; k < s->num_active_planes; ++k) { + struct dml2_core_internal_DmlPipe *myPipe = &s->myPipe; + + DML_LOG_VERBOSE("DML::%s: k=%d MaxVStartupLines = %u\n", __func__, k, s->MaxVStartupLines[k]); + mode_lib->mp.TWait[k] = dcn5_calculate_t_wait( + display_cfg->plane_descriptors[k].overrides.reserved_vblank_time_ns, + mode_lib->mp.UrgentLatency, + mode_lib->mp.TripToMemory, + utm_soc_bb->power_management_parameters.g7_ppt_blackout_us, + display_cfg->stream_descriptors->timing.drr_config.enabled); + + myPipe->Dppclk = mode_lib->mp.Dppclk[k]; + myPipe->Dispclk = mode_lib->mp.Dispclk; + myPipe->PixelClock = ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + myPipe->DCFClkDeepSleep = mode_lib->mp.DCFCLKDeepSleep; + myPipe->DPPPerSurface = mode_lib->mp.NoOfDPP[k]; + myPipe->ScalerEnabled = display_cfg->plane_descriptors[k].composition.scaler_info.enabled; + myPipe->VRatio = display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + myPipe->VRatioChroma = display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + myPipe->VTaps = display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_taps; + myPipe->VTapsChroma = display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_taps; + myPipe->RotationAngle = display_cfg->plane_descriptors[k].composition.rotation_angle; + myPipe->mirrored = display_cfg->plane_descriptors[k].composition.mirrored; + myPipe->BlockWidth256BytesY = mode_lib->mp.Read256BlockWidthY[k]; + myPipe->BlockHeight256BytesY = mode_lib->mp.Read256BlockHeightY[k]; + myPipe->BlockWidth256BytesC = mode_lib->mp.Read256BlockWidthC[k]; + myPipe->BlockHeight256BytesC = mode_lib->mp.Read256BlockHeightC[k]; + myPipe->InterlaceEnable = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.interlaced; + myPipe->NumberOfCursors = display_cfg->plane_descriptors[k].cursor.num_cursors; + myPipe->VBlank = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_active; + myPipe->HTotal = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total; + myPipe->HActive = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_active; + myPipe->DCCEnable = display_cfg->plane_descriptors[k].surface.dcc.enable; + myPipe->ODMMode = mode_lib->mp.ODMMode[k]; + myPipe->SourcePixelFormat = display_cfg->plane_descriptors[k].pixel_format; + myPipe->BytePerPixelY = mode_lib->mp.BytePerPixelY[k]; + myPipe->BytePerPixelC = mode_lib->mp.BytePerPixelC[k]; + myPipe->ProgressiveToInterlaceUnitInOPP = mode_lib->ip.ptoi_supported; + DML_LOG_VERBOSE("DML::%s: Calling CalculatePrefetchSchedule for k=%u\n", __func__, k); + + CalculatePrefetchSchedule_params->display_cfg = display_cfg; + CalculatePrefetchSchedule_params->HostVMInefficiencyFactor = s->HostVMInefficiencyFactorPrefetch; + CalculatePrefetchSchedule_params->myPipe = myPipe; + CalculatePrefetchSchedule_params->DSCDelay = mode_lib->mp.DSCDelay[k]; + CalculatePrefetchSchedule_params->DPPCLKDelaySubtotalPlusCNVCFormater = mode_lib->ip.dppclk_delay_subtotal + mode_lib->ip.dppclk_delay_cnvc_formatter; + CalculatePrefetchSchedule_params->DPPCLKDelaySCL = mode_lib->ip.dppclk_delay_scl; + CalculatePrefetchSchedule_params->DPPCLKDelaySCLLBOnly = mode_lib->ip.dppclk_delay_scl_lb_only; + CalculatePrefetchSchedule_params->DPPCLKDelayCNVCCursor = mode_lib->ip.dppclk_delay_cnvc_cursor; + CalculatePrefetchSchedule_params->DISPCLKDelaySubtotal = mode_lib->ip.dispclk_delay_subtotal; + CalculatePrefetchSchedule_params->DPP_RECOUT_WIDTH = (unsigned int)(mode_lib->mp.SwathWidthY[k] / display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio); + CalculatePrefetchSchedule_params->OutputFormat = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_format; + CalculatePrefetchSchedule_params->MaxInterDCNTileRepeaters = mode_lib->ip.max_inter_dcn_tile_repeaters; + CalculatePrefetchSchedule_params->VStartup = s->MaxVStartupLines[k]; + CalculatePrefetchSchedule_params->HostVMMinPageSize = display_cfg->plane_descriptors[k].overrides.hostvm_min_page_size_kbytes; + CalculatePrefetchSchedule_params->DynamicMetadataEnable = display_cfg->plane_descriptors[k].dynamic_meta_data.enable; + CalculatePrefetchSchedule_params->DynamicMetadataVMEnabled = mode_lib->ip.dynamic_metadata_vm_enabled; + CalculatePrefetchSchedule_params->DynamicMetadataLinesBeforeActiveRequired = display_cfg->plane_descriptors[k].dynamic_meta_data.lines_before_active_required; + CalculatePrefetchSchedule_params->DynamicMetadataTransmittedBytes = display_cfg->plane_descriptors[k].dynamic_meta_data.transmitted_bytes; + CalculatePrefetchSchedule_params->ExtraLatencyPrefetch = mode_lib->mp.ExtraLatencyPrefetch; + CalculatePrefetchSchedule_params->TCalc = mode_lib->mp.TCalc; + CalculatePrefetchSchedule_params->vm_bytes = mode_lib->mp.vm_bytes[k]; + CalculatePrefetchSchedule_params->PixelPTEBytesPerRow = mode_lib->mp.PixelPTEBytesPerRow[k]; + CalculatePrefetchSchedule_params->PrefetchSourceLinesY = mode_lib->mp.PrefetchSourceLinesY[k]; + CalculatePrefetchSchedule_params->VInitPreFillY = mode_lib->mp.VInitPreFillY[k]; + CalculatePrefetchSchedule_params->MaxNumSwathY = mode_lib->mp.MaxNumSwathY[k]; + CalculatePrefetchSchedule_params->PrefetchSourceLinesC = mode_lib->mp.PrefetchSourceLinesC[k]; + CalculatePrefetchSchedule_params->VInitPreFillC = mode_lib->mp.VInitPreFillC[k]; + CalculatePrefetchSchedule_params->MaxNumSwathC = mode_lib->mp.MaxNumSwathC[k]; + CalculatePrefetchSchedule_params->swath_width_luma_ub = mode_lib->mp.swath_width_luma_ub[k]; + CalculatePrefetchSchedule_params->swath_width_chroma_ub = mode_lib->mp.swath_width_chroma_ub[k]; + CalculatePrefetchSchedule_params->SwathHeightY = mode_lib->mp.SwathHeightY[k]; + CalculatePrefetchSchedule_params->SwathHeightC = mode_lib->mp.SwathHeightC[k]; + CalculatePrefetchSchedule_params->TWait = mode_lib->mp.TWait[k]; + CalculatePrefetchSchedule_params->Ttrip = mode_lib->mp.TripToMemory; + CalculatePrefetchSchedule_params->Turg = mode_lib->mp.UrgentLatency; + CalculatePrefetchSchedule_params->setup_for_tdlut = display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut; + CalculatePrefetchSchedule_params->tdlut_pte_bytes_per_frame = s->tdlut_pte_bytes_per_frame[k]; + CalculatePrefetchSchedule_params->tdlut_bytes_per_frame = s->tdlut_bytes_per_frame[k]; + CalculatePrefetchSchedule_params->tdlut_opt_time = s->tdlut_opt_time[k]; + CalculatePrefetchSchedule_params->tdlut_drain_time = s->tdlut_drain_time[k]; + CalculatePrefetchSchedule_params->num_cursors = (display_cfg->plane_descriptors[k].cursor.cursor_width > 0); + CalculatePrefetchSchedule_params->cursor_bytes_per_chunk = s->cursor_bytes_per_chunk[k]; + CalculatePrefetchSchedule_params->cursor_bytes_per_line = s->cursor_bytes_per_line[k]; + CalculatePrefetchSchedule_params->dcc_enable = display_cfg->plane_descriptors[k].surface.dcc.enable; + CalculatePrefetchSchedule_params->mrq_present = mode_lib->ip.dcn_mrq_present; + CalculatePrefetchSchedule_params->meta_row_bytes = mode_lib->mp.meta_row_bytes[k]; + + // output + CalculatePrefetchSchedule_params->DSTXAfterScaler = &mode_lib->mp.DSTXAfterScaler[k]; + CalculatePrefetchSchedule_params->DSTYAfterScaler = &mode_lib->mp.DSTYAfterScaler[k]; + CalculatePrefetchSchedule_params->dst_y_prefetch = &mode_lib->mp.dst_y_prefetch[k]; + CalculatePrefetchSchedule_params->dst_y_per_vm_vblank = &mode_lib->mp.dst_y_per_vm_vblank[k]; + CalculatePrefetchSchedule_params->dst_y_per_row_vblank = &mode_lib->mp.dst_y_per_row_vblank[k]; + CalculatePrefetchSchedule_params->VRatioPrefetchY = &mode_lib->mp.VRatioPrefetchY[k]; + CalculatePrefetchSchedule_params->VRatioPrefetchC = &mode_lib->mp.VRatioPrefetchC[k]; + CalculatePrefetchSchedule_params->RequiredPrefetchPixelDataBWLuma = &mode_lib->mp.RequiredPrefetchPixelDataBWLuma[k]; + CalculatePrefetchSchedule_params->RequiredPrefetchPixelDataBWChroma = &mode_lib->mp.RequiredPrefetchPixelDataBWChroma[k]; + CalculatePrefetchSchedule_params->NotEnoughTimeForDynamicMetadata = &mode_lib->mp.NotEnoughTimeForDynamicMetadata[k]; + CalculatePrefetchSchedule_params->Tno_bw = &mode_lib->mp.Tno_bw[k]; + CalculatePrefetchSchedule_params->Tno_bw_flip = &mode_lib->mp.Tno_bw_flip[k]; + CalculatePrefetchSchedule_params->prefetch_vmrow_bw = &mode_lib->mp.prefetch_vmrow_bw[k]; + CalculatePrefetchSchedule_params->Tdmdl_vm = &mode_lib->mp.Tdmdl_vm[k]; + CalculatePrefetchSchedule_params->Tdmdl = &mode_lib->mp.Tdmdl[k]; + CalculatePrefetchSchedule_params->TSetup = &mode_lib->mp.TSetup[k]; + CalculatePrefetchSchedule_params->Tvm_trips = &s->Tvm_trips[k]; + CalculatePrefetchSchedule_params->Tr0_trips = &s->Tr0_trips[k]; + CalculatePrefetchSchedule_params->Tvm_trips_flip = &s->Tvm_trips_flip[k]; + CalculatePrefetchSchedule_params->Tr0_trips_flip = &s->Tr0_trips_flip[k]; + CalculatePrefetchSchedule_params->Tvm_trips_flip_rounded = &s->Tvm_trips_flip_rounded[k]; + CalculatePrefetchSchedule_params->Tr0_trips_flip_rounded = &s->Tr0_trips_flip_rounded[k]; + CalculatePrefetchSchedule_params->VUpdateOffsetPix = &mode_lib->mp.VUpdateOffsetPix[k]; + CalculatePrefetchSchedule_params->VUpdateWidthPix = &mode_lib->mp.VUpdateWidthPix[k]; + CalculatePrefetchSchedule_params->VReadyOffsetPix = &mode_lib->mp.VReadyOffsetPix[k]; + CalculatePrefetchSchedule_params->prefetch_cursor_bw = &mode_lib->mp.prefetch_cursor_bw[k]; + CalculatePrefetchSchedule_params->prefetch_sw_bytes = &s->prefetch_sw_bytes[k]; + CalculatePrefetchSchedule_params->Tpre_rounded = &s->Tpre_rounded[k]; + CalculatePrefetchSchedule_params->Tpre_oto = &s->Tpre_oto[k]; + + mode_lib->mp.NoTimeToPrefetch[k] = dcn5_calculate_prefetch_schedule(&mode_lib->scratch, CalculatePrefetchSchedule_params); + DML_LOG_VERBOSE("DML::%s: k=%0u NoTimeToPrefetch=%0d\n", __func__, k, mode_lib->mp.NoTimeToPrefetch[k]); + mode_lib->mp.VStartupMin[k] = s->MaxVStartupLines[k]; + } // for k + + mode_lib->mp.PrefetchModeSupported = true; + for (k = 0; k < s->num_active_planes; ++k) { + if (mode_lib->mp.NoTimeToPrefetch[k] == true || + mode_lib->mp.NotEnoughTimeForDynamicMetadata[k] || + mode_lib->mp.DSTYAfterScaler[k] > 8) { + DML_LOG_VERBOSE("DML::%s: k=%u, NoTimeToPrefetch = %0d\n", __func__, k, mode_lib->mp.NoTimeToPrefetch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, NotEnoughTimeForDynamicMetadata=%u\n", __func__, k, mode_lib->mp.NotEnoughTimeForDynamicMetadata[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, DSTYAfterScaler=%u (should be <= 0)\n", __func__, k, mode_lib->mp.DSTYAfterScaler[k]); + mode_lib->mp.PrefetchModeSupported = false; + } + if (mode_lib->mp.dst_y_prefetch[k] < 2) + s->DestinationLineTimesForPrefetchLessThan2 = true; + + if (mode_lib->mp.VRatioPrefetchY[k] > __DML2_CALCS_MAX_VRATIO_PRE__ || + mode_lib->mp.VRatioPrefetchC[k] > __DML2_CALCS_MAX_VRATIO_PRE__) { + s->VRatioPrefetchMoreThanMax = true; + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioPrefetchY=%f (should not be < %f)\n", __func__, k, mode_lib->mp.VRatioPrefetchY[k], __DML2_CALCS_MAX_VRATIO_PRE__); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioPrefetchC=%f (should not be < %f)\n", __func__, k, mode_lib->mp.VRatioPrefetchC[k], __DML2_CALCS_MAX_VRATIO_PRE__); + DML_LOG_VERBOSE("DML::%s: VRatioPrefetchMoreThanMax = %u\n", __func__, s->VRatioPrefetchMoreThanMax); + } + + if (mode_lib->mp.NotEnoughUrgentLatencyHiding[k]) { + DML_LOG_VERBOSE("DML::%s: k=%u, NotEnoughUrgentLatencyHiding = %u\n", __func__, k, mode_lib->mp.NotEnoughUrgentLatencyHiding[k]); + mode_lib->mp.PrefetchModeSupported = false; + } + } + + if (s->VRatioPrefetchMoreThanMax == true || s->DestinationLineTimesForPrefetchLessThan2 == true) { + DML_LOG_VERBOSE("DML::%s: VRatioPrefetchMoreThanMax = %u\n", __func__, s->VRatioPrefetchMoreThanMax); + DML_LOG_VERBOSE("DML::%s: DestinationLineTimesForPrefetchLessThan2 = %u\n", __func__, s->DestinationLineTimesForPrefetchLessThan2); + mode_lib->mp.PrefetchModeSupported = false; + } + + DML_LOG_VERBOSE("DML::%s: Prefetch schedule is %sOK at vstartup = %u\n", __func__, + mode_lib->mp.PrefetchModeSupported ? "" : "NOT ", CalculatePrefetchSchedule_params->VStartup); + + // Prefetch schedule OK, now check prefetch bw + if (mode_lib->mp.PrefetchModeSupported == true) { + for (k = 0; k < s->num_active_planes; ++k) { + double line_time_us = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / + ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + dcn5_calculate_urgent_burst_factor( + &display_cfg->plane_descriptors[k], + mode_lib->mp.swath_width_luma_ub[k], + mode_lib->mp.swath_width_chroma_ub[k], + mode_lib->mp.SwathHeightY[k], + mode_lib->mp.SwathHeightC[k], + line_time_us, + mode_lib->mp.UrgentLatency, + mode_lib->mp.VRatioPrefetchY[k], + mode_lib->mp.VRatioPrefetchC[k], + mode_lib->mp.BytePerPixelInDETY[k], + mode_lib->mp.BytePerPixelInDETC[k], + mode_lib->mp.DETBufferSizeY[k], + mode_lib->mp.DETBufferSizeC[k], + /* Output */ + &mode_lib->mp.UrgentBurstFactorLumaPre[k], + &mode_lib->mp.UrgentBurstFactorChromaPre[k], + &mode_lib->mp.NotEnoughUrgentLatencyHidingPre[k]); + + DML_LOG_VERBOSE("DML::%s: k=%0u DPPPerSurface=%u\n", __func__, k, mode_lib->mp.NoOfDPP[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u UrgentBurstFactorLuma=%f\n", __func__, k, mode_lib->mp.UrgentBurstFactorLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u UrgentBurstFactorChroma=%f\n", __func__, k, mode_lib->mp.UrgentBurstFactorChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u UrgentBurstFactorLumaPre=%f\n", __func__, k, mode_lib->mp.UrgentBurstFactorLumaPre[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u UrgentBurstFactorChromaPre=%f\n", __func__, k, mode_lib->mp.UrgentBurstFactorChromaPre[k]); + + DML_LOG_VERBOSE("DML::%s: k=%0u VRatioPrefetchY=%f\n", __func__, k, mode_lib->mp.VRatioPrefetchY[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u VRatioY=%f\n", __func__, k, display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio); + + DML_LOG_VERBOSE("DML::%s: k=%0u prefetch_vmrow_bw=%f\n", __func__, k, mode_lib->mp.prefetch_vmrow_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u vactive_sw_bw_l=%f\n", __func__, k, mode_lib->mp.vactive_sw_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u vactive_sw_bw_c=%f\n", __func__, k, mode_lib->mp.vactive_sw_bw_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u cursor_bw=%f\n", __func__, k, mode_lib->mp.cursor_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u dpte_row_bw=%f\n", __func__, k, mode_lib->mp.dpte_row_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u meta_row_bw=%f\n", __func__, k, mode_lib->mp.meta_row_bw[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u RequiredPrefetchPixelDataBWLuma=%f\n", __func__, k, mode_lib->mp.RequiredPrefetchPixelDataBWLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u RequiredPrefetchPixelDataBWChroma=%f\n", __func__, k, mode_lib->mp.RequiredPrefetchPixelDataBWChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%0u prefetch_cursor_bw=%f\n", __func__, k, mode_lib->mp.prefetch_cursor_bw[k]); + } + + for (k = 0; k <= s->num_active_planes - 1; k++) + mode_lib->mp.final_flip_bw[k] = 0; + + calculate_peak_bandwidth_params->urg_vactive_bandwidth_required = mode_lib->mp.urg_vactive_bandwidth_required; + calculate_peak_bandwidth_params->urg_bandwidth_required = mode_lib->mp.urg_bandwidth_required; + calculate_peak_bandwidth_params->urg_bandwidth_required_qual = mode_lib->mp.urg_bandwidth_required_qual; + calculate_peak_bandwidth_params->non_urg_bandwidth_required = mode_lib->mp.non_urg_bandwidth_required; + calculate_peak_bandwidth_params->surface_avg_vactive_required_bw = s->surface_dummy_bw; + calculate_peak_bandwidth_params->surface_peak_required_bw = s->surface_dummy_bw0; + + calculate_peak_bandwidth_params->display_cfg = display_cfg; + calculate_peak_bandwidth_params->inc_flip_bw = 0; + calculate_peak_bandwidth_params->num_active_planes = s->num_active_planes; + calculate_peak_bandwidth_params->num_of_dpp = mode_lib->mp.NoOfDPP; + calculate_peak_bandwidth_params->dcc_dram_bw_nom_overhead_factor_p0 = mode_lib->mp.dcc_dram_bw_nom_overhead_factor_p0; + calculate_peak_bandwidth_params->dcc_dram_bw_nom_overhead_factor_p1 = mode_lib->mp.dcc_dram_bw_nom_overhead_factor_p1; + calculate_peak_bandwidth_params->dcc_dram_bw_pref_overhead_factor_p0 = mode_lib->mp.dcc_dram_bw_pref_overhead_factor_p0; + calculate_peak_bandwidth_params->dcc_dram_bw_pref_overhead_factor_p1 = mode_lib->mp.dcc_dram_bw_pref_overhead_factor_p1; + + calculate_peak_bandwidth_params->surface_read_bandwidth_l = mode_lib->mp.vactive_sw_bw_l; + calculate_peak_bandwidth_params->surface_read_bandwidth_c = mode_lib->mp.vactive_sw_bw_c; + calculate_peak_bandwidth_params->prefetch_bandwidth_l = mode_lib->mp.RequiredPrefetchPixelDataBWLuma; + calculate_peak_bandwidth_params->prefetch_bandwidth_c = mode_lib->mp.RequiredPrefetchPixelDataBWChroma; + calculate_peak_bandwidth_params->excess_vactive_fill_bw_l = mode_lib->mp.excess_vactive_fill_bw_l; + calculate_peak_bandwidth_params->excess_vactive_fill_bw_c = mode_lib->mp.excess_vactive_fill_bw_c; + calculate_peak_bandwidth_params->cursor_bw = mode_lib->mp.cursor_bw; + calculate_peak_bandwidth_params->dpte_row_bw = mode_lib->mp.dpte_row_bw; + calculate_peak_bandwidth_params->meta_row_bw = mode_lib->mp.meta_row_bw; + calculate_peak_bandwidth_params->prefetch_cursor_bw = mode_lib->mp.prefetch_cursor_bw; + calculate_peak_bandwidth_params->prefetch_vmrow_bw = mode_lib->mp.prefetch_vmrow_bw; + calculate_peak_bandwidth_params->flip_bw = mode_lib->mp.final_flip_bw; + calculate_peak_bandwidth_params->urgent_burst_factor_l = mode_lib->mp.UrgentBurstFactorLuma; + calculate_peak_bandwidth_params->urgent_burst_factor_c = mode_lib->mp.UrgentBurstFactorChroma; + calculate_peak_bandwidth_params->urgent_burst_factor_cursor = mode_lib->mp.UrgentBurstFactorCursor; + calculate_peak_bandwidth_params->urgent_burst_factor_prefetch_l = mode_lib->mp.UrgentBurstFactorLumaPre; + calculate_peak_bandwidth_params->urgent_burst_factor_prefetch_c = mode_lib->mp.UrgentBurstFactorChromaPre; + calculate_peak_bandwidth_params->urgent_burst_factor_prefetch_cursor = mode_lib->mp.UrgentBurstFactorCursorPre; + + dcn5_calculate_peak_bandwidth_required( + &mode_lib->scratch, + calculate_peak_bandwidth_params); + + // Check urg peak bandwidth against available urg bw + // check at SDP and DRAM, for all soc states (Sys Active) + dcn5_check_urgent_bandwidth_support( + &mode_lib->mp.FractionOfUrgentBandwidth, // double* frac_urg_bandwidth + &mode_lib->mp.PrefetchModeSupported, // prefetch bw ok + + **mode_lib->mp.non_urg_bandwidth_required, + **mode_lib->mp.urg_bandwidth_required, + mode_lib->mp.dram_bw_mbps); + + if (!mode_lib->mp.PrefetchModeSupported) + DML_LOG_VERBOSE("DML::%s: Bandwidth not sufficient for prefetch!\n", __func__); + + for (k = 0; k < s->num_active_planes; ++k) { + if (mode_lib->mp.NotEnoughUrgentLatencyHidingPre[k]) { + DML_LOG_VERBOSE("DML::%s: k=%u, NotEnoughUrgentLatencyHidingPre = %u\n", __func__, k, mode_lib->mp.NotEnoughUrgentLatencyHidingPre[k]); + mode_lib->mp.PrefetchModeSupported = false; + } + } + } // prefetch schedule ok + + // Prefetch schedule and prefetch bw ok, now check flip bw + if (mode_lib->mp.PrefetchModeSupported == true) { // prefetch schedule and prefetch bw ok, now check flip bw + + mode_lib->mp.BandwidthAvailableForImmediateFlip = + dcn5_get_bandwidth_available_for_immediate_flip( + **mode_lib->mp.urg_bandwidth_required_qual, // no flip + mode_lib->mp.dram_bw_mbps); + + mode_lib->mp.TotImmediateFlipBytes = 0; + + for (k = 0; k < s->num_active_planes; ++k) { + if (display_cfg->plane_descriptors[k].immediate_flip) { + s->per_pipe_flip_bytes[k] = dcn5_get_pipe_flip_bytes(s->HostVMInefficiencyFactor, + mode_lib->mp.vm_bytes[k], + mode_lib->mp.PixelPTEBytesPerRow[k], + mode_lib->mp.meta_row_bytes[k]); + } else { + s->per_pipe_flip_bytes[k] = 0; + } + mode_lib->mp.TotImmediateFlipBytes += s->per_pipe_flip_bytes[k] * mode_lib->mp.NoOfDPP[k]; + DML_LOG_VERBOSE("DML::%s: k = %u\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: DPPPerSurface = %u\n", __func__, mode_lib->mp.NoOfDPP[k]); + DML_LOG_VERBOSE("DML::%s: vm_bytes = %u\n", __func__, mode_lib->mp.vm_bytes[k]); + DML_LOG_VERBOSE("DML::%s: PixelPTEBytesPerRow = %u\n", __func__, mode_lib->mp.PixelPTEBytesPerRow[k]); + DML_LOG_VERBOSE("DML::%s: meta_row_bytes = %u\n", __func__, mode_lib->mp.meta_row_bytes[k]); + DML_LOG_VERBOSE("DML::%s: TotImmediateFlipBytes = %u\n", __func__, mode_lib->mp.TotImmediateFlipBytes); + } + for (k = 0; k < s->num_active_planes; ++k) { + dcn5_calculate_flip_schedule( + &mode_lib->scratch, + display_cfg->plane_descriptors[k].immediate_flip, + 0, // use_lb_flip_bw + s->HostVMInefficiencyFactor, + s->Tvm_trips_flip[k], + s->Tr0_trips_flip[k], + s->Tvm_trips_flip_rounded[k], + s->Tr0_trips_flip_rounded[k], + display_cfg->gpuvm_enable, + mode_lib->mp.vm_bytes[k], + mode_lib->mp.PixelPTEBytesPerRow[k], + mode_lib->mp.BandwidthAvailableForImmediateFlip, + mode_lib->mp.TotImmediateFlipBytes, + display_cfg->plane_descriptors[k].pixel_format, + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000), + display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio, + display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio, + mode_lib->mp.Tno_bw[k], + mode_lib->mp.dpte_row_height[k], + mode_lib->mp.dpte_row_height_chroma[k], + mode_lib->mp.use_one_row_for_frame_flip[k], + mode_lib->ip.max_flip_time_us, + mode_lib->ip.max_flip_time_lines, + s->per_pipe_flip_bytes[k], + mode_lib->mp.meta_row_bytes[k], + mode_lib->mp.meta_row_height[k], + mode_lib->mp.meta_row_height_chroma[k], + mode_lib->ip.dcn_mrq_present && display_cfg->plane_descriptors[k].surface.dcc.enable, + + // Output + &mode_lib->mp.dst_y_per_vm_flip[k], + &mode_lib->mp.dst_y_per_row_flip[k], + &mode_lib->mp.final_flip_bw[k], + &mode_lib->mp.ImmediateFlipSupportedForPipe[k]); + } + + calculate_peak_bandwidth_params->urg_vactive_bandwidth_required = s->dummy_bw; + calculate_peak_bandwidth_params->urg_bandwidth_required = mode_lib->mp.urg_bandwidth_required_flip; + calculate_peak_bandwidth_params->urg_bandwidth_required_qual = s->dummy_bw; + calculate_peak_bandwidth_params->non_urg_bandwidth_required = mode_lib->mp.non_urg_bandwidth_required_flip; + calculate_peak_bandwidth_params->surface_avg_vactive_required_bw = s->surface_dummy_bw; + calculate_peak_bandwidth_params->surface_peak_required_bw = s->surface_dummy_bw0; + + calculate_peak_bandwidth_params->display_cfg = display_cfg; + calculate_peak_bandwidth_params->inc_flip_bw = 1; + calculate_peak_bandwidth_params->num_active_planes = s->num_active_planes; + calculate_peak_bandwidth_params->num_of_dpp = mode_lib->mp.NoOfDPP; + calculate_peak_bandwidth_params->dcc_dram_bw_nom_overhead_factor_p0 = mode_lib->mp.dcc_dram_bw_nom_overhead_factor_p0; + calculate_peak_bandwidth_params->dcc_dram_bw_nom_overhead_factor_p1 = mode_lib->mp.dcc_dram_bw_nom_overhead_factor_p1; + calculate_peak_bandwidth_params->dcc_dram_bw_pref_overhead_factor_p0 = mode_lib->mp.dcc_dram_bw_pref_overhead_factor_p0; + calculate_peak_bandwidth_params->dcc_dram_bw_pref_overhead_factor_p1 = mode_lib->mp.dcc_dram_bw_pref_overhead_factor_p1; + + calculate_peak_bandwidth_params->surface_read_bandwidth_l = mode_lib->mp.vactive_sw_bw_l; + calculate_peak_bandwidth_params->surface_read_bandwidth_c = mode_lib->mp.vactive_sw_bw_c; + calculate_peak_bandwidth_params->prefetch_bandwidth_l = mode_lib->mp.RequiredPrefetchPixelDataBWLuma; + calculate_peak_bandwidth_params->prefetch_bandwidth_c = mode_lib->mp.RequiredPrefetchPixelDataBWChroma; + calculate_peak_bandwidth_params->excess_vactive_fill_bw_l = mode_lib->mp.excess_vactive_fill_bw_l; + calculate_peak_bandwidth_params->excess_vactive_fill_bw_c = mode_lib->mp.excess_vactive_fill_bw_c; + calculate_peak_bandwidth_params->cursor_bw = mode_lib->mp.cursor_bw; + calculate_peak_bandwidth_params->dpte_row_bw = mode_lib->mp.dpte_row_bw; + calculate_peak_bandwidth_params->meta_row_bw = mode_lib->mp.meta_row_bw; + calculate_peak_bandwidth_params->prefetch_cursor_bw = mode_lib->mp.prefetch_cursor_bw; + calculate_peak_bandwidth_params->prefetch_vmrow_bw = mode_lib->mp.prefetch_vmrow_bw; + calculate_peak_bandwidth_params->flip_bw = mode_lib->mp.final_flip_bw; + calculate_peak_bandwidth_params->urgent_burst_factor_l = mode_lib->mp.UrgentBurstFactorLuma; + calculate_peak_bandwidth_params->urgent_burst_factor_c = mode_lib->mp.UrgentBurstFactorChroma; + calculate_peak_bandwidth_params->urgent_burst_factor_cursor = mode_lib->mp.UrgentBurstFactorCursor; + calculate_peak_bandwidth_params->urgent_burst_factor_prefetch_l = mode_lib->mp.UrgentBurstFactorLumaPre; + calculate_peak_bandwidth_params->urgent_burst_factor_prefetch_c = mode_lib->mp.UrgentBurstFactorChromaPre; + calculate_peak_bandwidth_params->urgent_burst_factor_prefetch_cursor = mode_lib->mp.UrgentBurstFactorCursorPre; + + dcn5_calculate_peak_bandwidth_required( + &mode_lib->scratch, + calculate_peak_bandwidth_params); + + dcn5_check_immediate_flip_bandwidth_support( + &mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip, // double* frac_urg_bandwidth_flip + &mode_lib->mp.ImmediateFlipSupported, // bool* flip_bandwidth_support_ok + **mode_lib->mp.urg_bandwidth_required_flip, + **mode_lib->mp.non_urg_bandwidth_required_flip, + mode_lib->mp.dram_bw_mbps); + + if (!mode_lib->mp.ImmediateFlipSupported) + DML_LOG_VERBOSE("DML::%s: Bandwidth not sufficient for flip!", __func__); + + for (k = 0; k < s->num_active_planes; ++k) { + if (display_cfg->plane_descriptors[k].immediate_flip && mode_lib->mp.ImmediateFlipSupportedForPipe[k] == false) { + mode_lib->mp.ImmediateFlipSupported = false; + DML_LOG_VERBOSE("DML::%s: Pipe %0d not supporting iflip!\n", __func__, k); + } + } + } else { // flip or prefetch not support + mode_lib->mp.ImmediateFlipSupported = false; + } + + // consider flip support is okay if the flip bw is ok or (when user does't require a iflip and there is no host vm) + must_support_iflip = display_cfg->hostvm_enable || s->immediate_flip_required; + mode_lib->mp.PrefetchAndImmediateFlipSupported = (mode_lib->mp.PrefetchModeSupported == true && (!must_support_iflip || mode_lib->mp.ImmediateFlipSupported)); + + DML_LOG_VERBOSE("DML::%s: PrefetchModeSupported = %u\n", __func__, mode_lib->mp.PrefetchModeSupported); + for (k = 0; k < s->num_active_planes; ++k) + DML_LOG_VERBOSE("DML::%s: immediate_flip_required[%u] = %u\n", __func__, k, display_cfg->plane_descriptors[k].immediate_flip); + DML_LOG_VERBOSE("DML::%s: HostVMEnable = %u\n", __func__, display_cfg->hostvm_enable); + DML_LOG_VERBOSE("DML::%s: ImmediateFlipSupported = %u\n", __func__, mode_lib->mp.ImmediateFlipSupported); + DML_LOG_VERBOSE("DML::%s: PrefetchAndImmediateFlipSupported = %u\n", __func__, mode_lib->mp.PrefetchAndImmediateFlipSupported); + DML_LOG_VERBOSE("DML::%s: Done one iteration: k=%d, MaxVStartupLines=%u\n", __func__, k, s->MaxVStartupLines[k]); + } + + for (k = 0; k < s->num_active_planes; ++k) + DML_LOG_VERBOSE("DML::%s: k=%d MaxVStartupLines = %u\n", __func__, k, s->MaxVStartupLines[k]); + + if (!mode_lib->mp.PrefetchAndImmediateFlipSupported) { + DML_LOG_VERBOSE("DML::%s: Bad, Prefetch and flip scheduling solution NOT found!\n", __func__); + } else { + DML_LOG_VERBOSE("DML::%s: Good, Prefetch and flip scheduling solution found\n", __func__); + + // DCC Configuration + for (k = 0; k < s->num_active_planes; ++k) { + DML_LOG_VERBOSE("DML::%s: Calculate DCC configuration for surface k=%u\n", __func__, k); + dcn5_calculate_dcc_configuration( + display_cfg->plane_descriptors[k].surface.dcc.enable, + display_cfg->overrides.dcc_programming_assumes_scan_direction_unknown, + display_cfg->plane_descriptors[k].pixel_format, + display_cfg->plane_descriptors[k].surface.plane0.width, + display_cfg->plane_descriptors[k].surface.plane1.width, + display_cfg->plane_descriptors[k].surface.plane0.height, + display_cfg->plane_descriptors[k].surface.plane1.height, + s->NomDETInKByte, + mode_lib->mp.Read256BlockHeightY[k], + mode_lib->mp.Read256BlockHeightC[k], + display_cfg->plane_descriptors[k].surface.tiling, + mode_lib->mp.BytePerPixelY[k], + mode_lib->mp.BytePerPixelC[k], + mode_lib->mp.BytePerPixelInDETY[k], + mode_lib->mp.BytePerPixelInDETC[k], + display_cfg->plane_descriptors[k].composition.rotation_angle, + + /* Output */ + &mode_lib->mp.RequestLuma[k], + &mode_lib->mp.RequestChroma[k], + &mode_lib->mp.DCCYMaxUncompressedBlock[k], + &mode_lib->mp.DCCCMaxUncompressedBlock[k], + &mode_lib->mp.DCCYMaxCompressedBlock[k], + &mode_lib->mp.DCCCMaxCompressedBlock[k], + &mode_lib->mp.DCCYIndependentBlock[k], + &mode_lib->mp.DCCCIndependentBlock[k]); + } + + //Watermarks and NB P-State/DRAM Clock Change Support + s->mmSOCParameters.UrgentLatency = mode_lib->mp.UrgentLatency; + s->mmSOCParameters.ExtraLatency = mode_lib->mp.ExtraLatency; + s->mmSOCParameters.ExtraLatency_sr = mode_lib->mp.ExtraLatency_sr; + s->mmSOCParameters.WritebackLatency = utm_soc_bb->writeback_base_latency_us; + s->mmSOCParameters.DRAMClockChangeLatency = utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + s->mmSOCParameters.FCLKChangeLatency = utm_soc_bb->power_management_parameters.fclk_change_blackout_us; + s->mmSOCParameters.SRExitTime = utm_soc_bb->power_management_parameters.stutter_exit_latency_us; + s->mmSOCParameters.SREnterPlusExitTime = utm_soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us; + s->mmSOCParameters.SRExitZ8Time = utm_soc_bb->power_management_parameters.z8_stutter_exit_latency_us; + s->mmSOCParameters.SREnterPlusExitZ8Time = utm_soc_bb->power_management_parameters.z8_stutter_enter_plus_exit_latency_us; + s->mmSOCParameters.USRRetrainingLatency = 0; + s->mmSOCParameters.SMNLatency = 0; + s->mmSOCParameters.temp_read_or_ppt_blackout_us = utm_soc_bb->power_management_parameters.g7_ppt_blackout_us; + s->mmSOCParameters.qos_type = dml2_qos_param_type_dcn4x; + + CalculateWatermarks_params->display_cfg = display_cfg; + CalculateWatermarks_params->USRRetrainingRequired = false; + CalculateWatermarks_params->NumberOfActiveSurfaces = s->num_active_planes; + CalculateWatermarks_params->MaxLineBufferLines = mode_lib->ip.max_line_buffer_lines; + CalculateWatermarks_params->LineBufferSize = mode_lib->ip.line_buffer_size_bits; + CalculateWatermarks_params->WritebackInterfaceBufferSize = mode_lib->ip.writeback_interface_buffer_size_kbytes; + CalculateWatermarks_params->DCFCLK = mode_lib->mp.Dcfclk; + CalculateWatermarks_params->SynchronizeTimings = display_cfg->overrides.synchronize_timings; + CalculateWatermarks_params->SynchronizeDRRDisplaysForUCLKPStateChange = display_cfg->overrides.synchronize_ddr_displays_for_uclk_pstate_change; + CalculateWatermarks_params->dpte_group_bytes = mode_lib->mp.dpte_group_bytes; + CalculateWatermarks_params->mmSOCParameters = s->mmSOCParameters; + CalculateWatermarks_params->WritebackChunkSize = mode_lib->ip.writeback_chunk_size_kbytes; + CalculateWatermarks_params->SOCCLK = s->SOCCLK; + CalculateWatermarks_params->DCFClkDeepSleep = mode_lib->mp.DCFCLKDeepSleep; + CalculateWatermarks_params->DETBufferSizeY = mode_lib->mp.DETBufferSizeY; + CalculateWatermarks_params->DETBufferSizeC = mode_lib->mp.DETBufferSizeC; + CalculateWatermarks_params->SwathHeightY = mode_lib->mp.SwathHeightY; + CalculateWatermarks_params->SwathHeightC = mode_lib->mp.SwathHeightC; + //CalculateWatermarks_params->LBBitPerPixel = 57; //FIXME_STAGE2 + CalculateWatermarks_params->SwathWidthY = mode_lib->mp.SwathWidthY; + CalculateWatermarks_params->SwathWidthC = mode_lib->mp.SwathWidthC; + CalculateWatermarks_params->BytePerPixelDETY = mode_lib->mp.BytePerPixelInDETY; + CalculateWatermarks_params->BytePerPixelDETC = mode_lib->mp.BytePerPixelInDETC; + CalculateWatermarks_params->DSTXAfterScaler = mode_lib->mp.DSTXAfterScaler; + CalculateWatermarks_params->DSTYAfterScaler = mode_lib->mp.DSTYAfterScaler; + CalculateWatermarks_params->UnboundedRequestEnabled = mode_lib->mp.UnboundedRequestEnabled; + CalculateWatermarks_params->CompressedBufferSizeInkByte = mode_lib->mp.CompressedBufferSizeInkByte; + CalculateWatermarks_params->meta_row_height_l = mode_lib->mp.meta_row_height; + CalculateWatermarks_params->meta_row_height_c = mode_lib->mp.meta_row_height_chroma; + CalculateWatermarks_params->DPPPerSurface = mode_lib->mp.NoOfDPP; + CalculateWatermarks_params->uclk_pstate_switch_modes = mode_lib->mp.uclk_pstate_switch_modes; + + // Output + CalculateWatermarks_params->Watermark = &mode_lib->mp.Watermark; + CalculateWatermarks_params->DRAMClockChangeSupport = mode_lib->mp.DRAMClockChangeSupport; + CalculateWatermarks_params->global_dram_clock_change_support_required = &s->dummy_boolean[0]; + CalculateWatermarks_params->global_dram_clock_change_supported = &mode_lib->mp.global_dram_clock_change_supported; + CalculateWatermarks_params->MaxActiveDRAMClockChangeLatencySupported = mode_lib->mp.MaxActiveDRAMClockChangeLatencySupported; + CalculateWatermarks_params->FCLKChangeSupport = mode_lib->mp.FCLKChangeSupport; + CalculateWatermarks_params->global_fclk_change_supported = &mode_lib->mp.global_fclk_change_supported; + CalculateWatermarks_params->MaxActiveFCLKChangeLatencySupported = &mode_lib->mp.MaxActiveFCLKChangeLatencySupported; + CalculateWatermarks_params->USRRetrainingSupport = &mode_lib->mp.USRRetrainingSupport; + CalculateWatermarks_params->temp_read_or_ppt_support = mode_lib->mp.temp_read_or_ppt_support; + CalculateWatermarks_params->global_temp_read_or_ppt_supported = &mode_lib->mp.global_temp_read_or_ppt_supported; + CalculateWatermarks_params->VActiveLatencyHidingMargin = 0; + CalculateWatermarks_params->VActiveLatencyHidingUs = 0; + + dcn5_calculate_watermarks_and_dram_speed_change_support(&mode_lib->scratch, CalculateWatermarks_params); + + for (k = 0; k < s->num_active_planes; ++k) { + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream > 0) { + mode_lib->mp.WritebackAllowDRAMClockChangeEndPosition[k] = math_max2(0, mode_lib->mp.VStartupMin[k] * display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / + ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) - mode_lib->mp.Watermark.WritebackDRAMClockChangeWatermark); + mode_lib->mp.WritebackAllowFCLKChangeEndPosition[k] = math_max2(0, mode_lib->mp.VStartupMin[k] * display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / + ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) - mode_lib->mp.Watermark.WritebackFCLKChangeWatermark); + } else { + mode_lib->mp.WritebackAllowDRAMClockChangeEndPosition[k] = 0; + mode_lib->mp.WritebackAllowFCLKChangeEndPosition[k] = 0; + } + } + + dcn5_calculate_pstate_keepout_dst_lines(display_cfg, &mode_lib->mp.Watermark, mode_lib->mp.pstate_keepout_dst_lines); + + DML_LOG_VERBOSE("DML::%s: DEBUG stream_index = %0d\n", __func__, display_cfg->plane_descriptors[0].stream_index); + DML_LOG_VERBOSE("DML::%s: DEBUG PixelClock = %ld kHz\n", __func__, (display_cfg->stream_descriptors[display_cfg->plane_descriptors[0].stream_index].timing.pixel_clock_khz)); + + //Display Pipeline Delivery Time in Prefetch, Groups + dcn5_calculate_pixel_delivery_times( + display_cfg, + mode_lib->mp.NoOfDPP, + s->num_active_planes, + mode_lib->mp.VRatioPrefetchY, + mode_lib->mp.VRatioPrefetchC, + mode_lib->mp.swath_width_luma_ub, + mode_lib->mp.swath_width_chroma_ub, + mode_lib->mp.PSCL_THROUGHPUT, + mode_lib->mp.PSCL_THROUGHPUT_CHROMA, + mode_lib->mp.Dppclk, + mode_lib->mp.DCFCLKDeepSleep, + mode_lib->mp.BytePerPixelY, + mode_lib->mp.BytePerPixelC, + mode_lib->mp.req_per_swath_ub_l, + mode_lib->mp.req_per_swath_ub_c, + + /* Output */ + mode_lib->mp.DisplayPipeLineDeliveryTimeLuma, + mode_lib->mp.DisplayPipeLineDeliveryTimeChroma, + mode_lib->mp.DisplayPipeLineDeliveryTimeLumaPrefetch, + mode_lib->mp.DisplayPipeLineDeliveryTimeChromaPrefetch, + mode_lib->mp.DisplayPipeRequestDeliveryTimeLuma, + mode_lib->mp.DisplayPipeRequestDeliveryTimeChroma, + mode_lib->mp.DisplayPipeRequestDeliveryTimeLumaPrefetch, + mode_lib->mp.DisplayPipeRequestDeliveryTimeChromaPrefetch); + + CalculateMetaAndPTETimes_params->scratch = &mode_lib->scratch; + CalculateMetaAndPTETimes_params->display_cfg = display_cfg; + CalculateMetaAndPTETimes_params->NumberOfActiveSurfaces = s->num_active_planes; + CalculateMetaAndPTETimes_params->use_one_row_for_frame = mode_lib->mp.use_one_row_for_frame; + CalculateMetaAndPTETimes_params->dst_y_per_row_vblank = mode_lib->mp.dst_y_per_row_vblank; + CalculateMetaAndPTETimes_params->dst_y_per_row_flip = mode_lib->mp.dst_y_per_row_flip; + CalculateMetaAndPTETimes_params->BytePerPixelY = mode_lib->mp.BytePerPixelY; + CalculateMetaAndPTETimes_params->BytePerPixelC = mode_lib->mp.BytePerPixelC; + CalculateMetaAndPTETimes_params->dpte_row_height = mode_lib->mp.dpte_row_height; + CalculateMetaAndPTETimes_params->dpte_row_height_chroma = mode_lib->mp.dpte_row_height_chroma; + CalculateMetaAndPTETimes_params->dpte_group_bytes = mode_lib->mp.dpte_group_bytes; + CalculateMetaAndPTETimes_params->PTERequestSizeY = mode_lib->mp.PTERequestSizeY; + CalculateMetaAndPTETimes_params->PTERequestSizeC = mode_lib->mp.PTERequestSizeC; + CalculateMetaAndPTETimes_params->PixelPTEReqWidthY = mode_lib->mp.PixelPTEReqWidthY; + CalculateMetaAndPTETimes_params->PixelPTEReqHeightY = mode_lib->mp.PixelPTEReqHeightY; + CalculateMetaAndPTETimes_params->PixelPTEReqWidthC = mode_lib->mp.PixelPTEReqWidthC; + CalculateMetaAndPTETimes_params->PixelPTEReqHeightC = mode_lib->mp.PixelPTEReqHeightC; + CalculateMetaAndPTETimes_params->dpte_row_width_luma_ub = mode_lib->mp.dpte_row_width_luma_ub; + CalculateMetaAndPTETimes_params->dpte_row_width_chroma_ub = mode_lib->mp.dpte_row_width_chroma_ub; + CalculateMetaAndPTETimes_params->tdlut_groups_per_2row_ub = s->tdlut_groups_per_2row_ub; + CalculateMetaAndPTETimes_params->mrq_present = mode_lib->ip.dcn_mrq_present; + + CalculateMetaAndPTETimes_params->MetaChunkSize = mode_lib->ip.meta_chunk_size_kbytes; + CalculateMetaAndPTETimes_params->MinMetaChunkSizeBytes = mode_lib->ip.min_meta_chunk_size_bytes; + CalculateMetaAndPTETimes_params->meta_row_width = mode_lib->mp.meta_row_width; + CalculateMetaAndPTETimes_params->meta_row_width_chroma = mode_lib->mp.meta_row_width_chroma; + CalculateMetaAndPTETimes_params->meta_row_height = mode_lib->mp.meta_row_height; + CalculateMetaAndPTETimes_params->meta_row_height_chroma = mode_lib->mp.meta_row_height_chroma; + CalculateMetaAndPTETimes_params->meta_req_width = mode_lib->mp.meta_req_width; + CalculateMetaAndPTETimes_params->meta_req_width_chroma = mode_lib->mp.meta_req_width_chroma; + CalculateMetaAndPTETimes_params->meta_req_height = mode_lib->mp.meta_req_height; + CalculateMetaAndPTETimes_params->meta_req_height_chroma = mode_lib->mp.meta_req_height_chroma; + + CalculateMetaAndPTETimes_params->time_per_tdlut_group = mode_lib->mp.time_per_tdlut_group; + CalculateMetaAndPTETimes_params->DST_Y_PER_PTE_ROW_NOM_L = mode_lib->mp.DST_Y_PER_PTE_ROW_NOM_L; + CalculateMetaAndPTETimes_params->DST_Y_PER_PTE_ROW_NOM_C = mode_lib->mp.DST_Y_PER_PTE_ROW_NOM_C; + CalculateMetaAndPTETimes_params->time_per_pte_group_nom_luma = mode_lib->mp.time_per_pte_group_nom_luma; + CalculateMetaAndPTETimes_params->time_per_pte_group_vblank_luma = mode_lib->mp.time_per_pte_group_vblank_luma; + CalculateMetaAndPTETimes_params->time_per_pte_group_flip_luma = mode_lib->mp.time_per_pte_group_flip_luma; + CalculateMetaAndPTETimes_params->time_per_pte_group_nom_chroma = mode_lib->mp.time_per_pte_group_nom_chroma; + CalculateMetaAndPTETimes_params->time_per_pte_group_vblank_chroma = mode_lib->mp.time_per_pte_group_vblank_chroma; + CalculateMetaAndPTETimes_params->time_per_pte_group_flip_chroma = mode_lib->mp.time_per_pte_group_flip_chroma; + CalculateMetaAndPTETimes_params->DST_Y_PER_META_ROW_NOM_L = mode_lib->mp.DST_Y_PER_META_ROW_NOM_L; + CalculateMetaAndPTETimes_params->DST_Y_PER_META_ROW_NOM_C = mode_lib->mp.DST_Y_PER_META_ROW_NOM_C; + CalculateMetaAndPTETimes_params->TimePerMetaChunkNominal = mode_lib->mp.TimePerMetaChunkNominal; + CalculateMetaAndPTETimes_params->TimePerChromaMetaChunkNominal = mode_lib->mp.TimePerChromaMetaChunkNominal; + CalculateMetaAndPTETimes_params->TimePerMetaChunkVBlank = mode_lib->mp.TimePerMetaChunkVBlank; + CalculateMetaAndPTETimes_params->TimePerChromaMetaChunkVBlank = mode_lib->mp.TimePerChromaMetaChunkVBlank; + CalculateMetaAndPTETimes_params->TimePerMetaChunkFlip = mode_lib->mp.TimePerMetaChunkFlip; + CalculateMetaAndPTETimes_params->TimePerChromaMetaChunkFlip = mode_lib->mp.TimePerChromaMetaChunkFlip; + + dcn5_calculate_meta_and_pte_times(CalculateMetaAndPTETimes_params); + + dcn5_calculate_vm_group_and_request_times( + display_cfg, + s->num_active_planes, + mode_lib->mp.BytePerPixelC, + mode_lib->mp.dst_y_per_vm_vblank, + mode_lib->mp.dst_y_per_vm_flip, + mode_lib->mp.dpte_row_width_luma_ub, + mode_lib->mp.dpte_row_width_chroma_ub, + mode_lib->mp.vm_group_bytes, + mode_lib->mp.dpde0_bytes_per_frame_ub_l, + mode_lib->mp.dpde0_bytes_per_frame_ub_c, + s->tdlut_pte_bytes_per_frame, + mode_lib->mp.meta_pte_bytes_per_frame_ub_l, + mode_lib->mp.meta_pte_bytes_per_frame_ub_c, + mode_lib->ip.dcn_mrq_present, + + /* Output */ + mode_lib->mp.TimePerVMGroupVBlank, + mode_lib->mp.TimePerVMGroupFlip, + mode_lib->mp.TimePerVMRequestVBlank, + mode_lib->mp.TimePerVMRequestFlip); + + // VStartup Adjustment + for (k = 0; k < s->num_active_planes; ++k) { + bool isInterlaceTiming; + + mode_lib->mp.MinTTUVBlank[k] = mode_lib->mp.TWait[k] + mode_lib->mp.ExtraLatency; + if (!display_cfg->plane_descriptors[k].dynamic_meta_data.enable) + mode_lib->mp.MinTTUVBlank[k] = mode_lib->mp.TCalc + mode_lib->mp.MinTTUVBlank[k]; + + DML_LOG_VERBOSE("DML::%s: k=%u, MinTTUVBlank = %f (before vstartup margin)\n", __func__, k, mode_lib->mp.MinTTUVBlank[k]); + s->Tvstartup_margin = (s->MaxVStartupLines[k] - mode_lib->mp.VStartupMin[k]) * display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + mode_lib->mp.MinTTUVBlank[k] = mode_lib->mp.MinTTUVBlank[k] + s->Tvstartup_margin; + + DML_LOG_VERBOSE("DML::%s: k=%u, Tvstartup_margin = %f\n", __func__, k, s->Tvstartup_margin); + DML_LOG_VERBOSE("DML::%s: k=%u, MaxVStartupLines = %u\n", __func__, k, s->MaxVStartupLines[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, MinTTUVBlank = %f\n", __func__, k, mode_lib->mp.MinTTUVBlank[k]); + + mode_lib->mp.Tdmdl[k] = mode_lib->mp.Tdmdl[k] + s->Tvstartup_margin; + if (display_cfg->plane_descriptors[k].dynamic_meta_data.enable && mode_lib->ip.dynamic_metadata_vm_enabled) { + mode_lib->mp.Tdmdl_vm[k] = mode_lib->mp.Tdmdl_vm[k] + s->Tvstartup_margin; + } + + isInterlaceTiming = (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.interlaced && !mode_lib->ip.ptoi_supported); + + // The actual positioning of the vstartup + mode_lib->mp.VStartup[k] = (isInterlaceTiming ? (2 * s->MaxVStartupLines[k]) : s->MaxVStartupLines[k]); + + s->dlg_vblank_start = ((isInterlaceTiming ? math_floor2((display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_front_porch) / 2.0, 1.0) : + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total) - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_front_porch); + s->LSetup = math_floor2(4.0 * mode_lib->mp.TSetup[k] / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)), 1.0) / 4.0; + s->blank_lines_remaining = (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_active) - mode_lib->mp.VStartup[k]; + + if (s->blank_lines_remaining < 0) { + DML_LOG_VERBOSE("ERROR: Vstartup is larger than vblank!?\n"); + s->blank_lines_remaining = 0; + DML_ASSERT(0); + } + mode_lib->mp.MIN_DST_Y_NEXT_START[k] = s->dlg_vblank_start + s->blank_lines_remaining + s->LSetup; + + // debug only + if (((mode_lib->mp.VUpdateOffsetPix[k] + mode_lib->mp.VUpdateWidthPix[k] + mode_lib->mp.VReadyOffsetPix[k]) / (double) display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total) <= + (isInterlaceTiming ? + math_floor2((display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_active - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_front_porch - mode_lib->mp.VStartup[k]) / 2.0, 1.0) : + (int)(display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_active - display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_front_porch - mode_lib->mp.VStartup[k]))) { + mode_lib->mp.VREADY_AT_OR_AFTER_VSYNC[k] = true; + } else { + mode_lib->mp.VREADY_AT_OR_AFTER_VSYNC[k] = false; + } + DML_LOG_VERBOSE("DML::%s: k=%u, VStartup = %u (max)\n", __func__, k, mode_lib->mp.VStartup[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VStartupMin = %u (max)\n", __func__, k, mode_lib->mp.VStartupMin[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VUpdateOffsetPix = %u\n", __func__, k, mode_lib->mp.VUpdateOffsetPix[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VUpdateWidthPix = %u\n", __func__, k, mode_lib->mp.VUpdateWidthPix[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VReadyOffsetPix = %u\n", __func__, k, mode_lib->mp.VReadyOffsetPix[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, HTotal = %lu\n", __func__, k, display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total); + DML_LOG_VERBOSE("DML::%s: k=%u, VTotal = %lu\n", __func__, k, display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_total); + DML_LOG_VERBOSE("DML::%s: k=%u, VActive = %lu\n", __func__, k, display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_active); + DML_LOG_VERBOSE("DML::%s: k=%u, VFrontPorch = %lu\n", __func__, k, display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.v_front_porch); + DML_LOG_VERBOSE("DML::%s: k=%u, TSetup = %f\n", __func__, k, mode_lib->mp.TSetup[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, MIN_DST_Y_NEXT_START = %f\n", __func__, k, mode_lib->mp.MIN_DST_Y_NEXT_START[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VREADY_AT_OR_AFTER_VSYNC = %u\n", __func__, k, mode_lib->mp.VREADY_AT_OR_AFTER_VSYNC[k]); + } + + //Maximum Bandwidth Used + mode_lib->mp.TotalWRBandwidth = 0; + for (k = 0; k < display_cfg->num_streams; ++k) { + for (j = 0; j < display_cfg->stream_descriptors[k].writeback.active_writebacks_per_stream; ++j) { + double writeback_bytes_per_pixel; + if (display_cfg->stream_descriptors[k].writeback.writeback_stream[j].pixel_format == dml2_444_64) + writeback_bytes_per_pixel = 8.0; + else if (display_cfg->stream_descriptors[k].writeback.writeback_stream[j].pixel_format == dml2_444_32) + writeback_bytes_per_pixel = 4.0; + else if (display_cfg->stream_descriptors[k].writeback.writeback_stream[j].pixel_format == dml2_420_8) + writeback_bytes_per_pixel = 1.5; + else if (display_cfg->stream_descriptors[k].writeback.writeback_stream[j].pixel_format == dml2_420_10) + writeback_bytes_per_pixel = 3.0; + else if (display_cfg->stream_descriptors[k].writeback.writeback_stream[j].pixel_format == dml2_422_packed_8) + writeback_bytes_per_pixel = 2.0; + else if (display_cfg->stream_descriptors[k].writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) + writeback_bytes_per_pixel = 4.0; + else + writeback_bytes_per_pixel = 0.0; + s->WRBandwidth = display_cfg->stream_descriptors[k].writeback.writeback_stream[j].output_height + * display_cfg->stream_descriptors[k].writeback.writeback_stream[j].output_width + / (display_cfg->stream_descriptors[k].timing.h_total + * display_cfg->stream_descriptors[k].writeback.writeback_stream[j].input_height + / ((double)display_cfg->stream_descriptors[k].timing.pixel_clock_khz / 1000)) * writeback_bytes_per_pixel; + mode_lib->mp.TotalWRBandwidth = mode_lib->mp.TotalWRBandwidth + s->WRBandwidth; + } + } + + mode_lib->mp.TotalDataReadBandwidth = 0; + for (k = 0; k < s->num_active_planes; ++k) { + mode_lib->mp.TotalDataReadBandwidth = mode_lib->mp.TotalDataReadBandwidth + mode_lib->mp.vactive_sw_bw_l[k] + mode_lib->mp.vactive_sw_bw_c[k]; + DML_LOG_VERBOSE("DML::%s: k=%u, TotalDataReadBandwidth = %f\n", __func__, k, mode_lib->mp.TotalDataReadBandwidth); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_l = %f\n", __func__, k, mode_lib->mp.vactive_sw_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_c = %f\n", __func__, k, mode_lib->mp.vactive_sw_bw_c[k]); + } + + CalculateStutterEfficiency_params->display_cfg = display_cfg; + CalculateStutterEfficiency_params->CompressedBufferSizeInkByte = mode_lib->mp.CompressedBufferSizeInkByte; + CalculateStutterEfficiency_params->UnboundedRequestEnabled = mode_lib->mp.UnboundedRequestEnabled; + CalculateStutterEfficiency_params->MetaFIFOSizeInKEntries = mode_lib->ip.meta_fifo_size_in_kentries; + CalculateStutterEfficiency_params->ZeroSizeBufferEntries = mode_lib->ip.zero_size_buffer_entries; + CalculateStutterEfficiency_params->PixelChunkSizeInKByte = mode_lib->ip.pixel_chunk_size_kbytes; + CalculateStutterEfficiency_params->NumberOfActiveSurfaces = s->num_active_planes; + CalculateStutterEfficiency_params->ROBBufferSizeInKByte = mode_lib->ip.rob_buffer_size_kbytes; + CalculateStutterEfficiency_params->TotalDataReadBandwidth = mode_lib->mp.TotalDataReadBandwidth; + CalculateStutterEfficiency_params->DCFCLK = mode_lib->mp.Dcfclk; + CalculateStutterEfficiency_params->ReturnBW = mode_lib->mp.dram_bw_mbps; + CalculateStutterEfficiency_params->CompbufReservedSpace64B = mode_lib->mp.compbuf_reserved_space_64b; + CalculateStutterEfficiency_params->CompbufReservedSpaceZs = mode_lib->ip.compbuf_reserved_space_zs; + CalculateStutterEfficiency_params->SRExitTime = utm_soc_bb->power_management_parameters.stutter_exit_latency_us; + CalculateStutterEfficiency_params->SRExitZ8Time = utm_soc_bb->power_management_parameters.z8_stutter_exit_latency_us; + CalculateStutterEfficiency_params->SynchronizeTimings = display_cfg->overrides.synchronize_timings; + CalculateStutterEfficiency_params->StutterEnterPlusExitWatermark = mode_lib->mp.Watermark.StutterEnterPlusExitWatermark; + CalculateStutterEfficiency_params->Z8StutterEnterPlusExitWatermark = mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark; + CalculateStutterEfficiency_params->ProgressiveToInterlaceUnitInOPP = mode_lib->ip.ptoi_supported; + CalculateStutterEfficiency_params->MinTTUVBlank = mode_lib->mp.MinTTUVBlank; + CalculateStutterEfficiency_params->DPPPerSurface = mode_lib->mp.NoOfDPP; + CalculateStutterEfficiency_params->DETBufferSizeY = mode_lib->mp.DETBufferSizeY; + CalculateStutterEfficiency_params->BytePerPixelY = mode_lib->mp.BytePerPixelY; + CalculateStutterEfficiency_params->BytePerPixelDETY = mode_lib->mp.BytePerPixelInDETY; + CalculateStutterEfficiency_params->SwathWidthY = mode_lib->mp.SwathWidthY; + CalculateStutterEfficiency_params->SwathHeightY = mode_lib->mp.SwathHeightY; + CalculateStutterEfficiency_params->SwathHeightC = mode_lib->mp.SwathHeightC; + CalculateStutterEfficiency_params->BlockHeight256BytesY = mode_lib->mp.Read256BlockHeightY; + CalculateStutterEfficiency_params->BlockWidth256BytesY = mode_lib->mp.Read256BlockWidthY; + CalculateStutterEfficiency_params->BlockHeight256BytesC = mode_lib->mp.Read256BlockHeightC; + CalculateStutterEfficiency_params->BlockWidth256BytesC = mode_lib->mp.Read256BlockWidthC; + CalculateStutterEfficiency_params->DCCYMaxUncompressedBlock = mode_lib->mp.DCCYMaxUncompressedBlock; + CalculateStutterEfficiency_params->DCCCMaxUncompressedBlock = mode_lib->mp.DCCCMaxUncompressedBlock; + CalculateStutterEfficiency_params->ReadBandwidthSurfaceLuma = mode_lib->mp.vactive_sw_bw_l; + CalculateStutterEfficiency_params->ReadBandwidthSurfaceChroma = mode_lib->mp.vactive_sw_bw_c; + CalculateStutterEfficiency_params->dpte_row_bw = mode_lib->mp.dpte_row_bw; + CalculateStutterEfficiency_params->meta_row_bw = mode_lib->mp.meta_row_bw; + CalculateStutterEfficiency_params->rob_alloc_compressed = mode_lib->ip.dcn_mrq_present; + + // output + CalculateStutterEfficiency_params->StutterEfficiencyNotIncludingVBlank = &mode_lib->mp.StutterEfficiencyNotIncludingVBlank; + CalculateStutterEfficiency_params->StutterEfficiency = &mode_lib->mp.StutterEfficiency; + CalculateStutterEfficiency_params->NumberOfStutterBurstsPerFrame = &mode_lib->mp.NumberOfStutterBurstsPerFrame; + CalculateStutterEfficiency_params->Z8StutterEfficiencyNotIncludingVBlank = &mode_lib->mp.Z8StutterEfficiencyNotIncludingVBlank; + CalculateStutterEfficiency_params->Z8StutterEfficiency = &mode_lib->mp.Z8StutterEfficiency; + CalculateStutterEfficiency_params->Z8NumberOfStutterBurstsPerFrame = &mode_lib->mp.Z8NumberOfStutterBurstsPerFrame; + CalculateStutterEfficiency_params->StutterPeriod = &mode_lib->mp.StutterPeriod; + CalculateStutterEfficiency_params->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE = &mode_lib->mp.DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE; + + // Stutter Efficiency + dcn5_calculate_stutter_efficiency(&mode_lib->scratch, CalculateStutterEfficiency_params); + +#ifdef __DML_VBA_ALLOW_DELTA__ + // Calculate z8 stutter eff assuming 0 reserved space + CalculateStutterEfficiency_params->CompbufReservedSpace64B = 0; + CalculateStutterEfficiency_params->CompbufReservedSpaceZs = 0; + + CalculateStutterEfficiency_params->Z8StutterEfficiencyNotIncludingVBlank = &mode_lib->mp.Z8StutterEfficiencyNotIncludingVBlankBestCase; + CalculateStutterEfficiency_params->Z8StutterEfficiency = &mode_lib->mp.Z8StutterEfficiencyBestCase; + CalculateStutterEfficiency_params->Z8NumberOfStutterBurstsPerFrame = &mode_lib->mp.Z8NumberOfStutterBurstsPerFrameBestCase; + CalculateStutterEfficiency_params->StutterPeriod = &mode_lib->mp.StutterPeriodBestCase; + + // Stutter Efficiency + dcn5_calculate_stutter_efficiency(&mode_lib->scratch, CalculateStutterEfficiency_params); +#else + mode_lib->mp.Z8StutterEfficiencyNotIncludingVBlankBestCase = mode_lib->mp.Z8StutterEfficiencyNotIncludingVBlank; + mode_lib->mp.Z8StutterEfficiencyBestCase = mode_lib->mp.Z8StutterEfficiency; + mode_lib->mp.Z8NumberOfStutterBurstsPerFrameBestCase = mode_lib->mp.Z8NumberOfStutterBurstsPerFrame; + mode_lib->mp.StutterPeriodBestCase = mode_lib->mp.StutterPeriod; +#endif + } // PrefetchAndImmediateFlipSupported + + min_return_latency_in_DCFCLK_cycles = (min_return_uclk_cycles / (max_sop.uclk_khz / 1000.0) + min_return_fclk_cycles / (max_sop.fclk_khz / 1000.0)) * mode_lib->mp.Dcfclk; + mode_lib->mp.min_return_latency_in_dcfclk = (unsigned int)min_return_latency_in_DCFCLK_cycles; + mode_lib->mp.dcfclk_deep_sleep_hysteresis = (unsigned int)math_max2(32, (double)mode_lib->ip.pixel_chunk_size_kbytes * 1024 * 3 / 4 / 64 - min_return_latency_in_DCFCLK_cycles); + DML_ASSERT(mode_lib->mp.dcfclk_deep_sleep_hysteresis < 256); + + DML_LOG_VERBOSE("DML::%s: max_sop.fclk_khz = %d\n", __func__, max_sop.fclk_khz); + DML_LOG_VERBOSE("DML::%s: max_sop.dcfclk_khz = %d\n", __func__, max_sop.dcfclk_khz); + DML_LOG_VERBOSE("DML::%s: max_sop.uclk_khz = %d\n", __func__, max_sop.uclk_khz); + DML_LOG_VERBOSE("DML::%s: min_sop.fclk_khz = %d\n", __func__, min_sop.fclk_khz); + DML_LOG_VERBOSE("DML::%s: min_sop.dcfclk_khz = %d\n", __func__, min_sop.dcfclk_khz); + DML_LOG_VERBOSE("DML::%s: min_sop.uclk_khz = %d\n", __func__, min_sop.uclk_khz); + DML_LOG_VERBOSE("DML::%s: min_return_uclk_cycles = %ld\n", __func__, min_return_uclk_cycles); + DML_LOG_VERBOSE("DML::%s: min_return_fclk_cycles = %ld\n", __func__, min_return_fclk_cycles); + DML_LOG_VERBOSE("DML::%s: min_return_latency_in_DCFCLK_cycles = %f\n", __func__, min_return_latency_in_DCFCLK_cycles); + DML_LOG_VERBOSE("DML::%s: dcfclk_deep_sleep_hysteresis = %d \n", __func__, mode_lib->mp.dcfclk_deep_sleep_hysteresis); + DML_LOG_VERBOSE("DML::%s: --- END --- \n", __func__); + return in_out_params->mode_lib->mp.PrefetchAndImmediateFlipSupported; +} + +static void dcn5_get_global_sync_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, union dml2_global_sync_programming *out, int pipe_index) +{ + out->dcn4x.vready_offset_pixels = mode_lib->mp.VReadyOffsetPix[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.vstartup_lines = mode_lib->mp.VStartup[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.vupdate_offset_pixels = mode_lib->mp.VUpdateOffsetPix[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.vupdate_vupdate_width_pixels = mode_lib->mp.VUpdateWidthPix[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.pstate_keepout_start_lines = mode_lib->mp.pstate_keepout_dst_lines[mode_lib->mp.pipe_plane[pipe_index]]; +} + +static void dcn5_get_stream_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_per_stream_programming *out, int pipe_index) +{ + dcn5_get_global_sync_programming(mode_lib, &out->global_sync, pipe_index); +} + +static void dcn5_populate_fams2_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_core_calcs_mode_programming_ex *mode_prog, + union dmub_cmd_fams2_config *fams2_base_programming, + union dmub_cmd_fams2_config *fams2_sub_programming, + enum dml2_pstate_method pstate_method, + int plane_index) +{ + const struct dml2_display_cfg *disp_cfg = mode_prog->in_display_cfg; + const struct dml2_plane_parameters *plane_descriptor = &disp_cfg->plane_descriptors[plane_index]; + const struct dml2_stream_parameters *stream_descriptor = &disp_cfg->stream_descriptors[plane_descriptor->stream_index]; + const struct dml2_pstate_meta *stream_pstate_meta = &mode_prog->uclk_params->stream_pstate_meta[plane_descriptor->stream_index]; + + struct dmub_fams2_cmd_stream_static_base_state *base_programming = &fams2_base_programming->stream_v1.base; + union dmub_fams2_cmd_stream_static_sub_state *sub_programming = &fams2_sub_programming->stream_v1.sub_state; + + unsigned int i; + + if (disp_cfg->overrides.all_streams_blanked) { + /* stream is blanked, so do nothing */ + return; + } + + /* from display configuration */ + base_programming->htotal = (uint16_t)stream_descriptor->timing.h_total; + base_programming->vtotal = (uint16_t)stream_descriptor->timing.v_total; + base_programming->vblank_start = (uint16_t)(stream_pstate_meta->nom_vtotal - + stream_descriptor->timing.v_front_porch); + base_programming->vblank_end = (uint16_t)(stream_pstate_meta->nom_vtotal - + stream_descriptor->timing.v_front_porch - + stream_descriptor->timing.v_active); + base_programming->config.bits.is_drr = stream_descriptor->timing.drr_config.enabled; + + /* from meta */ + base_programming->otg_vline_time_ns = + (unsigned int)(stream_pstate_meta->otg_vline_time_us * 1000.0); + base_programming->scheduling_delay_otg_vlines = (uint8_t)stream_pstate_meta->scheduling_delay_otg_vlines; + base_programming->contention_delay_otg_vlines = (uint8_t)stream_pstate_meta->contention_delay_otg_vlines; + base_programming->vline_int_ack_delay_otg_vlines = (uint8_t)stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines; + base_programming->drr_keepout_otg_vline = (uint16_t)(stream_pstate_meta->nom_vtotal - + stream_descriptor->timing.v_front_porch - + stream_pstate_meta->method_drr.programming_delay_otg_vlines); + base_programming->allow_to_target_delay_otg_vlines = (uint8_t)stream_pstate_meta->allow_to_target_delay_otg_vlines; + base_programming->max_vtotal = (uint16_t)stream_pstate_meta->max_vtotal; + + /* from core */ + base_programming->config.bits.min_ttu_vblank_usable = true; + for (i = 0; i < disp_cfg->num_planes; i++) { + /* check if all planes support p-state in blank */ + if (disp_cfg->plane_descriptors[i].stream_index == plane_descriptor->stream_index && + mode_lib->mp.MinTTUVBlank[i] <= mode_lib->mp.Watermark.DRAMClockChangeWatermark) { + base_programming->config.bits.min_ttu_vblank_usable = false; + break; + } + } + + switch (pstate_method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + /* legacy vactive */ + base_programming->type = FAMS2_STREAM_TYPE_VACTIVE; + sub_programming->legacy.vactive_det_fill_delay_otg_vlines = + (uint8_t)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines; + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_vactive.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_vactive.common.allow_end_otg_vline; + base_programming->config.bits.clamp_vtotal_min = true; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + /* legacy vblank */ + base_programming->type = FAMS2_STREAM_TYPE_VBLANK; + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_vblank.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_vblank.common.allow_end_otg_vline; + base_programming->config.bits.clamp_vtotal_min = true; + break; + case dml2_pstate_method_fw_drr: + /* drr */ + base_programming->type = FAMS2_STREAM_TYPE_DRR; + sub_programming->drr.programming_delay_otg_vlines = + (uint8_t)stream_pstate_meta->method_drr.programming_delay_otg_vlines; + sub_programming->drr.nom_stretched_vtotal = + (uint16_t)stream_pstate_meta->method_drr.stretched_vtotal; + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_drr.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_drr.common.allow_end_otg_vline; + /* drr only clamps to vtotal min for single display */ + base_programming->config.bits.clamp_vtotal_min = disp_cfg->num_streams == 1; + sub_programming->drr.only_stretch_if_required = true; + break; + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_alternate: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_na: + case dml2_pstate_method_count: + default: + /* this should never happen */ + break; + } +} + +static void dcn5_populate_global_fams2_programming(struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_core_calcs_mode_programming_ex *mode_prog, + struct dmub_cmd_fams2_global_config *fams2_global_config) +{ + fams2_global_config->features.bits.enable = mode_prog->uclk_params->fams2_required; + + if (fams2_global_config->features.bits.enable) { + fams2_global_config->features.bits.enable_stall_recovery = true; + fams2_global_config->features.bits.allow_delay_check_mode = FAMS2_ALLOW_DELAY_CHECK_FROM_START; + + fams2_global_config->max_allow_delay_us = mode_lib->ip_caps.fams2.max_allow_delay_us; + fams2_global_config->lock_wait_time_us = mode_lib->ip_caps.fams2.lock_timeout_us; + fams2_global_config->recovery_timeout_us = mode_lib->ip_caps.fams2.recovery_timeout_us; + fams2_global_config->hwfq_flip_programming_delay_us = mode_lib->ip_caps.fams2.flip_programming_delay_us; + + fams2_global_config->num_streams = mode_prog->in_display_cfg->num_streams; + } +} + +static void dcn5_populate_mcache_allocation(struct dml2_display_cfg_programming *programming, + const struct dml2_display_solution *solution) +{ + unsigned int i; + + for (i = 0; i < solution->dispcfg.num_planes; i++) + programming->plane_programming[i].mcache_allocation = solution->mcache_allocations[i]; +} + +static void dcn5_pack_mode_programming_params( + struct dml2_core_instance *core, + struct dml2_core_calcs_mode_programming_ex *mode_prog, + struct dml2_display_cfg_programming *programming) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = mode_prog->mode_lib; + const struct dml2_utm_soc_bb *utm_soc_bb = core->utm_soc_bb; + unsigned int pipe_offset; + int dml_internal_pipe_index; + int total_pipe_regs_copied = 0; + int total_dwb_regs_copied = 0; + int stream_already_populated_mask = 0; + + int main_stream_index; + unsigned int plane_index; + unsigned int dwb_index; + + memcpy(&programming->display_config, mode_prog->in_display_cfg, sizeof(struct dml2_display_cfg)); + + dcn5_get_arb_params(&programming->display_config, mode_lib, utm_soc_bb, &programming->global_regs.arb_regs); + programming->global_regs.num_watermark_sets = 1; + dcn5_get_watermarks(&programming->display_config, mode_lib, utm_soc_bb, &programming->global_regs.wm_regs[0]); + dcn5_populate_mcache_allocation(programming, mode_prog->solution); + + dcn5_get_mcif_arb_params(mode_lib, &programming->mcif_global_regs); + programming->mcif_global_regs.num_watermark_sets = 1; + + dml_internal_pipe_index = 0; + + if (mode_prog->uclk_params) { + if (programming->uclk_pstate_supported) { + programming->fams2_required = mode_prog->uclk_params->fams2_required; + dcn5_populate_global_fams2_programming(mode_lib, mode_prog, &programming->fams2_global_config); + } + } + + DML_LOG_VERBOSE("DML::%s: num_planes=%d\n", __func__, programming->display_config.num_planes); + + for (plane_index = 0; plane_index < programming->display_config.num_planes; plane_index++) { + programming->plane_programming[plane_index].num_dpps_required = mode_lib->mp.NoOfDPP[plane_index]; + + // Setup the appropriate p-state strategy for each plane + if (mode_prog->uclk_params) { + if (programming->uclk_pstate_supported) + programming->plane_programming[plane_index].uclk_pstate_support_method = mode_prog->uclk_params->pstate_switch_modes[plane_index]; + else + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_na; + } else { + if (mode_lib->mp.MaxActiveDRAMClockChangeLatencySupported[plane_index] >= utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us) + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_vactive; + else if (mode_lib->mp.TWait[plane_index] >= utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us) + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_vblank; + else + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_na; + } + + for (pipe_offset = 0; pipe_offset < programming->plane_programming[plane_index].num_dpps_required; pipe_offset++) { + programming->plane_programming[plane_index].plane_descriptor = &programming->display_config.plane_descriptors[plane_index]; + + // Assign storage for this pipe's register values + programming->plane_programming[plane_index].pipe_regs[pipe_offset] = &programming->pipe_regs[total_pipe_regs_copied]; + memset(programming->plane_programming[plane_index].pipe_regs[pipe_offset], 0, sizeof(struct dml2_dchub_per_pipe_register_set)); + total_pipe_regs_copied++; + + // Populate + dcn5_get_pipe_regs(&programming->display_config, mode_lib, programming->plane_programming[plane_index].pipe_regs[pipe_offset], dml_internal_pipe_index, utm_soc_bb, &mode_lib->scratch); + + main_stream_index = programming->display_config.plane_descriptors[plane_index].stream_index; + + // Multiple planes can refer to the same stream index, so it's only necessary to populate it once + if (!(stream_already_populated_mask & (0x1 << main_stream_index))) { + programming->stream_programming[main_stream_index].uclk_pstate_method = programming->plane_programming[plane_index].uclk_pstate_support_method; + programming->stream_programming[main_stream_index].stream_descriptor = &programming->display_config.stream_descriptors[main_stream_index]; + programming->stream_programming[main_stream_index].num_odms_required = mode_prog->cfg_support_info->stream_support_info[main_stream_index].odms_used; + dcn5_get_stream_programming(mode_lib, &programming->stream_programming[main_stream_index], dml_internal_pipe_index); + + if (mode_prog->uclk_params) { + dcn5_populate_fams2_programming(mode_lib, + mode_prog, + &programming->stream_programming[main_stream_index].fams2_base_params, + &programming->stream_programming[main_stream_index].fams2_sub_params, + programming->stream_programming[main_stream_index].uclk_pstate_method, + plane_index); + } + + /* populate DWB */ + for (dwb_index = 0; dwb_index < programming->display_config.stream_descriptors[main_stream_index].writeback.active_writebacks_per_stream; dwb_index++) { + programming->stream_programming[main_stream_index].mcif_regs[dwb_index] = &programming->mcif_regs[total_dwb_regs_copied]; + memset(programming->stream_programming[main_stream_index].mcif_regs[dwb_index], 0, sizeof(struct dml2_mcif_per_pipe_register_set)); + total_dwb_regs_copied++; + + dcn5_get_per_dwb_params(&programming->display_config, + mode_lib, + programming->stream_programming[main_stream_index].mcif_regs[dwb_index], + main_stream_index, + dwb_index); + } + + stream_already_populated_mask |= (0x1 << main_stream_index); + } + dml_internal_pipe_index++; + } + } + + // DCN5: Calculate reduced viewport pstate recout reduction lines per plane + // Complete the formula calculation using memory clock and base calculation from prefetch schedule + + // Initialize all planes to default value + for (unsigned int plane_idx = 0; plane_idx < DML2_MAX_PLANES; plane_idx++) { + programming->informative.misc.pstate_recout_reduction_lines[plane_idx] = 0; + } + + // DCN5: Calculate pstate recout reduction lines feature + // Get DRAM clock change latency from SOC parameters + double dram_clk_change_latency_us = utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + + if (programming->display_config.num_planes == 1) { + // Calculate for all planes (inactive planes should naturally yield 0) + for (unsigned int plane_idx = 0; plane_idx < programming->display_config.num_planes; plane_idx++) { + // Use original equation from specifications + // Formula: (Tmclk – (Tvstartup – (Tsetup + Tcalc + Tpre + Tout))) / LineTime + // Where Tpre = dst_y_prefetch * line_time (as specified) + + int stream_index = programming->display_config.plane_descriptors[plane_idx].stream_index; + + // Calculate line time from display config timing (HTotal / PixelClock) + double pixel_clock_mhz = (double)programming->display_config.stream_descriptors[stream_index].timing.pixel_clock_khz / 1000.0; + double htotal = (double)programming->display_config.stream_descriptors[stream_index].timing.h_total; + double line_time_us = htotal / pixel_clock_mhz; + + // Use actual calculated values from mode library (not hardcoded defaults) + double tmclk_us = dram_clk_change_latency_us; + double tvstartup_us = mode_lib->mp.VStartup[plane_idx] * line_time_us; + double tsetup_us = mode_lib->mp.TSetup[plane_idx]; + double tcalc_us = mode_lib->mp.TCalc; + double tpre_us = mode_lib->mp.dst_y_prefetch[plane_idx] * line_time_us; // As specified: dst_y_prefetch * line_time + double tout_us = (mode_lib->mp.DSTYAfterScaler[plane_idx] + + ((double)mode_lib->mp.DSTXAfterScaler[plane_idx] / htotal)) * line_time_us; // delay_after_scaler per spec + + // Formula: (Tmclk - (Tvstartup - (Tsetup + Tcalc + Tpre + Tout))) / LineTime + programming->informative.misc.pstate_recout_reduction_lines[plane_idx] = + (unsigned int) math_max2(0.0, (tmclk_us - (tvstartup_us - (tsetup_us + tcalc_us + tpre_us + tout_us))) / line_time_us); + } + } +} + +static void dcn5_mp_assign_qos_bound(struct dml2_core_instance *core, + struct dml2_display_cfg_programming *programming) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = &core->clean_me_up.mode_lib; + struct dml2_core_calcs_mode_programming_locals *s = &mode_lib->scratch.dml_core_mode_programming_locals; + + mode_lib->mp.Dcfclk = programming->min_clocks.dcn4x.active.dcfclk_khz / 1000.0; + mode_lib->mp.FabricClock = programming->min_clocks.dcn4x.active.fclk_khz / 1000.0; + mode_lib->mp.uclk_freq_mhz = programming->min_clocks.dcn4x.active.uclk_khz / 1000.0; + mode_lib->mp.dram_bw_mbps = programming->qos_bound.bandwidth_lb.dcn5.urgent_bandwidth_kbps / 1000.0; + mode_lib->mp.urg_bandwidth_available[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_dram] = mode_lib->mp.dram_bw_mbps; + mode_lib->mp.UrgentLatency = programming->qos_bound.latency_ub.dcn5.urgent_ramp; + mode_lib->mp.TripToMemory = programming->qos_bound.latency_ub.dcn5.t_trip; + mode_lib->mp.MetaTripToMemory = programming->qos_bound.latency_ub.dcn5.meta_trip_to_mem; + s->mmSOCParameters.max_urgent_latency_us = programming->qos_bound.latency_ub.dcn5.max_req_latency_urg; + s->mmSOCParameters.df_response_time_us = programming->qos_bound.latency_ub.dcn5.df_response_time_us; + + DML_LOG_VERBOSE("DML::%s: Dcfclk = %f\n", __func__, mode_lib->mp.Dcfclk); + DML_LOG_VERBOSE("DML::%s: FabricClock = %f\n", __func__, mode_lib->mp.FabricClock); + DML_LOG_VERBOSE("DML::%s: uclk_freq_mhz = %f\n", __func__, mode_lib->mp.uclk_freq_mhz); + DML_LOG_VERBOSE("DML::%s: dram_bw_mbps = %f\n", __func__, mode_lib->mp.dram_bw_mbps); + DML_LOG_VERBOSE("DML::%s: UrgentLatency (urgent ramp) = %f\n", __func__, programming->qos_bound.latency_ub.dcn5.urgent_ramp); + DML_LOG_VERBOSE("DML::%s: TripToMemory = %f\n", __func__, mode_lib->mp.TripToMemory); + DML_LOG_VERBOSE("DML::%s: max_urgent_latency_us = %f\n", __func__, s->mmSOCParameters.max_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: GlobalDPPCLK = %f\n", __func__, mode_lib->mp.GlobalDPPCLK); +} + +enum dml2_status dml2_core_dcn5_funcs_populate_programming(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming) +{ + struct dml2_core_calcs_mode_programming_ex *params = &core->scratch.mode_programming_locals.mode_programming_ex_params; + struct dml2_core_internal_display_mode_lib *mode_lib = &core->clean_me_up.mode_lib; + bool result; + + memset(&mode_lib->scratch, 0, sizeof(struct dml2_core_internal_scratch)); + memset(&mode_lib->mp, 0, sizeof(struct dml2_core_internal_mode_program)); + + dcn5_mp_assign_qos_bound(core, programming); + params->mode_lib = mode_lib; + params->in_display_cfg = &solution->dispcfg; + params->cfg_support_info = &solution->validation_result.mode_support.cfg_support_info; + params->programming = programming; + params->uclk_params = &solution->uclk_pstate_params; + params->utm_soc_bb = core->utm_soc_bb; + params->solution = solution; + result = dcn5_mode_programming(params); + if (result) + dcn5_pack_mode_programming_params(core, params, programming); + + return result ? DML2_STATUS_OK : DML2_STATUS_POPULATE_FAIL_PROGRAMMING; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.h new file mode 100644 index 000000000000..1e4e91a62300 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN5_FUNCS_MODE_PROGRAMMING_H__ +#define __DML2_CORE_DCN5_FUNCS_MODE_PROGRAMMING_H__ +#include "dml2_internal_shared_types.h" +enum dml2_status dml2_core_dcn5_funcs_populate_programming(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming); +#endif /* __DML2_CORE_DCN5_FUNCS_MODE_PROGRAMMING_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.c new file mode 100644 index 000000000000..9d68c9dd0a1d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.c @@ -0,0 +1,2957 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn5_funcs_mode_support.h" +#include "dml2_core_utils.h" +#include "dml2_core_dcn5_calcs_dchub.h" +#include "dml2_core_dcn5_calcs_display_pipe.h" + +static void dcn5_ms_check_input_sanity( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + + mode_lib->ms.support.ViewportExceedsSurface = false; + if (!display_cfg->overrides.hw.surface_viewport_size_check_disable) { + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->composition.viewport.plane0.width > plane->surface.plane0.width + || plane->composition.viewport.plane0.height > plane->surface.plane0.height) { + mode_lib->ms.support.ViewportExceedsSurface = true; + DML_LOG_VERBOSE("DML::%s: k=%u ViewportWidth = %ld\n", __func__, k, plane->composition.viewport.plane0.width); + DML_LOG_VERBOSE("DML::%s: k=%u SurfaceWidthY = %ld\n", __func__, k, plane->surface.plane0.width); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportHeight = %ld\n", __func__, k, plane->composition.viewport.plane0.height); + DML_LOG_VERBOSE("DML::%s: k=%u SurfaceHeightY = %ld\n", __func__, k, plane->surface.plane0.height); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportExceedsSurface = %d\n", __func__, k, mode_lib->ms.support.ViewportExceedsSurface); + } + if (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) + || plane->pixel_format == dml2_rgbe_alpha) { + if (plane->composition.viewport.plane1.width > plane->surface.plane1.width + || plane->composition.viewport.plane1.height > plane->surface.plane1.height) { + mode_lib->ms.support.ViewportExceedsSurface = true; + } + } + } + } +} + +static void dcn5_ms_calculate_max_det_and_min_compressed_buffer_size( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + dcn5_calculate_max_det_and_min_compressed_buffer_size( + mode_lib->ip.config_return_buffer_size_in_kbytes, + mode_lib->ip.config_return_buffer_segment_size_in_kbytes, + mode_lib->ip.rob_buffer_size_kbytes, + mode_lib->ip.max_num_dpp, + display_cfg->overrides.hw.force_nom_det_size_kbytes.enable, + display_cfg->overrides.hw.force_nom_det_size_kbytes.value, + mode_lib->ip.dcn_mrq_present, + + /* Output */ + &mode_lib->ms.MaxTotalDETInKByte, + &mode_lib->ms.NomDETInKByte, + &mode_lib->ms.MinCompressedBufferSizeInKByte); +} + +static void dcn5_ms_calculate_effective_pixel_clock( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + /* + * This function should probably be removed since ptoi is never true, so the function is a noop; not really + * obvious if the comment means DML2.1 doesn't support interlace today. + */ + dcn5_adjust_pixel_clock_for_progressive_to_interlace_unit(display_cfg, mode_lib->ip.ptoi_supported, mode_lib->ms.PixelClockBackEnd); +} + +static bool dcn5_ms_check_scaler_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + /*Scale Ratio, taps Support Check*/ + unsigned int k; + bool support = true; + const struct dml2_plane_parameters *plane; + + // Many core tests are still setting scaling parameters "incorrectly" + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->composition.scaler_info.enabled == false + && (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) || dml2_core_utils_is_422_packed(plane->pixel_format) + || plane->composition.scaler_info.plane0.h_ratio != 1.0 + || plane->composition.scaler_info.plane0.h_taps != 1.0 + || plane->composition.scaler_info.plane0.v_ratio != 1.0 + || plane->composition.scaler_info.plane0.v_taps != 1.0)) { + support = false; + } else if (plane->composition.scaler_info.plane0.v_taps < 1.0 + || plane->composition.scaler_info.plane0.v_taps > 8.0 + || plane->composition.scaler_info.plane0.h_taps < 1.0 + || plane->composition.scaler_info.plane0.h_taps > 8.0 + || (plane->composition.scaler_info.plane0.h_taps > 1.0 + && (plane->composition.scaler_info.plane0.h_taps % 2) == 1) + || plane->composition.scaler_info.plane0.h_ratio > mode_lib->ip.max_hscl_ratio + || plane->composition.scaler_info.plane0.v_ratio > mode_lib->ip.max_vscl_ratio + || plane->composition.scaler_info.plane0.h_ratio > plane->composition.scaler_info.plane0.h_taps + || plane->composition.scaler_info.plane0.v_ratio > plane->composition.scaler_info.plane0.v_taps + || ((dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format)) + && (plane->composition.scaler_info.plane1.v_taps < 1 + || plane->composition.scaler_info.plane1.v_taps > 8 + || plane->composition.scaler_info.plane1.h_taps < 1 + || plane->composition.scaler_info.plane1.h_taps > 8 + || (plane->composition.scaler_info.plane1.h_taps > 1 + && plane->composition.scaler_info.plane1.h_taps % 2 == 1) + || plane->composition.scaler_info.plane1.h_ratio > mode_lib->ip.max_hscl_ratio + || plane->composition.scaler_info.plane1.v_ratio > mode_lib->ip.max_vscl_ratio + || plane->composition.scaler_info.plane1.h_ratio > plane->composition.scaler_info.plane1.h_taps + || plane->composition.scaler_info.plane1.v_ratio > plane->composition.scaler_info.plane1.v_taps))) { + support = false; + } + } + return support; +} + +static bool dcn5_ms_check_source_format_and_scan_direction( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + /*Source Format, Pixel Format and Scan Support Check*/ + unsigned int k; + bool support = true; + const struct dml2_plane_parameters *plane; + + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->surface.tiling == dml2_sw_linear + && dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle)) { + support = false; + } + } + return support; +} + +static void dcn5_ms_calculate_byte_per_pixel_and_block_sizes( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + plane = &display_cfg->plane_descriptors[k]; + dcn5_calculate_byte_per_pixel_and_block_sizes( + plane->pixel_format, + plane->surface.tiling, + plane->surface.plane0.pitch, + plane->surface.plane1.pitch, + /* Output */ + &mode_lib->ms.BytePerPixelY[k], + &mode_lib->ms.BytePerPixelC[k], + &mode_lib->ms.BytePerPixelInDETY[k], + &mode_lib->ms.BytePerPixelInDETC[k], + &mode_lib->ms.Read256BlockHeightY[k], + &mode_lib->ms.Read256BlockHeightC[k], + &mode_lib->ms.Read256BlockWidthY[k], + &mode_lib->ms.Read256BlockWidthC[k], + &mode_lib->ms.MacroTileHeightY[k], + &mode_lib->ms.MacroTileHeightC[k], + &mode_lib->ms.MacroTileWidthY[k], + &mode_lib->ms.MacroTileWidthC[k], + &mode_lib->ms.surf_linear128_l[k], + &mode_lib->ms.surf_linear128_c[k]); + } +} + +static void dcn5_ms_calculate_read_bandwidth( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + /* Bandwidth Support Check */ + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle)) { + mode_lib->ms.SwathWidthYSingleDPP[k] = plane->composition.viewport.plane0.width; + mode_lib->ms.SwathWidthCSingleDPP[k] = plane->composition.viewport.plane1.width; + } else { + mode_lib->ms.SwathWidthYSingleDPP[k] = plane->composition.viewport.plane0.height; + mode_lib->ms.SwathWidthCSingleDPP[k] = plane->composition.viewport.plane1.height; + } + } + for (k = 0; k < mode_lib->ms.num_active_planes ; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + mode_lib->ms.vactive_sw_bw_l[k] = mode_lib->ms.SwathWidthYSingleDPP[k] + * math_ceil2(mode_lib->ms.BytePerPixelY[k], 1.0) + / (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)) + * plane->composition.scaler_info.plane0.v_ratio; + mode_lib->ms.vactive_sw_bw_c[k] = mode_lib->ms.SwathWidthCSingleDPP[k] + * math_ceil2(mode_lib->ms.BytePerPixelC[k], 2.0) + / (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)) + * plane->composition.scaler_info.plane1.v_ratio; + mode_lib->ms.cursor_bw[k] = plane->cursor.num_cursors + * plane->cursor.cursor_width + * plane->cursor.cursor_bpp + / 8.0 + / (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)); + DML_LOG_VERBOSE("DML::%s: k=%u, old_ReadBandwidthLuma = %f\n", __func__, k, mode_lib->ms.SwathWidthYSingleDPP[k] * math_ceil2(mode_lib->ms.BytePerPixelInDETY[k], 1.0) / (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)) * display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u, old_ReadBandwidthChroma = %f\n", __func__, k, mode_lib->ms.SwathWidthYSingleDPP[k] / 2 * math_ceil2(mode_lib->ms.BytePerPixelInDETC[k], 2.0) / (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)) * display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio / 2.0); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_l = %f\n", __func__, k, + mode_lib->ms.vactive_sw_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_c = %f\n", __func__, k, + mode_lib->ms.vactive_sw_bw_c[k]); + } +} + +static void dcn5_ms_calculate_writeback_bandwidth( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k, j; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + // Writeback bandwidth + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + mode_lib->ms.WriteBandwidth[k][j] = stream->writeback.writeback_stream[j].output_height * stream->writeback.writeback_stream[j].output_width + / (stream->writeback.writeback_stream[j].input_height * stream->timing.h_total / ((double)stream->timing.pixel_clock_khz / 1000)); + if (stream->writeback.writeback_stream[j].pixel_format == dml2_444_64) { + mode_lib->ms.WriteBandwidth[k][j] *= 8.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_444_32) { + mode_lib->ms.WriteBandwidth[k][j] *= 4.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8) { + mode_lib->ms.WriteBandwidth[k][j] *= 1.5; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) { + mode_lib->ms.WriteBandwidth[k][j] *= 3.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_8) { + mode_lib->ms.WriteBandwidth[k][j] *= 2.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) { + mode_lib->ms.WriteBandwidth[k][j] *= 4.0; + } else { + mode_lib->ms.WriteBandwidth[k][j] = 0.0; + } + } + } +} + +static bool dcn5_ms_check_writeback_bandwidth_latency_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + /*Writeback Latency support check*/ + unsigned int k, j; + bool support = true; + const struct dml2_stream_parameters *stream; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8 || stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) {// In planar mode just check luma bw does not exceed half latency hiding buffer + if ((mode_lib->ms.WriteBandwidth[k][j] / 1.5 > + mode_lib->ip.writeback_interface_buffer_size_kbytes * 1024 / 2.0 // half buffer for luma + * ((stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) ? 1.6 : 1.0) // 16 bit frame buffer to 10 bit buffer packing + / utm_soc_bb->writeback_base_latency_us)) + support = false; + else + if ((mode_lib->ms.WriteBandwidth[k][j] > + mode_lib->ip.writeback_interface_buffer_size_kbytes * 1024 + * ((stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) ? 1.6 : 1.0) + / utm_soc_bb->writeback_base_latency_us)) + support = false; + } + } + } + return support; +} + +static bool dcn5_ms_check_writeback_scale_ratio_and_taps_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + /* Writeback Scale Ratio and Taps Support Check */ + unsigned int k; + unsigned int j; + bool support = true; + const struct dml2_stream_parameters *stream; + + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + + double h_ratio_chroma; + double output_width_chroma; + + if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8 || stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_8 + || stream->writeback.writeback_stream[j].pixel_format == dml2_420_10 || stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) { + h_ratio_chroma = 2.0 * stream->writeback.writeback_stream[j].h_ratio; + output_width_chroma = 0.5 * stream->writeback.writeback_stream[j].output_width; + } else { + h_ratio_chroma = stream->writeback.writeback_stream[j].h_ratio; + output_width_chroma = stream->writeback.writeback_stream[j].output_width; + } + + double v_ratio_chroma = ((stream->writeback.writeback_stream[j].pixel_format == dml2_420_8 || stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) ? 2.0 : 1.0) + * stream->writeback.writeback_stream[j].v_ratio; + + if (stream->writeback.writeback_stream[j].h_ratio > mode_lib->ip.writeback_max_hscl_ratio + || stream->writeback.writeback_stream[j].v_ratio > mode_lib->ip.writeback_max_vscl_ratio + || stream->writeback.writeback_stream[j].h_ratio < mode_lib->ip.writeback_min_hscl_ratio + || stream->writeback.writeback_stream[j].v_ratio < mode_lib->ip.writeback_min_vscl_ratio + || stream->writeback.writeback_stream[j].h_taps > (unsigned int) mode_lib->ip.writeback_max_hscl_taps + || stream->writeback.writeback_stream[j].v_taps > (unsigned int) mode_lib->ip.writeback_max_vscl_taps + || stream->writeback.writeback_stream[j].h_taps_chroma > (unsigned int)mode_lib->ip.writeback_max_hscl_taps + || stream->writeback.writeback_stream[j].v_taps_chroma > (unsigned int)mode_lib->ip.writeback_max_vscl_taps + || stream->writeback.writeback_stream[j].h_ratio > (unsigned int)stream->writeback.writeback_stream[j].h_taps + || stream->writeback.writeback_stream[j].v_ratio > (unsigned int)stream->writeback.writeback_stream[j].v_taps + || h_ratio_chroma > (unsigned int)stream->writeback.writeback_stream[j].h_taps_chroma + || v_ratio_chroma > (unsigned int)stream->writeback.writeback_stream[j].v_taps_chroma + || (stream->writeback.writeback_stream[j].h_taps > 2.0 && ((stream->writeback.writeback_stream[j].h_taps % 2) == 1)) + || (stream->writeback.writeback_stream[j].h_taps_chroma > 2.0 && ((stream->writeback.writeback_stream[j].h_taps_chroma % 2) == 1))) { + DML_LOG_VERBOSE("DML::%s: k=%d, j=%d, not support (%d)\n", __func__, k, j, __LINE__); + support = false; + } + + double writeback_luma_vextra = (stream->writeback.writeback_stream[j].v_ratio < 1) ? + math_max2(1 - 2.0 / math_ceil2(1 / stream->writeback.writeback_stream[j].v_ratio, 1.0), 0.0) : -1.0; + + if (stream->writeback.writeback_stream[j].output_width * (stream->writeback.writeback_stream[j].v_taps + writeback_luma_vextra) + > mode_lib->ip.writeback_line_buffer_buffer_size / 3.0 / 10.0) { // One third of the buffer per each component Y Cb Cr + DML_LOG_VERBOSE("DML::%s: k=%d, j=%d, not support (%d)\n", __func__, k, j, __LINE__); + support = false; + } + + double writeback_chroma_vextra = (v_ratio_chroma < 1) ? math_max2(1 - 2.0 / math_ceil2(1 / v_ratio_chroma, 1.0), 0.0) : -1.0; + + if (output_width_chroma * (stream->writeback.writeback_stream[j].v_taps_chroma + writeback_chroma_vextra) + > mode_lib->ip.writeback_line_buffer_buffer_size / 3.0 / 10.0) { // One third of the buffer per each component Y Cb Cr + DML_LOG_VERBOSE("DML::%s: k=%d, j=%d, not support (%d)\n", __func__, k, j, __LINE__); + support = false; + } + } + } + //support = true; + return support; +} + +static void dcn5_ms_calculate_single_pipe_dppclk_and_pscl_factor( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + dcn5_calculate_single_pipe_dppclk_and_scl_throughput( + plane->composition.scaler_info.plane0.h_ratio, + plane->composition.scaler_info.plane1.h_ratio, + plane->composition.scaler_info.plane0.v_ratio, + plane->composition.scaler_info.plane1.v_ratio, + mode_lib->ip.max_dchub_pscl_bw_pix_per_clk, + mode_lib->ip.max_pscl_lb_bw_pix_per_clk, + ((double) stream->timing.pixel_clock_khz / 1000), + plane->pixel_format, + plane->composition.scaler_info.plane0.h_taps, + plane->composition.scaler_info.plane1.h_taps, + plane->composition.scaler_info.plane0.v_taps, + plane->composition.scaler_info.plane1.v_taps, + + /* Output */ + &mode_lib->ms.PSCL_FACTOR[k], + &mode_lib->ms.PSCL_FACTOR_CHROMA[k], + &mode_lib->ms.MinDPPCLKUsingSingleDPP[k]); + } +} + +static void dcn5_ms_calculate_max_swath_widths( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + // Max Viewport Size support + unsigned int k; + const struct dml2_plane_parameters *plane; + unsigned int lb_buffer_size_bits_luma; + unsigned int lb_buffer_size_bits_chroma; + unsigned int maximumSwathWidthSupportLuma; + unsigned int maximumSwathWidthSupportChroma; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->surface.tiling == dml2_sw_linear) + maximumSwathWidthSupportLuma = 15360; + else if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) + && mode_lib->ms.BytePerPixelC[k] > 0 + && plane->pixel_format != dml2_rgbe_alpha) + // horz video + maximumSwathWidthSupportLuma = 7680 + 16; + else if (dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) + && mode_lib->ms.BytePerPixelC[k] > 0 + && plane->pixel_format != dml2_rgbe_alpha) + // vert video + maximumSwathWidthSupportLuma = 4320 + 16; + else if (plane->pixel_format == dml2_rgbe_alpha) + // rgbe + alpha + maximumSwathWidthSupportLuma = 5120 + 16; + else if (dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) + && mode_lib->ms.BytePerPixelY[k] == 8 + && plane->surface.dcc.enable == true) + // vert 64bpp + maximumSwathWidthSupportLuma = 3072 + 16; + else + maximumSwathWidthSupportLuma = 6144 + 16; + + if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) && dml2_core_utils_is_420(plane->pixel_format)) + maximumSwathWidthSupportChroma = (unsigned int) (maximumSwathWidthSupportLuma / 2.0); + else if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) && dml2_core_utils_is_422_planar(plane->pixel_format)) + maximumSwathWidthSupportChroma = (unsigned int)(maximumSwathWidthSupportLuma / 2.0); + else if (dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) && dml2_core_utils_is_420(plane->pixel_format)) + maximumSwathWidthSupportChroma = (unsigned int)(maximumSwathWidthSupportLuma / 2.0); + else + maximumSwathWidthSupportChroma = maximumSwathWidthSupportLuma; + + lb_buffer_size_bits_luma = mode_lib->ip.line_buffer_size_bits; + lb_buffer_size_bits_chroma = mode_lib->ip.line_buffer_size_bits; + + mode_lib->ms.MaximumSwathWidthInLineBufferLuma = lb_buffer_size_bits_luma + * math_max2(plane->composition.scaler_info.plane0.h_ratio, 1.0) + / 57 + / (plane->composition.scaler_info.plane0.v_taps + + math_max2(math_ceil2(plane->composition.scaler_info.plane0.v_ratio, 1.0) - 2, 0.0)); + if (mode_lib->ms.BytePerPixelC[k] == 0.0) + mode_lib->ms.MaximumSwathWidthInLineBufferChroma = 0; + else + mode_lib->ms.MaximumSwathWidthInLineBufferChroma = lb_buffer_size_bits_chroma + * math_max2(plane->composition.scaler_info.plane1.h_ratio, 1.0) + / 57 + / (plane->composition.scaler_info.plane1.v_taps + + math_max2(math_ceil2(plane->composition.scaler_info.plane1.v_ratio, 1.0) - 2, 0.0)); + mode_lib->ms.MaximumSwathWidthLuma[k] = math_min2(maximumSwathWidthSupportLuma, mode_lib->ms.MaximumSwathWidthInLineBufferLuma); + mode_lib->ms.MaximumSwathWidthChroma[k] = math_min2(maximumSwathWidthSupportChroma, mode_lib->ms.MaximumSwathWidthInLineBufferChroma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthLuma=%f\n", __func__, k, mode_lib->ms.MaximumSwathWidthLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthSupportLuma=%u\n", __func__, k, maximumSwathWidthSupportLuma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthInLineBufferLuma=%f\n", __func__, k, mode_lib->ms.MaximumSwathWidthInLineBufferLuma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthChroma=%f\n", __func__, k, mode_lib->ms.MaximumSwathWidthChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthSupportChroma=%u\n", __func__, k, maximumSwathWidthSupportChroma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthInLineBufferChroma=%f\n", __func__, k, mode_lib->ms.MaximumSwathWidthInLineBufferChroma); + } +} + +static bool dcn5_ms_check_cursor_support(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + /* Cursor Support Check */ + unsigned int k; + const struct dml2_plane_parameters *plane; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->cursor.cursor_width > 0.0) { + if (plane->cursor.cursor_bpp == 64 + && mode_lib->ip.cursor_64bpp_support == false) { + support = false; + } + } + } + return support; +} + +static bool dcn5_ms_check_surface_alginment_requirements( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + bool support = true; + + /* Valid Pitch Check */ + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + // data pitch + + plane = &display_cfg->plane_descriptors[k]; + unsigned int pixel_per_element = dml2_core_utils_is_422_packed(plane->pixel_format) ? 2 : 1; + unsigned int alignment_l = mode_lib->ms.MacroTileWidthY[k]; + if (mode_lib->ms.surf_linear128_l[k]) + alignment_l = alignment_l / 2; + DML_LOG_VERBOSE("DML::%s: alignment_l = %d\n", __func__, alignment_l); + + mode_lib->ms.support.AlignedYPitch[k] = (unsigned int)math_ceil2(math_max2(plane->surface.plane0.pitch, plane->surface.plane0.width / pixel_per_element), alignment_l / pixel_per_element); + if (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) || plane->pixel_format == dml2_rgbe_alpha) { + unsigned int alignment_c = mode_lib->ms.MacroTileWidthC[k]; + + if (mode_lib->ms.surf_linear128_c[k]) + alignment_c = alignment_c / 2; + mode_lib->ms.support.AlignedCPitch[k] = (unsigned int)math_ceil2(math_max2(plane->surface.plane1.pitch, plane->surface.plane1.width), alignment_c); + } else { + mode_lib->ms.support.AlignedCPitch[k] = plane->surface.plane1.pitch; + } + + if (mode_lib->ms.support.AlignedYPitch[k] > plane->surface.plane0.pitch || + mode_lib->ms.support.AlignedCPitch[k] > plane->surface.plane1.pitch) { + support = false; + DML_LOG_VERBOSE("DML::%s: k=%u AlignedYPitch = %d\n", __func__, k, mode_lib->ms.support.AlignedYPitch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u PitchY = %ld\n", __func__, k, plane->surface.plane0.pitch); + DML_LOG_VERBOSE("DML::%s: k=%u AlignedCPitch = %d\n", __func__, k, mode_lib->ms.support.AlignedCPitch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u PitchC = %ld\n", __func__, k, plane->surface.plane1.pitch); + DML_LOG_VERBOSE("DML::%s: k=%u PitchSupport = %d\n", __func__, k, mode_lib->ms.support.PitchSupport); + } + + // meta pitch + if (mode_lib->ip.dcn_mrq_present && plane->surface.dcc.enable) { + mode_lib->ms.support.AlignedDCCMetaPitchY[k] = (unsigned int)math_ceil2(math_max2(plane->surface.dcc.plane0.pitch, + plane->surface.plane0.width), 64.0 * mode_lib->ms.Read256BlockWidthY[k]); + + if (mode_lib->ms.support.AlignedDCCMetaPitchY[k] > plane->surface.dcc.plane0.pitch) + support = false; + + if (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) || plane->pixel_format == dml2_rgbe_alpha) { + mode_lib->ms.support.AlignedDCCMetaPitchC[k] = (unsigned int)math_ceil2(math_max2(plane->surface.dcc.plane1.pitch, + plane->surface.plane1.width), 64.0 * mode_lib->ms.Read256BlockWidthC[k]); + + if (mode_lib->ms.support.AlignedDCCMetaPitchC[k] > plane->surface.dcc.plane1.pitch) + support = false; + } + } else { + mode_lib->ms.support.AlignedDCCMetaPitchY[k] = 0; + mode_lib->ms.support.AlignedDCCMetaPitchC[k] = 0; + } + } + + return support; +} + +static void dcn5_ms_calculate_swath_and_det_configuration_for_single_dpp( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_core_calcs_mode_support_locals *s) +{ + /* + * FIXME - The whole point of this call seems to be to figure out SingleDPPViewportSizeSupportPerSurface, which + * if 0, means you need 2 DPPs. + */ + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *p = + &mode_lib->scratch.CalculateSwathAndDETConfiguration_params; + + p->display_cfg = display_cfg; + p->ConfigReturnBufferSizeInKByte = mode_lib->ip.config_return_buffer_size_in_kbytes; + p->MaxTotalDETInKByte = mode_lib->ms.MaxTotalDETInKByte; + p->MinCompressedBufferSizeInKByte = mode_lib->ms.MinCompressedBufferSizeInKByte; + p->rob_buffer_size_kbytes = mode_lib->ip.rob_buffer_size_kbytes; + p->pixel_chunk_size_kbytes = mode_lib->ip.pixel_chunk_size_kbytes; + p->rob_buffer_size_kbytes = mode_lib->ip.rob_buffer_size_kbytes; + p->pixel_chunk_size_kbytes = mode_lib->ip.pixel_chunk_size_kbytes; + p->ForceSingleDPP = 1; + p->NumberOfActiveSurfaces = mode_lib->ms.num_active_planes; + p->nomDETInKByte = mode_lib->ms.NomDETInKByte; + p->ConfigReturnBufferSegmentSizeInkByte = mode_lib->ip.config_return_buffer_segment_size_in_kbytes; + p->CompressedBufferSegmentSizeInkByte = mode_lib->ip.compressed_buffer_segment_size_in_kbytes; + p->ReadBandwidthLuma = mode_lib->ms.vactive_sw_bw_l; + p->ReadBandwidthChroma = mode_lib->ms.vactive_sw_bw_c; + p->MaximumSwathWidthLuma = mode_lib->ms.MaximumSwathWidthLuma; + p->MaximumSwathWidthChroma = mode_lib->ms.MaximumSwathWidthChroma; + p->Read256BytesBlockHeightY = mode_lib->ms.Read256BlockHeightY; + p->Read256BytesBlockHeightC = mode_lib->ms.Read256BlockHeightC; + p->Read256BytesBlockWidthY = mode_lib->ms.Read256BlockWidthY; + p->Read256BytesBlockWidthC = mode_lib->ms.Read256BlockWidthC; + p->surf_linear128_l = mode_lib->ms.surf_linear128_l; + p->surf_linear128_c = mode_lib->ms.surf_linear128_c; + p->ODMMode = s->dummy_odm_mode; + p->BytePerPixY = mode_lib->ms.BytePerPixelY; + p->BytePerPixC = mode_lib->ms.BytePerPixelC; + p->BytePerPixDETY = mode_lib->ms.BytePerPixelInDETY; + p->BytePerPixDETC = mode_lib->ms.BytePerPixelInDETC; + p->DPPPerSurface = s->dummy_integer_array[2]; + p->mrq_present = mode_lib->ip.dcn_mrq_present; + // output + p->req_per_swath_ub_l = s->dummy_integer_array[0]; + p->req_per_swath_ub_c = s->dummy_integer_array[1]; + p->swath_width_luma_ub = s->dummy_integer_array[3]; + p->swath_width_chroma_ub = s->dummy_integer_array[4]; + p->SwathWidth = s->dummy_integer_array[5]; + p->SwathWidthChroma = s->dummy_integer_array[6]; + p->SwathHeightY = s->dummy_integer_array[7]; + p->SwathHeightC = s->dummy_integer_array[8]; + p->request_size_bytes_luma = s->dummy_integer_array[26]; + p->request_size_bytes_chroma = s->dummy_integer_array[27]; + p->DETBufferSizeInKByte = s->dummy_integer_array[9]; + p->DETBufferSizeY = s->dummy_integer_array[10]; + p->DETBufferSizeC = s->dummy_integer_array[11]; + p->full_swath_bytes_l = mode_lib->ms.full_swath_bytes_l; + p->full_swath_bytes_c = mode_lib->ms.full_swath_bytes_c; + p->full_swath_bytes_single_dpp_l = s->dummy_integer_array[28]; + p->full_swath_bytes_single_dpp_c = s->dummy_integer_array[29]; + p->UnboundedRequestEnabled = &s->dummy_boolean[0]; + p->compbuf_reserved_space_64b = &s->dummy_integer[1]; + p->hw_debug5 = &s->dummy_boolean[2]; + p->CompressedBufferSizeInkByte = &s->dummy_integer[0]; + p->ViewportSizeSupportPerSurface = mode_lib->ms.SingleDPPViewportSizeSupportPerSurface; + p->ViewportSizeSupport = &s->dummy_boolean[1]; + // This calls is just to find out if there is enough DET space to support full vp in 1 pipe. + dcn5_calculate_swath_and_det_configuration(&mode_lib->scratch, p); +} + +static void dcn5_ms_calculate_dsc_slices_per_plane( + unsigned int k, + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + const struct dml2_stream_parameters *stream = + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + /*Number Of DSC Slices*/ + if (stream->timing.dsc.enable == dml2_dsc_enable + || stream->timing.dsc.enable == dml2_dsc_enable_if_necessary) { + if (stream->timing.dsc.overrides.num_slices != 0) + mode_lib->ms.support.NumberOfDSCSlices[k] = stream->timing.dsc.overrides.num_slices; + else { + if (mode_lib->ms.PixelClockBackEnd[k] > 7200) { + mode_lib->ms.support.NumberOfDSCSlices[k] = 16; + } else if (mode_lib->ms.PixelClockBackEnd[k] > 3200) { + mode_lib->ms.support.NumberOfDSCSlices[k] = 12; + } else if (mode_lib->ms.PixelClockBackEnd[k] > 1360) { + mode_lib->ms.support.NumberOfDSCSlices[k] = 8; + } else if (mode_lib->ms.PixelClockBackEnd[k] > 680) { + mode_lib->ms.support.NumberOfDSCSlices[k] = 4; + } else if (mode_lib->ms.PixelClockBackEnd[k] > 340) { + mode_lib->ms.support.NumberOfDSCSlices[k] = 2; + } else { + mode_lib->ms.support.NumberOfDSCSlices[k] = 1; + } + } + } else { + mode_lib->ms.support.NumberOfDSCSlices[k] = 0; + } +} + +static void dcn5_ms_calculate_odm_mode_per_plane( + unsigned int k, + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + const struct dml2_stream_parameters *stream = + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + DML_LOG_VERBOSE("DML_CORE::%s: k=%d stream_index=%d overrides.odm_mode=%d\n", __func__, k, + display_cfg->plane_descriptors[k].stream_index, stream->overrides.odm_mode); + dcn5_calculate_odm_mode( + mode_lib->ip.maximum_pixels_per_line_per_dsc_unit, + stream->timing.h_active, + stream->output.output_format, + stream->output.output_encoder, + stream->overrides.odm_mode, + mode_lib->ms.max_dispclk_freq_mhz, + false, // DSCEnable + mode_lib->ms.TotalNumberOfActiveDPP, + mode_lib->ip.max_num_dpp, + ((double)stream->timing.pixel_clock_khz / 1000), + mode_lib->ip.maximum_dsc_slices_per_pipe, + mode_lib->ms.support.NumberOfDSCSlices[k], + mode_lib->ip.odm_combine_support_mask, + + /* Output */ + &mode_lib->ms.TotalAvailablePipesSupportNoDSC, + &mode_lib->ms.NumberOfDPPNoDSC, + &mode_lib->ms.ODMModeNoDSC, + &mode_lib->ms.RequiredDISPCLKPerSurfaceNoDSC); + + dcn5_calculate_odm_mode( + mode_lib->ip.maximum_pixels_per_line_per_dsc_unit, + stream->timing.h_active, + stream->output.output_format, + stream->output.output_encoder, + stream->overrides.odm_mode, + mode_lib->ms.max_dispclk_freq_mhz, + true, // DSCEnable + mode_lib->ms.TotalNumberOfActiveDPP, + mode_lib->ip.max_num_dpp, + ((double)stream->timing.pixel_clock_khz / 1000), + mode_lib->ip.maximum_dsc_slices_per_pipe, + mode_lib->ms.support.NumberOfDSCSlices[k], + mode_lib->ip.odm_combine_support_mask, + + /* Output */ + &mode_lib->ms.TotalAvailablePipesSupportDSC, + &mode_lib->ms.NumberOfDPPDSC, + &mode_lib->ms.ODMModeDSC, + &mode_lib->ms.RequiredDISPCLKPerSurfaceDSC); +} + +static void dcn5_ms_calculate_output_link( + unsigned int k, + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + const struct dml2_stream_parameters *stream = + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + dcn5_calculate_output_link( + &mode_lib->scratch, + ((double) utm_soc_bb->max_phyclk_khz / 1000), + ((double) utm_soc_bb->max_phyclk_d18_khz / 1000), + ((double) utm_soc_bb->max_phyclk_d32_khz / 1000), + utm_soc_bb->phy_downspread_percent, + stream->output.output_encoder, + stream->output.output_format, + stream->timing.h_total, + stream->timing.h_active, + mode_lib->ms.PixelClockBackEnd[k], + mode_lib->ms.DesiredOutputBpp[k], + mode_lib->ip.maximum_dsc_bits_per_component, + mode_lib->ms.support.NumberOfDSCSlices[k], + stream->output.audio_sample_rate, + stream->output.audio_sample_layout, + mode_lib->ms.ODMModeNoDSC, + mode_lib->ms.ODMModeDSC, + stream->timing.dsc.enable, + stream->output.output_dp_lane_count, + stream->output.output_dp_link_rate, + /* Output */ + &mode_lib->ms.RequiresDSC[k], + &mode_lib->ms.RequiresFEC[k], + &mode_lib->ms.OutputBpp[k], + &mode_lib->ms.OutputType[k], + &mode_lib->ms.OutputRate[k], + &mode_lib->ms.RequiredSlots[k]); + if (mode_lib->ms.DesiredOutputBpp[k] == 0.0) + mode_lib->ms.DesiredOutputBpp[k] = mode_lib->ms.OutputBpp[k]; +} + +static void dcn5_ms_calculate_final_odm_mode_per_plane(unsigned int k, struct dml2_core_internal_display_mode_lib *mode_lib) +{ + if (mode_lib->ms.RequiresDSC[k] == false) { + mode_lib->ms.ODMMode[k] = mode_lib->ms.ODMModeNoDSC; + mode_lib->ms.RequiredDISPCLKPerSurface[k] = mode_lib->ms.RequiredDISPCLKPerSurfaceNoDSC; + if (!mode_lib->ms.TotalAvailablePipesSupportNoDSC) + /* FIXME: ODM Mode decision should not depend on pipe availability to avoid circular dependency */ + mode_lib->ms.support.TotalAvailablePipesSupport = false; + mode_lib->ms.TotalNumberOfActiveDPP = mode_lib->ms.TotalNumberOfActiveDPP + mode_lib->ms.NumberOfDPPNoDSC; + } else { + mode_lib->ms.ODMMode[k] = mode_lib->ms.ODMModeDSC; + mode_lib->ms.RequiredDISPCLKPerSurface[k] = mode_lib->ms.RequiredDISPCLKPerSurfaceDSC; + if (!mode_lib->ms.TotalAvailablePipesSupportDSC) + /* FIXME: ODM Mode decision should not depend on pipe availability to avoid circular dependency */ + mode_lib->ms.support.TotalAvailablePipesSupport = false; + mode_lib->ms.TotalNumberOfActiveDPP = mode_lib->ms.TotalNumberOfActiveDPP + mode_lib->ms.NumberOfDPPDSC; + } + DML_LOG_VERBOSE("DML::%s: k=%d RequiresDSC = %d\n", __func__, k, mode_lib->ms.RequiresDSC[k]); + DML_LOG_VERBOSE("DML::%s: k=%d ODMMode = %d\n", __func__, k, mode_lib->ms.ODMMode[k]); +} + +static void dcn5_ms_calculate_final_dsc_slices_per_plane(unsigned int k, + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + const struct dml2_stream_parameters *stream = + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + // ensure the number dsc slices is integer multiple based on ODM mode + mode_lib->ms.support.DSCSlicesODMModeSupported = true; + if (mode_lib->ms.RequiresDSC[k]) { + // fail a ms check if the override num_slices doesn't align with odm mode setting + if (stream->timing.dsc.overrides.num_slices != 0) { + if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_2to1) + mode_lib->ms.support.DSCSlicesODMModeSupported = + ((mode_lib->ms.support.NumberOfDSCSlices[k] % 2) == 0); + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_3to1) + mode_lib->ms.support.DSCSlicesODMModeSupported = + (mode_lib->ms.support.NumberOfDSCSlices[k] == 12); + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_4to1) + mode_lib->ms.support.DSCSlicesODMModeSupported = + ((mode_lib->ms.support.NumberOfDSCSlices[k] % 4) == 0); + if (!mode_lib->ms.support.DSCSlicesODMModeSupported) { + DML_LOG_VERBOSE("DML::%s: k=%d Invalid dsc num_slices and ODM mode setting\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: k=%d num_slices = %d\n", __func__, k, stream->timing.dsc.overrides.num_slices); + DML_LOG_VERBOSE("DML::%s: k=%d ODMMode = %d\n", __func__, k, mode_lib->ms.ODMMode[k]); + } + } else { + // safe guard to ensure the dml derived dsc slices and odm setting are compatible + if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_2to1) + mode_lib->ms.support.NumberOfDSCSlices[k] = 2 * (unsigned int) math_ceil2(mode_lib->ms.support.NumberOfDSCSlices[k] / 2.0, 1.0); + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_3to1) + mode_lib->ms.support.NumberOfDSCSlices[k] = 12; + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_4to1) + mode_lib->ms.support.NumberOfDSCSlices[k] = 4 * (unsigned int) math_ceil2(mode_lib->ms.support.NumberOfDSCSlices[k] / 4.0, 1.0); + } + } else { + mode_lib->ms.support.NumberOfDSCSlices[k] = 0; + } +} + +static void dcn5_ms_calculate_num_of_dpp_required( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + plane = &display_cfg->plane_descriptors[k]; + mode_lib->ms.MPCCombine[k] = false; + mode_lib->ms.NoOfDPP[k] = 1; + if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_4to1) { + mode_lib->ms.MPCCombine[k] = false; + mode_lib->ms.NoOfDPP[k] = 4; + } else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_3to1) { + mode_lib->ms.MPCCombine[k] = false; + mode_lib->ms.NoOfDPP[k] = 3; + } else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_2to1) { + mode_lib->ms.MPCCombine[k] = false; + mode_lib->ms.NoOfDPP[k] = 2; + } else if (plane->overrides.mpcc_combine_factor == 2) { + mode_lib->ms.MPCCombine[k] = true; + mode_lib->ms.NoOfDPP[k] = 2; + mode_lib->ms.TotalNumberOfActiveDPP++; + } else if (plane->overrides.mpcc_combine_factor == 1) { + mode_lib->ms.MPCCombine[k] = false; + mode_lib->ms.NoOfDPP[k] = 1; + if (!mode_lib->ms.SingleDPPViewportSizeSupportPerSurface[k]) { + DML_LOG_VERBOSE("WARNING: DML::%s: MPCC is override to disable but viewport is too large to be supported with single pipe!\n", __func__); + } + } else { + if ((mode_lib->ms.MinDPPCLKUsingSingleDPP[k] > mode_lib->ms.max_dppclk_freq_mhz) + || !mode_lib->ms.SingleDPPViewportSizeSupportPerSurface[k]) { + mode_lib->ms.MPCCombine[k] = true; + mode_lib->ms.NoOfDPP[k] = 2; + mode_lib->ms.TotalNumberOfActiveDPP++; + } + } + DML_LOG_VERBOSE("DML::%s: k=%d, NoOfDPP = %d\n", __func__, k, + mode_lib->ms.NoOfDPP[k]); + } +} + +static bool dcn5_ms_check_total_available_pipes_support( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + return mode_lib->ms.TotalNumberOfActiveDPP <= (unsigned int) mode_lib->ip.max_num_dpp; +} + +static void dcn5_ms_calculate_total_num_of_single_dpp_surfaces( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + + for (k = 0; k < (unsigned int) mode_lib->ms.num_active_planes; ++k) { + if (mode_lib->ms.NoOfDPP[k] == 1) + mode_lib->ms.TotalNumberOfSingleDPPSurfaces = + mode_lib->ms.TotalNumberOfSingleDPPSurfaces + 1; + } +} + +static void dcn5_ms_calculate_dispclk_and_dppclk_required( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k, j; + const struct dml2_stream_parameters *stream; + double writeback_required_dispclk; + + //DISPCLK/DPPCLK + mode_lib->ms.WritebackRequiredDISPCLK = 0; + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; ++j) { + writeback_required_dispclk = dcn5_calculate_write_back_dispclk( + stream->writeback.writeback_stream[j].pixel_format, + ((double)stream->timing.pixel_clock_khz / 1000), + mode_lib->ms.ODMMode[k], + stream->writeback.writeback_stream[j].h_ratio, + stream->writeback.writeback_stream[j].v_ratio, + stream->writeback.writeback_stream[j].h_taps, + stream->writeback.writeback_stream[j].v_taps, + stream->writeback.writeback_stream[j].h_taps_chroma, + stream->writeback.writeback_stream[j].v_taps_chroma, + stream->writeback.writeback_stream[j].input_width, + stream->writeback.writeback_stream[j].output_width, + stream->timing.h_total, + mode_lib->ip.writeback_line_buffer_buffer_size); + mode_lib->ms.WritebackRequiredDISPCLK = math_max2( + mode_lib->ms.WritebackRequiredDISPCLK, + writeback_required_dispclk); + } + } + + mode_lib->ms.RequiredDISPCLK = mode_lib->ms.WritebackRequiredDISPCLK; + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + mode_lib->ms.RequiredDISPCLK = math_max2( + mode_lib->ms.RequiredDISPCLK, + mode_lib->ms.RequiredDISPCLKPerSurface[k]); + } + + mode_lib->ms.GlobalDPPCLK = 0; + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + mode_lib->ms.RequiredDPPCLK[k] = mode_lib->ms.MinDPPCLKUsingSingleDPP[k] / mode_lib->ms.NoOfDPP[k]; + mode_lib->ms.GlobalDPPCLK = math_max2(mode_lib->ms.GlobalDPPCLK, mode_lib->ms.RequiredDPPCLK[k]); + } +} + +static bool dcn5_ms_check_dispclk_and_dppclk_support(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + + return (mode_lib->ms.RequiredDISPCLK <= mode_lib->ms.max_dispclk_freq_mhz) + && (mode_lib->ms.GlobalDPPCLK <= mode_lib->ms.max_dppclk_freq_mhz); +} + +static bool dcn5_ms_check_otg_count_support( + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int TotalNumberOfActiveOTG = 0; + bool stream_visited[DML2_MAX_PLANES] = { 0 }; + + for (unsigned int k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (!stream_visited[display_cfg->plane_descriptors[k].stream_index]) { + stream_visited[display_cfg->plane_descriptors[k].stream_index] = true; + TotalNumberOfActiveOTG = TotalNumberOfActiveOTG + 1; + } + } + return TotalNumberOfActiveOTG <= mode_lib->ip.max_num_otg; +} + +static bool dcn5_ms_check_hpo_frl_encoder_count_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + bool stream_visited[DML2_MAX_PLANES] = { 0 }; + unsigned int totalNumberOfActiveHDMIFRL = 0; + + for (unsigned int k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (!stream_visited[display_cfg->plane_descriptors[k].stream_index]) { + stream_visited[display_cfg->plane_descriptors[k].stream_index] = true; + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_encoder == dml2_hdmifrl) + totalNumberOfActiveHDMIFRL++; + } + } + return totalNumberOfActiveHDMIFRL <= mode_lib->ip.max_num_hdmi_frl_outputs; +} + +static bool dcn5_ms_check_hpo_dp_encoder_count_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + bool stream_visited[DML2_MAX_PLANES] = { 0 }; + unsigned int totalNumberOfActiveDP2p0 = 0; + unsigned int totalNumberOfActiveDP2p0Outputs = 0; + + for (unsigned int k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (!stream_visited[display_cfg->plane_descriptors[k].stream_index]) { + stream_visited[display_cfg->plane_descriptors[k].stream_index] = true; + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_encoder == dml2_dp2p0) { + totalNumberOfActiveDP2p0++; + // FIXME_STAGE2: SW not using backend related stuff, need mapping for mst setup + //if (display_cfg->output.OutputMultistreamId[k] == k || display_cfg->output.OutputMultistreamEn[k] == false) { + totalNumberOfActiveDP2p0Outputs++; + } + } + } + return (totalNumberOfActiveDP2p0 <= mode_lib->ip.max_num_dp2p0_streams) + && (totalNumberOfActiveDP2p0Outputs <= mode_lib->ip.max_num_dp2p0_outputs); +} + +static bool dcn5_ms_check_writeback_count_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int stream_idx; + unsigned int totalNumberOfActiveWriteback = 0; + bool writeback_per_stream_supported = true; + + for (stream_idx = 0; stream_idx < display_cfg->num_streams; stream_idx++) { + totalNumberOfActiveWriteback += + display_cfg->stream_descriptors[stream_idx].writeback.active_writebacks_per_stream; + + /* >1 writeback per stream is currently not supported */ + if (display_cfg->stream_descriptors[stream_idx].writeback.active_writebacks_per_stream > 1) { + writeback_per_stream_supported = false; + } + } + + return writeback_per_stream_supported && + totalNumberOfActiveWriteback <= mode_lib->ip.max_num_wb; +} + +static bool dcn5_ms_check_link_bandwidth_support( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + if (!dml2_core_utils_is_stream_encoder_required(stream) + || stream->output.output_disabled) + continue; + + support &= (mode_lib->ms.OutputBpp[k] > 0); + } + return support; +} + +static void dcn5_ms_check_misc_link_supports( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + if (!dml2_core_utils_is_stream_encoder_required(stream)) + continue; + + if (stream->output.output_format == dml2_420 + && stream->timing.interlaced + && mode_lib->ip.ptoi_supported) + mode_lib->ms.support.P2IWith420 = true; + + if (stream->timing.dsc.enable == dml2_dsc_enable + || stream->timing.dsc.enable == dml2_dsc_enable_if_necessary) { + if (stream->output.output_format == dml2_n422 + && !mode_lib->ip.dsc422_native_support) + mode_lib->ms.support.DSC422NativeNotSupported = true; + } + + if (dml2_core_utils_is_dp_8b_10b_link_rate(stream->output.output_dp_link_rate) + && !dml2_core_utils_is_dio_dp_encoder(stream)) + mode_lib->ms.support.LinkRateDoesNotMatchDPVersion = true; + else if (dml2_core_utils_is_dp_128b_132b_link_rate(stream->output.output_dp_link_rate) + && !dml2_core_utils_is_hpo_dp_encoder(stream)) + mode_lib->ms.support.LinkRateDoesNotMatchDPVersion = true; + + if (dml2_core_utils_is_odm_split(stream->overrides.odm_mode) + && !dml2_core_utils_is_dp_encoder(stream)) + mode_lib->ms.support.MSOOrODMSplitWithNonDPLink = true; + + if (stream->overrides.odm_mode == dml2_odm_mode_mso_1to2 + && stream->output.output_dp_lane_count < 2) + mode_lib->ms.support.NotEnoughLanesForMSO = true; + else if (stream->overrides.odm_mode == dml2_odm_mode_mso_1to4 + && stream->output.output_dp_lane_count < 4) + mode_lib->ms.support.NotEnoughLanesForMSO = true; + } +} + +static void dcn5_ms_calculate_dtbclk_required( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + if (stream->output.output_encoder == dml2_hdmifrl) { + mode_lib->ms.RequiredDTBCLK[k] = dcn5_calculate_required_dtbclk( + mode_lib->ms.RequiresDSC[k], + mode_lib->ms.PixelClockBackEnd[k], + stream->output.output_format, + mode_lib->ms.OutputBpp[k], + mode_lib->ms.support.NumberOfDSCSlices[k], + stream->timing.h_total, + stream->timing.h_active, + stream->output.audio_sample_rate, + stream->output.audio_sample_layout); + } + } +} + +static bool dcn5_ms_check_dtbclk_support( + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int k; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) + if (mode_lib->ms.RequiredDTBCLK[k] > ((double) utm_soc_bb->max_dtbclk_khz / 1000)) + support = false; + return support; +} + +static void dcn5_ms_calculate_dscclk_required( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + unsigned int DSCFormatFactor; + double pixelClockBackEndFactor; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + if (!dml2_core_utils_is_encoder_dsc_capable(stream)) + continue; + + if (stream->output.output_format == dml2_420 + || stream->output.output_format == dml2_n422) + DSCFormatFactor = 2; + else + DSCFormatFactor = 1; + + DML_LOG_VERBOSE("DML::%s: k=%u, RequiresDSC = %u\n", __func__, k, mode_lib->ms.RequiresDSC[k]); + if (!mode_lib->ms.RequiresDSC[k]) + continue; + + unsigned int num_dsc_units; + + if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_4to1) + num_dsc_units = 4; + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_3to1) + num_dsc_units = 3; + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_2to1) + num_dsc_units = 2; + else + num_dsc_units = 1; + + pixelClockBackEndFactor = 3.0 * num_dsc_units; + + if (mode_lib->ms.support.NumberOfDSCSlices[k] > num_dsc_units) + pixelClockBackEndFactor *= 2; + + mode_lib->ms.required_dscclk_freq_mhz[k] = mode_lib->ms.PixelClockBackEnd[k] / pixelClockBackEndFactor / (double) DSCFormatFactor; + if (mode_lib->ms.required_dscclk_freq_mhz[k] > mode_lib->ms.max_dscclk_freq_mhz) + mode_lib->ms.support.DSCCLKRequiredMoreThanSupported = true; + DML_LOG_VERBOSE("DML::%s: k=%u, PixelClockBackEnd = %f\n", __func__, k, mode_lib->ms.PixelClockBackEnd[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, required_dscclk_freq_mhz = %f\n", __func__, k, mode_lib->ms.required_dscclk_freq_mhz[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, DSCFormatFactor = %u\n", __func__, k, DSCFormatFactor); + DML_LOG_VERBOSE("DML::%s: k=%u, DSCCLKRequiredMoreThanSupported = %u\n", __func__, k, mode_lib->ms.support.DSCCLKRequiredMoreThanSupported); + } +} + +static bool dcn5_ms_check_dscclk_support( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) + if (mode_lib->ms.required_dscclk_freq_mhz[k] > mode_lib->ms.max_dscclk_freq_mhz) + support = false; + return support; +} + +static void dcn5_ms_check_dsc_engine_supports( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + unsigned int totalDSCUnitsRequired = 0; + unsigned int numDSCUnitRequired; + unsigned int stream_visited_bit_map = 0; + unsigned int stream_index; + + mode_lib->ms.support.PixelsPerLinePerDSCUnitSupport = true; + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + stream_index = display_cfg->plane_descriptors[k].stream_index; + + /* Check if stream has been visited */ + if (stream_visited_bit_map & (1 << stream_index)) + continue; + + /* Mark stream as visited */ + stream_visited_bit_map |= (1 << stream_index); + + if (!mode_lib->ms.RequiresDSC[k]) + continue; + + if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_4to1) + numDSCUnitRequired = 4; + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_3to1) + numDSCUnitRequired = 3; + else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_2to1) + numDSCUnitRequired = 2; + else + numDSCUnitRequired = 1; + + stream = &display_cfg->stream_descriptors[stream_index]; + + if (stream->timing.h_active > numDSCUnitRequired * mode_lib->ip.maximum_pixels_per_line_per_dsc_unit) + mode_lib->ms.support.PixelsPerLinePerDSCUnitSupport = false; + totalDSCUnitsRequired += numDSCUnitRequired; + + if (mode_lib->ms.support.NumberOfDSCSlices[k] > numDSCUnitRequired * mode_lib->ip.maximum_dsc_slices_per_pipe) + mode_lib->ms.support.NotEnoughDSCSlices = true; + } + + if (totalDSCUnitsRequired > mode_lib->ip.num_dsc) + mode_lib->ms.support.NotEnoughDSCUnits = true; +} + +static void dcn5_ms_calculate_dsc_delay( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + + /*DSC Delay per state*/ + for (k = 0; k < display_cfg->num_planes; ++k) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + mode_lib->ms.DSCDelay[k] = dcn5_calculate_dsc_delay_requirement( + mode_lib->ms.RequiresDSC[k], + mode_lib->ms.ODMMode[k], + mode_lib->ip.maximum_dsc_bits_per_component, + mode_lib->ms.DesiredOutputBpp[k], + stream->timing.h_active, + stream->timing.h_total, + mode_lib->ms.support.NumberOfDSCSlices[k], + stream->output.output_format, + stream->output.output_encoder, + ((double) stream->timing.pixel_clock_khz / 1000), + mode_lib->ms.PixelClockBackEnd[k], + mode_lib->ms.use_legacy_dsc_delay_formula); + } +} + +static void dcn5_ms_calculate_swath_and_det_configuration( + struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_core_calcs_mode_support_locals *s) +{ + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *p = + &mode_lib->scratch.CalculateSwathAndDETConfiguration_params; + + // Figure out the swath and DET configuration after the num dpp per plane is figured out + p->ForceSingleDPP = false; + p->ODMMode = mode_lib->ms.ODMMode; + p->DPPPerSurface = mode_lib->ms.NoOfDPP; + // output + p->req_per_swath_ub_l = s->dummy_integer_array[0]; + p->req_per_swath_ub_c = s->dummy_integer_array[1]; + p->swath_width_luma_ub = mode_lib->ms.swath_width_luma_ub; + p->swath_width_chroma_ub = mode_lib->ms.swath_width_chroma_ub; + p->SwathWidth = mode_lib->ms.SwathWidthY; + p->SwathWidthChroma = mode_lib->ms.SwathWidthC; + p->SwathHeightY = mode_lib->ms.SwathHeightY; + p->SwathHeightC = mode_lib->ms.SwathHeightC; + p->request_size_bytes_luma = mode_lib->ms.support.request_size_bytes_luma; + p->request_size_bytes_chroma = mode_lib->ms.support.request_size_bytes_chroma; + p->DETBufferSizeInKByte = mode_lib->ms.DETBufferSizeInKByte; // FIXME: This is per pipe but the pipes in plane will use that + p->DETBufferSizeY = mode_lib->ms.DETBufferSizeY; + p->DETBufferSizeC = mode_lib->ms.DETBufferSizeC; + p->UnboundedRequestEnabled = &mode_lib->ms.UnboundedRequestEnabled; + p->compbuf_reserved_space_64b = s->dummy_integer_array[3]; + p->hw_debug5 = s->dummy_boolean_array[1]; + p->CompressedBufferSizeInkByte = &mode_lib->ms.CompressedBufferSizeInkByte; + p->ViewportSizeSupportPerSurface = s->dummy_boolean_array[0]; + p->ViewportSizeSupport = &mode_lib->ms.support.ViewportSizeSupport; + dcn5_calculate_swath_and_det_configuration(&mode_lib->scratch, p); +} + +static void dcn5_ms_calculate_total_num_of_dcc_active_dpp( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + + mode_lib->ms.TotalNumberOfDCCActiveDPP = 0; + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->surface.dcc.enable == true) { + mode_lib->ms.TotalNumberOfDCCActiveDPP = + mode_lib->ms.TotalNumberOfDCCActiveDPP + mode_lib->ms.NoOfDPP[k]; + } + } +} + +static void dcn5_ms_calculate_vm_row_and_swath_and_calculate_dcc_meta_cache_requirements( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_core_calcs_mode_support_locals *s) +{ + unsigned int k; + struct dml2_core_calcs_CalculateVMRowAndSwath_params *p; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + struct dml2_core_internal_DmlPipe *surfParameters; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + surfParameters = &s->SurfParameters[k]; + + surfParameters->PixelClock = ((double) stream->timing.pixel_clock_khz / 1000); + surfParameters->DPPPerSurface = mode_lib->ms.NoOfDPP[k]; + surfParameters->RotationAngle = plane->composition.rotation_angle; + surfParameters->ViewportHeight = plane->composition.viewport.plane0.height; + surfParameters->ViewportHeightC = plane->composition.viewport.plane1.height; + surfParameters->BlockWidth256BytesY = mode_lib->ms.Read256BlockWidthY[k]; + surfParameters->BlockHeight256BytesY = mode_lib->ms.Read256BlockHeightY[k]; + surfParameters->BlockWidth256BytesC = mode_lib->ms.Read256BlockWidthC[k]; + surfParameters->BlockHeight256BytesC = mode_lib->ms.Read256BlockHeightC[k]; + surfParameters->BlockWidthY = mode_lib->ms.MacroTileWidthY[k]; + surfParameters->BlockHeightY = mode_lib->ms.MacroTileHeightY[k]; + surfParameters->BlockWidthC = mode_lib->ms.MacroTileWidthC[k]; + surfParameters->BlockHeightC = mode_lib->ms.MacroTileHeightC[k]; + surfParameters->InterlaceEnable = stream->timing.interlaced; + surfParameters->HTotal = stream->timing.h_total; + surfParameters->DCCEnable = plane->surface.dcc.enable; + surfParameters->SourcePixelFormat = plane->pixel_format; + surfParameters->SurfaceTiling = plane->surface.tiling; + surfParameters->BytePerPixelY = mode_lib->ms.BytePerPixelY[k]; + surfParameters->BytePerPixelC = mode_lib->ms.BytePerPixelC[k]; + surfParameters->ProgressiveToInterlaceUnitInOPP = mode_lib->ip.ptoi_supported; + surfParameters->VRatio = plane->composition.scaler_info.plane0.v_ratio; + surfParameters->VRatioChroma = plane->composition.scaler_info.plane1.v_ratio; + surfParameters->VTaps = plane->composition.scaler_info.plane0.v_taps; + surfParameters->VTapsChroma = plane->composition.scaler_info.plane1.v_taps; + surfParameters->PitchY = plane->surface.plane0.pitch; + surfParameters->PitchC = plane->surface.plane1.pitch; + surfParameters->ViewportStationary = plane->composition.viewport.stationary; + surfParameters->ViewportXStart = plane->composition.viewport.plane0.x_start; + surfParameters->ViewportYStart = plane->composition.viewport.plane0.y_start; + surfParameters->ViewportXStartC = plane->composition.viewport.plane1.y_start; + surfParameters->ViewportYStartC = plane->composition.viewport.plane1.y_start; + surfParameters->FORCE_ONE_ROW_FOR_FRAME = plane->overrides.hw.force_one_row_for_frame; + surfParameters->SwathHeightY = mode_lib->ms.SwathHeightY[k]; + surfParameters->SwathHeightC = mode_lib->ms.SwathHeightC[k]; + surfParameters->DCCMetaPitchY = plane->surface.dcc.plane0.pitch; + surfParameters->DCCMetaPitchC = plane->surface.dcc.plane1.pitch; + surfParameters->UPSPEnabled = plane->composition.scaler_info.upsp_enabled; + surfParameters->UPSPVTaps = plane->composition.scaler_info.upsp_vtaps; + surfParameters->UPSPSamplePositioning = plane->composition.scaler_info.upsp_sample_positioning; + } + + p = &mode_lib->scratch.CalculateVMRowAndSwath_params; + p->display_cfg = display_cfg; + p->uclk_pstate_switch_modes = mode_lib->ms.uclk_pstate_switch_modes; + p->NumberOfActiveSurfaces = mode_lib->ms.num_active_planes; + p->myPipe = s->SurfParameters; + p->PTEBufferSizeInRequestsLuma = mode_lib->ip.dpte_buffer_size_in_pte_reqs_luma; + p->PTEBufferSizeInRequestsChroma = mode_lib->ip.dpte_buffer_size_in_pte_reqs_chroma; + p->SwathWidthY = mode_lib->ms.SwathWidthY; + p->SwathWidthC = mode_lib->ms.SwathWidthC; + p->DCCMetaBufferSizeBytes = mode_lib->ip.dcc_meta_buffer_size_bytes; + p->mrq_present = mode_lib->ip.dcn_mrq_present; + // output + p->PTEBufferSizeNotExceeded = mode_lib->ms.PTEBufferSizeNotExceeded; + p->dpte_row_width_luma_ub = s->dummy_integer_array[12]; + p->dpte_row_width_chroma_ub = s->dummy_integer_array[13]; + p->dpte_row_height_luma = mode_lib->ms.dpte_row_height; + p->dpte_row_height_chroma = mode_lib->ms.dpte_row_height_chroma; + p->dpte_row_height_linear_luma = s->dummy_integer_array[14]; // VBA_DELTA + p->dpte_row_height_linear_chroma = s->dummy_integer_array[15]; // VBA_DELTA + p->vm_group_bytes = s->dummy_integer_array[16]; + p->dpte_group_bytes = mode_lib->ms.dpte_group_bytes; + p->PixelPTEReqWidthY = s->dummy_integer_array[17]; + p->PixelPTEReqHeightY = s->dummy_integer_array[18]; + p->PTERequestSizeY = s->dummy_integer_array[19]; + p->PixelPTEReqWidthC = s->dummy_integer_array[20]; + p->PixelPTEReqHeightC = s->dummy_integer_array[21]; + p->PTERequestSizeC = s->dummy_integer_array[22]; + p->vmpg_width_y = mode_lib->ms.vmpg_width_y; + p->vmpg_height_y = mode_lib->ms.vmpg_height_y; + p->vmpg_width_c = mode_lib->ms.vmpg_width_c; + p->vmpg_height_c = mode_lib->ms.vmpg_height_c; + p->dpde0_bytes_per_frame_ub_l = s->dummy_integer_array[23]; + p->dpde0_bytes_per_frame_ub_c = s->dummy_integer_array[24]; + p->PrefetchSourceLinesY = mode_lib->ms.PrefetchLinesY; + p->PrefetchSourceLinesC = mode_lib->ms.PrefetchLinesC; + p->VInitPreFillY = mode_lib->ms.PrefillY; + p->VInitPreFillC = mode_lib->ms.PrefillC; + p->MaxNumSwathY = mode_lib->ms.MaxNumSwathY; + p->MaxNumSwathC = mode_lib->ms.MaxNumSwathC; + p->dpte_row_bw = mode_lib->ms.dpte_row_bw; + p->PixelPTEBytesPerRow = mode_lib->ms.DPTEBytesPerRow; + p->dpte_row_bytes_per_row_l = mode_lib->ms.dpte_row_bytes_per_row_l; + p->dpte_row_bytes_per_row_c = mode_lib->ms.dpte_row_bytes_per_row_c; + p->vm_bytes = mode_lib->ms.vm_bytes; + p->use_one_row_for_frame = mode_lib->ms.use_one_row_for_frame; + p->use_one_row_for_frame_flip = mode_lib->ms.use_one_row_for_frame_flip; + p->PTE_BUFFER_MODE = s->dummy_boolean_array[1]; + p->BIGK_FRAGMENT_SIZE = s->dummy_integer_array[25]; + p->DCCMetaBufferSizeNotExceeded = mode_lib->ms.DCCMetaBufferSizeNotExceeded; + p->meta_row_bw = mode_lib->ms.meta_row_bw; + p->meta_row_bytes = mode_lib->ms.meta_row_bytes; + p->meta_row_bytes_per_row_ub_l = mode_lib->ms.meta_row_bytes_per_row_ub_l; + p->meta_row_bytes_per_row_ub_c = mode_lib->ms.meta_row_bytes_per_row_ub_c; + p->meta_req_width_luma = s->dummy_integer_array[26]; + p->meta_req_height_luma = s->dummy_integer_array[27]; + p->meta_row_width_luma = s->dummy_integer_array[28]; + p->meta_row_height_luma = mode_lib->ms.meta_row_height_luma; + p->meta_pte_bytes_per_frame_ub_l = s->dummy_integer_array[29]; + p->meta_req_width_chroma = s->dummy_integer_array[30]; + p->meta_req_height_chroma = s->dummy_integer_array[31]; + p->meta_row_width_chroma = s->dummy_integer_array[32]; + p->meta_row_height_chroma = mode_lib->ms.meta_row_height_chroma; + p->meta_pte_bytes_per_frame_ub_c = s->dummy_integer_array[33]; + + dcn5_calculate_vm_row_and_swath(&mode_lib->scratch, p); +} + +static bool dcn5_ms_check_pte_buffer_size_support( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (mode_lib->ms.PTEBufferSizeNotExceeded[k] == false) + support = false; + + DML_LOG_VERBOSE("DML::%s: k=%u, PTEBufferSizeNotExceeded = %u\n", __func__, k, + mode_lib->ms.PTEBufferSizeNotExceeded[k]); + } + DML_LOG_VERBOSE("DML::%s: PTEBufferSizeNotExceeded = %u\n", __func__, support); + return support; +} + +static bool dcn5_ms_check_dcc_meta_cache_support( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (mode_lib->ms.DCCMetaBufferSizeNotExceeded[k] == false) + support = false; + + DML_LOG_VERBOSE("DML::%s: k=%u, DCCMetaBufferSizeNotExceeded = %u\n", __func__, k, + mode_lib->ms.DCCMetaBufferSizeNotExceeded[k]); + } + DML_LOG_VERBOSE("DML::%s: DCCMetaBufferSizeNotExceeded = %u\n", __func__, support); + return support; +} + +static void dcn5_ms_calculate_vactive_uclk_pstate_requirements( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int k; + struct dml2_core_calcs_calculate_bytes_to_fetch_required_to_hide_latency_params *p = + &mode_lib->scratch.calculate_bytes_to_fetch_required_to_hide_latency_params; + + /* VActive bytes to fetch for UCLK P-State */ + p->display_cfg = display_cfg; + p->mrq_present = mode_lib->ip.dcn_mrq_present; + p->num_active_planes = mode_lib->ms.num_active_planes; + p->num_of_dpp = mode_lib->ms.NoOfDPP; + p->meta_row_height_l = mode_lib->ms.meta_row_height_luma; + p->meta_row_height_c = mode_lib->ms.meta_row_height_chroma; + p->meta_row_bytes_per_row_ub_l = mode_lib->ms.meta_row_bytes_per_row_ub_l; + p->meta_row_bytes_per_row_ub_c = mode_lib->ms.meta_row_bytes_per_row_ub_c; + p->dpte_row_height_l = mode_lib->ms.dpte_row_height; + p->dpte_row_height_c = mode_lib->ms.dpte_row_height_chroma; + p->dpte_bytes_per_row_l = mode_lib->ms.dpte_row_bytes_per_row_l; + p->dpte_bytes_per_row_c = mode_lib->ms.dpte_row_bytes_per_row_c; + p->byte_per_pix_l = mode_lib->ms.BytePerPixelY; + p->byte_per_pix_c = mode_lib->ms.BytePerPixelC; + p->swath_width_l = mode_lib->ms.SwathWidthY; + p->swath_width_c = mode_lib->ms.SwathWidthC; + p->swath_height_l = mode_lib->ms.SwathHeightY; + p->swath_height_c = mode_lib->ms.SwathHeightC; + for (k = 0; k < display_cfg->num_planes; k++) { + p->latency_to_hide_us[k] = utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + } + /* outputs */ + p->bytes_required_l = mode_lib->ms.pstate_bytes_required_l[dml2_pstate_type_uclk]; + p->bytes_required_c = mode_lib->ms.pstate_bytes_required_c[dml2_pstate_type_uclk]; + dcn5_calculate_bytes_to_fetch_required_to_hide_latency(p); + + /* Excess VActive bandwidth required to fill DET */ + dcn5_calculate_excess_vactive_bandwidth_required(display_cfg, + mode_lib->ms.num_active_planes, + mode_lib->ms.pstate_bytes_required_l[dml2_pstate_type_uclk], + mode_lib->ms.pstate_bytes_required_c[dml2_pstate_type_uclk], + + /* outputs */ + mode_lib->ms.excess_vactive_fill_bw_l, + mode_lib->ms.excess_vactive_fill_bw_c); +} + +/* FIXME - break it down according the function name */ +static void dcn5_ms_calculate_det_buffer_time_value_urgent_burst_factor_and_urgent_latency_hiding( + const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + double line_time_us; + bool cursor_not_enough_urgent_latency_hiding; + unsigned int cursor_lines_per_chunk; + unsigned int cursor_bytes; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + line_time_us = stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000); + cursor_not_enough_urgent_latency_hiding = 0; + dcn5_calculate_cursor_req_attributes( + plane->cursor.cursor_width, + plane->cursor.cursor_bpp, + // output + &cursor_lines_per_chunk, + &mode_lib->ms.cursor_bytes_per_line[k], + &mode_lib->ms.cursor_bytes_per_chunk[k], + &cursor_bytes); + dcn5_calculate_cursor_urgent_burst_factor( + mode_lib->ip.cursor_buffer_size, + plane->cursor.cursor_width, + mode_lib->ms.cursor_bytes_per_chunk[k], + cursor_lines_per_chunk, line_time_us, + mode_lib->ms.UrgLatency, + + // output + &mode_lib->ms.UrgentBurstFactorCursor[k], + &cursor_not_enough_urgent_latency_hiding); + mode_lib->ms.UrgentBurstFactorCursorPre[k] = mode_lib->ms.UrgentBurstFactorCursor[k]; + DML_LOG_VERBOSE("DML::%s: k=%d, Calling CalculateUrgentBurstFactor\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatio=%f\n", __func__, k, + plane->composition.scaler_info.plane0.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioChroma=%f\n", __func__, k, + plane->composition.scaler_info.plane1.v_ratio); + dcn5_calculate_urgent_burst_factor( + &display_cfg->plane_descriptors[k], + mode_lib->ms.swath_width_luma_ub[k], + mode_lib->ms.swath_width_chroma_ub[k], + mode_lib->ms.SwathHeightY[k], + mode_lib->ms.SwathHeightC[k], + line_time_us, + mode_lib->ms.UrgLatency, + plane->composition.scaler_info.plane0.v_ratio, + plane->composition.scaler_info.plane1.v_ratio, + mode_lib->ms.BytePerPixelInDETY[k], + mode_lib->ms.BytePerPixelInDETC[k], + mode_lib->ms.DETBufferSizeY[k], + mode_lib->ms.DETBufferSizeC[k], + + // Output + &mode_lib->ms.UrgentBurstFactorLuma[k], + &mode_lib->ms.UrgentBurstFactorChroma[k], + &mode_lib->ms.NotEnoughUrgentLatencyHiding[k]); + + mode_lib->ms.NotEnoughUrgentLatencyHiding[k] = mode_lib->ms.NotEnoughUrgentLatencyHiding[k] + || cursor_not_enough_urgent_latency_hiding; + } +} + +static void dcn5_ms_calculate_min_dcfclk_deepsleep_clock(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + dcn5_calculate_dcfclk_deep_sleep( + display_cfg, + mode_lib->ms.num_active_planes, + mode_lib->ms.BytePerPixelY, + mode_lib->ms.BytePerPixelC, + mode_lib->ms.SwathWidthY, + mode_lib->ms.SwathWidthC, + mode_lib->ms.NoOfDPP, + mode_lib->ms.PSCL_FACTOR, + mode_lib->ms.PSCL_FACTOR_CHROMA, + mode_lib->ms.RequiredDPPCLK, + mode_lib->ms.vactive_sw_bw_l, + mode_lib->ms.vactive_sw_bw_c, + utm_soc_bb->return_bus_width_bytes, + /* Output */ + &mode_lib->ms.dcfclk_deepsleep); +} + +static void dcn5_ms_calculate_writeback_delay(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int k; + unsigned int j; + const struct dml2_stream_parameters *stream; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + mode_lib->ms.WritebackDelayTime[k] = 0.0; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + mode_lib->ms.WritebackDelayTime[k] = math_max2(mode_lib->ms.WritebackDelayTime[k], + utm_soc_bb->writeback_base_latency_us + + dcn5_calculate_write_back_delay( + stream->writeback.writeback_stream[j].pixel_format, + stream->writeback.writeback_stream[j].h_ratio, + stream->writeback.writeback_stream[j].v_ratio, + stream->writeback.writeback_stream[j].v_taps, + stream->writeback.writeback_stream[j].v_taps_chroma, + stream->writeback.writeback_stream[j].output_width, + stream->writeback.writeback_stream[j].output_height, + stream->writeback.writeback_stream[j].input_width, + stream->writeback.writeback_stream[j].input_height, + stream->timing.h_total) + / mode_lib->ms.RequiredDISPCLK); + } + } +} + +static void dcn5_ms_calculate_max_vstartup(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_stream_parameters *stream; + + // MaximumVStartup is actually Tvstartup_min in DCN4 programming guide + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + mode_lib->ms.MaximumVStartup[k] = dcn5_calculate_max_vstartup( + mode_lib->ip.ptoi_supported, + mode_lib->ip.vblank_nom_default_us, + &stream->timing, + mode_lib->ms.WritebackDelayTime[k]); + } + DML_LOG_VERBOSE("DML::%s: k=%u, MaximumVStartup = %u\n", __func__, k, mode_lib->ms.MaximumVStartup[k]); +} + +static void dcn5_ms_check_average_latency_supports(struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + double outstanding_latency_us = 0; + unsigned int k; + + mode_lib->ms.support.OutstandingRequestsSupport = true; + mode_lib->ms.support.OutstandingRequestsUrgencyAvoidance = true; + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + outstanding_latency_us = utm_soc_bb->max_outstanding_reqs + * mode_lib->ms.support.request_size_bytes_luma[k] + / (mode_lib->ms.DCFCLK * utm_soc_bb->return_bus_width_bytes); + if (outstanding_latency_us < mode_lib->ms.support.avg_urgent_latency_us) { + mode_lib->ms.support.OutstandingRequestsSupport = false; + DML_LOG_VERBOSE("DML::%s: DCFCLK = %f\n", __func__, mode_lib->ms.DCFCLK); + } + if (outstanding_latency_us < mode_lib->ms.support.avg_non_urgent_latency_us) { + mode_lib->ms.support.OutstandingRequestsUrgencyAvoidance = false; + } + DML_LOG_VERBOSE("DML::%s: avg_urgent_latency_us = %f\n", __func__, mode_lib->ms.support.avg_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: avg_non_urgent_latency_us = %f\n", __func__, mode_lib->ms.support.avg_non_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: k=%d, request_size_bytes_luma = %d\n", __func__, k, mode_lib->ms.support.request_size_bytes_luma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, outstanding_latency_us = %f (luma)\n", __func__, k, outstanding_latency_us); + if (mode_lib->ms.BytePerPixelC[k] > 0) { + outstanding_latency_us = utm_soc_bb->max_outstanding_reqs + * mode_lib->ms.support.request_size_bytes_chroma[k] + / (mode_lib->ms.DCFCLK * utm_soc_bb->return_bus_width_bytes); + if (outstanding_latency_us < mode_lib->ms.support.avg_urgent_latency_us) { + mode_lib->ms.support.OutstandingRequestsSupport = false; + } + if (outstanding_latency_us < mode_lib->ms.support.avg_non_urgent_latency_us) { + mode_lib->ms.support.OutstandingRequestsUrgencyAvoidance = false; + } + DML_LOG_VERBOSE("DML::%s: k=%d, request_size_bytes_chroma = %d\n", __func__, k, mode_lib->ms.support.request_size_bytes_chroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, outstanding_latency_us = %f (chroma)\n", __func__, k, outstanding_latency_us); + } + } +} + +static void dcn5_ms_calculate_mcache_setting(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + struct dml2_core_calcs_calculate_mcache_setting_params *p; + + if (utm_soc_bb->mcache_size_bytes == 0 || mode_lib->ip.dcn_mrq_present) { + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0[k] = 1.0; + mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p0[k] = 1.0; + mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1[k] = 1.0; + mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p1[k] = 1.0; + } + } else { + p = &mode_lib->scratch.calculate_mcache_setting_params; + memset(p, 0, sizeof(struct dml2_core_calcs_calculate_mcache_setting_params)); + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + p->dcc_enable = plane->surface.dcc.enable; + p->num_chans = utm_soc_bb->dram_config.channel_count; + p->mem_word_bytes = utm_soc_bb->mem_word_bytes; + p->mcache_size_bytes = utm_soc_bb->mcache_size_bytes; + p->mcache_line_size_bytes = utm_soc_bb->mcache_line_size_bytes; + p->gpuvm_enable = display_cfg->gpuvm_enable; + p->gpuvm_page_size_kbytes = plane->overrides.gpuvm_min_page_size_kbytes; + p->source_format = plane->pixel_format; + p->surf_vert = dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle); + p->vp_stationary = plane->composition.viewport.stationary; + p->tiling_mode = plane->surface.tiling; + p->imall_enable = mode_lib->ip.imall_supported; + p->vp_start_x_l = plane->composition.viewport.plane0.x_start; + p->vp_start_y_l = plane->composition.viewport.plane0.y_start; + p->full_vp_width_l = plane->composition.viewport.plane0.width; + p->full_vp_height_l = plane->composition.viewport.plane0.height; + p->blk_width_l = mode_lib->ms.MacroTileWidthY[k]; + p->blk_height_l = mode_lib->ms.MacroTileHeightY[k]; + p->vmpg_width_l = mode_lib->ms.vmpg_width_y[k]; + p->vmpg_height_l = mode_lib->ms.vmpg_height_y[k]; + p->full_swath_bytes_l = mode_lib->ms.full_swath_bytes_l[k]; + p->bytes_per_pixel_l = mode_lib->ms.BytePerPixelY[k]; + p->vp_start_x_c = plane->composition.viewport.plane1.x_start; + p->vp_start_y_c = plane->composition.viewport.plane1.y_start; + p->full_vp_width_c = plane->composition.viewport.plane1.width; + p->full_vp_height_c = plane->composition.viewport.plane1.height; + p->blk_width_c = mode_lib->ms.MacroTileWidthC[k]; + p->blk_height_c = mode_lib->ms.MacroTileHeightC[k]; + p->vmpg_width_c = mode_lib->ms.vmpg_width_c[k]; + p->vmpg_height_c = mode_lib->ms.vmpg_height_c[k]; + p->full_swath_bytes_c = mode_lib->ms.full_swath_bytes_c[k]; + p->bytes_per_pixel_c = mode_lib->ms.BytePerPixelC[k]; + // output + p->dcc_dram_bw_nom_overhead_factor_l = &mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0[k]; + p->dcc_dram_bw_pref_overhead_factor_l = &mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p0[k]; + p->dcc_dram_bw_nom_overhead_factor_c = &mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1[k]; + p->dcc_dram_bw_pref_overhead_factor_c = &mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p1[k]; + p->num_mcaches_l = &mode_lib->ms.num_mcaches_l[k]; + p->mcache_row_bytes_l = &mode_lib->ms.mcache_row_bytes_l[k]; + p->mcache_row_bytes_per_channel_l = &mode_lib->ms.mcache_row_bytes_per_channel_l[k]; + p->mcache_offsets_l = mode_lib->ms.mcache_offsets_l[k]; + p->mcache_shift_granularity_l = &mode_lib->ms.mcache_shift_granularity_l[k]; + p->num_mcaches_c = &mode_lib->ms.num_mcaches_c[k]; + p->mcache_row_bytes_c = &mode_lib->ms.mcache_row_bytes_c[k]; + p->mcache_row_bytes_per_channel_c = &mode_lib->ms.mcache_row_bytes_per_channel_c[k]; + p->mcache_offsets_c = mode_lib->ms.mcache_offsets_c[k]; + p->mcache_shift_granularity_c = &mode_lib->ms.mcache_shift_granularity_c[k]; + p->mall_comb_mcache_l = &mode_lib->ms.mall_comb_mcache_l[k]; + p->mall_comb_mcache_c = &mode_lib->ms.mall_comb_mcache_c[k]; + p->lc_comb_mcache = &mode_lib->ms.lc_comb_mcache[k]; + dcn5_calculate_mcache_setting(&mode_lib->scratch, p); + } + } +} + +static void dcn5_ms_calculate_avg_bandwidth_and_dcfclk_lb_required(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb) +{ + (void)display_cfg; + // Average BW support check + dcn5_calculate_avg_bandwidth_required( + *mode_lib->ms.support.avg_bandwidth_required, + // input + mode_lib->ms.num_active_planes, + mode_lib->ms.vactive_sw_bw_l, + mode_lib->ms.vactive_sw_bw_c, + mode_lib->ms.cursor_bw, + mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0, + mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1); + + DML_LOG_VERBOSE("DML::%s: lower_bound_bandwidth_dchub=%f\n", __func__, utm_soc_bb->lower_bound_bandwidth_dchub); + DML_LOG_VERBOSE("DML::%s: avg_bandwidth_required=%f\n", __func__, mode_lib->ms.support.avg_bandwidth_required[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp]); + + mode_lib->ms.DCFCLK = math_max2(utm_soc_bb->lower_bound_bandwidth_dchub * 1000.0, mode_lib->ms.support.avg_bandwidth_required[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp]) + / utm_soc_bb->return_bus_width_bytes; + + DML_LOG_VERBOSE("DML::%s: DCFCLK=%f\n", __func__, mode_lib->ms.DCFCLK); +} + +static bool dcn5_ms_check_urgent_latency_hiding_support( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (mode_lib->ms.NotEnoughUrgentLatencyHiding[k]) { + support = false; + DML_LOG_VERBOSE("DML::%s: k=%u NotEnoughUrgentLatencyHiding set\n", __func__, k); + } + } + return support; +} + +static void dcn5_ms_calculate_t_calc(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + mode_lib->ms.TimeCalc = 24 / mode_lib->ms.dcfclk_deepsleep; +} + +static void dcn5_ms_calculate_hostvm_inefficiency_factor(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + dcn5_calculate_hostvm_inefficiency_factor( + &mode_lib->ms.HostVMInefficiencyFactor, + &mode_lib->ms.HostVMInefficiencyFactorPrefetch, + display_cfg->gpuvm_enable, + display_cfg->hostvm_enable, + mode_lib->ip.remote_iommu_outstanding_translations, + utm_soc_bb->max_outstanding_reqs, + 1.0, + 0.5); +} + +// Using approximate ratio for VM bandwidth + +static void dcn5_ms_calculate_3dlut_settings(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + struct dml2_core_calcs_calculate_tdlut_setting_params *p = &mode_lib->scratch.calculate_tdlut_setting_params; + unsigned int tdlut_groups_per_2row_ub; + + mode_lib->ms.Total3dlutActive = 0; + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->tdlut.setup_for_tdlut) + mode_lib->ms.Total3dlutActive = mode_lib->ms.Total3dlutActive + 1; + + // Calculate tdlut schedule related terms + p->dispclk_mhz = mode_lib->ms.RequiredDISPCLK; + p->setup_for_tdlut = plane->tdlut.setup_for_tdlut; + p->tdlut_width_mode = plane->tdlut.tdlut_width_mode; + p->tdlut_addressing_mode = plane->tdlut.tdlut_addressing_mode; + p->cursor_buffer_size = mode_lib->ip.cursor_buffer_size; + p->gpuvm_enable = display_cfg->gpuvm_enable; + p->gpuvm_page_size_kbytes = plane->overrides.gpuvm_min_page_size_kbytes; + p->tdlut_mpc_width_flag = plane->tdlut.tdlut_mpc_width_flag; + /* TODO: The logic casts integer version value into boolean. This doesn't seem to be correct. */ + p->is_gfx11 = dml2_core_utils_get_gfx_version(plane->surface.tiling); + // output + p->tdlut_pte_bytes_per_frame = &mode_lib->ms.tdlut_pte_bytes_per_frame[k]; + p->tdlut_bytes_per_frame = &mode_lib->ms.tdlut_bytes_per_frame[k]; + p->tdlut_groups_per_2row_ub = &tdlut_groups_per_2row_ub; + p->tdlut_opt_time = &mode_lib->ms.tdlut_opt_time[k]; + p->tdlut_drain_time = &mode_lib->ms.tdlut_drain_time[k]; + p->tdlut_bytes_per_group = &mode_lib->ms.tdlut_bytes_per_group[k]; + dcn5_calculate_tdlut_setting(&mode_lib->scratch, p); + } +} + +static void dcn5_ms_calculate_urgent_latency(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + double min_return_bw_for_latency = mode_lib->ms.DCFCLK * utm_soc_bb->return_bus_width_bytes; + + DML_LOG_VERBOSE("DML::%s: DCFCLK=%f\n", __func__, mode_lib->ms.DCFCLK); + DML_LOG_VERBOSE("DML::%s: return_bus_width_bytes=%ld\n", __func__, utm_soc_bb->return_bus_width_bytes); + DML_LOG_VERBOSE("DML::%s: min_return_bw_for_latency=%f\n", __func__, min_return_bw_for_latency); + + dcn5_calculate_extra_latency( + display_cfg, + mode_lib->ip.rob_buffer_size_kbytes, + 0, + 0, + mode_lib->ms.DCFCLK, + 0.0, + mode_lib->ip.pixel_chunk_size_kbytes, + min_return_bw_for_latency, + mode_lib->ms.num_active_planes, + mode_lib->ms.NoOfDPP, + mode_lib->ms.dpte_group_bytes, + mode_lib->ms.tdlut_bytes_per_group, + mode_lib->ms.HostVMInefficiencyFactor, + mode_lib->ms.HostVMInefficiencyFactorPrefetch, + dml2_qos_param_type_dcn4x, + !(display_cfg->overrides.max_outstanding_when_urgent_expected_disable), + utm_soc_bb->max_outstanding_reqs, + mode_lib->ms.support.request_size_bytes_luma, + mode_lib->ms.support.request_size_bytes_chroma, + mode_lib->ip.meta_chunk_size_kbytes, + mode_lib->ip.dchub_arb_to_ret_delay, + mode_lib->ms.TripToMemory, + mode_lib->ip.hostvm_mode, + // output + &mode_lib->ms.ExtraLatency, + &mode_lib->ms.ExtraLatency_sr, + &mode_lib->ms.ExtraLatencyPrefetch); +} + +static void dcn5_ms_calculate_prefetch_schedule(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_core_calcs_mode_support_locals *s, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + struct dml2_core_internal_DmlPipe *pipe = &s->myPipe; + struct dml2_core_calcs_CalculatePrefetchSchedule_params *p = &mode_lib->scratch.CalculatePrefetchSchedule_params; + double Tvm_trip; + double Tr0_trip; + double prefetch_sw_bytes; + double Tpre_rounded; + double Tpre_oto; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + mode_lib->ms.TWait[k] = dcn5_calculate_t_wait( + plane->overrides.reserved_vblank_time_ns, + mode_lib->ms.UrgLatency, + mode_lib->ms.TripToMemory, + utm_soc_bb->power_management_parameters.g7_ppt_blackout_us, + display_cfg->stream_descriptors->timing.drr_config.enabled); + pipe->Dppclk = mode_lib->ms.RequiredDPPCLK[k]; + pipe->Dispclk = mode_lib->ms.RequiredDISPCLK; + pipe->PixelClock = ((double) stream->timing.pixel_clock_khz / 1000); + pipe->DCFClkDeepSleep = mode_lib->ms.dcfclk_deepsleep; + pipe->DPPPerSurface = mode_lib->ms.NoOfDPP[k]; + pipe->ScalerEnabled = plane->composition.scaler_info.enabled; + pipe->VRatio = plane->composition.scaler_info.plane0.v_ratio; + pipe->VRatioChroma = plane->composition.scaler_info.plane1.v_ratio; + pipe->VTaps = plane->composition.scaler_info.plane0.v_taps; + pipe->VTapsChroma = plane->composition.scaler_info.plane1.v_taps; + pipe->RotationAngle = plane->composition.rotation_angle; + pipe->mirrored = plane->composition.mirrored; + pipe->BlockWidth256BytesY = mode_lib->ms.Read256BlockWidthY[k]; + pipe->BlockHeight256BytesY = mode_lib->ms.Read256BlockHeightY[k]; + pipe->BlockWidth256BytesC = mode_lib->ms.Read256BlockWidthC[k]; + pipe->BlockHeight256BytesC = mode_lib->ms.Read256BlockHeightC[k]; + pipe->InterlaceEnable = stream->timing.interlaced; + pipe->NumberOfCursors = plane->cursor.num_cursors; + pipe->VBlank = stream->timing.v_total - stream->timing.v_active; + pipe->HTotal = stream->timing.h_total; + pipe->HActive = stream->timing.h_active; + pipe->DCCEnable = plane->surface.dcc.enable; + pipe->ODMMode = mode_lib->ms.ODMMode[k]; + pipe->SourcePixelFormat = plane->pixel_format; + pipe->BytePerPixelY = mode_lib->ms.BytePerPixelY[k]; + pipe->BytePerPixelC = mode_lib->ms.BytePerPixelC[k]; + pipe->ProgressiveToInterlaceUnitInOPP = mode_lib->ip.ptoi_supported; + DML_LOG_VERBOSE("DML::%s: Calling CalculatePrefetchSchedule for k=%u\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: MaximumVStartup = %u\n", __func__, mode_lib->ms.MaximumVStartup[k]); + p->display_cfg = display_cfg; + p->HostVMInefficiencyFactor = mode_lib->ms.HostVMInefficiencyFactorPrefetch; + p->myPipe = pipe; + p->DSCDelay = mode_lib->ms.DSCDelay[k]; + p->DPPCLKDelaySubtotalPlusCNVCFormater = mode_lib->ip.dppclk_delay_subtotal + mode_lib->ip.dppclk_delay_cnvc_formatter; + p->DPPCLKDelaySCL = mode_lib->ip.dppclk_delay_scl; + p->DPPCLKDelaySCLLBOnly = mode_lib->ip.dppclk_delay_scl_lb_only; + p->DPPCLKDelayCNVCCursor = mode_lib->ip.dppclk_delay_cnvc_cursor; + p->DISPCLKDelaySubtotal = mode_lib->ip.dispclk_delay_subtotal; + p->DPP_RECOUT_WIDTH = (unsigned int) (mode_lib->ms.SwathWidthY[k] / plane->composition.scaler_info.plane0.h_ratio); + p->OutputFormat = stream->output.output_format; + p->MaxInterDCNTileRepeaters = mode_lib->ip.max_inter_dcn_tile_repeaters; + p->VStartup = mode_lib->ms.MaximumVStartup[k]; + p->HostVMMinPageSize = plane->overrides.hostvm_min_page_size_kbytes; + p->DynamicMetadataEnable = plane->dynamic_meta_data.enable; + p->DynamicMetadataVMEnabled = mode_lib->ip.dynamic_metadata_vm_enabled; + p->DynamicMetadataLinesBeforeActiveRequired = plane->dynamic_meta_data.lines_before_active_required; + p->DynamicMetadataTransmittedBytes = plane->dynamic_meta_data.transmitted_bytes; + p->ExtraLatencyPrefetch = mode_lib->ms.ExtraLatencyPrefetch; + p->TCalc = mode_lib->ms.TimeCalc; + p->vm_bytes = mode_lib->ms.vm_bytes[k]; + p->PixelPTEBytesPerRow = mode_lib->ms.DPTEBytesPerRow[k]; + p->PrefetchSourceLinesY = mode_lib->ms.PrefetchLinesY[k]; + p->VInitPreFillY = mode_lib->ms.PrefillY[k]; + p->MaxNumSwathY = mode_lib->ms.MaxNumSwathY[k]; + p->PrefetchSourceLinesC = mode_lib->ms.PrefetchLinesC[k]; + p->VInitPreFillC = mode_lib->ms.PrefillC[k]; + p->MaxNumSwathC = mode_lib->ms.MaxNumSwathC[k]; + p->swath_width_luma_ub = mode_lib->ms.swath_width_luma_ub[k]; + p->swath_width_chroma_ub = mode_lib->ms.swath_width_chroma_ub[k]; + p->SwathHeightY = mode_lib->ms.SwathHeightY[k]; + p->SwathHeightC = mode_lib->ms.SwathHeightC[k]; + p->TWait = mode_lib->ms.TWait[k]; + p->Ttrip = mode_lib->ms.TripToMemory; + p->Turg = mode_lib->ms.UrgLatency; + p->setup_for_tdlut = plane->tdlut.setup_for_tdlut; + p->tdlut_pte_bytes_per_frame = mode_lib->ms.tdlut_pte_bytes_per_frame[k]; + p->tdlut_bytes_per_frame = mode_lib->ms.tdlut_bytes_per_frame[k]; + p->tdlut_opt_time = mode_lib->ms.tdlut_opt_time[k]; + p->tdlut_drain_time = mode_lib->ms.tdlut_drain_time[k]; + p->num_cursors = (plane->cursor.cursor_width > 0); + p->cursor_bytes_per_chunk = mode_lib->ms.cursor_bytes_per_chunk[k]; + p->cursor_bytes_per_line = mode_lib->ms.cursor_bytes_per_line[k]; + p->dcc_enable = plane->surface.dcc.enable; + p->mrq_present = mode_lib->ip.dcn_mrq_present; + p->meta_row_bytes = mode_lib->ms.meta_row_bytes[k]; + // output + p->DSTXAfterScaler = &mode_lib->ms.DSTXAfterScaler[k]; + p->DSTYAfterScaler = &mode_lib->ms.DSTYAfterScaler[k]; + p->dst_y_prefetch = &mode_lib->ms.dst_y_prefetch[k]; + p->dst_y_per_vm_vblank = &mode_lib->ms.LinesForVM[k]; + p->dst_y_per_row_vblank = &mode_lib->ms.LinesForDPTERow[k]; + p->VRatioPrefetchY = &mode_lib->ms.VRatioPreY[k]; + p->VRatioPrefetchC = &mode_lib->ms.VRatioPreC[k]; + p->RequiredPrefetchPixelDataBWLuma = &mode_lib->ms.RequiredPrefetchPixelDataBWLuma[k]; // prefetch_sw_bw_l + p->RequiredPrefetchPixelDataBWChroma = &mode_lib->ms.RequiredPrefetchPixelDataBWChroma[k]; // prefetch_sw_bw_c + p->NotEnoughTimeForDynamicMetadata = &mode_lib->ms.NoTimeForDynamicMetadata[k]; + p->Tno_bw = &mode_lib->ms.Tno_bw[k]; + p->Tno_bw_flip = &mode_lib->ms.Tno_bw_flip[k]; + p->prefetch_vmrow_bw = &mode_lib->ms.prefetch_vmrow_bw[k]; + p->Tdmdl_vm = &s->dummy_single[0]; + p->Tdmdl = &s->dummy_single[1]; + p->TSetup = &s->dummy_single[2]; + p->Tvm_trips = &Tvm_trip; + p->Tr0_trips = &Tr0_trip; + p->Tvm_trips_flip = &mode_lib->ms.Tvm_trips_flip[k]; + p->Tr0_trips_flip = &mode_lib->ms.Tr0_trips_flip[k]; + p->Tvm_trips_flip_rounded = &mode_lib->ms.Tvm_trips_flip_rounded[k]; + p->Tr0_trips_flip_rounded = &mode_lib->ms.Tr0_trips_flip_rounded[k]; + p->VUpdateOffsetPix = &s->dummy_integer[0]; + p->VUpdateWidthPix = &s->dummy_integer[1]; + p->VReadyOffsetPix = &s->dummy_integer[2]; + p->prefetch_cursor_bw = &mode_lib->ms.prefetch_cursor_bw[k]; + p->prefetch_sw_bytes = &prefetch_sw_bytes; + p->Tpre_rounded = &Tpre_rounded; + p->Tpre_oto = &Tpre_oto; + + mode_lib->ms.NoTimeForPrefetch[k] = dcn5_calculate_prefetch_schedule(&mode_lib->scratch, p); + DML_LOG_VERBOSE("DML::%s: k=%d, dst_y_per_vm_vblank = %f\n", __func__, k, *p->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML::%s: k=%d, dst_y_per_row_vblank = %f\n", __func__, k, *p->dst_y_per_row_vblank); + } +} + +static bool dcn5_ms_check_prefetch_support(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support = true; + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + if (mode_lib->ms.dst_y_prefetch[k] < 2.0 + || mode_lib->ms.LinesForVM[k] >= 32.0 + || mode_lib->ms.LinesForDPTERow[k] >= 16.0 + || mode_lib->ms.NoTimeForPrefetch[k] == true + || mode_lib->ms.DSTYAfterScaler[k] > 8) { + support = false; + DML_LOG_VERBOSE("DML::%s: k=%d, dst_y_prefetch=%f (should not be < 2)\n", __func__, k, mode_lib->ms.dst_y_prefetch[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, LinesForVM=%f (should not be >= 32)\n", __func__, k, mode_lib->ms.LinesForVM[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, LinesForDPTERow=%f (should not be >= 16)\n", __func__, k, mode_lib->ms.LinesForDPTERow[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, DSTYAfterScaler=%d (should be <= 8)\n", __func__, k, mode_lib->ms.DSTYAfterScaler[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, NoTimeForPrefetch=%d\n", __func__, k, mode_lib->ms.NoTimeForPrefetch[k]); + } + } + return support; +} + +static bool dcn5_ms_check_dynamic_metadata_support(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support; + + support = true; + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + if (mode_lib->ms.NoTimeForDynamicMetadata[k] == true) { + support = false; + } + } + return support; +} + +static bool dcn5_ms_check_v_ratio_in_prefetch_support( + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + bool support = true; + unsigned int k; + + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + if (mode_lib->ms.VRatioPreY[k] > __DML2_CALCS_MAX_VRATIO_PRE__ + || mode_lib->ms.VRatioPreC[k] > __DML2_CALCS_MAX_VRATIO_PRE__) { + support = false; + DML_LOG_VERBOSE("DML::%s: k=%d VRatioPreY = %f (should be <= %f)\n", __func__, k, mode_lib->ms.VRatioPreY[k], __DML2_CALCS_MAX_VRATIO_PRE__); + DML_LOG_VERBOSE("DML::%s: k=%d VRatioPreC = %f (should be <= %f)\n", __func__, k, mode_lib->ms.VRatioPreC[k], __DML2_CALCS_MAX_VRATIO_PRE__); + DML_LOG_VERBOSE("DML::%s: VRatioInPrefetchSupported = %u\n", __func__, mode_lib->ms.support.VRatioInPrefetchSupported); + } + } + return support; +} + +static void dcn5_ms_calculate_urgent_burst_factor_for_prefetch( + const struct dml2_display_cfg *display_cfg, struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + double line_time_us; + const struct dml2_stream_parameters *stream; + + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + line_time_us = stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000); + DML_LOG_VERBOSE("DML::%s: k=%d, Calling CalculateUrgentBurstFactor (for prefetch)\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioPreY=%f\n", __func__, k, mode_lib->ms.VRatioPreY[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioPreC=%f\n", __func__, k, mode_lib->ms.VRatioPreC[k]); + dcn5_calculate_urgent_burst_factor( + &display_cfg->plane_descriptors[k], + mode_lib->ms.swath_width_luma_ub[k], + mode_lib->ms.swath_width_chroma_ub[k], + mode_lib->ms.SwathHeightY[k], + mode_lib->ms.SwathHeightC[k], + line_time_us, + mode_lib->ms.UrgLatency, + mode_lib->ms.VRatioPreY[k], + mode_lib->ms.VRatioPreC[k], + mode_lib->ms.BytePerPixelInDETY[k], + mode_lib->ms.BytePerPixelInDETC[k], + mode_lib->ms.DETBufferSizeY[k], + mode_lib->ms.DETBufferSizeC[k], + /* Output */ + &mode_lib->ms.UrgentBurstFactorLumaPre[k], + &mode_lib->ms.UrgentBurstFactorChromaPre[k], + &mode_lib->ms.NotEnoughUrgentLatencyHidingPre[k]); + } +} + +static void dcn5_ms_calculate_peak_bandwidth_required(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + struct dml2_core_calcs_calculate_peak_bandwidth_required_params *p = + &mode_lib->scratch.calculate_peak_bandwidth_params; + + // Calculate urgent bandwidth required, both urg and non urg peak bandwidth + // assume flip bw is 0 at this point + for (k = 0; k < mode_lib->ms.num_active_planes; k++) + mode_lib->ms.final_flip_bw[k] = 0; + p->urg_vactive_bandwidth_required = mode_lib->ms.support.urg_vactive_bandwidth_required; + p->urg_bandwidth_required = mode_lib->ms.support.urg_bandwidth_required; + p->urg_bandwidth_required_qual = mode_lib->ms.support.urg_bandwidth_required_qual; + p->non_urg_bandwidth_required = mode_lib->ms.support.non_urg_bandwidth_required; + p->surface_avg_vactive_required_bw = mode_lib->ms.surface_avg_vactive_required_bw; + p->surface_peak_required_bw = mode_lib->ms.surface_peak_required_bw; + p->display_cfg = display_cfg; + p->inc_flip_bw = 0; + p->num_active_planes = mode_lib->ms.num_active_planes; + p->num_of_dpp = mode_lib->ms.NoOfDPP; + p->dcc_dram_bw_nom_overhead_factor_p0 = mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0; + p->dcc_dram_bw_nom_overhead_factor_p1 = mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1; + p->dcc_dram_bw_pref_overhead_factor_p0 = mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p0; + p->dcc_dram_bw_pref_overhead_factor_p1 = mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p1; + p->surface_read_bandwidth_l = mode_lib->ms.vactive_sw_bw_l; + p->surface_read_bandwidth_c = mode_lib->ms.vactive_sw_bw_c; + p->prefetch_bandwidth_l = mode_lib->ms.RequiredPrefetchPixelDataBWLuma; + p->prefetch_bandwidth_c = mode_lib->ms.RequiredPrefetchPixelDataBWChroma; + p->excess_vactive_fill_bw_l = mode_lib->ms.excess_vactive_fill_bw_l; + p->excess_vactive_fill_bw_c = mode_lib->ms.excess_vactive_fill_bw_c; + p->cursor_bw = mode_lib->ms.cursor_bw; + p->dpte_row_bw = mode_lib->ms.dpte_row_bw; + p->meta_row_bw = mode_lib->ms.meta_row_bw; + p->prefetch_cursor_bw = mode_lib->ms.prefetch_cursor_bw; + p->prefetch_vmrow_bw = mode_lib->ms.prefetch_vmrow_bw; + p->flip_bw = mode_lib->ms.final_flip_bw; + p->urgent_burst_factor_l = mode_lib->ms.UrgentBurstFactorLuma; + p->urgent_burst_factor_c = mode_lib->ms.UrgentBurstFactorChroma; + p->urgent_burst_factor_cursor = mode_lib->ms.UrgentBurstFactorCursor; + p->urgent_burst_factor_prefetch_l = mode_lib->ms.UrgentBurstFactorLumaPre; + p->urgent_burst_factor_prefetch_c = mode_lib->ms.UrgentBurstFactorChromaPre; + p->urgent_burst_factor_prefetch_cursor = mode_lib->ms.UrgentBurstFactorCursorPre; + dcn5_calculate_peak_bandwidth_required(&mode_lib->scratch, p); +} + +static bool dcn5_ms_check_final_prefetch_support(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool support; + support = true; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + if (mode_lib->ms.NotEnoughUrgentLatencyHidingPre[k]) { + support = false; + DML_LOG_VERBOSE("DML::%s: k=%d, NotEnoughUrgentLatencyHidingPre=%d\n", __func__, k, mode_lib->ms.NotEnoughUrgentLatencyHidingPre[k]); + } + } + return support; +} + +static void dcn5_ms_calculate_flip_schedule(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + unsigned int per_pipe_flip_bytes; + + mode_lib->ms.TotImmediateFlipBytes = 0; + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + + per_pipe_flip_bytes = 0; + if (plane->immediate_flip) + per_pipe_flip_bytes = dcn5_get_pipe_flip_bytes(mode_lib->ms.HostVMInefficiencyFactor, + mode_lib->ms.vm_bytes[k], mode_lib->ms.DPTEBytesPerRow[k], + mode_lib->ms.meta_row_bytes[k]); + + mode_lib->ms.TotImmediateFlipBytes += per_pipe_flip_bytes * mode_lib->ms.NoOfDPP[k]; + + dcn5_calculate_flip_schedule( + &mode_lib->scratch, + display_cfg->plane_descriptors[k].immediate_flip, + 1, // use_lb_flip_bw + mode_lib->ms.HostVMInefficiencyFactor, + mode_lib->ms.Tvm_trips_flip[k], + mode_lib->ms.Tr0_trips_flip[k], + mode_lib->ms.Tvm_trips_flip_rounded[k], + mode_lib->ms.Tr0_trips_flip_rounded[k], + display_cfg->gpuvm_enable, + mode_lib->ms.vm_bytes[k], + mode_lib->ms.DPTEBytesPerRow[k], + 0.0, + mode_lib->ms.TotImmediateFlipBytes, + plane->pixel_format, + (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)), + plane->composition.scaler_info.plane0.v_ratio, + plane->composition.scaler_info.plane1.v_ratio, + mode_lib->ms.Tno_bw_flip[k], + mode_lib->ms.dpte_row_height[k], + mode_lib->ms.dpte_row_height_chroma[k], + mode_lib->ms.use_one_row_for_frame_flip[k], + mode_lib->ip.max_flip_time_us, + mode_lib->ip.max_flip_time_lines, + per_pipe_flip_bytes, + mode_lib->ms.meta_row_bytes[k], + mode_lib->ms.meta_row_height_luma[k], + mode_lib->ms.meta_row_height_chroma[k], + mode_lib->ip.dcn_mrq_present && plane->surface.dcc.enable, + /* Output */ + &mode_lib->ms.dst_y_per_vm_flip[k], + &mode_lib->ms.dst_y_per_row_flip[k], + &mode_lib->ms.final_flip_bw[k], + &mode_lib->ms.ImmediateFlipSupportedForPipe[k]); + } +} + +static void dcn5_ms_calculate_bandwidth_required_for_flip(const struct dml2_display_cfg *display_cfg, + struct dml2_core_calcs_mode_support_locals *s, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + struct dml2_core_calcs_calculate_peak_bandwidth_required_params *p = + &mode_lib->scratch.calculate_peak_bandwidth_params; + + p->urg_vactive_bandwidth_required = s->dummy_bw; + p->urg_bandwidth_required = mode_lib->ms.support.urg_bandwidth_required_flip; + p->urg_bandwidth_required_qual = s->dummy_bw; + p->non_urg_bandwidth_required = mode_lib->ms.support.non_urg_bandwidth_required_flip; + p->surface_avg_vactive_required_bw = s->surface_dummy_bw; + p->surface_peak_required_bw = mode_lib->ms.surface_peak_required_bw; + p->display_cfg = display_cfg; + p->inc_flip_bw = 1; + p->num_active_planes = mode_lib->ms.num_active_planes; + p->num_of_dpp = mode_lib->ms.NoOfDPP; + p->dcc_dram_bw_nom_overhead_factor_p0 = mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0; + p->dcc_dram_bw_nom_overhead_factor_p1 = mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1; + p->dcc_dram_bw_pref_overhead_factor_p0 = mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p0; + p->dcc_dram_bw_pref_overhead_factor_p1 = mode_lib->ms.dcc_dram_bw_pref_overhead_factor_p1; + p->surface_read_bandwidth_l = mode_lib->ms.vactive_sw_bw_l; + p->surface_read_bandwidth_c = mode_lib->ms.vactive_sw_bw_c; + p->prefetch_bandwidth_l = mode_lib->ms.RequiredPrefetchPixelDataBWLuma; + p->prefetch_bandwidth_c = mode_lib->ms.RequiredPrefetchPixelDataBWChroma; + p->excess_vactive_fill_bw_l = mode_lib->ms.excess_vactive_fill_bw_l; + p->excess_vactive_fill_bw_c = mode_lib->ms.excess_vactive_fill_bw_c; + p->cursor_bw = mode_lib->ms.cursor_bw; + p->dpte_row_bw = mode_lib->ms.dpte_row_bw; + p->meta_row_bw = mode_lib->ms.meta_row_bw; + p->prefetch_cursor_bw = mode_lib->ms.prefetch_cursor_bw; + p->prefetch_vmrow_bw = mode_lib->ms.prefetch_vmrow_bw; + p->flip_bw = mode_lib->ms.final_flip_bw; + p->urgent_burst_factor_l = mode_lib->ms.UrgentBurstFactorLuma; + p->urgent_burst_factor_c = mode_lib->ms.UrgentBurstFactorChroma; + p->urgent_burst_factor_cursor = mode_lib->ms.UrgentBurstFactorCursor; + p->urgent_burst_factor_prefetch_l = mode_lib->ms.UrgentBurstFactorLumaPre; + p->urgent_burst_factor_prefetch_c = mode_lib->ms.UrgentBurstFactorChromaPre; + p->urgent_burst_factor_prefetch_cursor = mode_lib->ms.UrgentBurstFactorCursorPre; + dcn5_calculate_peak_bandwidth_required(&mode_lib->scratch, p); +} + +static bool dcn5_ms_check_reordering_support(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + bool support = true; + + //Re-ordering Buffer Support Check + if (((mode_lib->ip.rob_buffer_size_kbytes - mode_lib->ip.pixel_chunk_size_kbytes) * 1024 + / mode_lib->ms.support.non_urg_bandwidth_required_flip[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp]) >= mode_lib->ms.support.max_urgent_latency_us) { + support = true; + } else { + support = false; + } + + DML_LOG_VERBOSE("DML::%s: max_urgent_latency_us = %f\n", __func__, mode_lib->ms.support.max_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: ROBSupport = %u\n", __func__, support); + return support; +} + +static void dcn5_ms_calculate_vactive_det_fill_latency(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + /* VActive fill time calculations (informative) */ + dcn5_calculate_vactive_det_fill_latency( + display_cfg, + mode_lib->ms.num_active_planes, + mode_lib->ms.pstate_bytes_required_l[dml2_pstate_type_uclk], + mode_lib->ms.pstate_bytes_required_c[dml2_pstate_type_uclk], + mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0, + mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1, + mode_lib->ms.vactive_sw_bw_l, + mode_lib->ms.vactive_sw_bw_c, + **mode_lib->ms.surface_avg_vactive_required_bw, + **mode_lib->ms.surface_peak_required_bw, + /* outputs */ + mode_lib->ms.pstate_vactive_det_fill_delay_us[dml2_pstate_type_uclk]); +} + +static bool dcn5_ms_check_mode_support(const struct dml2_display_cfg *display_cfg, + struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + bool immediateFlipRequired = false; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) + immediateFlipRequired = immediateFlipRequired + || display_cfg->plane_descriptors[k].immediate_flip; + + /*Mode Support, Voltage State and SOC Configuration*/ + if (mode_lib->ms.support.ScaleRatioAndTapsSupport == false) + goto fail; + if (mode_lib->ms.support.SourceFormatPixelAndScanSupport == false) + goto fail; + if (mode_lib->ms.support.ViewportSizeSupport == false) + goto fail; + if (mode_lib->ms.support.LinkRateDoesNotMatchDPVersion) + goto fail; + if (mode_lib->ms.support.LinkRateForMultistreamNotIndicated) + goto fail; + if (mode_lib->ms.support.BPPForMultistreamNotIndicated) + goto fail; + if (mode_lib->ms.support.MultistreamWithHDMIOreDP) + goto fail; + if (mode_lib->ms.support.ExceededMultistreamSlots) + goto fail; + if (mode_lib->ms.support.MSOOrODMSplitWithNonDPLink) + goto fail; + if (mode_lib->ms.support.NotEnoughLanesForMSO) + goto fail; + if (mode_lib->ms.support.P2IWith420) + goto fail; + if (mode_lib->ms.support.DSC422NativeNotSupported) + goto fail; + if (mode_lib->ms.support.DSCSlicesODMModeSupported == false) + goto fail; + if (mode_lib->ms.support.NotEnoughDSCUnits) + goto fail; + if (mode_lib->ms.support.NotEnoughDSCSlices) + goto fail; + if (mode_lib->ms.support.DSCCLKRequiredMoreThanSupported) + goto fail; + if (mode_lib->ms.support.PixelsPerLinePerDSCUnitSupport == false) + goto fail; + if (mode_lib->ms.support.DTBCLKRequiredMoreThanSupported) + goto fail; + if (mode_lib->ms.support.ROBSupport == false) + goto fail; + if (mode_lib->ms.support.OutstandingRequestsSupport == false) + goto fail; + if (mode_lib->ms.support.OutstandingRequestsUrgencyAvoidance == false) + goto fail; + if (mode_lib->ms.support.DISPCLK_DPPCLK_Support == false) + goto fail; + if (mode_lib->ms.support.TotalAvailablePipesSupport == false) + goto fail; + if (mode_lib->ms.support.NumberOfOTGSupport == false) + goto fail; + if (mode_lib->ms.support.NumberOfHDMIFRLSupport == false) + goto fail; + if (mode_lib->ms.support.NumberOfDP2p0Support == false) + goto fail; + if (mode_lib->ms.support.EnoughWritebackUnits == false) + goto fail; + if (mode_lib->ms.support.WritebackLatencySupport == false) + goto fail; + if (mode_lib->ms.support.WritebackScaleRatioAndTapsSupport == false) + goto fail; + if (mode_lib->ms.support.CursorSupport == false) + goto fail; + if (mode_lib->ms.support.PitchSupport == false) + goto fail; + if (mode_lib->ms.support.ViewportExceedsSurface) + goto fail; + if (mode_lib->ms.support.PrefetchSupported == false) + goto fail; + if (mode_lib->ms.support.EnoughUrgentLatencyHidingSupport == false) + goto fail; + if (mode_lib->ms.support.AvgBandwidthSupport == false) + goto fail; + if (mode_lib->ms.support.DynamicMetadataSupported == false) + goto fail; + if (mode_lib->ms.support.VRatioInPrefetchSupported == false) + goto fail; + if (mode_lib->ms.support.PTEBufferSizeNotExceeded == false) + goto fail; + if (mode_lib->ms.support.DCCMetaBufferSizeNotExceeded == false) + goto fail; + if (mode_lib->ms.support.global_temp_read_or_ppt_supported == false) + goto fail; + if (mode_lib->ms.support.global_dram_clock_change_supported == false) + if (mode_lib->ms.support.global_dram_clock_change_support_required) + goto fail; + if (mode_lib->ms.support.ImmediateFlipSupport == false) { + if (immediateFlipRequired) + goto fail; + if (display_cfg->hostvm_enable) + goto fail; + } + DML_LOG_VERBOSE("DML::%s: mode is supported\n", __func__); + return true; +fail: + dml2_core_utils_print_mode_support_info(&mode_lib->ms.support, true); + DML_LOG_VERBOSE("DML::%s: mode is NOT supported\n", __func__); + return false; +} + +static void dcn5_ms_populate_informative(struct dml2_core_internal_display_mode_lib *mode_lib) +{ + unsigned int k; + + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + mode_lib->ms.support.MPCCombineEnable[k] = mode_lib->ms.MPCCombine[k]; + mode_lib->ms.support.DPPPerSurface[k] = mode_lib->ms.NoOfDPP[k]; + } + for (k = 0; k <= mode_lib->ms.num_active_planes - 1; k++) { + mode_lib->ms.support.ODMMode[k] = mode_lib->ms.ODMMode[k]; + mode_lib->ms.support.DSCEnabled[k] = mode_lib->ms.RequiresDSC[k]; + mode_lib->ms.support.FECEnabled[k] = mode_lib->ms.RequiresFEC[k]; + mode_lib->ms.support.OutputBpp[k] = mode_lib->ms.OutputBpp[k]; + mode_lib->ms.support.OutputType[k] = mode_lib->ms.OutputType[k]; + mode_lib->ms.support.OutputRate[k] = mode_lib->ms.OutputRate[k]; + DML_LOG_VERBOSE("DML::%s: k=%d, ODMMode = %u\n", __func__, k, mode_lib->ms.support.ODMMode[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, DSCEnabled = %u\n", __func__, k, mode_lib->ms.support.DSCEnabled[k]); + } +} + +static void dcn5_ms_get_plane_support_info(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct core_plane_support_info *out, + int plane_idx) +{ + const struct dml2_stream_parameters *stream = + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[plane_idx].stream_index]; + + out->nominal_vblank_pstate_latency_hiding_us = (int)( + stream->timing.h_total + / ((double)stream->timing.pixel_clock_khz / 1000) + * mode_lib->ms.TWait[plane_idx]); + + DML_LOG_VERBOSE("DML::%s: plane_idx=%d, VActiveLatencyHidingMargin = %f\n", __func__, plane_idx, mode_lib->ms.VActiveLatencyHidingMargin[plane_idx]); + out->dram_change_latency_hiding_margin_in_active = (int)mode_lib->ms.VActiveLatencyHidingMargin[plane_idx]; + + out->active_latency_hiding_us = (int)mode_lib->ms.VActiveLatencyHidingUs[plane_idx]; + + out->vactive_det_fill_delay_us[dml2_pstate_type_uclk] = (unsigned int)math_ceil( + mode_lib->ms.pstate_vactive_det_fill_delay_us[dml2_pstate_type_uclk][plane_idx]); +} + +static void dcn5_ms_get_stream_support_info(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct core_stream_support_info *out, + int plane_index) +{ + (void)mode_lib; + const struct dml2_plane_parameters *plane = &display_cfg->plane_descriptors[plane_index]; + + out->vblank_reserved_time_us = plane->overrides.reserved_vblank_time_ns / 1000; + DML_LOG_VERBOSE("DML::%s: vblank_reserved_time_us = %u\n", __func__, out->vblank_reserved_time_us); +} + +static void dcn5_ms_setup_mode_lib_constants(struct dml2_core_calcs_mode_support_ex *in_out_params) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = in_out_params->mode_lib; + const struct dml2_display_cfg *display_cfg = in_out_params->in_display_cfg; + const struct dml2_utm_soc_bb *utm_soc_bb = in_out_params->utm_soc_bb; + unsigned int k; + + /* + * This function should be refactored away so that funcs/calcs that rely on these mode_lib constants take them + * as input, alternatively, these constants should be defined as their own structure and stored inside mode lib + * so it becomes obvious that these are constants that must/will always be valid when calling calcs + */ + memset(&mode_lib->scratch, 0, + sizeof(struct dml2_core_internal_scratch)); + memset(&mode_lib->ms, 0, + sizeof(struct dml2_core_internal_mode_support)); + mode_lib->ms.use_legacy_dsc_delay_formula = mode_lib->ip.use_legacy_dsc_delay_formula; + mode_lib->ms.num_active_planes = display_cfg->num_planes; + mode_lib->ms.max_dispclk_freq_mhz = (double) utm_soc_bb->max_dispclk_khz / 1000; + mode_lib->ms.max_dscclk_freq_mhz = (double) utm_soc_bb->max_dscclk_khz / 1000; + mode_lib->ms.max_dppclk_freq_mhz = (double) utm_soc_bb->max_dppclk_khz / 1000; + + DML_LOG_VERBOSE("DML::%s: --- START --- \n", __func__); + DML_LOG_VERBOSE("DML::%s: num_active_planes = %u\n", __func__, + mode_lib->ms.num_active_planes); + DML_LOG_VERBOSE("DML::%s: max_dispclk_freq_mhz = %f\n", __func__, + mode_lib->ms.max_dispclk_freq_mhz); + DML_LOG_VERBOSE("DML::%s: max_dscclk_freq_mhz = %f\n", __func__, + mode_lib->ms.max_dscclk_freq_mhz); + DML_LOG_VERBOSE("DML::%s: max_dppclk_freq_mhz = %f\n", __func__, + mode_lib->ms.max_dppclk_freq_mhz); + DML_LOG_VERBOSE("DML::%s: ip.compressed_buffer_segment_size_in_kbytes = %u\n", + __func__, + mode_lib->ip.compressed_buffer_segment_size_in_kbytes); + DML_LOG_VERBOSE("DML::%s: ip.dcn_mrq_present = %u\n", __func__, + mode_lib->ip.dcn_mrq_present); + for (k = 0; k < mode_lib->ms.num_active_planes; k++) + DML_LOG_VERBOSE("DML::%s: plane_%d: reserved_vblank_time_ns = %lu\n", __func__, k, + display_cfg->plane_descriptors[k].overrides.reserved_vblank_time_ns); +} + +static void dcn5_ms_populate_mode_support_result( + struct dml2_core_instance *core, + struct dml2_core_calcs_mode_support_ex *mode_support_ex_params, + struct dml2_core_mode_support_result *result) +{ + unsigned int i, stream_index, stream_bitmask; + int unsigned odm_count, num_odm_output_segments, dpp_count; + + *mode_support_ex_params->out_evaluation_info = mode_support_ex_params->mode_lib->ms.support; + + result->global.dispclk_khz = (unsigned int)(core->clean_me_up.mode_lib.ms.RequiredDISPCLK * 1000); + result->global.dcfclk_deepsleep_khz = (unsigned int)(core->clean_me_up.mode_lib.ms.dcfclk_deepsleep * 1000); + + result->global.fclk_pstate_supported = mode_support_ex_params->out_evaluation_info->global_fclk_change_supported; + result->global.uclk_pstate_supported = mode_support_ex_params->out_evaluation_info->global_dram_clock_change_supported; + + result->global.active.average_bw_sdp_kbps = 0; + result->global.active.urgent_bw_dram_kbps = 0; + + result->global.active.average_bw_sdp_kbps = (unsigned long)math_ceil2((mode_support_ex_params->out_evaluation_info->avg_bandwidth_required[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp] * 1000), 1.0); + result->global.active.urgent_bw_sdp_kbps = (unsigned long)math_ceil2((mode_support_ex_params->out_evaluation_info->urg_bandwidth_required_flip[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp] * 1000), 1.0); + + result->global.active.average_bw_dram_kbps = (unsigned long)math_ceil2((mode_support_ex_params->out_evaluation_info->avg_bandwidth_required[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_dram] * 1000), 1.0); + result->global.active.urgent_bw_dram_kbps = (unsigned long)math_ceil2((mode_support_ex_params->out_evaluation_info->urg_bandwidth_required_flip[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_dram] * 1000), 1.0); + DML_LOG_VERBOSE("DML::%s: result->global.active.urgent_bw_sdp_kbps = %ld\n", __func__, result->global.active.urgent_bw_sdp_kbps); + DML_LOG_VERBOSE("DML::%s: result->global.active.urgent_bw_dram_kbps = %ld\n", __func__, result->global.active.urgent_bw_dram_kbps); + + for (i = 0; i < mode_support_ex_params->in_display_cfg->num_planes; i++) { + result->per_plane[i].dppclk_khz = (unsigned int)(core->clean_me_up.mode_lib.ms.RequiredDPPCLK[i] * 1000); + } + + stream_bitmask = 0; + for (i = 0; i < mode_support_ex_params->in_display_cfg->num_planes; i++) { + odm_count = 1; + dpp_count = mode_support_ex_params->out_evaluation_info->DPPPerSurface[i]; + num_odm_output_segments = 1; + + switch (mode_support_ex_params->out_evaluation_info->ODMMode[i]) { + case dml2_odm_mode_bypass: + odm_count = 1; + dpp_count = mode_support_ex_params->out_evaluation_info->DPPPerSurface[i]; + break; + case dml2_odm_mode_combine_2to1: + odm_count = 2; + dpp_count = 2; + break; + case dml2_odm_mode_combine_3to1: + odm_count = 3; + dpp_count = 3; + break; + case dml2_odm_mode_combine_4to1: + odm_count = 4; + dpp_count = 4; + break; + case dml2_odm_mode_split_1to2: + case dml2_odm_mode_mso_1to2: + num_odm_output_segments = 2; + break; + case dml2_odm_mode_mso_1to4: + num_odm_output_segments = 4; + break; + case dml2_odm_mode_auto: + default: + odm_count = 1; + dpp_count = mode_support_ex_params->out_evaluation_info->DPPPerSurface[i]; + break; + } + + result->cfg_support_info.plane_support_info[i].dpps_used = dpp_count; + + dcn5_ms_get_plane_support_info(mode_support_ex_params->in_display_cfg, &core->clean_me_up.mode_lib, &result->cfg_support_info.plane_support_info[i], i); + + stream_index = mode_support_ex_params->in_display_cfg->plane_descriptors[i].stream_index; + + result->per_stream[stream_index].dscclk_khz = (unsigned int)core->clean_me_up.mode_lib.ms.required_dscclk_freq_mhz[i] * 1000; + DML_LOG_VERBOSE("CORE_DCN4::%s: i=%d stream_index=%d, result->per_stream[stream_index].dscclk_khz = %u\n", __func__, i, stream_index, result->per_stream[stream_index].dscclk_khz); + + if (!((stream_bitmask >> stream_index) & 0x1)) { + result->cfg_support_info.stream_support_info[stream_index].odms_used = odm_count; + result->cfg_support_info.stream_support_info[stream_index].num_odm_output_segments = num_odm_output_segments; + result->cfg_support_info.stream_support_info[stream_index].dsc_enable = mode_support_ex_params->out_evaluation_info->DSCEnabled[i]; + result->cfg_support_info.stream_support_info[stream_index].num_dsc_slices = mode_support_ex_params->out_evaluation_info->NumberOfDSCSlices[i]; + dcn5_ms_get_stream_support_info(mode_support_ex_params->in_display_cfg, &core->clean_me_up.mode_lib, &result->cfg_support_info.stream_support_info[stream_index], i); + result->per_stream[stream_index].dtbclk_khz = (unsigned int)(core->clean_me_up.mode_lib.ms.RequiredDTBCLK[i] * 1000); + stream_bitmask |= 0x1 << stream_index; + } + } + result->bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps = (unsigned long)(mode_support_ex_params->mode_lib->ms.support.urg_bandwidth_required_flip[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp] * 1000); + result->bandwidth_upper_bound.dcn5.non_urgent_bandwidth_kbps = (unsigned long)(mode_support_ex_params->mode_lib->ms.support.non_urg_bandwidth_required_flip[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp] * 1000); +} + +static void dcn5_ms_calculate_watermarks(const struct dml2_display_cfg *display_cfg, + struct dml2_core_calcs_mode_support_locals *s, + struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p = + &mode_lib->scratch.CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params; + + s->mSOCParameters.UrgentLatency = mode_lib->ms.UrgLatency; + s->mSOCParameters.ExtraLatency = mode_lib->ms.ExtraLatency; + s->mSOCParameters.ExtraLatency_sr = mode_lib->ms.ExtraLatency_sr; + s->mSOCParameters.WritebackLatency = utm_soc_bb->writeback_base_latency_us; + s->mSOCParameters.DRAMClockChangeLatency = + utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + s->mSOCParameters.FCLKChangeLatency = utm_soc_bb->power_management_parameters.fclk_change_blackout_us; + s->mSOCParameters.SRExitTime = utm_soc_bb->power_management_parameters.stutter_exit_latency_us; + s->mSOCParameters.SREnterPlusExitTime = + utm_soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us; + s->mSOCParameters.SRExitZ8Time = utm_soc_bb->power_management_parameters.z8_stutter_exit_latency_us; + s->mSOCParameters.SREnterPlusExitZ8Time = + utm_soc_bb->power_management_parameters.z8_stutter_enter_plus_exit_latency_us; + s->mSOCParameters.USRRetrainingLatency = 0; + s->mSOCParameters.SMNLatency = 0; + s->mSOCParameters.temp_read_or_ppt_blackout_us = utm_soc_bb->power_management_parameters.g7_ppt_blackout_us; + s->mSOCParameters.max_urgent_latency_us = mode_lib->ms.support.max_urgent_latency_us; + s->mSOCParameters.df_response_time_us = mode_lib->ms.support.df_response_time_us; + s->mSOCParameters.qos_type = dml2_qos_param_type_dcn4x; + + p->display_cfg = display_cfg; + p->USRRetrainingRequired = false; + p->NumberOfActiveSurfaces = mode_lib->ms.num_active_planes; + p->MaxLineBufferLines = mode_lib->ip.max_line_buffer_lines; + p->LineBufferSize = mode_lib->ip.line_buffer_size_bits; + p->WritebackInterfaceBufferSize = mode_lib->ip.writeback_interface_buffer_size_kbytes; + p->DCFCLK = mode_lib->ms.DCFCLK; + p->SynchronizeTimings = display_cfg->overrides.synchronize_timings; + p->SynchronizeDRRDisplaysForUCLKPStateChange = + display_cfg->overrides.synchronize_ddr_displays_for_uclk_pstate_change; + p->dpte_group_bytes = mode_lib->ms.dpte_group_bytes; + p->mmSOCParameters = s->mSOCParameters; + p->WritebackChunkSize = mode_lib->ip.writeback_chunk_size_kbytes; + p->SOCCLK = utm_soc_bb->qos_model_dchub_v1.socclks_khz[0] / 1000.0; + p->DCFClkDeepSleep = mode_lib->ms.dcfclk_deepsleep; + p->DETBufferSizeY = mode_lib->ms.DETBufferSizeY; + p->DETBufferSizeC = mode_lib->ms.DETBufferSizeC; + p->SwathHeightY = mode_lib->ms.SwathHeightY; + p->SwathHeightC = mode_lib->ms.SwathHeightC; + //CalculateWatermarks_params->LBBitPerPixel = 57; // FIXME_STAGE2, need a new ip param? + p->SwathWidthY = mode_lib->ms.SwathWidthY; + p->SwathWidthC = mode_lib->ms.SwathWidthC; + p->DPPPerSurface = mode_lib->ms.NoOfDPP; + p->BytePerPixelDETY = mode_lib->ms.BytePerPixelInDETY; + p->BytePerPixelDETC = mode_lib->ms.BytePerPixelInDETC; + p->DSTXAfterScaler = mode_lib->ms.DSTXAfterScaler; + p->DSTYAfterScaler = mode_lib->ms.DSTYAfterScaler; + p->UnboundedRequestEnabled = mode_lib->ms.UnboundedRequestEnabled; + p->CompressedBufferSizeInkByte = mode_lib->ms.CompressedBufferSizeInkByte; + p->meta_row_height_l = mode_lib->ms.meta_row_height_luma; + p->meta_row_height_c = mode_lib->ms.meta_row_height_chroma; + p->uclk_pstate_switch_modes = mode_lib->ms.uclk_pstate_switch_modes; + // Output + p->Watermark = &mode_lib->ms.support.watermarks; // Watermarks *Watermark + p->DRAMClockChangeSupport = mode_lib->ms.support.DRAMClockChangeSupport; + p->global_dram_clock_change_support_required = &mode_lib->ms.support.global_dram_clock_change_support_required; + p->global_dram_clock_change_supported = &mode_lib->ms.support.global_dram_clock_change_supported; + p->MaxActiveDRAMClockChangeLatencySupported = &s->dummy_single_array[0]; // double *MaxActiveDRAMClockChangeLatencySupported[] + p->FCLKChangeSupport = mode_lib->ms.support.FCLKChangeSupport; + p->global_fclk_change_supported = &mode_lib->ms.support.global_fclk_change_supported; + p->MaxActiveFCLKChangeLatencySupported = &s->dummy_single[0]; // double *MaxActiveFCLKChangeLatencySupported + p->USRRetrainingSupport = &mode_lib->ms.support.USRRetrainingSupport; + p->temp_read_or_ppt_support = mode_lib->ms.support.temp_read_or_ppt_support; + p->global_temp_read_or_ppt_supported = &mode_lib->ms.support.global_temp_read_or_ppt_supported; + p->VActiveLatencyHidingMargin = mode_lib->ms.VActiveLatencyHidingMargin; + p->VActiveLatencyHidingUs = mode_lib->ms.VActiveLatencyHidingUs; + dcn5_calculate_watermarks_and_dram_speed_change_support(&mode_lib->scratch, p); +} + +static bool dcn5_ms_validate_prefetch(struct dml2_core_calcs_mode_support_ex *in_out_params) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = in_out_params->mode_lib; + const struct dml2_display_cfg *display_cfg = in_out_params->in_display_cfg; + struct dml2_core_calcs_mode_support_locals *s = &mode_lib->scratch.dml_core_mode_support_locals; + const struct dml2_utm_soc_bb *utm_soc_bb = in_out_params->utm_soc_bb; + + // dcn5_ms_calculate_avg_bandwidth_and_dcfclk_lb_required(display_cfg, mode_lib); + + /* FIXME - break it down according the function name */ + dcn5_ms_calculate_det_buffer_time_value_urgent_burst_factor_and_urgent_latency_hiding(display_cfg, mode_lib); + + mode_lib->ms.support.EnoughUrgentLatencyHidingSupport = dcn5_ms_check_urgent_latency_hiding_support(mode_lib); + + dcn5_ms_calculate_min_dcfclk_deepsleep_clock(display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_calculate_writeback_delay(display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_calculate_max_vstartup(display_cfg, mode_lib); + + dcn5_ms_calculate_mcache_setting(display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_calculate_avg_bandwidth_and_dcfclk_lb_required(display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_check_average_latency_supports(mode_lib, utm_soc_bb); + + mode_lib->ms.support.AvgBandwidthSupport = true; + /* Prefetch Check */ + { + dcn5_ms_calculate_t_calc(mode_lib); + + dcn5_ms_calculate_hostvm_inefficiency_factor(display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_calculate_3dlut_settings(display_cfg, mode_lib); + + dcn5_ms_calculate_urgent_latency(display_cfg, mode_lib, utm_soc_bb); + { + dcn5_ms_calculate_prefetch_schedule(display_cfg, mode_lib, s, utm_soc_bb); + + mode_lib->ms.support.PrefetchSupported = dcn5_ms_check_prefetch_support(mode_lib); + + mode_lib->ms.support.DynamicMetadataSupported = dcn5_ms_check_dynamic_metadata_support(mode_lib); + + mode_lib->ms.support.VRatioInPrefetchSupported = dcn5_ms_check_v_ratio_in_prefetch_support(mode_lib); + + // Only do urg vs prefetch bandwidth check, flip schedule check, power saving feature support check IF the Prefetch Schedule Check is ok + if (mode_lib->ms.support.PrefetchSupported) { + dcn5_ms_calculate_urgent_burst_factor_for_prefetch(display_cfg, mode_lib); + + dcn5_ms_calculate_peak_bandwidth_required(display_cfg, mode_lib); + + mode_lib->ms.support.PrefetchSupported = dcn5_ms_check_final_prefetch_support(mode_lib); + + // Both prefetch schedule and BW okay + if (mode_lib->ms.support.PrefetchSupported == true + && mode_lib->ms.support.VRatioInPrefetchSupported == true) { + + dcn5_ms_calculate_flip_schedule(display_cfg, mode_lib); + + dcn5_ms_calculate_bandwidth_required_for_flip(display_cfg, s, mode_lib); + + mode_lib->ms.support.ImmediateFlipSupport = true; + } else { // if prefetch not support, assume iflip is not supported too + mode_lib->ms.support.ImmediateFlipSupport = false; + } + } // prefetch schedule + } + dcn5_ms_calculate_watermarks(display_cfg, s, mode_lib, utm_soc_bb); + + DML_LOG_VERBOSE("DML::%s: Done prefetch calculation\n", __func__); + } // End of Prefetch Check + + mode_lib->ms.support.ROBSupport = dcn5_ms_check_reordering_support(mode_lib); + + dcn5_ms_calculate_vactive_det_fill_latency(display_cfg, mode_lib); + + /* Immediate Flip parameters */ + mode_lib->ms.support.ModeSupport = dcn5_ms_check_mode_support(display_cfg, mode_lib); + + dcn5_ms_populate_informative(mode_lib); + + return mode_lib->ms.support.ModeSupport; +} + +static bool dcn5_mode_support(struct dml2_core_calcs_mode_support_ex *in_out_params) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = in_out_params->mode_lib; + const struct dml2_display_cfg *display_cfg = in_out_params->in_display_cfg; + struct dml2_core_calcs_mode_support_locals *s = &mode_lib->scratch.dml_core_mode_support_locals; + const struct dml2_utm_soc_bb *utm_soc_bb = in_out_params->utm_soc_bb; + unsigned int k; + + dcn5_ms_check_input_sanity(display_cfg, mode_lib); + + mode_lib->ms.support.ScaleRatioAndTapsSupport = dcn5_ms_check_scaler_support(display_cfg, mode_lib); + + mode_lib->ms.support.SourceFormatPixelAndScanSupport = dcn5_ms_check_source_format_and_scan_direction( + display_cfg, mode_lib); + + dcn5_ms_calculate_byte_per_pixel_and_block_sizes(display_cfg, mode_lib); + + dcn5_ms_calculate_read_bandwidth(display_cfg, mode_lib); + + dcn5_ms_calculate_writeback_bandwidth(display_cfg, mode_lib); + + mode_lib->ms.support.WritebackLatencySupport = dcn5_ms_check_writeback_bandwidth_latency_support(display_cfg, mode_lib, utm_soc_bb); + + mode_lib->ms.support.WritebackScaleRatioAndTapsSupport = dcn5_ms_check_writeback_scale_ratio_and_taps_support(display_cfg, mode_lib); + + dcn5_ms_calculate_single_pipe_dppclk_and_pscl_factor(display_cfg, mode_lib); + + dcn5_ms_calculate_max_swath_widths(display_cfg, mode_lib); + + mode_lib->ms.support.CursorSupport = dcn5_ms_check_cursor_support(display_cfg, mode_lib); + + mode_lib->ms.support.PitchSupport = dcn5_ms_check_surface_alginment_requirements(display_cfg, mode_lib); + + mode_lib->ms.support.TotalAvailablePipesSupport = true; + + dcn5_ms_calculate_effective_pixel_clock(display_cfg, mode_lib); + + dml2_core_utils_get_stream_output_bpp(mode_lib->ms.DesiredOutputBpp, display_cfg); + + for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { + dcn5_ms_calculate_dsc_slices_per_plane(k, display_cfg, mode_lib); + + dcn5_ms_calculate_odm_mode_per_plane(k, display_cfg, mode_lib); + + dcn5_ms_calculate_output_link(k, display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_calculate_final_odm_mode_per_plane(k, mode_lib); + + dcn5_ms_calculate_final_dsc_slices_per_plane(k, display_cfg, mode_lib); + } + + dcn5_ms_calculate_max_det_and_min_compressed_buffer_size(display_cfg, mode_lib); + + dcn5_ms_calculate_swath_and_det_configuration_for_single_dpp(display_cfg, mode_lib, s); + + dcn5_ms_calculate_num_of_dpp_required(display_cfg, mode_lib); + + /* FIXME: TotalAvailablePipesSupport is also modified in dcn5_ms_calculate_final_dsc_slices_per_plane */ + mode_lib->ms.support.TotalAvailablePipesSupport &= dcn5_ms_check_total_available_pipes_support(mode_lib); + + dcn5_ms_calculate_total_num_of_single_dpp_surfaces(mode_lib); + + dcn5_ms_calculate_dispclk_and_dppclk_required(display_cfg, mode_lib); + + mode_lib->ms.support.DISPCLK_DPPCLK_Support = dcn5_ms_check_dispclk_and_dppclk_support(mode_lib); + + mode_lib->ms.support.NumberOfOTGSupport = dcn5_ms_check_otg_count_support(display_cfg, mode_lib); + + mode_lib->ms.support.NumberOfHDMIFRLSupport = dcn5_ms_check_hpo_frl_encoder_count_support(display_cfg, mode_lib); + + mode_lib->ms.support.NumberOfDP2p0Support = dcn5_ms_check_hpo_dp_encoder_count_support(display_cfg, mode_lib); + + mode_lib->ms.support.EnoughWritebackUnits = dcn5_ms_check_writeback_count_support(display_cfg, mode_lib); + + mode_lib->ms.support.LinkCapacitySupport = dcn5_ms_check_link_bandwidth_support(display_cfg, mode_lib); + + dcn5_ms_check_misc_link_supports(display_cfg, mode_lib); + + dcn5_ms_calculate_dtbclk_required(display_cfg, mode_lib); + + mode_lib->ms.support.DTBCLKRequiredMoreThanSupported = !dcn5_ms_check_dtbclk_support(mode_lib, utm_soc_bb); + + dcn5_ms_calculate_dscclk_required(display_cfg, mode_lib); + + mode_lib->ms.support.DSCCLKRequiredMoreThanSupported = !dcn5_ms_check_dscclk_support(mode_lib); + + dcn5_ms_check_dsc_engine_supports(display_cfg, mode_lib); + + dcn5_ms_calculate_dsc_delay(display_cfg, mode_lib); + + dcn5_ms_calculate_swath_and_det_configuration(mode_lib, s); + + dcn5_ms_calculate_total_num_of_dcc_active_dpp(display_cfg, mode_lib); + + dcn5_ms_calculate_vm_row_and_swath_and_calculate_dcc_meta_cache_requirements(display_cfg, mode_lib, s); + + mode_lib->ms.support.PTEBufferSizeNotExceeded = dcn5_ms_check_pte_buffer_size_support(mode_lib); + + mode_lib->ms.support.DCCMetaBufferSizeNotExceeded = dcn5_ms_check_dcc_meta_cache_support(mode_lib); + + dcn5_ms_calculate_vactive_uclk_pstate_requirements(display_cfg, mode_lib, utm_soc_bb); + + dcn5_ms_validate_prefetch(in_out_params); + + DML_LOG_VERBOSE("DML::%s: done\n", __func__); + return mode_lib->ms.support.ModeSupport; +} + +static void dcn5_ms_assign_sop_constraint(struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_sop_constraint *sop_constraint) +{ + mode_lib->ms.UrgLatency = sop_constraint->dcn5.latency.dcn5.urgent_ramp; + mode_lib->ms.TripToMemory = math_max2(sop_constraint->dcn5.latency.dcn5.t_trip, + sop_constraint->dcn5.latency.dcn5.urgent_ramp); + mode_lib->ms.support.avg_urgent_latency_us = sop_constraint->dcn5.latency.dcn5.avg_req_latency_urg; + mode_lib->ms.support.avg_non_urgent_latency_us = sop_constraint->dcn5.latency.dcn5.avg_req_latency_non_urg; + mode_lib->ms.support.max_urgent_latency_us = sop_constraint->dcn5.latency.dcn5.max_req_latency_urg; + mode_lib->ms.support.max_non_urgent_latency_us = sop_constraint->dcn5.latency.dcn5.max_req_latency_non_urg; + mode_lib->ms.support.df_response_time_us = sop_constraint->dcn5.latency.dcn5.df_response_time_us; + + DML_LOG_VERBOSE("DML::%s: urgent_ramp=%f\n", __func__, sop_constraint->dcn5.latency.dcn5.urgent_ramp); + DML_LOG_VERBOSE("DML::%s: t_trip=%f\n", __func__, sop_constraint->dcn5.latency.dcn5.t_trip); + DML_LOG_VERBOSE("DML::%s: avg_req_latency_urg=%f\n", __func__, + sop_constraint->dcn5.latency.dcn5.avg_req_latency_urg); + DML_LOG_VERBOSE("DML::%s: avg_req_latency_non_urg=%f\n", __func__, + sop_constraint->dcn5.latency.dcn5.avg_req_latency_non_urg); + DML_LOG_VERBOSE("DML::%s: max_req_latency_urg=%f\n", __func__, + sop_constraint->dcn5.latency.dcn5.max_req_latency_urg); + DML_LOG_VERBOSE("DML::%s: max_req_latency_non_urg=%f\n", __func__, + sop_constraint->dcn5.latency.dcn5.max_req_latency_non_urg); +} + +static void dcn5_ms_setup_pstate_options(struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_display_solution *solution) +{ + memcpy(mode_lib->ms.uclk_pstate_switch_modes, + solution->uclk_pstate_params.pstate_switch_modes, + sizeof(solution->uclk_pstate_params.pstate_switch_modes)); +} + +enum dml2_status dml2_core_dcn5_funcs_validate_solution(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_validation_result *result) +{ + enum dml2_status status = DML2_STATUS_OK; + struct dml2_core_mode_support_locals *l = &core->scratch.mode_support_locals; + const struct dml2_sop_table *sop_table = &core->utm_soc_bb->sop_table; + unsigned int i; + + if (solution->unvalidated_change.bits.mpc_combine_overrides + || solution->unvalidated_change.bits.odm_combine_overrides + || solution->unvalidated_change.bits.reserved_vblank_time + || solution->unvalidated_change.bits.uclk_pstate_method) + result->is_mode_support_valid = false; + if (solution->unvalidated_change.bits.sop_index) + result->is_prefetch_valid = false; + + if (!result->is_mode_support_valid) { + l->mode_support_ex_params.mode_lib = &core->clean_me_up.mode_lib; + l->mode_support_ex_params.in_display_cfg = &solution->dispcfg; + l->mode_support_ex_params.out_evaluation_info = &result->mode_support.cfg_support_info.clean_me_up.support_info; + l->mode_support_ex_params.utm_soc_bb = core->utm_soc_bb; + dcn5_ms_setup_mode_lib_constants(&l->mode_support_ex_params); + dcn5_ms_assign_sop_constraint(l->mode_support_ex_params.mode_lib, &solution->sop_constraint); + dcn5_ms_setup_pstate_options(l->mode_support_ex_params.mode_lib, solution); + result->is_mode_support_valid = dcn5_mode_support(&l->mode_support_ex_params); + result->mode_support.cfg_support_info.is_supported = result->is_mode_support_valid; + if (result->is_mode_support_valid) { + dcn5_ms_populate_mode_support_result(core, &l->mode_support_ex_params, &result->mode_support); + result->is_prefetch_valid = sop_table->is_bw_supported_at_index(sop_table, + &result->mode_support.bandwidth_upper_bound, solution->sop_constraint.dcn5.min_sop_index); + } else { + result->is_prefetch_valid = false; + } + } else if (!result->is_prefetch_valid) { + dcn5_ms_assign_sop_constraint(l->mode_support_ex_params.mode_lib, &solution->sop_constraint); + if (dcn5_ms_validate_prefetch(&l->mode_support_ex_params)) { + dcn5_ms_populate_mode_support_result(core, &l->mode_support_ex_params, &result->mode_support); + result->is_prefetch_valid = sop_table->is_bw_supported_at_index(sop_table, + &result->mode_support.bandwidth_upper_bound, solution->sop_constraint.dcn5.min_sop_index); + } + } + + if (!result->is_mcache_allocation_valid) { + result->is_mcache_allocation_valid = true; + for (i = 0; i < solution->dispcfg.num_planes; i++) { + if (!solution->dispcfg.plane_descriptors[i].surface.dcc.enable) { + memset(&result->mcache_allocations[i], 0, sizeof(struct dml2_mcache_surface_allocation)); + continue; + } + + l->calc_mcache_allocation_params.instance = core; + l->calc_mcache_allocation_params.plane_descriptor = &solution->dispcfg.plane_descriptors[i]; + l->calc_mcache_allocation_params.mcache_allocation = &result->mcache_allocations[i]; + l->calc_mcache_allocation_params.plane_index = i; + if (!core->calculate_mcache_allocation(&l->calc_mcache_allocation_params)) { + result->is_mcache_allocation_valid = false; + break; + } + } + } + + DML_LOG_VERBOSE("DML::%s: result->is_mode_support_valid = %d\n", __func__, result->is_mode_support_valid); + DML_LOG_VERBOSE("DML::%s: result->is_prefetch_valid = %d\n", __func__, result->is_prefetch_valid); + DML_LOG_VERBOSE("DML::%s: result->is_mcache_allocation_valid = %d\n", __func__, result->is_mcache_allocation_valid); + DML_LOG_VERBOSE("DML::%s: done\n", __func__); + + status = !result->is_mode_support_valid ? DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT : + !result->is_prefetch_valid ? DML2_STATUS_VALIDATE_FAIL_PREFETCH : + !result->is_mcache_allocation_valid ? DML2_STATUS_VALIDATE_FAIL_MCACHE : DML2_STATUS_OK; + + return status; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.h new file mode 100644 index 000000000000..fa0eaa69a9af --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_support.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN5_FUNCS_MODE_SUPPORT_H__ +#define __DML2_CORE_DCN5_FUNCS_MODE_SUPPORT_H__ +#include "dml2_internal_shared_types.h" +enum dml2_status dml2_core_dcn5_funcs_validate_solution(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_validation_result *result); +#endif /* __DML2_CORE_DCN5_FUNCS_MODE_SUPPORT_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.c new file mode 100644 index 000000000000..c86e9c5feec4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn6_calcs.h" + +/* + * Placeholder for future DCN6 calcs table implementation. + * Remove this typedef once real declarations are added here. + */ +typedef int dml2_core_dcn6_calcs_placeholder; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.h new file mode 100644 index 000000000000..35f7d8a050a7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN6_CALCS_H__ +#define __DML2_CORE_DCN6_CALCS_H__ + +#endif /* __DML2_CORE_DCN6_CALCS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c new file mode 100644 index 000000000000..cae6bee93fe3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c @@ -0,0 +1,1951 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn5_calcs_dchub.h" +#include "dml2_core_dcn6_calcs_dchub.h" +#include "dml_top_display_cfg_types.h" +#include "dml2_core_utils.h" +#include "dml_top_types.h" + +unsigned int dcn6_calculate_max_vstartup( + bool ptoi_supported, + unsigned int vblank_nom_default_us, + const struct dml2_timing_cfg *timing, + enum dml2_uclk_pstate_change_strategy pstate_strategy, + double write_back_delay_us, + unsigned int svp_lines) +{ + unsigned int vblank_size = 0; + unsigned int max_vstartup_lines = 0; + + double line_time_us = (double)timing->h_total / ((double)timing->pixel_clock_khz / 1000); + unsigned int vblank_actual = timing->v_total - timing->v_active; + unsigned int vblank_nom_default_in_line = (unsigned int)math_floor2((double)vblank_nom_default_us / line_time_us, 1.0); + unsigned int vblank_avail = (timing->vblank_nom == 0) ? vblank_nom_default_in_line : (unsigned int)timing->vblank_nom; + + vblank_size = (unsigned int)math_min2(vblank_actual, vblank_avail); + + if (timing->interlaced && !ptoi_supported) + max_vstartup_lines = (unsigned int)(math_floor2((vblank_size - 1) / 2.0, 1.0)); + else + max_vstartup_lines = vblank_size - (unsigned int)math_max2(1.0, math_ceil2(write_back_delay_us / line_time_us, 1.0)); + + if (pstate_strategy == dml2_uclk_pstate_change_strategy_force_alternate) + max_vstartup_lines = (unsigned int)math_min2(max_vstartup_lines, svp_lines); + + max_vstartup_lines = (unsigned int)math_min2(max_vstartup_lines, __DML2_CALCS_MAX_VSTARTUP__); + + DML_LOG_VERBOSE("DML::%s: VBlankNom = %lu\n", __func__, timing->vblank_nom); + DML_LOG_VERBOSE("DML::%s: vblank_nom_default_us = %u\n", __func__, vblank_nom_default_us); + DML_LOG_VERBOSE("DML::%s: line_time_us = %f\n", __func__, line_time_us); + DML_LOG_VERBOSE("DML::%s: vblank_actual = %u\n", __func__, vblank_actual); + DML_LOG_VERBOSE("DML::%s: vblank_avail = %u\n", __func__, vblank_avail); + DML_LOG_VERBOSE("DML::%s: max_vstartup_lines = %u\n", __func__, max_vstartup_lines); + return max_vstartup_lines; +} + +void dcn6_calculate_alternate_svp_lines(struct dml2_core_calcs_calculate_alternate_svp_lines *p) +{ + unsigned int i, j; + double line_time_us, max_line_time_us = 0, svp0_time_us, svp1_time_us, vratio, vratio_c, swath_time_us, swath_time_c_us; + double max_swath_time_all_planes_us = 0; + double pad_us = 0; + + for (i = 0; i < p->display_cfg->num_streams; i++) { + line_time_us = ((double)p->display_cfg->stream_descriptors[i].timing.h_total * 1000 / p->display_cfg->stream_descriptors[i].timing.pixel_clock_khz); + for (j = 0; j < p->display_cfg->num_planes; j++) { + if (p->display_cfg->plane_descriptors[j].stream_index == i) { + vratio = p->display_cfg->plane_descriptors[j].composition.scaler_info.plane0.v_ratio; + vratio_c = p->display_cfg->plane_descriptors[j].composition.scaler_info.plane1.v_ratio; + /* For now swath_time calculated based only on vratio - can use hdl schedule later once calculated + * (though HDL scehdule may produce the same result as just calc from vratio) */ + swath_time_us = ((double)p->SwathHeightY[j] / vratio) * line_time_us; + swath_time_c_us = p->BytePerPixelInDETC[j] > 0 ? ((double)p->SwathHeightC[j] / vratio_c) * line_time_us : 0; + if (swath_time_us > max_swath_time_all_planes_us || swath_time_c_us > max_swath_time_all_planes_us) + max_swath_time_all_planes_us = swath_time_us > swath_time_c_us ? swath_time_us : swath_time_c_us; + } + } + if (line_time_us > max_line_time_us) + max_line_time_us = line_time_us; + } + pad_us = max_swath_time_all_planes_us + max_line_time_us; + svp0_time_us = p->dram_blackout_us + pad_us + max_swath_time_all_planes_us; + svp1_time_us = p->dram_blackout_us + pad_us; + + for (i = 0; i < p->display_cfg->num_streams; i++) { + line_time_us = ((double)p->display_cfg->stream_descriptors[i].timing.h_total * 1000 / p->display_cfg->stream_descriptors[i].timing.pixel_clock_khz); + p->svp0_dst_lines[i] = (unsigned int)math_ceil(svp0_time_us / line_time_us); + p->svp1_dst_lines[i] = (unsigned int)math_ceil(svp1_time_us / line_time_us); + p->svp_req_limit[i] = (unsigned int)math_ceil((pad_us + max_swath_time_all_planes_us) / line_time_us); + } + DML_LOG_VERBOSE("DML::%s: svp0_time_us = %f\n", __func__, svp0_time_us); + DML_LOG_VERBOSE("DML::%s: svp1_time_us = %f\n", __func__, svp1_time_us); + DML_LOG_VERBOSE("DML::%s: max_swath_time_all_planes_us = %f\n", __func__, max_swath_time_all_planes_us); + DML_LOG_VERBOSE("DML::%s: pad_us = %f\n", __func__, pad_us); +} + +struct plane_params { + unsigned int viewport_start; + unsigned int viewport_size; + unsigned int swath_height; + double prefetch_hdl_delta; + double recout_hdl_delta; + double vratio; + unsigned int vinit; +}; + +/** + * ***************************************************************************************************************************** + * get_plane_params: Get plane related params for chroma vs. luma depending on the chroma flag + * ***************************************************************************************************************************** + */ +static void get_plane_params( + const struct dml2_core_calcs_calculate_alternate_params *p, + unsigned int plane_idx, + bool chroma, + struct plane_params *out) +{ + const struct dml2_plane_parameters *plane = &p->display_cfg->plane_descriptors[plane_idx]; + bool vertical_access = p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 || p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270; + + if (!chroma) { + out->viewport_start = vertical_access ? plane->composition.viewport.plane0.x_start : plane->composition.viewport.plane0.y_start; + out->viewport_size = vertical_access ? plane->composition.viewport.plane0.width : plane->composition.viewport.plane0.height; + out->swath_height = p->SwathHeightY[plane_idx]; + out->prefetch_hdl_delta = p->prefetch_hdl_delta[plane_idx]; + out->recout_hdl_delta = p->recout_hdl_delta[plane_idx]; + out->vratio = p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane0.v_ratio; + out->vinit = p->VInitPrefillY[plane_idx]; + } else { + out->viewport_start = vertical_access ? plane->composition.viewport.plane1.x_start : plane->composition.viewport.plane1.y_start; + out->viewport_size = vertical_access ? plane->composition.viewport.plane1.width : plane->composition.viewport.plane1.height; + out->swath_height = p->SwathHeightC[plane_idx]; + out->prefetch_hdl_delta = p->prefetch_hdl_delta_c[plane_idx]; + out->recout_hdl_delta = p->recout_hdl_delta_c[plane_idx]; + out->vratio = p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane1.v_ratio; + out->vinit = p->VInitPrefillC[plane_idx]; + } +} + +/** + * ***************************************************************************************************************************** + * compute_pre_rec_first_hdl: Computes the first hdl position of pre and rec swath given the input params + * ***************************************************************************************************************************** + */ +static void compute_pre_rec_first_hdl( + const struct dml2_core_calcs_calculate_alternate_params *p, + bool chroma, + unsigned int stream_idx, + unsigned int plane_idx, + double *pre_first_hdl_out, + double *rec_first_hdl_out) +{ + unsigned long vtotal = p->display_cfg->stream_descriptors[stream_idx].timing.v_total; + unsigned int vblank_end = p->display_cfg->stream_descriptors[stream_idx].timing.v_blank_end; + bool access_direction = (p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 && !p->display_cfg->plane_descriptors[plane_idx].composition.mirrored) || + (p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270 && p->display_cfg->plane_descriptors[plane_idx].composition.mirrored) || + (p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_180); + double dst_y_prefetch = p->dst_y_prefetch[plane_idx]; + double dst_y_per_vm_vblank = p->dst_y_per_vm_vblank[plane_idx]; + double dst_y_per_row_vblank = p->dst_y_per_row_vblank[plane_idx]; + unsigned int dst_y_after_scaler = p->DSTYAfterScaler[plane_idx]; + int prefetch_end_line = (vblank_end - dst_y_after_scaler) % vtotal; + double prefetch_start_line = (prefetch_end_line > dst_y_prefetch) ? (prefetch_end_line - dst_y_prefetch) : (prefetch_end_line - dst_y_prefetch + vtotal); + double pre_first_hdl, rec_first_hdl; + struct plane_params in = { 0 }; + + get_plane_params(p, plane_idx, chroma, &in); + + pre_first_hdl = prefetch_start_line + dst_y_per_vm_vblank + 2.0 * dst_y_per_row_vblank + in.prefetch_hdl_delta; + rec_first_hdl = access_direction ? + (in.viewport_start + in.viewport_size - in.vinit - in.swath_height - math_floor2(in.viewport_start + in.viewport_size - in.vinit, in.swath_height)) / in.vratio + (vblank_end - dst_y_after_scaler + in.recout_hdl_delta) : + (math_floor2(in.viewport_start + in.vinit - 1, in.swath_height) - in.viewport_start - in.vinit) / in.vratio + (vblank_end - dst_y_after_scaler + in.recout_hdl_delta); + + *pre_first_hdl_out = pre_first_hdl; + *rec_first_hdl_out = rec_first_hdl; +} + +/** + * ************************************************************************************************************************************ + * calculate_copy_swaths: Calculates a tight upper bound for the swaths required for copy given swath params and svp0 + svp1 dst lines + * + * Given the first hdl position of pre and rec swaths, and the delta in dst lines between the hdls, this function calculates the + * number of swaths to be copied in the worst case given svp0 and svp1 dst lines. The upper bound is "tight" because this function + * assumes svp0 and svp1 cannot both overlap with prefetch (prefetch potentially has the most amount of swaths per dst line). + * + * ************************************************************************************************************************************ + */ +static unsigned int calculate_copy_swaths(double pre_first_hdl, + double rec_first_hdl, + double pre_hdl_delta, + double rec_hdl_delta, + unsigned int prefetch_swaths, + unsigned int total_swaths, + unsigned int svp0_dst_lines, + unsigned int svp1_dst_lines, + unsigned int vtotal) +{ + unsigned int svp_dst_lines = svp0_dst_lines + svp1_dst_lines; + double prefetch_dst_lines = (prefetch_swaths - 1) * pre_hdl_delta + 1; + double lines_between_pre_rec_first_hdl = pre_first_hdl < rec_first_hdl ? rec_first_hdl - pre_first_hdl : rec_first_hdl - pre_first_hdl + vtotal; + double lines_for_rec_swaths = svp_dst_lines - lines_between_pre_rec_first_hdl; + unsigned int num_swaths; + + if (lines_for_rec_swaths > 0) + num_swaths = prefetch_swaths + (unsigned int)math_ceil((lines_for_rec_swaths + rec_hdl_delta - 1) / rec_hdl_delta); + else + num_swaths = svp_dst_lines > prefetch_dst_lines ? prefetch_swaths + 1 : (unsigned int)math_ceil((svp_dst_lines + pre_hdl_delta - 1) / pre_hdl_delta); + + if (num_swaths > total_swaths) + num_swaths = total_swaths; + + return num_swaths; +} + + /** + * ******************************************************************************************************************************************************* + * calculate_max_mem_size_per_plane_per_dpp: Calculate (loose) upper bound for total number of bytes reserved in memory for the copy given number of swaths + * + * - The copy width / height is the min of the vp_width/height and maximum number of pixels that can fit across an ODM slice + * - This is to account for recout positions that cross the ODM seam but are "mostly" within the same ODM slice + * - For mem width assume an extra block width and tile width is required (the memory reserved must take into account pitch which must be tiled aligned) + * - For mem height assumes two extra block heights are required + * + * ******************************************************************************************************************************************************* + */ +static unsigned int calculate_max_mem_size_per_plane_per_dpp(const struct dml2_core_calcs_calculate_alternate_params *p, unsigned int plane_idx, unsigned int copy_swaths, bool chroma) +{ + bool vertical_access = p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 || p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270; + unsigned int h_active = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[plane_idx].stream_index].timing.h_active; + double h_ratio = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane1.h_ratio : p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane0.h_ratio; + unsigned int copy_src_lines = copy_swaths * (chroma ? p->SwathHeightC[plane_idx] : p->SwathHeightY[plane_idx]); + unsigned int vp_width = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.width : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.width; + unsigned int vp_height = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.height : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.height; + unsigned int block256_width = chroma ? p->Read256BlockWidthC[plane_idx] : p->Read256BlockWidthY[plane_idx]; + unsigned int block256_height = chroma ? p->Read256BlockHeightC[plane_idx] : p->Read256BlockHeightY[plane_idx]; + unsigned int tile_width = chroma ? p->MacroTileWidthC[plane_idx] : p->MacroTileWidthY[plane_idx]; + unsigned int byte_per_pixel = chroma ? p->BytePerPixelC[plane_idx] : p->BytePerPixelY[plane_idx]; + unsigned int mem_width; + unsigned int mem_height; + unsigned int odm_combine_factor; + double odm_slice_pixels; + + if (p->ODMMode[plane_idx] == dml2_odm_mode_combine_4to1) + odm_combine_factor = 4; + else if (p->ODMMode[plane_idx] == dml2_odm_mode_combine_3to1) + odm_combine_factor = 3; + else if (p->ODMMode[plane_idx] == dml2_odm_mode_combine_2to1) + odm_combine_factor = 2; + else + odm_combine_factor = 1; + + odm_slice_pixels = (double)h_active / odm_combine_factor * h_ratio + (odm_combine_factor == 3 ? 2 : 0); + mem_width = (vertical_access ? copy_src_lines : (unsigned int)math_ceil(math_min2(odm_slice_pixels, vp_width))) + block256_width + tile_width; + mem_height = (vertical_access ? (unsigned int)math_ceil(math_min2(odm_slice_pixels, vp_height)) : copy_src_lines) + 2 * block256_height; + + return (unsigned int)math_ceil2(mem_width * mem_height * byte_per_pixel, 256); +} + + /** + * **************************************************************************************************************************************** + * calculate_ub_copy_size_per_plane_per_dpp: Calculate tight upper bound for total number of bytes required for the copy given number of swaths. + * + * - This function is intended to be used to calculate the total copy time, since we want to reduce the copy time upper bound as much as possible + * - For copy width assume two extra block widths (the copy itself does not need to be tiled aligned, only block aligned, so we don't add an extra + * tile to the copy width + * - For copy height assumes two extra block heights are required + * + * Note: This function could be optimized further (i.e., an even tighter upper bound) if we take into account + * vp_x_start and vp_y_start positions which will tell us if the start and end positions of the copy are already + * blocked aligned (then we would not need to add the extra block width/height). + * + * **************************************************************************************************************************************** + */ +static unsigned int calculate_ub_copy_size_per_plane_per_dpp(const struct dml2_core_calcs_calculate_alternate_params *p, unsigned int plane_idx, unsigned int copy_swaths, bool chroma) +{ + bool vertical_access = p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 || p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270; + unsigned int copy_src_lines = copy_swaths * (chroma ? p->SwathHeightC[plane_idx] : p->SwathHeightY[plane_idx]); + unsigned int vp_width = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.width : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.width; + unsigned int vp_height = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.height : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.height; + unsigned int block256_width = chroma ? p->Read256BlockWidthC[plane_idx] : p->Read256BlockWidthY[plane_idx]; + unsigned int block256_height = chroma ? p->Read256BlockHeightC[plane_idx] : p->Read256BlockHeightY[plane_idx]; + unsigned int copy_width = (vertical_access ? copy_src_lines : vp_width / p->NoOfDPP[plane_idx]) + 2 * block256_width; + unsigned int copy_height = (vertical_access ? vp_height / p->NoOfDPP[plane_idx] : copy_src_lines) + 2 * block256_height; + unsigned int byte_per_pixel = chroma ? p->BytePerPixelC[plane_idx] : p->BytePerPixelY[plane_idx]; + + return (unsigned int)math_ceil2(copy_width * copy_height * byte_per_pixel, 256); +} + +/** + * ***************************************************************************************************************************** + * calculate_alt_copy_time_us: Calculates a tight upper bound for the copy time over all planes and streams + * + * This function calculates a tight upper bound for the copy time over all planes and streams. This upper bound is "tight" + * because it does the calculation assuming SVP0 and SVP1 cannot both overlap with prefetch (which potentially has the "most" + * amount of bytes to copy per dst line). + * + * This function uses the exact NoOfDPP as calculated by DML (i.e., it does not use an input num_dpp param). This is because we + * want the copy time calculation to be precise in order to minimize FW latency / overhead. + * + * ***************************************************************************************************************************** + */ +static unsigned int calculate_alt_copy_time_us(const struct dml2_core_calcs_calculate_alternate_params *p) +{ + unsigned int i, j; + double pre_first_hdl = 0.0, rec_first_hdl = 0.0; + double pre_first_hdl_c = 0.0, rec_first_hdl_c = 0.0; + double rec_hdl_delta, rec_hdl_delta_c; + double pre_hdl_delta, pre_hdl_delta_c; + unsigned int copy_swaths, copy_swaths_c; + unsigned int vtotal; + unsigned int copy_size_bytes = 0; + + for (i = 0; i < p->display_cfg->num_streams; i++) { + vtotal = p->display_cfg->stream_descriptors[i].timing.v_total; + for (j = 0; j < p->display_cfg->num_planes; j++) { + if (p->display_cfg->plane_descriptors[j].stream_index != i) + continue; + compute_pre_rec_first_hdl(p, false, i, j, &pre_first_hdl, &rec_first_hdl); + rec_hdl_delta = p->recout_hdl_delta[j]; + pre_hdl_delta = p->prefetch_hdl_delta[j]; + copy_swaths = calculate_copy_swaths(pre_first_hdl, rec_first_hdl, pre_hdl_delta, rec_hdl_delta, p->prefetch_swaths[j], p->total_swaths[j], p->svp0_dst_lines[i], p->svp1_dst_lines[i], vtotal); + copy_size_bytes += calculate_ub_copy_size_per_plane_per_dpp(p, j, copy_swaths, false) * p->NoOfDPP[j]; + if (p->BytePerPixelInDETC[j] > 0) { + compute_pre_rec_first_hdl(p, true, i, j, &pre_first_hdl_c, &rec_first_hdl_c); + rec_hdl_delta_c = p->recout_hdl_delta_c[j]; + pre_hdl_delta_c = p->prefetch_hdl_delta_c[j]; + copy_swaths_c = calculate_copy_swaths(pre_first_hdl_c, rec_first_hdl_c, pre_hdl_delta_c, rec_hdl_delta_c, p->prefetch_swaths_c[j], p->total_swaths_c[j], p->svp0_dst_lines[i], p->svp1_dst_lines[i], vtotal); + copy_size_bytes += calculate_ub_copy_size_per_plane_per_dpp(p, j, copy_swaths_c, false) * p->NoOfDPP[j]; + } + } + } + return (unsigned int)math_ceil((double)copy_size_bytes * 1000 / *p->lsdma_bw_req_for_alt_kbps); +} + +/** + * ***************************************************************************************************************************** + * calculate_ub_copy_size_per_plane_per_dpp_per_svp: Calculates the upper bound copy size in bytes for a given plane and svp_dst_lines + * + * @input: p - alternate related params (input only) + * svp_dst_lines - number of lines (in dst space) for the svp (one of svp0 or svp1 dst lines) + * plane_idx - plane index to calculate for + * chroma - flag to indicate if chroma or luma plane + * + * TODO: The check for total copy size versus total alt-channel aperture size can be moved directly into mode support (from + * optimize / admissibility check layer) + * + * Note 2: This function calculates a loose upper bound for the size required for the copy. The reason for this is because each + * SVP aperture needs enough space to hold the worst case copy size (i.e., prefetch + some recout swaths), so we calculate + * such that any SVP needs to consider the prefetch swaths in the size required. But in actuality only one of SVP0 or SVP1 + * would have prefetch swaths required to be copied since there cannot be the case where both SVPs overlap with prefetch. + * Therefore the result of this function should not be used to calculate the total copy time required (as it would be too long). + * + * ***************************************************************************************************************************** + */ +static unsigned int calculate_ub_copy_size_per_plane_per_dpp_per_svp(const struct dml2_core_calcs_calculate_alternate_params *p, unsigned int svp_dst_lines, unsigned int plane_idx, bool chroma) +{ + unsigned int svp_lines_for_va; + unsigned int copy_swaths; + unsigned int copy_size = 0; + + if (chroma && p->BytePerPixelInDETC[plane_idx] > 0) { + /* chomra plane */ + svp_lines_for_va = svp_dst_lines - (unsigned int)((p->prefetch_swaths_c[plane_idx] - 1) * p->prefetch_hdl_delta_c[plane_idx]); + copy_swaths = p->prefetch_swaths_c[plane_idx] + (unsigned int)math_ceil(svp_lines_for_va / p->recout_hdl_delta_c[plane_idx]); + if (copy_swaths > p->total_swaths_c[plane_idx]) + copy_swaths = p->total_swaths_c[plane_idx]; + copy_size = calculate_max_mem_size_per_plane_per_dpp(p, plane_idx, copy_swaths, chroma); + } else if (!chroma) { + /* luma plane */ + svp_lines_for_va = svp_dst_lines - (unsigned int)((p->prefetch_swaths[plane_idx] - 1) * p->prefetch_hdl_delta[plane_idx]); + copy_swaths = p->prefetch_swaths[plane_idx] + (unsigned int)math_ceil(svp_lines_for_va / p->recout_hdl_delta[plane_idx]); + if (copy_swaths > p->total_swaths[plane_idx]) + copy_swaths = p->total_swaths[plane_idx]; + copy_size = calculate_max_mem_size_per_plane_per_dpp(p, plane_idx, copy_swaths, chroma); + } + + return copy_size; +} + +struct swath_params { + unsigned int prefetch_swaths; + unsigned int total_swaths; + double recout_hdl_delta; + double prefetch_hdl_delta; +}; + +/** + * *********************************************************************************************************** + * calculate_swath_params: Function that calculates swath related params for alt-channel and returns the values + * + * This function calculates: + * - number of prefetch swaths + * - number of total swaths + * - recout_hdl_delta (number of dst lines between hdls in recout) + * - prefetch_hdl_delta (number of dst lines between hdls in prefetch) + * + * And returns it to the out parameter. + * + * *********************************************************************************************************** + */ +static void calculate_swath_params(const struct dml2_core_calcs_calculate_alternate_params *p, unsigned int plane_idx, bool chroma, struct swath_params *out) +{ + bool vertical_access = p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 || p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270; + bool access_direction = (p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 && !p->display_cfg->plane_descriptors[plane_idx].composition.mirrored) || + (p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270 && p->display_cfg->plane_descriptors[plane_idx].composition.mirrored) || + (p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_180); + unsigned int vp_x_start = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.x_start : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.x_start; + unsigned int vp_y_start = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.y_start : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.y_start; + unsigned int vp_height = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.height : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.height; + unsigned int vp_width = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.width : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.width; + double vratio = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane1.v_ratio : p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane0.v_ratio; + double vratio_pre = chroma ? p->VRatioPrefetchC[plane_idx] : p->VRatioPrefetchY[plane_idx]; + unsigned int swath_height = chroma ? p->SwathHeightC[plane_idx] : p->SwathHeightY[plane_idx]; + unsigned int vinit = chroma ? p->VInitPrefillC[plane_idx] : p->VInitPrefillY[plane_idx]; + unsigned int viewport_start = vertical_access ? vp_x_start : vp_y_start; + unsigned int viewport_size = vertical_access ? vp_width : vp_height; + unsigned int src_y_last_pref_sw_algn, src_y_first_sw_algn, src_y_last_va_sw_algn; + + if (access_direction) { + src_y_first_sw_algn = (unsigned int)math_floor2(viewport_start + viewport_size - 1, swath_height); + src_y_last_pref_sw_algn = (unsigned int)math_floor2(viewport_start + viewport_size - vinit, swath_height); + src_y_last_va_sw_algn = (unsigned int)math_floor2(viewport_start, swath_height); + } else { + src_y_first_sw_algn = (unsigned int)math_floor2(viewport_start, swath_height); + src_y_last_pref_sw_algn = (unsigned int)math_floor2(viewport_start + vinit - 1, swath_height); + src_y_last_va_sw_algn = (unsigned int)math_floor2(viewport_start + viewport_size - 1, swath_height); + } + out->prefetch_swaths = (access_direction ? src_y_first_sw_algn - src_y_last_pref_sw_algn : src_y_last_pref_sw_algn - src_y_first_sw_algn) / swath_height + 1; + out->total_swaths = (access_direction ? src_y_first_sw_algn - src_y_last_va_sw_algn : src_y_last_va_sw_algn - src_y_first_sw_algn) / swath_height + 1; + out->recout_hdl_delta = (double)swath_height / vratio; + out->prefetch_hdl_delta = (double)swath_height / vratio_pre; +} + +static unsigned int calc_svp_size_64kb_aligned(unsigned int total_size_bytes) +{ + return ((total_size_bytes + 0xFFFF) >> 16) << 16; // Round up to nearest 64KB boundary +} + +void dcn6_calculate_alternate_params(struct dml2_core_calcs_calculate_alternate_params *p) +{ + unsigned int i, j, k; + double line_time_us = 0, prefetch_time_us, max_prefetch_time_us = 0; + unsigned int svp_max_bytes[2]; + unsigned int svp_max_bytes_per_dpp[2]; + unsigned int svp_dst_lines[2]; + double copy_time_us; + double svp_req_lim_us = 0; + unsigned int fw_delay; + struct swath_params swath_params; + + *p->svp0_max_bytes = 0; + *p->svp1_max_bytes = 0; + svp_max_bytes[0] = 0; + svp_max_bytes[1] = 0; + /* This initial loop calculates a few params that are used for calculations / assignments in later parts of the function: + * - max_prefetch_time_us + */ + for (i = 0; i < p->display_cfg->num_streams; i++) { + line_time_us = ((double)p->display_cfg->stream_descriptors[i].timing.h_total * 1000 / p->display_cfg->stream_descriptors[i].timing.pixel_clock_khz); + if (p->svp_req_limit[i] * line_time_us > svp_req_lim_us) + svp_req_lim_us = p->svp_req_limit[i] * line_time_us; + for (j = 0; j < p->display_cfg->num_planes; j++) { + prefetch_time_us = line_time_us * p->dst_y_prefetch[j]; + if (prefetch_time_us > max_prefetch_time_us) + max_prefetch_time_us = prefetch_time_us; + } + } + + for (i = 0; i < p->display_cfg->num_streams; i++) { + svp_dst_lines[0] = p->svp0_dst_lines[i]; + svp_dst_lines[1] = p->svp1_dst_lines[i]; + line_time_us = ((double)p->display_cfg->stream_descriptors[i].timing.h_total * 1000 / p->display_cfg->stream_descriptors[i].timing.pixel_clock_khz); + p->max_prefetch_in_lines[i] = (unsigned int)math_ceil(max_prefetch_time_us / line_time_us); + for (j = 0; j < p->display_cfg->num_planes; j++) { + if (p->display_cfg->plane_descriptors[j].stream_index == i) { + calculate_swath_params(p, j, false, &swath_params); + p->prefetch_swaths[j] = swath_params.prefetch_swaths; + p->total_swaths[j] = swath_params.total_swaths; + p->recout_hdl_delta[j] = swath_params.recout_hdl_delta; + p->prefetch_hdl_delta[j] = swath_params.prefetch_hdl_delta; + for (k = 0; k < 2; k++) { + svp_max_bytes_per_dpp[k] = calculate_ub_copy_size_per_plane_per_dpp_per_svp(p, svp_dst_lines[k], j, false); + svp_max_bytes[k] += calc_svp_size_64kb_aligned(svp_max_bytes_per_dpp[k]) * p->NoOfDPP[j]; + } + p->svp0_max_bytes_per_dpp[j] = svp_max_bytes_per_dpp[0]; + p->svp1_max_bytes_per_dpp[j] = svp_max_bytes_per_dpp[1]; + + if (p->BytePerPixelInDETC[j] > 0) { + calculate_swath_params(p, j, true, &swath_params); + p->prefetch_swaths_c[j] = swath_params.prefetch_swaths; + p->total_swaths_c[j] = swath_params.total_swaths; + p->recout_hdl_delta_c[j] = swath_params.recout_hdl_delta; + p->prefetch_hdl_delta_c[j] = swath_params.prefetch_hdl_delta; + + for (k = 0; k < 2; k++) { + svp_max_bytes_per_dpp[k] = calculate_ub_copy_size_per_plane_per_dpp_per_svp(p, svp_dst_lines[k], j, true); + svp_max_bytes[k] += calc_svp_size_64kb_aligned(svp_max_bytes_per_dpp[k]) * p->NoOfDPP[j]; + } + p->svp0_max_bytes_per_dpp_c[j] = svp_max_bytes_per_dpp[0]; + p->svp1_max_bytes_per_dpp_c[j] = svp_max_bytes_per_dpp[1]; + } else { + p->svp0_max_bytes_per_dpp_c[j] = 0; + p->svp1_max_bytes_per_dpp_c[j] = 0; + } + } + } + } + + *p->svp0_max_bytes = svp_max_bytes[0]; + *p->svp1_max_bytes = svp_max_bytes[1]; + *p->lsdma_bw_req_for_alt_kbps = p->dcn_non_urgent_bandwidth_kbps; + copy_time_us = p->display_cfg->overrides.hw.force_alt_chan_copy_time.enable ? p->display_cfg->overrides.hw.force_alt_chan_copy_time.copy_time_us : calculate_alt_copy_time_us(p); + fw_delay = p->display_cfg->overrides.hw.force_alt_chan_fw_delay.enable ? p->display_cfg->overrides.hw.force_alt_chan_fw_delay.fw_delay_us : p->alt_chan_fw_delay_us; + for (i = 0; i < p->display_cfg->num_streams; i++) { + line_time_us = ((double)p->display_cfg->stream_descriptors[i].timing.h_total * 1000 / p->display_cfg->stream_descriptors[i].timing.pixel_clock_khz); + /* If copy_time is very short then clamp nom_req_limit to be equal to svp_req_limit. This is to prevent underflow + * because a short nom_req_limit prevents DCN from being able to request ahead. */ + p->nom_req_limit_alt[i] = (unsigned int)math_max2(p->svp_req_limit[i], math_ceil((copy_time_us) / line_time_us)); + p->min_lead_dst_lines[i] = (unsigned int)math_ceil((copy_time_us + fw_delay + math_max2(svp_req_lim_us, max_prefetch_time_us)) / line_time_us); + } +} + +void dcn6_calculate_flip_schedule( + struct dml2_core_internal_scratch *s, + bool iflip_enable, + bool ihostvm_enable, + bool iffbmm_enable, + double HostVMInefficiencyFactor, + double Tvm_trips_flip, + double Tr0_trips_flip, + double Tvm_trips_flip_rounded, + double Tr0_trips_flip_rounded, + bool GPUVMEnable, + double vm_bytes, // vm_bytes + double DPTEBytesPerRow, // dpte_row_bytes + enum dml2_source_format_class SourcePixelFormat, + double LineTime, + double VRatio, + double VRatioChroma, + double Tno_bw_flip, + unsigned int dpte_row_height, + unsigned int dpte_row_height_chroma, + unsigned int max_flip_time_us, + unsigned int max_flip_time_lines, + unsigned int meta_row_height, + unsigned int meta_row_height_chroma, + + // Output + double *dst_y_per_vm_flip, + double *dst_y_per_row_flip, + double *final_flip_bw, + bool *ImmediateFlipSupportedForPipe) +{ + struct dml2_core_shared_CalculateFlipSchedule_locals *l = &s->CalculateFlipSchedule_locals; + + l->dual_plane = dml2_core_utils_is_420(SourcePixelFormat) || dml2_core_utils_is_422_planar(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha; + l->dpte_row_bytes = DPTEBytesPerRow; + + DML_LOG_VERBOSE("DML::%s: GPUVMEnable = %u\n", __func__, GPUVMEnable); + DML_LOG_VERBOSE("DML::%s: ip.max_flip_time_us = %d\n", __func__, max_flip_time_us); + DML_LOG_VERBOSE("DML::%s: ip.max_flip_time_lines = %d\n", __func__, max_flip_time_lines); + DML_LOG_VERBOSE("DML::%s: iflip_enable = %u\n", __func__, iflip_enable); + DML_LOG_VERBOSE("DML::%s: HostVMInefficiencyFactor = %f\n", __func__, HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: LineTime = %f\n", __func__, LineTime); + DML_LOG_VERBOSE("DML::%s: Tno_bw_flip = %f\n", __func__, Tno_bw_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip = %f\n", __func__, Tvm_trips_flip); + DML_LOG_VERBOSE("DML::%s: Tr0_trips_flip = %f\n", __func__, Tr0_trips_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip_rounded = %f\n", __func__, Tvm_trips_flip_rounded); + DML_LOG_VERBOSE("DML::%s: Tr0_trips_flip_rounded = %f\n", __func__, Tr0_trips_flip_rounded); + DML_LOG_VERBOSE("DML::%s: vm_bytes = %f\n", __func__, vm_bytes); + DML_LOG_VERBOSE("DML::%s: DPTEBytesPerRow = %f\n", __func__, DPTEBytesPerRow); + DML_LOG_VERBOSE("DML::%s: dpte_row_bytes = %f\n", __func__, l->dpte_row_bytes); + DML_LOG_VERBOSE("DML::%s: dpte_row_height = %d\n", __func__, dpte_row_height); + DML_LOG_VERBOSE("DML::%s: meta_row_height = %d\n", __func__, meta_row_height); + DML_LOG_VERBOSE("DML::%s: VRatio = %f\n", __func__, VRatio); + + bool flip_enable = iflip_enable || (GPUVMEnable && (ihostvm_enable || iffbmm_enable)); + + if (GPUVMEnable) { + if (l->dual_plane) { + if (GPUVMEnable) { + l->min_row_height = dpte_row_height; + l->min_row_height_chroma = dpte_row_height_chroma; + } else { + l->min_row_height = meta_row_height; + l->min_row_height_chroma = meta_row_height_chroma; + } + l->min_row_time = math_min2(l->min_row_height * LineTime / VRatio, l->min_row_height_chroma * LineTime / VRatioChroma); + } else { + if (GPUVMEnable) + l->min_row_height = dpte_row_height; + else + l->min_row_height = meta_row_height; + + l->min_row_time = l->min_row_height * LineTime / VRatio; + } + DML_LOG_VERBOSE("DML::%s: min_row_time = %f\n", __func__, l->min_row_time); + DML_ASSERT(l->min_row_time > 0); + + // For mode check, calculation the flip bw requirement with worst case flip time + l->max_flip_time = math_min2(math_min2(l->min_row_time, (double)max_flip_time_lines * LineTime / VRatio), + math_max2(Tvm_trips_flip_rounded + 2 * Tr0_trips_flip_rounded, (double)max_flip_time_us)); + + //The lower bound on flip bandwidth + // Note: The get_urgent_bandwidth_required already consider dpte_row_bw and meta_row_bw in bandwidth calculation, so leave final_flip_bw = 0 if iflip not required + l->lb_flip_bw = 0; + + if (flip_enable) { + l->hvm_scaled_vm_bytes = vm_bytes * HostVMInefficiencyFactor; + l->num_rows = 2; + l->hvm_scaled_row_bytes = l->num_rows * l->dpte_row_bytes * HostVMInefficiencyFactor; + l->hvm_scaled_vm_row_bytes = l->hvm_scaled_vm_bytes + l->hvm_scaled_row_bytes; + l->lb_flip_bw = math_max3( + l->hvm_scaled_vm_row_bytes / (l->max_flip_time - Tno_bw_flip), + l->hvm_scaled_vm_bytes / (l->max_flip_time - Tno_bw_flip - 2 * Tr0_trips_flip_rounded), + l->hvm_scaled_row_bytes / (l->max_flip_time - Tvm_trips_flip_rounded)); + DML_LOG_VERBOSE("DML::%s: max_flip_time = %f\n", __func__, l->max_flip_time); + DML_LOG_VERBOSE("DML::%s: total vm bytes (hvm ineff scaled) = %f\n", __func__, l->hvm_scaled_vm_bytes); + DML_LOG_VERBOSE("DML::%s: total row bytes (%f row, hvm ineff scaled) = %f\n", __func__, l->num_rows, l->hvm_scaled_row_bytes); + DML_LOG_VERBOSE("DML::%s: total vm+row bytes (hvm ineff scaled) = %f\n", __func__, l->hvm_scaled_vm_row_bytes); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for vm and row = %f\n", __func__, l->hvm_scaled_vm_row_bytes / (l->max_flip_time - Tno_bw_flip)); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for vm = %f\n", __func__, l->hvm_scaled_vm_bytes / (l->max_flip_time - Tno_bw_flip - 2 * Tr0_trips_flip_rounded)); + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for row = %f\n", __func__, l->hvm_scaled_row_bytes / (l->max_flip_time - Tvm_trips_flip_rounded)); + + if (l->lb_flip_bw > 0) { + DML_LOG_VERBOSE("DML::%s: mode_support est Tvm_flip = %f (bw-based)\n", __func__, Tno_bw_flip + l->hvm_scaled_vm_bytes / l->lb_flip_bw); + DML_LOG_VERBOSE("DML::%s: mode_support est Tr0_flip = %f (bw-based)\n", __func__, l->hvm_scaled_row_bytes / l->lb_flip_bw / l->num_rows); + DML_LOG_VERBOSE("DML::%s: mode_support est dst_y_per_vm_flip = %f (bw-based)\n", __func__, Tno_bw_flip + l->hvm_scaled_vm_bytes / l->lb_flip_bw / LineTime); + DML_LOG_VERBOSE("DML::%s: mode_support est dst_y_per_row_flip = %f (bw-based)\n", __func__, l->hvm_scaled_row_bytes / l->lb_flip_bw / LineTime / l->num_rows); + DML_LOG_VERBOSE("DML::%s: Tvm_trips_flip_rounded + 2*Tr0_trips_flip_rounded = %f\n", __func__, (Tvm_trips_flip_rounded + 2 * Tr0_trips_flip_rounded)); + } + l->lb_flip_bw = math_max3(l->lb_flip_bw, + l->hvm_scaled_vm_bytes / (31 * LineTime) - Tno_bw_flip, + l->dpte_row_bytes * HostVMInefficiencyFactor / (15 * LineTime)); + + DML_LOG_VERBOSE("DML::%s: lb_flip_bw for vm reg limit = %f\n", __func__, l->hvm_scaled_vm_bytes / (31 * LineTime) - Tno_bw_flip); + } + + *final_flip_bw = l->lb_flip_bw; + + if (flip_enable) { + DML_LOG_VERBOSE("DML::%s: final_flip_bw = %f\n", __func__, *final_flip_bw); + if (*final_flip_bw == 0) { + l->Tvm_flip = 0; + l->Tr0_flip = 0; + } else { + l->Tvm_flip = math_max3(Tvm_trips_flip, + Tno_bw_flip + vm_bytes * HostVMInefficiencyFactor / *final_flip_bw, + LineTime / 4.0); + + l->Tr0_flip = math_max3(Tr0_trips_flip, + l->dpte_row_bytes * HostVMInefficiencyFactor / *final_flip_bw, + LineTime / 4.0); + } + DML_LOG_VERBOSE("DML::%s: total vm bytes (hvm ineff scaled) = %f\n", __func__, vm_bytes * HostVMInefficiencyFactor); + DML_LOG_VERBOSE("DML::%s: Tvm_flip = %f (bw-based), Tvm_trips_flip = %f (latency-based)\n", __func__, Tno_bw_flip + vm_bytes * HostVMInefficiencyFactor / l->ImmediateFlipBW, Tvm_trips_flip); + *dst_y_per_vm_flip = math_ceil2(4.0 * (l->Tvm_flip / LineTime), 1.0) / 4.0; + *dst_y_per_row_flip = math_ceil2(4.0 * (l->Tr0_flip / LineTime), 1.0) / 4.0; + + *final_flip_bw = math_max2(vm_bytes * HostVMInefficiencyFactor / (*dst_y_per_vm_flip * LineTime), + l->dpte_row_bytes * HostVMInefficiencyFactor / (*dst_y_per_row_flip * LineTime)); + + if (*dst_y_per_vm_flip >= 32 || *dst_y_per_row_flip >= 16 || l->Tvm_flip + 2 * l->Tr0_flip > l->min_row_time) { + *ImmediateFlipSupportedForPipe = false; + } else { + *ImmediateFlipSupportedForPipe = flip_enable; + } + } else { + l->Tvm_flip = 0; + l->Tr0_flip = 0; + *dst_y_per_vm_flip = 0; + *dst_y_per_row_flip = 0; + *final_flip_bw = 0; + *ImmediateFlipSupportedForPipe = flip_enable; + } + } else { + l->Tvm_flip = 0; + l->Tr0_flip = 0; + *dst_y_per_vm_flip = 0; + *dst_y_per_row_flip = 0; + *final_flip_bw = 0; + *ImmediateFlipSupportedForPipe = flip_enable; + } + + DML_LOG_VERBOSE("DML::%s: dst_y_per_vm_flip = %f (should be < 32)\n", __func__, *dst_y_per_vm_flip); + DML_LOG_VERBOSE("DML::%s: dst_y_per_row_flip = %f (should be < 16)\n", __func__, *dst_y_per_row_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_flip = %f (final)\n", __func__, l->Tvm_flip); + DML_LOG_VERBOSE("DML::%s: Tr0_flip = %f (final)\n", __func__, l->Tr0_flip); + DML_LOG_VERBOSE("DML::%s: Tvm_flip + 2*Tr0_flip = %f (should be <= min_row_time=%f)\n", __func__, l->Tvm_flip + 2 * l->Tr0_flip, l->min_row_time); + DML_LOG_VERBOSE("DML::%s: final_flip_bw = %f\n", __func__, *final_flip_bw); + DML_LOG_VERBOSE("DML::%s: ImmediateFlipSupportedForPipe = %u\n", __func__, *ImmediateFlipSupportedForPipe); +} + +static void dcn6_rq_dlg_get_dlg_reg( + struct dml2_core_internal_scratch *s, + struct dml2_display_dlg_regs *disp_dlg_regs, + struct dml2_display_ttu_regs *disp_ttu_regs, + const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + const unsigned int pipe_idx, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + struct dml2_core_shared_rq_dlg_get_dlg_reg_locals *l = &s->rq_dlg_get_dlg_reg_locals; + + memset(l, 0, sizeof(struct dml2_core_shared_rq_dlg_get_dlg_reg_locals)); + + DML_LOG_VERBOSE("DML_DLG::%s: Calculation for pipe_idx=%d\n", __func__, pipe_idx); + + l->plane_idx = mode_lib->mp.pipe_plane[pipe_idx]; + l->stream_idx = display_cfg->plane_descriptors[l->plane_idx].stream_index; + DML_ASSERT(l->plane_idx < DML2_MAX_PLANES); + + l->source_format = dml2_444_8; + l->odm_mode = dml2_odm_mode_bypass; + l->dual_plane = false; + l->htotal = 0; + l->hactive = 0; + l->hblank_end = 0; + l->vblank_end = 0; + l->interlaced = false; + l->pclk_freq_in_mhz = 0.0; + l->refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : utm_soc_bb->dchub_refclk_mhz; + l->ref_freq_to_pix_freq = 0.0; + + if (l->plane_idx < DML2_MAX_PLANES) { + + l->timing = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[l->plane_idx].stream_index].timing; + l->source_format = display_cfg->plane_descriptors[l->plane_idx].pixel_format; + l->odm_mode = mode_lib->mp.ODMMode[l->plane_idx]; + + l->dual_plane = dml2_core_utils_is_dual_plane(l->source_format); + + l->htotal = l->timing->h_total; + l->hactive = l->timing->h_active; + l->hblank_end = l->timing->h_blank_end; + l->vblank_end = l->timing->v_blank_end; + l->interlaced = l->timing->interlaced; + l->pclk_freq_in_mhz = (double)l->timing->pixel_clock_khz / 1000; + l->ref_freq_to_pix_freq = l->refclk_freq_in_mhz / l->pclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG::%s: plane_idx = %d\n", __func__, l->plane_idx); + DML_LOG_VERBOSE("DML_DLG: %s: htotal = %d\n", __func__, l->htotal); + DML_LOG_VERBOSE("DML_DLG: %s: refclk_freq_in_mhz = %3.2f\n", __func__, l->refclk_freq_in_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: dlg_ref_clk_mhz = %3.2f\n", __func__, display_cfg->overrides.hw.dlg_ref_clk_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: soc.refclk_mhz = %u\n", __func__, utm_soc_bb->dchub_refclk_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: pclk_freq_in_mhz = %3.2f\n", __func__, l->pclk_freq_in_mhz); + DML_LOG_VERBOSE("DML_DLG: %s: ref_freq_to_pix_freq = %3.2f\n", __func__, l->ref_freq_to_pix_freq); + DML_LOG_VERBOSE("DML_DLG: %s: interlaced = %d\n", __func__, l->interlaced); + + DML_ASSERT(l->refclk_freq_in_mhz != 0); + DML_ASSERT(l->pclk_freq_in_mhz != 0); + DML_ASSERT(l->ref_freq_to_pix_freq < 4.0); + + // Need to figure out which side of odm combine we're in + // Assume the pipe instance under the same plane is in order + + if (l->odm_mode == dml2_odm_mode_bypass) { + disp_dlg_regs->refcyc_h_blank_end = (unsigned int)((double)l->hblank_end * l->ref_freq_to_pix_freq); + } else if (l->odm_mode == dml2_odm_mode_combine_2to1 || l->odm_mode == dml2_odm_mode_combine_3to1 || l->odm_mode == dml2_odm_mode_combine_4to1) { + // find out how many pipe are in this plane + l->num_active_pipes = mode_lib->mp.num_active_pipes; + l->first_pipe_idx_in_plane = DML2_MAX_PLANES; + l->pipe_idx_in_combine = 0; // pipe index within the plane + l->odm_combine_factor = 2; + + if (l->odm_mode == dml2_odm_mode_combine_3to1) + l->odm_combine_factor = 3; + else if (l->odm_mode == dml2_odm_mode_combine_4to1) + l->odm_combine_factor = 4; + + for (unsigned int i = 0; i < l->num_active_pipes; i++) { + if (mode_lib->mp.pipe_plane[i] == l->plane_idx) { + if (i < l->first_pipe_idx_in_plane) { + l->first_pipe_idx_in_plane = i; + } + } + } + l->pipe_idx_in_combine = pipe_idx - l->first_pipe_idx_in_plane; // DML assumes the pipes in the same plane will have continuous indexing (i.e. plane 0 use pipe 0, 1, and plane 1 uses pipe 2, 3, etc.) + + disp_dlg_regs->refcyc_h_blank_end = (unsigned int)(((double)l->hblank_end + (double)l->pipe_idx_in_combine * (double)l->hactive / (double)l->odm_combine_factor) * l->ref_freq_to_pix_freq); + DML_LOG_VERBOSE("DML_DLG: %s: pipe_idx = %d\n", __func__, pipe_idx); + DML_LOG_VERBOSE("DML_DLG: %s: first_pipe_idx_in_plane = %d\n", __func__, l->first_pipe_idx_in_plane); + DML_LOG_VERBOSE("DML_DLG: %s: pipe_idx_in_combine = %d\n", __func__, l->pipe_idx_in_combine); + DML_LOG_VERBOSE("DML_DLG: %s: odm_combine_factor = %d\n", __func__, l->odm_combine_factor); + } + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_h_blank_end = %d\n", __func__, disp_dlg_regs->refcyc_h_blank_end); + + DML_ASSERT(disp_dlg_regs->refcyc_h_blank_end < (unsigned int)math_pow(2, 13)); + + disp_dlg_regs->ref_freq_to_pix_freq = (unsigned int)(l->ref_freq_to_pix_freq * math_pow(2, 19)); + disp_dlg_regs->refcyc_per_htotal = (unsigned int)(l->ref_freq_to_pix_freq * (double)l->htotal * math_pow(2, 8)); + disp_dlg_regs->dlg_vblank_end = l->interlaced ? (l->vblank_end / 2) : l->vblank_end; // 15 bits + + l->min_ttu_vblank = mode_lib->mp.MinTTUVBlank[mode_lib->mp.pipe_plane[pipe_idx]]; + l->min_dst_y_next_start = (unsigned int)(mode_lib->mp.MIN_DST_Y_NEXT_START[mode_lib->mp.pipe_plane[pipe_idx]]); + + DML_LOG_VERBOSE("DML_DLG: %s: min_ttu_vblank (us) = %3.2f\n", __func__, l->min_ttu_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: min_dst_y_next_start = %d\n", __func__, l->min_dst_y_next_start); + DML_LOG_VERBOSE("DML_DLG: %s: ref_freq_to_pix_freq = %3.2f\n", __func__, l->ref_freq_to_pix_freq); + + l->vready_after_vcount0 = (unsigned int)(mode_lib->mp.VREADY_AT_OR_AFTER_VSYNC[mode_lib->mp.pipe_plane[pipe_idx]]); + disp_dlg_regs->vready_after_vcount0 = l->vready_after_vcount0; + + DML_LOG_VERBOSE("DML_DLG: %s: vready_after_vcount0 = %d\n", __func__, disp_dlg_regs->vready_after_vcount0); + + l->dst_x_after_scaler = (unsigned int)(mode_lib->mp.DSTXAfterScaler[mode_lib->mp.pipe_plane[pipe_idx]]); + l->dst_y_after_scaler = (unsigned int)(mode_lib->mp.DSTYAfterScaler[mode_lib->mp.pipe_plane[pipe_idx]]); + + DML_LOG_VERBOSE("DML_DLG: %s: dst_x_after_scaler = %d\n", __func__, l->dst_x_after_scaler); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_after_scaler = %d\n", __func__, l->dst_y_after_scaler); + + l->dst_y_prefetch = mode_lib->mp.dst_y_prefetch[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_vm_vblank = mode_lib->mp.dst_y_per_vm_vblank[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_row_vblank = mode_lib->mp.dst_y_per_row_vblank[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_vm_flip = mode_lib->mp.dst_y_per_vm_flip[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_row_flip = mode_lib->mp.dst_y_per_row_flip[mode_lib->mp.pipe_plane[pipe_idx]]; + + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_prefetch (after rnd) = %3.2f\n", __func__, l->dst_y_prefetch); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_vm_flip = %3.2f\n", __func__, l->dst_y_per_vm_flip); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_row_flip = %3.2f\n", __func__, l->dst_y_per_row_flip); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_vm_vblank = %3.2f\n", __func__, l->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: dst_y_per_row_vblank = %3.2f\n", __func__, l->dst_y_per_row_vblank); + + if (l->dst_y_prefetch > 0 && l->dst_y_per_vm_vblank > 0 && l->dst_y_per_row_vblank > 0) { + DML_ASSERT(l->dst_y_prefetch > (l->dst_y_per_vm_vblank + l->dst_y_per_row_vblank)); + } + + l->vratio_pre_l = mode_lib->mp.VRatioPrefetchY[mode_lib->mp.pipe_plane[pipe_idx]]; + l->vratio_pre_c = mode_lib->mp.VRatioPrefetchC[mode_lib->mp.pipe_plane[pipe_idx]]; + + DML_LOG_VERBOSE("DML_DLG: %s: vratio_pre_l = %3.2f\n", __func__, l->vratio_pre_l); + DML_LOG_VERBOSE("DML_DLG: %s: vratio_pre_c = %3.2f\n", __func__, l->vratio_pre_c); + + // Active + l->refcyc_per_line_delivery_pre_l = mode_lib->mp.DisplayPipeLineDeliveryTimeLumaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_line_delivery_l = mode_lib->mp.DisplayPipeLineDeliveryTimeLuma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_pre_l = %3.2f\n", __func__, l->refcyc_per_line_delivery_pre_l); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_l = %3.2f\n", __func__, l->refcyc_per_line_delivery_l); + + l->refcyc_per_line_delivery_pre_c = 0.0; + l->refcyc_per_line_delivery_c = 0.0; + + if (l->dual_plane) { + l->refcyc_per_line_delivery_pre_c = mode_lib->mp.DisplayPipeLineDeliveryTimeChromaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_line_delivery_c = mode_lib->mp.DisplayPipeLineDeliveryTimeChroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_pre_c = %3.2f\n", __func__, l->refcyc_per_line_delivery_pre_c); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_line_delivery_c = %3.2f\n", __func__, l->refcyc_per_line_delivery_c); + } + + disp_dlg_regs->refcyc_per_vm_dmdata = (unsigned int)(mode_lib->mp.Tdmdl_vm[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + disp_dlg_regs->dmdata_dl_delta = (unsigned int)(mode_lib->mp.Tdmdl[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + + l->refcyc_per_req_delivery_pre_l = mode_lib->mp.DisplayPipeRequestDeliveryTimeLumaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_req_delivery_l = mode_lib->mp.DisplayPipeRequestDeliveryTimeLuma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_pre_l = %3.2f\n", __func__, l->refcyc_per_req_delivery_pre_l); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_l = %3.2f\n", __func__, l->refcyc_per_req_delivery_l); + + l->refcyc_per_req_delivery_pre_c = 0.0; + l->refcyc_per_req_delivery_c = 0.0; + if (l->dual_plane) { + l->refcyc_per_req_delivery_pre_c = mode_lib->mp.DisplayPipeRequestDeliveryTimeChromaPrefetch[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_req_delivery_c = mode_lib->mp.DisplayPipeRequestDeliveryTimeChroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_pre_c = %3.2f\n", __func__, l->refcyc_per_req_delivery_pre_c); + DML_LOG_VERBOSE("DML_DLG: %s: refcyc_per_req_delivery_c = %3.2f\n", __func__, l->refcyc_per_req_delivery_c); + } + + // TTU - Cursor + DML_ASSERT(display_cfg->plane_descriptors[l->plane_idx].cursor.num_cursors <= 1); + + // Assign to register structures + disp_dlg_regs->min_dst_y_next_start = (unsigned int)((double)l->min_dst_y_next_start * math_pow(2, 2)); + DML_ASSERT(disp_dlg_regs->min_dst_y_next_start < (unsigned int)math_pow(2, 18)); + + disp_dlg_regs->dst_y_after_scaler = l->dst_y_after_scaler; // in terms of line + disp_dlg_regs->refcyc_x_after_scaler = (unsigned int)((double)l->dst_x_after_scaler * l->ref_freq_to_pix_freq); // in terms of refclk + disp_dlg_regs->dst_y_prefetch = (unsigned int)(l->dst_y_prefetch * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_vm_vblank = (unsigned int)(l->dst_y_per_vm_vblank * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_row_vblank = (unsigned int)(l->dst_y_per_row_vblank * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_vm_flip = (unsigned int)(l->dst_y_per_vm_flip * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_row_flip = (unsigned int)(l->dst_y_per_row_flip * math_pow(2, 2)); + + disp_dlg_regs->vratio_prefetch = (unsigned int)(l->vratio_pre_l * math_pow(2, 19)); + disp_dlg_regs->vratio_prefetch_c = (unsigned int)(l->vratio_pre_c * math_pow(2, 19)); + + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_vm_vblank = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_row_vblank = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_row_vblank); + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_vm_flip = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_vm_flip); + DML_LOG_VERBOSE("DML_DLG: %s: disp_dlg_regs->dst_y_per_row_flip = 0x%x\n", __func__, disp_dlg_regs->dst_y_per_row_flip); + + disp_dlg_regs->refcyc_per_vm_group_vblank = (unsigned int)(mode_lib->mp.TimePerVMGroupVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + disp_dlg_regs->refcyc_per_vm_group_flip = (unsigned int)(mode_lib->mp.TimePerVMGroupFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz); + disp_dlg_regs->refcyc_per_vm_req_vblank = (unsigned int)(mode_lib->mp.TimePerVMRequestVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz * math_pow(2, 10)); + disp_dlg_regs->refcyc_per_vm_req_flip = (unsigned int)(mode_lib->mp.TimePerVMRequestFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz * math_pow(2, 10)); + + l->dst_y_per_pte_row_nom_l = mode_lib->mp.DST_Y_PER_PTE_ROW_NOM_L[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_pte_row_nom_c = mode_lib->mp.DST_Y_PER_PTE_ROW_NOM_C[mode_lib->mp.pipe_plane[pipe_idx]]; + l->refcyc_per_pte_group_nom_l = mode_lib->mp.time_per_pte_group_nom_luma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_nom_c = mode_lib->mp.time_per_pte_group_nom_chroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_vblank_l = mode_lib->mp.time_per_pte_group_vblank_luma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_vblank_c = mode_lib->mp.time_per_pte_group_vblank_chroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_flip_l = mode_lib->mp.time_per_pte_group_flip_luma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_pte_group_flip_c = mode_lib->mp.time_per_pte_group_flip_chroma[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_tdlut_group = mode_lib->mp.time_per_tdlut_group[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + disp_dlg_regs->dst_y_per_pte_row_nom_l = (unsigned int)(l->dst_y_per_pte_row_nom_l * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_pte_row_nom_c = (unsigned int)(l->dst_y_per_pte_row_nom_c * math_pow(2, 2)); + + disp_dlg_regs->refcyc_per_pte_group_nom_l = (unsigned int)(l->refcyc_per_pte_group_nom_l); + disp_dlg_regs->refcyc_per_pte_group_nom_c = (unsigned int)(l->refcyc_per_pte_group_nom_c); + disp_dlg_regs->refcyc_per_pte_group_vblank_l = (unsigned int)(l->refcyc_per_pte_group_vblank_l); + disp_dlg_regs->refcyc_per_pte_group_vblank_c = (unsigned int)(l->refcyc_per_pte_group_vblank_c); + disp_dlg_regs->refcyc_per_pte_group_flip_l = (unsigned int)(l->refcyc_per_pte_group_flip_l); + disp_dlg_regs->refcyc_per_pte_group_flip_c = (unsigned int)(l->refcyc_per_pte_group_flip_c); + disp_dlg_regs->refcyc_per_line_delivery_pre_l = (unsigned int)math_floor2(l->refcyc_per_line_delivery_pre_l, 1); + disp_dlg_regs->refcyc_per_line_delivery_l = (unsigned int)math_floor2(l->refcyc_per_line_delivery_l, 1); + disp_dlg_regs->refcyc_per_line_delivery_pre_c = (unsigned int)math_floor2(l->refcyc_per_line_delivery_pre_c, 1); + disp_dlg_regs->refcyc_per_line_delivery_c = (unsigned int)math_floor2(l->refcyc_per_line_delivery_c, 1); + + l->dst_y_per_meta_row_nom_l = mode_lib->mp.DST_Y_PER_META_ROW_NOM_L[mode_lib->mp.pipe_plane[pipe_idx]]; + l->dst_y_per_meta_row_nom_c = mode_lib->mp.DST_Y_PER_META_ROW_NOM_C[mode_lib->mp.pipe_plane[pipe_idx]]; + l->refcyc_per_meta_chunk_nom_l = mode_lib->mp.TimePerMetaChunkNominal[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_nom_c = mode_lib->mp.TimePerChromaMetaChunkNominal[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_vblank_l = mode_lib->mp.TimePerMetaChunkVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_vblank_c = mode_lib->mp.TimePerChromaMetaChunkVBlank[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_flip_l = mode_lib->mp.TimePerMetaChunkFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + l->refcyc_per_meta_chunk_flip_c = mode_lib->mp.TimePerChromaMetaChunkFlip[mode_lib->mp.pipe_plane[pipe_idx]] * l->refclk_freq_in_mhz; + + disp_dlg_regs->dst_y_per_meta_row_nom_l = (unsigned int)(l->dst_y_per_meta_row_nom_l * math_pow(2, 2)); + disp_dlg_regs->dst_y_per_meta_row_nom_c = (unsigned int)(l->dst_y_per_meta_row_nom_c * math_pow(2, 2)); + disp_dlg_regs->refcyc_per_meta_chunk_nom_l = (unsigned int)(l->refcyc_per_meta_chunk_nom_l); + disp_dlg_regs->refcyc_per_meta_chunk_nom_c = (unsigned int)(l->refcyc_per_meta_chunk_nom_c); + disp_dlg_regs->refcyc_per_meta_chunk_vblank_l = (unsigned int)(l->refcyc_per_meta_chunk_vblank_l); + disp_dlg_regs->refcyc_per_meta_chunk_vblank_c = (unsigned int)(l->refcyc_per_meta_chunk_vblank_c); + disp_dlg_regs->refcyc_per_meta_chunk_flip_l = (unsigned int)(l->refcyc_per_meta_chunk_flip_l); + disp_dlg_regs->refcyc_per_meta_chunk_flip_c = (unsigned int)(l->refcyc_per_meta_chunk_flip_c); + + disp_dlg_regs->refcyc_per_tdlut_group = (unsigned int)(l->refcyc_per_tdlut_group); + + /* Assign drq limit based on alt-channel enabled or not */ + if (display_cfg->plane_descriptors[l->plane_idx].overrides.uclk_pstate_change_strategy == dml2_uclk_pstate_change_strategy_force_alternate) { + disp_dlg_regs->dst_y_delta_drq_limit = mode_lib->mp.nom_req_limit_alt[l->stream_idx]; + disp_dlg_regs->dst_y_svp_drq_limit = mode_lib->mp.svp_req_limit[l->stream_idx]; + disp_dlg_regs->force_prefetch_to_vblank = 1; // For alt-channel, always force disp prefetch to vblank + disp_dlg_regs->force_cursor_to_disp_pref = 1; // For alt-channel, always force cursor to disp prefetch + } else { + disp_dlg_regs->dst_y_delta_drq_limit = 0x7fff; // off + disp_dlg_regs->dst_y_svp_drq_limit = 0x7fff; // off + disp_dlg_regs->force_prefetch_to_vblank = 0; // off + disp_dlg_regs->force_cursor_to_disp_pref = 0; // off + } + + disp_ttu_regs->refcyc_per_req_delivery_pre_l = (unsigned int)(l->refcyc_per_req_delivery_pre_l * math_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_l = (unsigned int)(l->refcyc_per_req_delivery_l * math_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_pre_c = (unsigned int)(l->refcyc_per_req_delivery_pre_c * math_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_c = (unsigned int)(l->refcyc_per_req_delivery_c * math_pow(2, 10)); + disp_ttu_regs->qos_level_low_wm = 0; + + disp_ttu_regs->qos_level_high_wm = (unsigned int)(4.0 * (double)l->htotal * l->ref_freq_to_pix_freq); + + disp_ttu_regs->qos_level_flip = 14; + disp_ttu_regs->qos_level_fixed_l = 8; + disp_ttu_regs->qos_level_fixed_c = 8; + disp_ttu_regs->qos_ramp_disable_l = 0; + disp_ttu_regs->qos_ramp_disable_c = 0; + disp_ttu_regs->min_ttu_vblank = (unsigned int)(l->min_ttu_vblank * l->refclk_freq_in_mhz); + + // CHECK for HW registers' range, DML_ASSERT or clamp + DML_ASSERT(l->refcyc_per_req_delivery_pre_l < math_pow(2, 13)); + DML_ASSERT(l->refcyc_per_req_delivery_l < math_pow(2, 13)); + DML_ASSERT(l->refcyc_per_req_delivery_pre_c < math_pow(2, 13)); + DML_ASSERT(l->refcyc_per_req_delivery_c < math_pow(2, 13)); + if (disp_dlg_regs->refcyc_per_vm_group_vblank >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_group_vblank = (unsigned int)(math_pow(2, 23) - 1); + + if (disp_dlg_regs->refcyc_per_vm_group_flip >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_group_flip = (unsigned int)(math_pow(2, 23) - 1); + + if (disp_dlg_regs->refcyc_per_vm_req_vblank >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_req_vblank = (unsigned int)(math_pow(2, 23) - 1); + + if (disp_dlg_regs->refcyc_per_vm_req_flip >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_vm_req_flip = (unsigned int)(math_pow(2, 23) - 1); + + + DML_ASSERT(disp_dlg_regs->dst_y_after_scaler < (unsigned int)8); + DML_ASSERT(disp_dlg_regs->refcyc_x_after_scaler < (unsigned int)math_pow(2, 13)); + + if (disp_dlg_regs->dst_y_per_pte_row_nom_l >= (unsigned int)math_pow(2, 17)) { + DML_LOG_VERBOSE("DML_DLG: %s: Warning DST_Y_PER_PTE_ROW_NOM_L %u > register max U15.2 %u, clamp to max\n", __func__, disp_dlg_regs->dst_y_per_pte_row_nom_l, (unsigned int)math_pow(2, 17) - 1); + l->dst_y_per_pte_row_nom_l = (unsigned int)math_pow(2, 17) - 1; + } + if (l->dual_plane) { + if (disp_dlg_regs->dst_y_per_pte_row_nom_c >= (unsigned int)math_pow(2, 17)) { + DML_LOG_VERBOSE("DML_DLG: %s: Warning DST_Y_PER_PTE_ROW_NOM_C %u > register max U15.2 %u, clamp to max\n", __func__, disp_dlg_regs->dst_y_per_pte_row_nom_c, (unsigned int)math_pow(2, 17) - 1); + l->dst_y_per_pte_row_nom_c = (unsigned int)math_pow(2, 17) - 1; + } + } + + if (disp_dlg_regs->refcyc_per_pte_group_nom_l >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_l = (unsigned int)(math_pow(2, 23) - 1); + if (l->dual_plane) { + if (disp_dlg_regs->refcyc_per_pte_group_nom_c >= (unsigned int)math_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_c = (unsigned int)(math_pow(2, 23) - 1); + } + DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_l < (unsigned int)math_pow(2, 13)); + if (l->dual_plane) { + DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_c < (unsigned int)math_pow(2, 13)); + } + + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_l < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_l < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_c < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_c < (unsigned int)math_pow(2, 13)); + DML_ASSERT(disp_ttu_regs->qos_level_low_wm < (unsigned int)math_pow(2, 14)); + DML_ASSERT(disp_ttu_regs->qos_level_high_wm < (unsigned int)math_pow(2, 14)); + DML_ASSERT(disp_ttu_regs->min_ttu_vblank < (unsigned int)math_pow(2, 24)); + DML_LOG_VERBOSE("DML_DLG::%s: Calculation for pipe[%d] done\n", __func__, pipe_idx); + } +} + +static void dcn6_rq_dlg_get_wm_regs(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_dchub_watermark_regs *wm_regs) +{ + double refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : utm_soc_bb->dchub_refclk_mhz; + + wm_regs->fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz); + wm_regs->sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_enter_low_power = (int unsigned)(mode_lib->mp.Watermark.LowPowerStutterEnterPlusExitWatermark * refclk_freq_in_mhz); + wm_regs->sr_exit_low_power = (int unsigned)(mode_lib->mp.Watermark.LowPowerStutterExitWatermark * refclk_freq_in_mhz); + wm_regs->temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz); + wm_regs->ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz); + wm_regs->uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz); + wm_regs->urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + wm_regs->usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz); + wm_regs->refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.UrgentLatency * refclk_freq_in_mhz); + wm_regs->refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.MetaTripToMemory * refclk_freq_in_mhz); + wm_regs->frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000); + wm_regs->frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000); +} + +void dcn6_get_pipe_regs(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_dchub_per_pipe_register_set *out, int pipe_index, + const struct dml2_utm_soc_bb *utm_soc_bb, + struct dml2_core_internal_scratch *s) +{ + dcn5_rq_dlg_get_rq_reg(&out->rq_regs, display_cfg, mode_lib, pipe_index); + dcn6_rq_dlg_get_dlg_reg(s, &out->dlg_regs, &out->ttu_regs, display_cfg, mode_lib, pipe_index, utm_soc_bb); + out->det_size = mode_lib->mp.DETBufferSizeInKByte[mode_lib->mp.pipe_plane[pipe_index]] / mode_lib->ip.config_return_buffer_segment_size_in_kbytes; +} + +static bool dcn6_calculate_pstate_support_method( + enum dml2_pstate_method method, + double peak_vactive_p_vblank_latency_hiding_margin_us, + double vactive_margin_us, + double reserved_vblank_us, + double blackout_us, + bool all_streams_blanked, + /* output */ + enum dml2_pstate_change_support *surface_pstate_change_support) +{ + *surface_pstate_change_support = dml2_pstate_change_unsupported; + if (method == dml2_pstate_method_na) { + /* automatic */ + if (all_streams_blanked || + (vactive_margin_us > 0 && reserved_vblank_us >= blackout_us)) + *surface_pstate_change_support = dml2_pstate_change_vblank_and_vactive; + else if (vactive_margin_us > 0) + *surface_pstate_change_support = dml2_pstate_change_vactive; + else if (reserved_vblank_us >= blackout_us) + *surface_pstate_change_support = dml2_pstate_change_vblank; + } else if (method == dml2_pstate_method_vactive || method == dml2_pstate_method_fw_vactive_drr) { + /* vactive */ + if (all_streams_blanked || + (vactive_margin_us > 0 && reserved_vblank_us >= blackout_us)) + *surface_pstate_change_support = dml2_pstate_change_vblank_and_vactive; + else if (vactive_margin_us > 0 || peak_vactive_p_vblank_latency_hiding_margin_us > 0) + *surface_pstate_change_support = dml2_pstate_change_vactive; + } else if ((method == dml2_pstate_method_vblank || method == dml2_pstate_method_fw_vblank_drr) && + reserved_vblank_us >= blackout_us) { + /* vblank */ + *surface_pstate_change_support = dml2_pstate_change_vblank; + } else if (method == dml2_pstate_method_fw_drr) { + /* drr */ + *surface_pstate_change_support = dml2_pstate_change_drr; + } else if (method == dml2_pstate_method_alternate) { + /* TODO - alternate */ + *surface_pstate_change_support = dml2_pstate_change_mall_svp; + } + + return *surface_pstate_change_support != dml2_pstate_change_unsupported; +} + +void dcn6_calculate_watermarks_and_dram_speed_change_support( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p) +{ + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_locals *s = &scratch->CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_locals; + + double reserved_vblank_time_us; + bool FoundCriticalSurface = false; + + s->TotalActiveWriteback = 0; + p->Watermark->UrgentWatermark = p->mmSOCParameters.UrgentLatency + p->mmSOCParameters.ExtraLatency; + + DML_LOG_VERBOSE("DML::%s: UrgentLatency = %f\n", __func__, p->mmSOCParameters.UrgentLatency); + DML_LOG_VERBOSE("DML::%s: ExtraLatency = %f\n", __func__, p->mmSOCParameters.ExtraLatency); + DML_LOG_VERBOSE("DML::%s: UrgentWatermark = %f\n", __func__, p->Watermark->UrgentWatermark); + + p->Watermark->USRRetrainingWatermark = p->mmSOCParameters.UrgentLatency + p->mmSOCParameters.ExtraLatency + p->mmSOCParameters.USRRetrainingLatency + p->mmSOCParameters.SMNLatency; + p->Watermark->DRAMClockChangeWatermark = p->mmSOCParameters.DRAMClockChangeLatency + p->Watermark->UrgentWatermark; + p->Watermark->FCLKChangeWatermark = p->mmSOCParameters.FCLKChangeLatency + p->Watermark->UrgentWatermark; + p->Watermark->StutterExitWatermark = p->mmSOCParameters.SRExitTime + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->StutterEnterPlusExitWatermark = p->mmSOCParameters.SREnterPlusExitTime + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->LowPowerStutterExitWatermark = p->mmSOCParameters.SRExitTimeLowPower + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->LowPowerStutterEnterPlusExitWatermark = p->mmSOCParameters.SREnterPlusExitTimeLowPower + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->Z8StutterExitWatermark = p->mmSOCParameters.SRExitZ8Time + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + p->Watermark->Z8StutterEnterPlusExitWatermark = p->mmSOCParameters.SREnterPlusExitZ8Time + p->mmSOCParameters.ExtraLatency_sr + 10 / p->DCFClkDeepSleep; + if (p->mmSOCParameters.qos_type == dml2_qos_param_type_dcn4x) { + p->Watermark->StutterExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->StutterEnterPlusExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->LowPowerStutterExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->LowPowerStutterEnterPlusExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->Z8StutterExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + p->Watermark->Z8StutterEnterPlusExitWatermark += p->mmSOCParameters.max_urgent_latency_us + p->mmSOCParameters.df_response_time_us; + } + p->Watermark->temp_read_or_ppt_watermark_us = p->mmSOCParameters.temp_read_or_ppt_blackout_us + p->Watermark->UrgentWatermark; + + DML_LOG_VERBOSE("DML::%s: UrgentLatency = %f\n", __func__, p->mmSOCParameters.UrgentLatency); + DML_LOG_VERBOSE("DML::%s: ExtraLatency = %f\n", __func__, p->mmSOCParameters.ExtraLatency); + DML_LOG_VERBOSE("DML::%s: DRAMClockChangeLatency = %f\n", __func__, p->mmSOCParameters.DRAMClockChangeLatency); + DML_LOG_VERBOSE("DML::%s: SREnterPlusExitZ8Time = %f\n", __func__, p->mmSOCParameters.SREnterPlusExitZ8Time); + DML_LOG_VERBOSE("DML::%s: SREnterPlusExitTime = %f\n", __func__, p->mmSOCParameters.SREnterPlusExitTime); + DML_LOG_VERBOSE("DML::%s: UrgentWatermark = %f\n", __func__, p->Watermark->UrgentWatermark); + DML_LOG_VERBOSE("DML::%s: USRRetrainingWatermark = %f\n", __func__, p->Watermark->USRRetrainingWatermark); + DML_LOG_VERBOSE("DML::%s: DRAMClockChangeWatermark = %f\n", __func__, p->Watermark->DRAMClockChangeWatermark); + DML_LOG_VERBOSE("DML::%s: FCLKChangeWatermark = %f\n", __func__, p->Watermark->FCLKChangeWatermark); + DML_LOG_VERBOSE("DML::%s: StutterExitWatermark = %f\n", __func__, p->Watermark->StutterExitWatermark); + DML_LOG_VERBOSE("DML::%s: StutterEnterPlusExitWatermark = %f\n", __func__, p->Watermark->StutterEnterPlusExitWatermark); + DML_LOG_VERBOSE("DML::%s: Z8StutterExitWatermark = %f\n", __func__, p->Watermark->Z8StutterExitWatermark); + DML_LOG_VERBOSE("DML::%s: Z8StutterEnterPlusExitWatermark = %f\n", __func__, p->Watermark->Z8StutterEnterPlusExitWatermark); + DML_LOG_VERBOSE("DML::%s: temp_read_or_ppt_watermark_us = %f\n", __func__, p->Watermark->temp_read_or_ppt_watermark_us); + + s->TotalActiveWriteback = 0; + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) + for (unsigned int j = 0; j < p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream; ++j) + s->TotalActiveWriteback = s->TotalActiveWriteback + 1; + + if (s->TotalActiveWriteback <= 1) { + p->Watermark->WritebackUrgentWatermark = p->mmSOCParameters.WritebackLatency; + } else { + p->Watermark->WritebackUrgentWatermark = p->mmSOCParameters.WritebackLatency + p->WritebackChunkSize * 1024.0 / 32.0 / p->SOCCLK; + } + if (p->USRRetrainingRequired) + p->Watermark->WritebackUrgentWatermark = p->Watermark->WritebackUrgentWatermark + p->mmSOCParameters.USRRetrainingLatency; + + if (s->TotalActiveWriteback <= 1) { + p->Watermark->WritebackDRAMClockChangeWatermark = p->mmSOCParameters.DRAMClockChangeLatency + p->mmSOCParameters.WritebackLatency; + p->Watermark->WritebackFCLKChangeWatermark = p->mmSOCParameters.FCLKChangeLatency + p->mmSOCParameters.WritebackLatency; + } else { + p->Watermark->WritebackDRAMClockChangeWatermark = p->mmSOCParameters.DRAMClockChangeLatency + p->mmSOCParameters.WritebackLatency + p->WritebackChunkSize * 1024.0 / 32.0 / p->SOCCLK; + p->Watermark->WritebackFCLKChangeWatermark = p->mmSOCParameters.FCLKChangeLatency + p->mmSOCParameters.WritebackLatency + p->WritebackChunkSize * 1024 / 32 / p->SOCCLK; + } + + if (p->USRRetrainingRequired) + p->Watermark->WritebackDRAMClockChangeWatermark = p->Watermark->WritebackDRAMClockChangeWatermark + p->mmSOCParameters.USRRetrainingLatency; + + if (p->USRRetrainingRequired) + p->Watermark->WritebackFCLKChangeWatermark = p->Watermark->WritebackFCLKChangeWatermark + p->mmSOCParameters.USRRetrainingLatency; + + DML_LOG_VERBOSE("DML::%s: WritebackDRAMClockChangeWatermark = %f\n", __func__, p->Watermark->WritebackDRAMClockChangeWatermark); + DML_LOG_VERBOSE("DML::%s: WritebackFCLKChangeWatermark = %f\n", __func__, p->Watermark->WritebackFCLKChangeWatermark); + DML_LOG_VERBOSE("DML::%s: WritebackUrgentWatermark = %f\n", __func__, p->Watermark->WritebackUrgentWatermark); + DML_LOG_VERBOSE("DML::%s: USRRetrainingRequired = %u\n", __func__, p->USRRetrainingRequired); + DML_LOG_VERBOSE("DML::%s: USRRetrainingLatency = %f\n", __func__, p->mmSOCParameters.USRRetrainingLatency); + + s->TotalPixelBW = 0.0; + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + double h_total = (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total; + double pixel_clock_mhz = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000.0; + double v_ratio = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + double v_ratio_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + s->TotalPixelBW = s->TotalPixelBW + p->DPPPerSurface[k] + * (p->SwathWidthY[k] * p->BytePerPixelDETY[k] * v_ratio + p->SwathWidthC[k] * p->BytePerPixelDETC[k] * v_ratio_c) / (h_total / pixel_clock_mhz); + } + + *p->global_fclk_change_supported = true; + *p->global_dram_clock_change_supported = true; + *p->global_temp_read_or_ppt_supported = true; + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + double h_total = (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total; + double pixel_clock_mhz = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000.0; + double v_ratio = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + double v_ratio_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_ratio; + double v_taps = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_taps; + double v_taps_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.v_taps; + double h_ratio = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio; + double h_ratio_c = p->display_cfg->plane_descriptors[k].composition.scaler_info.plane1.h_ratio; + double LBBitPerPixel = 57; + + s->LBLatencyHidingSourceLinesY[k] = (unsigned int)(math_min2((double)p->MaxLineBufferLines, math_floor2((double)p->LineBufferSize / LBBitPerPixel / ((double)p->SwathWidthY[k] / math_max2(h_ratio, 1.0)), 1)) - (v_taps - 1)); + s->LBLatencyHidingSourceLinesC[k] = (unsigned int)(math_min2((double)p->MaxLineBufferLines, math_floor2((double)p->LineBufferSize / LBBitPerPixel / ((double)p->SwathWidthC[k] / math_max2(h_ratio_c, 1.0)), 1)) - (v_taps_c - 1)); + + DML_LOG_VERBOSE("DML::%s: k=%u, MaxLineBufferLines= %u\n", __func__, k, p->MaxLineBufferLines); + DML_LOG_VERBOSE("DML::%s: k=%u, LineBufferSize = %u\n", __func__, k, p->LineBufferSize); + DML_LOG_VERBOSE("DML::%s: k=%u, LBBitPerPixel = %f\n", __func__, k, LBBitPerPixel); + DML_LOG_VERBOSE("DML::%s: k=%u, HRatio = %f\n", __func__, k, h_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u, VTaps = %f\n", __func__, k, v_taps); + + s->EffectiveLBLatencyHidingY = s->LBLatencyHidingSourceLinesY[k] / v_ratio * (h_total / pixel_clock_mhz); + s->EffectiveLBLatencyHidingC = s->LBLatencyHidingSourceLinesC[k] / v_ratio_c * (h_total / pixel_clock_mhz); + + s->EffectiveDETBufferSizeY = p->DETBufferSizeY[k]; + if (p->UnboundedRequestEnabled) { + s->EffectiveDETBufferSizeY = s->EffectiveDETBufferSizeY + p->CompressedBufferSizeInkByte * 1024 * (p->SwathWidthY[k] * p->BytePerPixelDETY[k] * v_ratio) / (h_total / pixel_clock_mhz) / s->TotalPixelBW; + } + + s->LinesInDETY[k] = (double)s->EffectiveDETBufferSizeY / p->BytePerPixelDETY[k] / p->SwathWidthY[k]; + s->LinesInDETYRoundedDownToSwath[k] = (unsigned int)(math_floor2(s->LinesInDETY[k], p->SwathHeightY[k])); + s->FullDETBufferingTimeY = s->LinesInDETYRoundedDownToSwath[k] * (h_total / pixel_clock_mhz) / v_ratio; + + s->ActiveClockChangeLatencyHidingY = s->EffectiveLBLatencyHidingY + s->FullDETBufferingTimeY - ((double)p->DSTXAfterScaler[k] / h_total + (double)p->DSTYAfterScaler[k]) * h_total / pixel_clock_mhz; + + if (p->NumberOfActiveSurfaces > 1) { + s->ActiveClockChangeLatencyHidingY = s->ActiveClockChangeLatencyHidingY - (1.0 - 1.0 / (double)p->NumberOfActiveSurfaces) * (double)p->SwathHeightY[k] * (double)h_total / pixel_clock_mhz / v_ratio; + } + + if (p->BytePerPixelDETC[k] > 0) { + s->LinesInDETC[k] = p->DETBufferSizeC[k] / p->BytePerPixelDETC[k] / p->SwathWidthC[k]; + s->LinesInDETCRoundedDownToSwath[k] = (unsigned int)(math_floor2(s->LinesInDETC[k], p->SwathHeightC[k])); + s->FullDETBufferingTimeC = s->LinesInDETCRoundedDownToSwath[k] * (h_total / pixel_clock_mhz) / v_ratio_c; + s->ActiveClockChangeLatencyHidingC = s->EffectiveLBLatencyHidingC + s->FullDETBufferingTimeC - ((double)p->DSTXAfterScaler[k] / (double)h_total + (double)p->DSTYAfterScaler[k]) * (double)h_total / pixel_clock_mhz; + if (p->NumberOfActiveSurfaces > 1) { + s->ActiveClockChangeLatencyHidingC = s->ActiveClockChangeLatencyHidingC - (1.0 - 1.0 / (double)p->NumberOfActiveSurfaces) * (double)p->SwathHeightC[k] * (double)h_total / pixel_clock_mhz / v_ratio_c; + } + s->ActiveClockChangeLatencyHiding = math_min2(s->ActiveClockChangeLatencyHidingY, s->ActiveClockChangeLatencyHidingC); + } else { + s->ActiveClockChangeLatencyHiding = s->ActiveClockChangeLatencyHidingY; + } + + DML_LOG_VERBOSE("DML::%s: ActiveClockChangeLatencyHidingY = %f\n", __func__, s->ActiveClockChangeLatencyHidingY); + DML_LOG_VERBOSE("DML::%s: ActiveClockChangeLatencyHidingC = %f\n", __func__, s->ActiveClockChangeLatencyHidingC); + DML_LOG_VERBOSE("DML::%s: ActiveClockChangeLatencyHiding = %f\n", __func__, s->ActiveClockChangeLatencyHiding); + + reserved_vblank_time_us = (double)p->display_cfg->plane_descriptors[k].overrides.reserved_vblank_time_ns / 1000; + + s->ActiveDRAMClockChangeLatencyMargin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->DRAMClockChangeWatermark; + s->ActiveFCLKChangeLatencyMargin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->FCLKChangeWatermark; + s->USRRetrainingLatencyMargin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->USRRetrainingWatermark; + s->temp_read_or_ppt_latency_margin[k] = s->ActiveClockChangeLatencyHiding - p->Watermark->temp_read_or_ppt_watermark_us; + s->peak_vactive_p_vblank_latency_hiding_us = s->ActiveClockChangeLatencyHiding + reserved_vblank_time_us; + + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveFCLKChangeLatencyMargin = %f\n", __func__, k, s->ActiveFCLKChangeLatencyMargin[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveDRAMClockChangeLatencyMargin = %f\n", __func__, k, s->ActiveDRAMClockChangeLatencyMargin[k]); + + if (p->VActiveLatencyHidingMargin) { + p->VActiveLatencyHidingMargin[k] = s->ActiveDRAMClockChangeLatencyMargin[k]; + DML_LOG_VERBOSE("DML::%s: k=%u, VActiveLatencyHidingMargin = %f\n", __func__, k, p->VActiveLatencyHidingMargin[k]); + } + + if (p->VActiveLatencyHidingUs) { + p->VActiveLatencyHidingUs[k] = s->ActiveClockChangeLatencyHiding; + DML_LOG_VERBOSE("DML::%s: k=%u, VActiveLatencyHidingUs = %f\n", __func__, k, p->VActiveLatencyHidingUs[k]); + } + + for (unsigned int j = 0; j < p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[j].stream_index].writeback.active_writebacks_per_stream; ++j) { + double byte_per_pixel_luma_in_buffer = 1.0; + double buffer_for_luma = (double)p->WritebackInterfaceBufferSize * 1024.0 / 2.0; + if (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format == dml2_444_64) { + byte_per_pixel_luma_in_buffer = 8.0; + buffer_for_luma = (double)p->WritebackInterfaceBufferSize * 1024.0; + } else if (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format == dml2_444_32) { + byte_per_pixel_luma_in_buffer = 4.0; + buffer_for_luma = (double)p->WritebackInterfaceBufferSize * 1024.0; + } else if (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format == dml2_422_packed_8 + || p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format == dml2_420_8) { + byte_per_pixel_luma_in_buffer = 1.0; + buffer_for_luma = (double)p->WritebackInterfaceBufferSize * 1024.0 / 2.0; + } else if (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format == dml2_422_packed_10 + || p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].pixel_format == dml2_420_10) { + byte_per_pixel_luma_in_buffer = 10.0 / 8.0; + buffer_for_luma = (double)p->WritebackInterfaceBufferSize * 1024.0 / 2.0; + } + + s->WritebackLatencyHiding = buffer_for_luma + / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].output_height + * (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].output_width + / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[j].input_height + * (double)h_total / pixel_clock_mhz)) / byte_per_pixel_luma_in_buffer; + + s->peak_vactive_p_vblank_latency_hiding_us = 0.0; /* not supported with writeback */ + s->WritebackDRAMClockChangeLatencyMargin = s->WritebackLatencyHiding - p->Watermark->WritebackDRAMClockChangeWatermark; + s->WritebackFCLKChangeLatencyMargin = s->WritebackLatencyHiding - p->Watermark->WritebackFCLKChangeWatermark; + s->ActiveDRAMClockChangeLatencyMargin[k] = math_min2(s->ActiveDRAMClockChangeLatencyMargin[k], s->WritebackDRAMClockChangeLatencyMargin); + s->ActiveFCLKChangeLatencyMargin[k] = math_min2(s->ActiveFCLKChangeLatencyMargin[k], s->WritebackFCLKChangeLatencyMargin); + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveFCLKChangeLatencyMargin = %f (WB)\n", __func__, k, s->ActiveFCLKChangeLatencyMargin[k]); + } + + p->MaxActiveDRAMClockChangeLatencySupported[k] = s->ActiveDRAMClockChangeLatencyMargin[k] + p->mmSOCParameters.DRAMClockChangeLatency; + + *p->global_fclk_change_supported &= dcn6_calculate_pstate_support_method( + dml2_pstate_method_vactive, + s->peak_vactive_p_vblank_latency_hiding_us - p->mmSOCParameters.FCLKChangeLatency, + s->ActiveFCLKChangeLatencyMargin[k], + reserved_vblank_time_us, + p->mmSOCParameters.FCLKChangeLatency, + p->display_cfg->overrides.all_streams_blanked, + /* output */ + &p->FCLKChangeSupport[k]); + + *p->global_temp_read_or_ppt_supported &= dcn6_calculate_pstate_support_method( + dml2_pstate_method_vactive, + s->peak_vactive_p_vblank_latency_hiding_us - p->mmSOCParameters.temp_read_or_ppt_blackout_us, + s->temp_read_or_ppt_latency_margin[k], + reserved_vblank_time_us, + p->mmSOCParameters.temp_read_or_ppt_blackout_us, + p->display_cfg->overrides.all_streams_blanked, + /* output */ + &p->temp_read_or_ppt_support[k]); + + *p->global_dram_clock_change_support_required |= p->uclk_pstate_switch_modes[k] != dml2_pstate_method_na; + *p->global_dram_clock_change_supported &= dcn6_calculate_pstate_support_method( + p->uclk_pstate_switch_modes[k], + s->peak_vactive_p_vblank_latency_hiding_us - p->mmSOCParameters.DRAMClockChangeLatency, + s->ActiveDRAMClockChangeLatencyMargin[k], + reserved_vblank_time_us, + p->mmSOCParameters.DRAMClockChangeLatency, + p->display_cfg->overrides.all_streams_blanked, + /* output */ + &p->DRAMClockChangeSupport[k]); + + s->dst_y_pstate = (unsigned int)(math_ceil2((p->mmSOCParameters.DRAMClockChangeLatency + p->mmSOCParameters.UrgentLatency) / (h_total / pixel_clock_mhz), 1)); + s->src_y_pstate_l = (unsigned int)(math_ceil2(s->dst_y_pstate * v_ratio, p->SwathHeightY[k])); + s->src_y_ahead_l = (unsigned int)(math_floor2(p->DETBufferSizeY[k] / p->BytePerPixelDETY[k] / p->SwathWidthY[k], p->SwathHeightY[k]) + s->LBLatencyHidingSourceLinesY[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, DETBufferSizeY = %u\n", __func__, k, p->DETBufferSizeY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, BytePerPixelDETY = %f\n", __func__, k, p->BytePerPixelDETY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathWidthY = %u\n", __func__, k, p->SwathWidthY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathHeightY = %u\n", __func__, k, p->SwathHeightY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, LBLatencyHidingSourceLinesY = %u\n", __func__, k, s->LBLatencyHidingSourceLinesY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, dst_y_pstate = %u\n", __func__, k, s->dst_y_pstate); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_pstate_l = %u\n", __func__, k, s->src_y_pstate_l); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_ahead_l = %u\n", __func__, k, s->src_y_ahead_l); + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_height_l = %u\n", __func__, k, p->meta_row_height_l[k]); + + if (p->BytePerPixelDETC[k] > 0) { + s->src_y_pstate_c = (unsigned int)(math_ceil2(s->dst_y_pstate * v_ratio_c, p->SwathHeightC[k])); + s->src_y_ahead_c = (unsigned int)(math_floor2(p->DETBufferSizeC[k] / p->BytePerPixelDETC[k] / p->SwathWidthC[k], p->SwathHeightC[k]) + s->LBLatencyHidingSourceLinesC[k]); + + DML_LOG_VERBOSE("DML::%s: k=%u, meta_row_height_c = %u\n", __func__, k, p->meta_row_height_c[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_pstate_c = %u\n", __func__, k, s->src_y_pstate_c); + DML_LOG_VERBOSE("DML::%s: k=%u, src_y_ahead_c = %u\n", __func__, k, s->src_y_ahead_c); + DML_LOG_VERBOSE("DML::%s: k=%u, sub_vp_lines_c = %u\n", __func__, k, s->sub_vp_lines_c); + } + } + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + DML_LOG_VERBOSE("DML::%s: k=%u, ActiveFCLKChangeLatencyMargin=%f\n", __func__, k, s->ActiveFCLKChangeLatencyMargin[k]); + if (((!FoundCriticalSurface) || ((s->ActiveFCLKChangeLatencyMargin[k] + p->mmSOCParameters.FCLKChangeLatency) < *p->MaxActiveFCLKChangeLatencySupported))) { + FoundCriticalSurface = true; + *p->MaxActiveFCLKChangeLatencySupported = s->ActiveFCLKChangeLatencyMargin[k] + p->mmSOCParameters.FCLKChangeLatency; + } + } + + DML_LOG_VERBOSE("DML::%s: DRAMClockChangeSupport = %u\n", __func__, *p->global_dram_clock_change_supported); + DML_LOG_VERBOSE("DML::%s: FCLKChangeSupport = %u\n", __func__, *p->global_fclk_change_supported); + DML_LOG_VERBOSE("DML::%s: MaxActiveFCLKChangeLatencySupported = %f\n", __func__, *p->MaxActiveFCLKChangeLatencySupported); + DML_LOG_VERBOSE("DML::%s: USRRetrainingSupport = %u\n", __func__, *p->USRRetrainingSupport); +} + +void dcn6_calculate_stutter_efficiency(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateStutterEfficiency_params *p) +{ + struct dml2_core_calcs_CalculateStutterEfficiency_locals *l = &scratch->CalculateStutterEfficiency_locals; + + unsigned int TotalNumberOfActiveOTG = 0; + double SinglePixelClock = 0; + unsigned int SingleHTotal = 0; + unsigned int SingleVTotal = 0; + bool SameTiming = true; + bool at_least_one_single_pipe_single_plane_surface = false; + bool FoundCriticalSurface = false; + + memset(l, 0, sizeof(struct dml2_core_calcs_CalculateStutterEfficiency_locals)); + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (p->display_cfg->plane_descriptors[k].surface.dcc.enable == true) { + if ((dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockWidth256BytesY[k] > p->SwathHeightY[k]) || (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockHeight256BytesY[k] > p->SwathHeightY[k]) || p->DCCYMaxUncompressedBlock[k] < 256) { + l->MaximumEffectiveCompressionLuma = 2; + } else { + l->MaximumEffectiveCompressionLuma = 4; + } + l->TotalCompressedReadBandwidth = l->TotalCompressedReadBandwidth + p->ReadBandwidthSurfaceLuma[k] / math_min2(p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane0, l->MaximumEffectiveCompressionLuma); + DML_LOG_VERBOSE("DML::%s: k=%u, ReadBandwidthSurfaceLuma = %f\n", __func__, k, p->ReadBandwidthSurfaceLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, NetDCCRateLuma = %f\n", __func__, k, p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane0); + DML_LOG_VERBOSE("DML::%s: k=%u, MaximumEffectiveCompressionLuma = %f\n", __func__, k, l->MaximumEffectiveCompressionLuma); + l->TotalZeroSizeRequestReadBandwidth = l->TotalZeroSizeRequestReadBandwidth + p->ReadBandwidthSurfaceLuma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane0; + l->TotalZeroSizeCompressedReadBandwidth = l->TotalZeroSizeCompressedReadBandwidth + p->ReadBandwidthSurfaceLuma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane0 / l->MaximumEffectiveCompressionLuma; + + if (p->ReadBandwidthSurfaceChroma[k] > 0) { + if ((dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockWidth256BytesC[k] > p->SwathHeightC[k]) || (!dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) && p->BlockHeight256BytesC[k] > p->SwathHeightC[k]) || p->DCCCMaxUncompressedBlock[k] < 256) { + l->MaximumEffectiveCompressionChroma = 2; + } else { + l->MaximumEffectiveCompressionChroma = 4; + } + l->TotalCompressedReadBandwidth = l->TotalCompressedReadBandwidth + p->ReadBandwidthSurfaceChroma[k] / math_min2(p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane1, l->MaximumEffectiveCompressionChroma); + DML_LOG_VERBOSE("DML::%s: k=%u, ReadBandwidthSurfaceChroma = %f\n", __func__, k, p->ReadBandwidthSurfaceChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, NetDCCRateChroma = %f\n", __func__, k, p->display_cfg->plane_descriptors[k].surface.dcc.informative.dcc_rate_plane1); + DML_LOG_VERBOSE("DML::%s: k=%u, MaximumEffectiveCompressionChroma = %f\n", __func__, k, l->MaximumEffectiveCompressionChroma); + l->TotalZeroSizeRequestReadBandwidth = l->TotalZeroSizeRequestReadBandwidth + p->ReadBandwidthSurfaceChroma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane1; + l->TotalZeroSizeCompressedReadBandwidth = l->TotalZeroSizeCompressedReadBandwidth + p->ReadBandwidthSurfaceChroma[k] * p->display_cfg->plane_descriptors[k].surface.dcc.informative.fraction_of_zero_size_request_plane1 / l->MaximumEffectiveCompressionChroma; + } + } else { + l->TotalCompressedReadBandwidth = l->TotalCompressedReadBandwidth + p->ReadBandwidthSurfaceLuma[k] + p->ReadBandwidthSurfaceChroma[k]; + } + l->TotalRowReadBandwidth = l->TotalRowReadBandwidth + p->DPPPerSurface[k] * (p->meta_row_bw[k] + p->dpte_row_bw[k]); + } + + l->AverageDCCCompressionRate = p->TotalDataReadBandwidth / l->TotalCompressedReadBandwidth; + l->AverageDCCZeroSizeFraction = l->TotalZeroSizeRequestReadBandwidth / p->TotalDataReadBandwidth; + + DML_LOG_VERBOSE("DML::%s: UnboundedRequestEnabled = %u\n", __func__, p->UnboundedRequestEnabled); + DML_LOG_VERBOSE("DML::%s: TotalCompressedReadBandwidth = %f\n", __func__, l->TotalCompressedReadBandwidth); + DML_LOG_VERBOSE("DML::%s: TotalZeroSizeRequestReadBandwidth = %f\n", __func__, l->TotalZeroSizeRequestReadBandwidth); + DML_LOG_VERBOSE("DML::%s: TotalZeroSizeCompressedReadBandwidth = %f\n", __func__, l->TotalZeroSizeCompressedReadBandwidth); + DML_LOG_VERBOSE("DML::%s: MaximumEffectiveCompressionLuma = %f\n", __func__, l->MaximumEffectiveCompressionLuma); + DML_LOG_VERBOSE("DML::%s: MaximumEffectiveCompressionChroma = %f\n", __func__, l->MaximumEffectiveCompressionChroma); + DML_LOG_VERBOSE("DML::%s: AverageDCCCompressionRate = %f\n", __func__, l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: AverageDCCZeroSizeFraction = %f\n", __func__, l->AverageDCCZeroSizeFraction); + + DML_LOG_VERBOSE("DML::%s: CompbufReservedSpace64B = %u (%f kbytes)\n", __func__, p->CompbufReservedSpace64B, p->CompbufReservedSpace64B * 64 / 1024.0); + DML_LOG_VERBOSE("DML::%s: CompbufReservedSpaceZs = %u\n", __func__, p->CompbufReservedSpaceZs); + DML_LOG_VERBOSE("DML::%s: CompressedBufferSizeInkByte = %u kbytes\n", __func__, p->CompressedBufferSizeInkByte); + DML_LOG_VERBOSE("DML::%s: ROBBufferSizeInKByte = %u kbytes\n", __func__, p->ROBBufferSizeInKByte); + if (l->AverageDCCZeroSizeFraction == 1) { + l->AverageZeroSizeCompressionRate = l->TotalZeroSizeRequestReadBandwidth / l->TotalZeroSizeCompressedReadBandwidth; + l->EffectiveCompressedBufferSize = (double)p->MetaFIFOSizeInKEntries * 1024 * 64 * l->AverageZeroSizeCompressionRate + ((double)p->ZeroSizeBufferEntries - p->CompbufReservedSpaceZs) * 64 * l->AverageZeroSizeCompressionRate; + + + } else if (l->AverageDCCZeroSizeFraction > 0) { + l->AverageZeroSizeCompressionRate = l->TotalZeroSizeRequestReadBandwidth / l->TotalZeroSizeCompressedReadBandwidth; + l->EffectiveCompressedBufferSize = math_min2((double)p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate, + (double)p->MetaFIFOSizeInKEntries * 1024 * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate + 1 / l->AverageDCCCompressionRate)) + + (p->rob_alloc_compressed ? math_min2(((double)p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64) * l->AverageDCCCompressionRate, + ((double)p->ZeroSizeBufferEntries - p->CompbufReservedSpaceZs) * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate)) + : ((double)p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64)); + + + DML_LOG_VERBOSE("DML::%s: min 1 = %f\n", __func__, p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: min 2 = %f\n", __func__, p->MetaFIFOSizeInKEntries * 1024 * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate + 1 / l->AverageDCCCompressionRate)); + DML_LOG_VERBOSE("DML::%s: min 3 = %d\n", __func__, (p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64)); + DML_LOG_VERBOSE("DML::%s: min 4 = %f\n", __func__, (p->ZeroSizeBufferEntries - p->CompbufReservedSpaceZs) * 64 / (l->AverageDCCZeroSizeFraction / l->AverageZeroSizeCompressionRate)); + } else { + l->EffectiveCompressedBufferSize = math_min2((double)p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate, + (double)p->MetaFIFOSizeInKEntries * 1024 * 64 * l->AverageDCCCompressionRate) + + ((double)p->ROBBufferSizeInKByte * 1024 - p->CompbufReservedSpace64B * 64) * (p->rob_alloc_compressed ? l->AverageDCCCompressionRate : 1.0); + + DML_LOG_VERBOSE("DML::%s: min 1 = %f\n", __func__, p->CompressedBufferSizeInkByte * 1024 * l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: min 2 = %f\n", __func__, p->MetaFIFOSizeInKEntries * 1024 * 64 * l->AverageDCCCompressionRate); + } + + DML_LOG_VERBOSE("DML::%s: MetaFIFOSizeInKEntries = %u\n", __func__, p->MetaFIFOSizeInKEntries); + DML_LOG_VERBOSE("DML::%s: ZeroSizeBufferEntries = %u\n", __func__, p->ZeroSizeBufferEntries); + DML_LOG_VERBOSE("DML::%s: AverageZeroSizeCompressionRate = %f\n", __func__, l->AverageZeroSizeCompressionRate); + DML_LOG_VERBOSE("DML::%s: EffectiveCompressedBufferSize = %f (%f kbytes)\n", __func__, l->EffectiveCompressedBufferSize, l->EffectiveCompressedBufferSize / 1024.0); + + *p->StutterPeriod = 0; + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + l->LinesInDETY = ((double)p->DETBufferSizeY[k] + (p->UnboundedRequestEnabled == true ? l->EffectiveCompressedBufferSize : 0) * p->ReadBandwidthSurfaceLuma[k] / p->TotalDataReadBandwidth) / p->BytePerPixelDETY[k] / p->SwathWidthY[k]; + l->LinesInDETYRoundedDownToSwath = math_floor2(l->LinesInDETY, p->SwathHeightY[k]); + l->DETBufferingTimeY = l->LinesInDETYRoundedDownToSwath * ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000)) / p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio; + at_least_one_single_pipe_single_plane_surface |= (p->DPPPerSurface[k] == 1) && (p->ReadBandwidthSurfaceChroma[k] == 0); + DML_LOG_VERBOSE("DML::%s: k=%u, DETBufferSizeY = %u (%u kbytes)\n", __func__, k, p->DETBufferSizeY[k], p->DETBufferSizeY[k] / 1024); + DML_LOG_VERBOSE("DML::%s: k=%u, BytePerPixelDETY = %f\n", __func__, k, p->BytePerPixelDETY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathWidthY = %u\n", __func__, k, p->SwathWidthY[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, ReadBandwidthSurfaceLuma = %f\n", __func__, k, p->ReadBandwidthSurfaceLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, TotalDataReadBandwidth = %f\n", __func__, k, p->TotalDataReadBandwidth); + DML_LOG_VERBOSE("DML::%s: k=%u, LinesInDETY = %f\n", __func__, k, l->LinesInDETY); + DML_LOG_VERBOSE("DML::%s: k=%u, LinesInDETYRoundedDownToSwath = %f\n", __func__, k, l->LinesInDETYRoundedDownToSwath); + DML_LOG_VERBOSE("DML::%s: k=%u, VRatio = %f\n", __func__, k, p->display_cfg->plane_descriptors[k].composition.scaler_info.plane0.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%u, DETBufferingTimeY = %f\n", __func__, k, l->DETBufferingTimeY); + + if (!FoundCriticalSurface || l->DETBufferingTimeY < *p->StutterPeriod) { + bool isInterlaceTiming = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.interlaced && !p->ProgressiveToInterlaceUnitInOPP; + + FoundCriticalSurface = true; + *p->StutterPeriod = l->DETBufferingTimeY; + l->FrameTimeCriticalSurface = (isInterlaceTiming ? math_floor2((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total / 2.0, 1.0) : p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total) * (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + l->VActiveTimeCriticalSurface = (isInterlaceTiming ? math_floor2((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_active / 2.0, 1.0) : p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_active) * (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + l->BytePerPixelYCriticalSurface = p->BytePerPixelY[k]; + l->SwathWidthYCriticalSurface = p->SwathWidthY[k]; + l->SwathHeightYCriticalSurface = p->SwathHeightY[k]; + l->BlockWidth256BytesYCriticalSurface = p->BlockWidth256BytesY[k]; + l->DETBufferSizeYCriticalSurface = p->DETBufferSizeY[k]; + l->MinTTUVBlankCriticalSurface = p->MinTTUVBlank[k]; + l->SinglePlaneCriticalSurface = (p->ReadBandwidthSurfaceChroma[k] == 0); + l->SinglePipeCriticalSurface = (p->DPPPerSurface[k] == 1); + + DML_LOG_VERBOSE("DML::%s: k=%u, FoundCriticalSurface = %u\n", __func__, k, FoundCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, StutterPeriod = %f\n", __func__, k, *p->StutterPeriod); + DML_LOG_VERBOSE("DML::%s: k=%u, MinTTUVBlankCriticalSurface = %f\n", __func__, k, l->MinTTUVBlankCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, FrameTimeCriticalSurface= %f\n", __func__, k, l->FrameTimeCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, VActiveTimeCriticalSurface = %f\n", __func__, k, l->VActiveTimeCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, BytePerPixelYCriticalSurface = %u\n", __func__, k, l->BytePerPixelYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathWidthYCriticalSurface = %f\n", __func__, k, l->SwathWidthYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SwathHeightYCriticalSurface = %f\n", __func__, k, l->SwathHeightYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, BlockWidth256BytesYCriticalSurface = %u\n", __func__, k, l->BlockWidth256BytesYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SinglePlaneCriticalSurface = %u\n", __func__, k, l->SinglePlaneCriticalSurface); + DML_LOG_VERBOSE("DML::%s: k=%u, SinglePipeCriticalSurface = %u\n", __func__, k, l->SinglePipeCriticalSurface); + } + } + + // for bounded req, the stutter period is calculated only based on DET size, but during burst there can be some return inside ROB/compressed buffer + // stutter period is calculated only on the det sizing + // if (cdb + rob >= det) the stutter burst will be absorbed by the cdb + rob which is before decompress + // else + // the cdb + rob part will be in compressed rate with urg bw (idea bw) + // the det part will be return at uncompressed rate with 64B/dcfclk + // + // for unbounded req, the stutter period should be calculated as total of CDB+ROB+DET, so the term "PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer" + // should be == EffectiveCompressedBufferSize which will returned a compressed rate, the rest of stutter period is from the DET will be returned at uncompressed rate with 64B/dcfclk + + l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer = math_min2(*p->StutterPeriod * p->TotalDataReadBandwidth, l->EffectiveCompressedBufferSize); + DML_LOG_VERBOSE("DML::%s: AverageDCCCompressionRate = %f\n", __func__, l->AverageDCCCompressionRate); + DML_LOG_VERBOSE("DML::%s: StutterPeriod*TotalDataReadBandwidth = %f (%f kbytes)\n", __func__, *p->StutterPeriod * p->TotalDataReadBandwidth, (*p->StutterPeriod * p->TotalDataReadBandwidth) / 1024.0); + DML_LOG_VERBOSE("DML::%s: EffectiveCompressedBufferSize = %f (%f kbytes)\n", __func__, l->EffectiveCompressedBufferSize, l->EffectiveCompressedBufferSize / 1024.0); + DML_LOG_VERBOSE("DML::%s: PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer = %f (%f kbytes)\n", __func__, l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer, l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer / 1024); + DML_LOG_VERBOSE("DML::%s: ReturnBW = %f\n", __func__, p->ReturnBW); + DML_LOG_VERBOSE("DML::%s: TotalDataReadBandwidth = %f\n", __func__, p->TotalDataReadBandwidth); + DML_LOG_VERBOSE("DML::%s: TotalRowReadBandwidth = %f\n", __func__, l->TotalRowReadBandwidth); + DML_LOG_VERBOSE("DML::%s: DCFCLK = %f\n", __func__, p->DCFCLK); + + l->StutterBurstTime = l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer + / (p->ReturnBW * (p->hw_debug5 ? 1 : l->AverageDCCCompressionRate)) + + (*p->StutterPeriod * p->TotalDataReadBandwidth - l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer) + / math_min2(p->DCFCLK * 64, p->ReturnBW * (p->hw_debug5 ? 1 : l->AverageDCCCompressionRate)) + + *p->StutterPeriod * l->TotalRowReadBandwidth / p->ReturnBW; + DML_LOG_VERBOSE("DML::%s: Part 1 = %f\n", __func__, l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer / p->ReturnBW / (p->hw_debug5 ? 1 : l->AverageDCCCompressionRate)); + DML_LOG_VERBOSE("DML::%s: Part 2 = %f\n", __func__, (*p->StutterPeriod * p->TotalDataReadBandwidth - l->PartOfUncompressedPixelBurstThatFitsInROBAndCompressedBuffer) / (p->DCFCLK * 64)); + DML_LOG_VERBOSE("DML::%s: Part 3 = %f\n", __func__, *p->StutterPeriod * l->TotalRowReadBandwidth / p->ReturnBW); + DML_LOG_VERBOSE("DML::%s: StutterBurstTime = %f\n", __func__, l->StutterBurstTime); + l->TotalActiveWriteback = 0; + memset(l->stream_visited, 0, DML2_MAX_PLANES * sizeof(bool)); + + for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { + if (!l->stream_visited[p->display_cfg->plane_descriptors[k].stream_index]) { + + for (unsigned int j = 0; j < p->display_cfg->stream_descriptors[k].writeback.active_writebacks_per_stream; j++) + l->TotalActiveWriteback = l->TotalActiveWriteback + 1; + + if (TotalNumberOfActiveOTG == 0) { // first otg + SinglePixelClock = ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000); + SingleHTotal = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total; + SingleVTotal = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total; + } else if (SinglePixelClock != ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000) || + SingleHTotal != p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.h_total || + SingleVTotal != p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].timing.v_total) { + SameTiming = false; + } + TotalNumberOfActiveOTG = TotalNumberOfActiveOTG + 1; + l->stream_visited[p->display_cfg->plane_descriptors[k].stream_index] = 1; + } + } + + if (l->TotalActiveWriteback == 0) { + DML_LOG_VERBOSE("DML::%s: SRExitTime = %f\n", __func__, p->SRExitTime); + DML_LOG_VERBOSE("DML::%s: SRExitZ8Time = %f\n", __func__, p->SRExitZ8Time); + DML_LOG_VERBOSE("DML::%s: SRExitTimeLowPower = %f\n", __func__, p->SRExitTime); + DML_LOG_VERBOSE("DML::%s: StutterPeriod = %f\n", __func__, *p->StutterPeriod); + *p->StutterEfficiencyNotIncludingVBlank = math_max2(0., 1 - (p->SRExitTime + l->StutterBurstTime) / *p->StutterPeriod) * 100; + *p->Z8StutterEfficiencyNotIncludingVBlank = math_max2(0., 1 - (p->SRExitZ8Time + l->StutterBurstTime) / *p->StutterPeriod) * 100; + *p->LowPowerStutterEfficiencyNotIncludingVBlank = math_max2(0., 1 - (p->SRExitTimeLowPower + l->StutterBurstTime) / *p->StutterPeriod) * 100; + *p->NumberOfStutterBurstsPerFrame = (*p->StutterEfficiencyNotIncludingVBlank > 0 ? (unsigned int)(math_ceil2(l->VActiveTimeCriticalSurface / *p->StutterPeriod, 1)) : 0); + *p->Z8NumberOfStutterBurstsPerFrame = (*p->Z8StutterEfficiencyNotIncludingVBlank > 0 ? (unsigned int)(math_ceil2(l->VActiveTimeCriticalSurface / *p->StutterPeriod, 1)) : 0); + *p->LowPowerNumberOfStutterBurstsPerFrame = (*p->LowPowerStutterEfficiencyNotIncludingVBlank > 0 ? (unsigned int)(math_ceil2(l->VActiveTimeCriticalSurface / *p->StutterPeriod, 1)) : 0); + + } else { + *p->StutterEfficiencyNotIncludingVBlank = 0.0; + *p->Z8StutterEfficiencyNotIncludingVBlank = 0.0; + *p->LowPowerStutterEfficiencyNotIncludingVBlank = 0.0; + *p->NumberOfStutterBurstsPerFrame = 0; + *p->Z8NumberOfStutterBurstsPerFrame = 0; + *p->LowPowerNumberOfStutterBurstsPerFrame = 0; + } + DML_LOG_VERBOSE("DML::%s: VActiveTimeCriticalSurface = %f\n", __func__, l->VActiveTimeCriticalSurface); + DML_LOG_VERBOSE("DML::%s: StutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->StutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: Z8StutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->Z8StutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: LowPowerStutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->LowPowerStutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: NumberOfStutterBurstsPerFrame = %u\n", __func__, *p->NumberOfStutterBurstsPerFrame); + DML_LOG_VERBOSE("DML::%s: Z8NumberOfStutterBurstsPerFrame = %u\n", __func__, *p->Z8NumberOfStutterBurstsPerFrame); + DML_LOG_VERBOSE("DML::%s: LowPowerNumberOfStutterBurstsPerFrame = %u\n", __func__, *p->LowPowerNumberOfStutterBurstsPerFrame); + + if (*p->StutterEfficiencyNotIncludingVBlank > 0) { + if (!((p->SynchronizeTimings || TotalNumberOfActiveOTG == 1) && SameTiming)) { + *p->StutterEfficiency = *p->StutterEfficiencyNotIncludingVBlank; + } else { + *p->StutterEfficiency = (1 - (*p->NumberOfStutterBurstsPerFrame * p->SRExitTime + l->StutterBurstTime * l->VActiveTimeCriticalSurface / *p->StutterPeriod) / l->FrameTimeCriticalSurface) * 100; + } + } else { + *p->StutterEfficiency = 0; + *p->NumberOfStutterBurstsPerFrame = 0; + } + + if (*p->Z8StutterEfficiencyNotIncludingVBlank > 0) { + if (!((p->SynchronizeTimings || TotalNumberOfActiveOTG == 1) && SameTiming)) { + *p->Z8StutterEfficiency = *p->Z8StutterEfficiencyNotIncludingVBlank; + } else { + *p->Z8StutterEfficiency = (1 - (*p->Z8NumberOfStutterBurstsPerFrame * p->SRExitZ8Time + l->StutterBurstTime * l->VActiveTimeCriticalSurface / *p->StutterPeriod) / l->FrameTimeCriticalSurface) * 100; + } + } else { + *p->Z8StutterEfficiency = 0.; + *p->Z8NumberOfStutterBurstsPerFrame = 0; + } + + if (*p->LowPowerStutterEfficiencyNotIncludingVBlank > 0) { + if (!((p->SynchronizeTimings || TotalNumberOfActiveOTG == 1) && SameTiming)) { + *p->LowPowerStutterEfficiency = *p->LowPowerStutterEfficiencyNotIncludingVBlank; + } else { + *p->LowPowerStutterEfficiency = (1 - (*p->LowPowerNumberOfStutterBurstsPerFrame * p->SRExitTimeLowPower + l->StutterBurstTime * l->VActiveTimeCriticalSurface / *p->StutterPeriod) / l->FrameTimeCriticalSurface) * 100; + } + } else { + *p->LowPowerStutterEfficiency = 0; + *p->LowPowerNumberOfStutterBurstsPerFrame = 0; + } + + DML_LOG_VERBOSE("DML::%s: TotalNumberOfActiveOTG = %u\n", __func__, TotalNumberOfActiveOTG); + DML_LOG_VERBOSE("DML::%s: SameTiming = %u\n", __func__, SameTiming); + DML_LOG_VERBOSE("DML::%s: SynchronizeTimings = %u\n", __func__, p->SynchronizeTimings); + DML_LOG_VERBOSE("DML::%s: LastZ8StutterPeriod = %f\n", __func__, *p->Z8StutterEfficiencyNotIncludingVBlank > 0 ? l->VActiveTimeCriticalSurface - (*p->Z8NumberOfStutterBurstsPerFrame - 1) * *p->StutterPeriod : 0); + DML_LOG_VERBOSE("DML::%s: Z8StutterEnterPlusExitWatermark = %f\n", __func__, p->Z8StutterEnterPlusExitWatermark); + DML_LOG_VERBOSE("DML::%s: StutterBurstTime = %f\n", __func__, l->StutterBurstTime); + DML_LOG_VERBOSE("DML::%s: StutterPeriod = %f\n", __func__, *p->StutterPeriod); + DML_LOG_VERBOSE("DML::%s: StutterEfficiency = %f\n", __func__, *p->StutterEfficiency); + DML_LOG_VERBOSE("DML::%s: Z8StutterEfficiency = %f\n", __func__, *p->Z8StutterEfficiency); + DML_LOG_VERBOSE("DML::%s: StutterEfficiencyNotIncludingVBlank = %f\n", __func__, *p->StutterEfficiencyNotIncludingVBlank); + DML_LOG_VERBOSE("DML::%s: LowPowerStutterEfficiency = %f\n", __func__, *p->LowPowerStutterEfficiency); + DML_LOG_VERBOSE("DML::%s: Z8NumberOfStutterBurstsPerFrame = %u\n", __func__, *p->Z8NumberOfStutterBurstsPerFrame); + + *p->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE = !(!p->UnboundedRequestEnabled && (TotalNumberOfActiveOTG == 1) && at_least_one_single_pipe_single_plane_surface); + + DML_LOG_VERBOSE("DML::%s: DETBufferSizeYCriticalSurface = %u\n", __func__, l->DETBufferSizeYCriticalSurface); + DML_LOG_VERBOSE("DML::%s: PixelChunkSizeInKByte = %u\n", __func__, p->PixelChunkSizeInKByte); + DML_LOG_VERBOSE("DML::%s: DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE = %u\n", __func__, *p->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE); +} + +void dcn6_get_watermarks(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_dchub_watermark_regs *out) +{ + dcn6_rq_dlg_get_wm_regs(display_cfg, mode_lib, utm_soc_bb, out); +} + +void dcn6_calculate_excess_vactive_bandwidth_required( + const struct dml2_display_cfg *display_cfg, + unsigned int bytes_required_l[dml2_pstate_type_count][DML2_MAX_PLANES], + unsigned int bytes_required_c[dml2_pstate_type_count][DML2_MAX_PLANES], + /* outputs */ + double excess_vactive_fill_bw_l[], + double excess_vactive_fill_bw_c[]) +{ + unsigned int plane_index; + enum dml2_pstate_type pstate_type; + + for (plane_index = 0; plane_index < display_cfg->num_planes; plane_index++) { + excess_vactive_fill_bw_l[plane_index] = 0.0; + excess_vactive_fill_bw_c[plane_index] = 0.0; + + for (pstate_type = 0; pstate_type < dml2_pstate_type_count; pstate_type++) { + if (display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[pstate_type] > 0) { + excess_vactive_fill_bw_l[plane_index] = math_max2( + (double)bytes_required_l[pstate_type][plane_index] / + (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[pstate_type], + excess_vactive_fill_bw_l[plane_index]); + excess_vactive_fill_bw_c[plane_index] = math_max2( + (double)bytes_required_c[pstate_type][plane_index] / + (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[pstate_type], + excess_vactive_fill_bw_c[plane_index]); + } + } + } +} + +void dcn6_calculate_pstate_schedule_windows( + int num_active_planes, + const unsigned int v_blank_start[DML2_MAX_PLANES], + const unsigned int v_blank_end[DML2_MAX_PLANES], + const double otg_vline_time_us[DML2_MAX_PLANES], + const double det_fill_delay_us[DML2_MAX_PLANES], + const double reserved_vblank_us[DML2_MAX_PLANES], + const double blackout_us, + // Outputs + double allow_start_us[DML2_MAX_PLANES], + double allow_end_us[DML2_MAX_PLANES]) +{ + int k; + int allow_start_otg_vlines, allow_end_otg_vlines; + int det_fill_delay_otg_vlines; + int blackout_otg_vlines; + int reserved_vblank_otg_vlines; + + /** + * Calculate allow start and end for pstate in vactive: + * + * |vblank end vblank start| + * |<------------------------ vactive ---------------------------->| + * |<-- det fill delay ->|<----- allow window ----->|<----------- blackout ------------>| + * | |allow start allow end| |<- reserved blank ->| + */ + for (k = 0; k < num_active_planes; k++) { + /* Calculate the allow window in units of vlines */ + blackout_otg_vlines = (int)(math_ceil(blackout_us / otg_vline_time_us[k])); + det_fill_delay_otg_vlines = (int)(math_ceil(det_fill_delay_us[k] / otg_vline_time_us[k])); + reserved_vblank_otg_vlines = (int)(math_ceil(reserved_vblank_us[k] / otg_vline_time_us[k])); + + allow_start_otg_vlines = v_blank_end[k] + det_fill_delay_otg_vlines; + allow_end_otg_vlines = v_blank_start[k] - (blackout_otg_vlines - reserved_vblank_otg_vlines); + + /* Convert them back to time since the start of a frame */ + allow_start_us[k] = allow_start_otg_vlines * otg_vline_time_us[k]; + allow_end_us[k] = allow_end_otg_vlines * otg_vline_time_us[k]; + } +} + +void dcn6_calculate_pstate_schedule_admissibility( + uint32_t num_active_planes, + double max_allow_delay_us, + double min_allow_width_us, + const uint32_t timing_group_id[DML2_MAX_PLANES], + uint32_t timing_group_count, + const double frame_time_us[DML2_MAX_PLANES], + const double allow_start_us[DML2_MAX_PLANES], + const double allow_end_us[DML2_MAX_PLANES], + const enum dml2_pstate_method pstate_method[DML2_MAX_PLANES], + const bool is_drr[DML2_MAX_DCN_PIPES], + // Output + double allow_window_us[DML2_MAX_DCN_PIPES], + double disallow_window_us[DML2_MAX_DCN_PIPES], + bool *pstate_admissible) +{ + unsigned int cur_id = 0, other_id = 0; + unsigned int k = 0, i = 0; + unsigned int sorted[DML2_MAX_DCN_PIPES]; // group IDs sorted by disallow window size, from highest to lowest + double sum_of_disallow_windows_us = 0.0; + double sum_of_allow_windows_us = 0.0; + + /* Initialize as not admissible first */ + *pstate_admissible = false; + + /** + * Calculate the allow and disallow window for each timing group. + * A timing group may contain multiple planes rendered under a synchronized and identical timing framework. + * Each plane has its own allow window slack described by allow_start_us and allow_end_us. + * The goal is to find the intersection of allow windows across all planes in a timing group. This is used as + * a key data point to determine the p-state admissibility result. + * Here is an example of 3 planes in the same timing group and how the allow window is calculated: + * |----------------------------frame_time_us-----------------------------| + * plane0 |XXXXXXXXXXXXXX|----------- allow window 0 ------------------|XXXXXXXXX| + * plane1 |XXXX|--------------------- allow window 1 -------------|XXXXXXXXXXXXXX| + * plane2 |XXXXXXXX|----------------- allow window 2 -----------|XXXXXXXXXXXXXXXX| + * group |XXXXXXXXXXXXXX|----------- allow window (group)------|XXXXXXXXXXXXXXXX| + */ + memset(allow_window_us, 0, sizeof(double) * DML2_MAX_DCN_PIPES); + memset(disallow_window_us, 0, sizeof(double) * DML2_MAX_DCN_PIPES); + for (cur_id = 0; cur_id < timing_group_count; cur_id++) { + double group_allow_start_us = 0.0; + double group_allow_end_us = 0.0; + double group_frame_time_us = 0.0; + bool first = true; + + for (k = 0; k < num_active_planes; k++) { + /* Skip planes that are not part of the current timing group */ + if (timing_group_id[k] != cur_id) + continue; + if (first) { + group_frame_time_us = frame_time_us[k]; + group_allow_start_us = allow_start_us[k]; + group_allow_end_us = allow_end_us[k]; + first = false; + } else { + group_allow_start_us = math_max2(group_allow_start_us, allow_start_us[k]); + group_allow_end_us = math_min2(group_allow_end_us, allow_end_us[k]); + } + } + + allow_window_us[cur_id] = group_allow_end_us - group_allow_start_us; + if (allow_window_us[cur_id] > group_frame_time_us) + /* Clamp allow window to frame time */ + allow_window_us[cur_id] = group_frame_time_us; + disallow_window_us[cur_id] = group_frame_time_us - allow_window_us[cur_id]; + } + + /* Calculate the total sum of all allow and disallow windows */ + for (cur_id = 0; cur_id < timing_group_count; cur_id++) { + sum_of_disallow_windows_us += disallow_window_us[cur_id]; + sum_of_allow_windows_us += allow_window_us[cur_id]; + } + + /** + * Check 1 - Every group has a positive allow window greater than or equal to the minimum allow width. + */ + for (cur_id = 0; cur_id < timing_group_count; cur_id++) + if (allow_window_us[cur_id] <= 0 + || allow_window_us[cur_id] < min_allow_width_us) + return; + + /** + * Check 2 - Every group has a disallow window within the FAMS maximum scheduling latency budget. + */ + for (cur_id = 0; cur_id < timing_group_count; cur_id++) + if (disallow_window_us[cur_id] > max_allow_delay_us) + return; + + /** + * Schedulable Case 1 - Single group + */ + if (timing_group_count == 1) { + *pstate_admissible = true; + return; + } + + /** + * Schedulable Case 2 - Positive allow fragment after recursive slice halving. + * Passing Conditions: + * 1. Total sum of disallow windows across all groups is less than the FAMS maximum scheduling latency budget. + * 2. Every group's remaining allow fragment is still positive after recursive slicing and halving. + */ + + if (sum_of_disallow_windows_us < max_allow_delay_us) { + *pstate_admissible = true; + /* Bubble sort group IDs by disallow window size, from highest to lowest */ + for (cur_id = 0; cur_id < timing_group_count; cur_id++) + sorted[cur_id] = cur_id; + for (cur_id = 0; cur_id < timing_group_count; cur_id++) + for (other_id = cur_id + 1; other_id < timing_group_count; other_id++) + if (disallow_window_us[sorted[cur_id]] < disallow_window_us[sorted[other_id]]) + swap(sorted[cur_id], sorted[other_id]); + /* Continuously slice each group's allow fragment */ + for (cur_id = 0; cur_id < timing_group_count; cur_id++) { + double allow_fragment_us = allow_window_us[cur_id]; + + for (i = 0; i < timing_group_count; i++) { + other_id = sorted[i]; + + if (cur_id == other_id || disallow_window_us[other_id] <= 0.0) + continue; + + // slicing + allow_fragment_us -= disallow_window_us[other_id]; + // halving + allow_fragment_us /= 2; + if (allow_window_us[other_id] < allow_fragment_us) + allow_fragment_us = allow_window_us[other_id]; + } + + if (allow_fragment_us <= 0.0) { + *pstate_admissible = false; + break; + } + } + + if (*pstate_admissible) + return; + } + + /** + * Schedulable Case 3 - Nesting frame times. + * Passing Conditions: + * 1. Total sum of disallow windows across all groups is less than the FAMS maximum scheduling latency budget. + * 2. Every group's frame time can be fully contained by another group's allow window recursively, like nesting dolls. + * 3. The nested groups must not have DRR enabled and active. + */ + if (sum_of_disallow_windows_us < max_allow_delay_us) { + *pstate_admissible = true; + /* Bubble sort group IDs by frame time, from highest to lowest */ + for (cur_id = 0; cur_id < timing_group_count; cur_id++) + sorted[cur_id] = cur_id; + for (cur_id = 0; cur_id < timing_group_count; cur_id++) { + double cur_frame_time_us = allow_window_us[cur_id] + disallow_window_us[cur_id]; + for (other_id = cur_id + 1; other_id < timing_group_count; other_id++) { + double other_frame_time_us = allow_window_us[other_id] + disallow_window_us[other_id]; + if (cur_frame_time_us < other_frame_time_us) + swap(sorted[cur_id], sorted[other_id]); + } + } + /* Check nesting */ + for (cur_id = 0; cur_id < timing_group_count - 1; cur_id++) { + other_id = cur_id + 1; + if (allow_window_us[sorted[cur_id]] < allow_window_us[sorted[other_id]] + disallow_window_us[sorted[other_id]]) { + *pstate_admissible = false; + break; + } + } + + /* Starting from the first nested group, check DRR support */ + for (cur_id = 1; cur_id < timing_group_count; cur_id++) + if (is_drr[sorted[cur_id]]) { + *pstate_admissible = false; + break; + } + + if (*pstate_admissible) + return; + } + + /** + * Schedulable Case 4 - Non-harmonic phase drifting. + * Passing Conditions: + * 1. Applicable only for 2 timing groups. + * 2. The small frame time does not perfectly align with the large frame time, so the allow and disallow + * windows drift in and out of phase across frames, providing opportunities for p-state changes. + * 3. The delay to recover from a worst-case phase shift to a common allow window is less than the FAMS + * maximum scheduling latency budget. + * 4. The drift per frame is smaller than the combined allow window (p-state can complete within the window + * as it drifts through). + */ + if (timing_group_count == 2 + && !((1 << pstate_method[0] | 1 << pstate_method[1]) & PMO_FW_STRATEGY_MASK)) { // neither is FW strategy + int small_group_id = 0; + int large_group_id = 1; + double shift_per_frame = 0.0; + double max_shift_us = 0.0; + double max_disallow_window_us = 0.0; + + *pstate_admissible = true; + if (allow_window_us[small_group_id] + disallow_window_us[small_group_id] + > allow_window_us[large_group_id] + disallow_window_us[large_group_id]) + swap(small_group_id, large_group_id); + + shift_per_frame = math_mod((allow_window_us[large_group_id] + disallow_window_us[large_group_id]), + (allow_window_us[small_group_id] + disallow_window_us[small_group_id])); + + max_shift_us = disallow_window_us[large_group_id] - allow_window_us[small_group_id]; + max_disallow_window_us = max_shift_us / shift_per_frame * (allow_window_us[large_group_id] + disallow_window_us[large_group_id]); + + if (shift_per_frame == 0.0) + /* Perfectly aligned, no drifting */ + *pstate_admissible = false; + if (shift_per_frame >= sum_of_allow_windows_us) + /* Drifting, but the shift per frame exceeds the total allow window; no opportunity for p-state change */ + *pstate_admissible = false; + + if (max_disallow_window_us >= max_allow_delay_us) + /** + * The delay to recover from a worst-case phase shift to a common allow window exceeds the FAMS + * maximum scheduling latency budget. + */ + *pstate_admissible = false; + + if (*pstate_admissible) + return; + } + + return; +} \ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.h new file mode 100644 index 000000000000..41be61907e1a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN6_CALCS_DCHUB_H__ +#define __DML2_CORE_DCN6_CALCS_DCHUB_H__ +#include "dml2_internal_shared_types.h" + +unsigned int dcn6_calculate_max_vstartup( + bool ptoi_supported, + unsigned int vblank_nom_default_us, + const struct dml2_timing_cfg *timing, + enum dml2_uclk_pstate_change_strategy pstate_strategy, + double write_back_delay_us, + unsigned int svp_lines); +void dcn6_calculate_alternate_params(struct dml2_core_calcs_calculate_alternate_params *p); +void dcn6_calculate_alternate_svp_lines(struct dml2_core_calcs_calculate_alternate_svp_lines *p); +void dcn6_calculate_alternate_lead_lines(struct dml2_core_calcs_calculate_alternate_lead_lines *p); + +void dcn6_calculate_flip_schedule( + struct dml2_core_internal_scratch *s, + bool iflip_enable, + bool ihostvm_enable, + bool iffbm_enable, + double HostVMInefficiencyFactor, + double Tvm_trips_flip, + double Tr0_trips_flip, + double Tvm_trips_flip_rounded, + double Tr0_trips_flip_rounded, + bool GPUVMEnable, + double vm_bytes, // vm_bytes + double DPTEBytesPerRow, // dpte_row_bytes + enum dml2_source_format_class SourcePixelFormat, + double LineTime, + double VRatio, + double VRatioChroma, + double Tno_bw_flip, + unsigned int dpte_row_height, + unsigned int dpte_row_height_chroma, + unsigned int max_flip_time_us, + unsigned int max_flip_time_lines, + unsigned int meta_row_height, + unsigned int meta_row_height_chroma, + + // Output + double *dst_y_per_vm_flip, + double *dst_y_per_row_flip, + double *final_flip_bw, + bool *ImmediateFlipSupportedForPipe); + +void dcn6_get_pipe_regs(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_dchub_per_pipe_register_set *out, int pipe_index, const struct dml2_utm_soc_bb *utm_soc_bb, + struct dml2_core_internal_scratch *s); + +void dcn6_calculate_watermarks_and_dram_speed_change_support( + struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p); + +void dcn6_calculate_stutter_efficiency(struct dml2_core_internal_scratch *scratch, + struct dml2_core_calcs_CalculateStutterEfficiency_params *p); + +void dcn6_get_watermarks(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, const struct dml2_utm_soc_bb *utm_soc_bb, struct dml2_dchub_watermark_regs *out); + +void dcn6_calculate_excess_vactive_bandwidth_required( + const struct dml2_display_cfg *display_cfg, + unsigned int bytes_required_l[dml2_pstate_type_count][DML2_MAX_PLANES], + unsigned int bytes_required_c[dml2_pstate_type_count][DML2_MAX_PLANES], + /* outputs */ + double excess_vactive_fill_bw_l[], + double excess_vactive_fill_bw_c[]); + +void dcn6_calculate_pstate_schedule_windows( + int num_active_planes, + const unsigned int v_blank_start[DML2_MAX_PLANES], + const unsigned int v_blank_end[DML2_MAX_PLANES], + const double otg_vline_time_us[DML2_MAX_PLANES], + const double det_fill_delay_us[DML2_MAX_PLANES], + const double reserved_vblank_us[DML2_MAX_PLANES], + const double blackout_us, + // Outputs + double allow_start_us[DML2_MAX_PLANES], + double allow_end_us[DML2_MAX_PLANES]); + +void dcn6_calculate_pstate_schedule_admissibility( + uint32_t num_active_planes, + double max_allow_delay_us, + double min_allow_width_us, + const uint32_t timing_group_id[DML2_MAX_PLANES], + uint32_t timing_group_count, + const double frame_time_us[DML2_MAX_PLANES], + const double allow_start_us[DML2_MAX_PLANES], + const double allow_end_us[DML2_MAX_PLANES], + const enum dml2_pstate_method pstate_method[DML2_MAX_PLANES], + const bool drr_enabled[DML2_MAX_DCN_PIPES], + // Output + double allow_window_us[DML2_MAX_DCN_PIPES], + double disallow_window_us[DML2_MAX_DCN_PIPES], + bool *pstate_admissible); + +#endif /* __DML2_CORE_DCN6_CALCS_DCHUB_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.c new file mode 100644 index 000000000000..23fd6edeb0cf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_core_dcn6_funcs_initialize.h" +#include "dml2_debug.h" + +struct dml2_core_ip_params core_dcn6_ip_caps_base = { + // currently copied from DCN4 + .vblank_nom_default_us = 668, + .remote_iommu_outstanding_translations = 512, + .rob_buffer_size_kbytes = 192, + .config_return_buffer_size_in_kbytes = 2112, + .config_return_buffer_segment_size_in_kbytes = 64, + .compressed_buffer_segment_size_in_kbytes = 64, + .dpte_buffer_size_in_pte_reqs_luma = 138, + .dpte_buffer_size_in_pte_reqs_chroma = 138, + .pixel_chunk_size_kbytes = 8, + .alpha_pixel_chunk_size_kbytes = 4, + .min_pixel_chunk_size_bytes = 1024, + .writeback_chunk_size_kbytes = 8, + .line_buffer_size_bits = 937536, + .max_line_buffer_lines = 32, + .writeback_interface_buffer_size_kbytes = 90, + //Number of pipes after DCN Pipe harvesting + .max_num_dpp = 4, + .max_num_otg = 4, + .max_num_wb = 1, + .zero_size_buffer_entries = 512, + .compbuf_reserved_space_zs = 64, + .dcc_meta_buffer_size_bytes = 6272, + .meta_chunk_size_kbytes = 2, + .min_meta_chunk_size_bytes = 256, + .max_dchub_pscl_bw_pix_per_clk = 4, + .max_pscl_lb_bw_pix_per_clk = 2, + .max_lb_vscl_bw_pix_per_clk = 4, + .max_vscl_hscl_bw_pix_per_clk = 4, + .max_hscl_ratio = 6, + .max_vscl_ratio = 6, + .max_hscl_taps = 8, + .max_vscl_taps = 8, + .dispclk_ramp_margin_percent = 1, + .dppclk_delay_subtotal = 41, + .dppclk_delay_scl = 50, + .dppclk_delay_scl_lb_only = 16, + .dppclk_delay_cnvc_formatter = 28, + .dppclk_delay_cnvc_cursor = 2, + .cursor_buffer_size = 24, + .cursor_chunk_size = 2, + .dispclk_delay_subtotal = 135, + .max_inter_dcn_tile_repeaters = 8, + .writeback_max_hscl_ratio = 1, + .writeback_max_vscl_ratio = 1, + .writeback_min_hscl_ratio = 1, + .writeback_min_vscl_ratio = 1, + .writeback_max_hscl_taps = 1, + .writeback_max_vscl_taps = 1, + .writeback_line_buffer_buffer_size = 0, + .odm_combine_support_mask = (1 << dml2_odm_mode_auto) | + (1 << dml2_odm_mode_bypass) | + (1 << dml2_odm_mode_combine_2to1) | + (1 << dml2_odm_mode_combine_3to1) | + (1 << dml2_odm_mode_combine_4to1) | + (1 << dml2_odm_mode_split_1to2) | + (1 << dml2_odm_mode_mso_1to2) | + (1 << dml2_odm_mode_mso_1to4), + .num_dsc = 4, + .maximum_dsc_slices_per_pipe = 8, + .maximum_dsc_bits_per_component = 12, + .maximum_pixels_per_line_per_dsc_unit = 5760, + .dsc422_native_support = true, + .dcc_supported = true, + .ptoi_supported = false, + + .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, + .max_flip_time_us = 80, + .max_flip_time_lines = 32, + .words_per_channel = 16, + .alt_chan_fw_delay_us = 955, // Overestimate for now, can reduce later: sum of worst case throttle, programming, scheduling and contention delays +}; + +static void patch_ip_caps_with_explicit_ip_params(struct dml2_ip_capabilities *ip_caps, const struct dml2_core_ip_params *ip_params) +{ + ip_caps->pipe_count = ip_params->max_num_dpp; + ip_caps->otg_count = ip_params->max_num_otg; + ip_caps->TDLUT_33cube_count = ip_params->TDLUT_33cube_count; + ip_caps->num_dsc = ip_params->num_dsc; + ip_caps->max_num_dp2p0_streams = ip_params->max_num_dp2p0_streams; + ip_caps->max_num_dp2p0_outputs = ip_params->max_num_dp2p0_outputs; + ip_caps->max_num_hdmi_frl_outputs = ip_params->max_num_hdmi_frl_outputs; + ip_caps->max_num_wb = ip_params->max_num_wb; + ip_caps->rob_buffer_size_kbytes = ip_params->rob_buffer_size_kbytes; + ip_caps->config_return_buffer_size_in_kbytes = ip_params->config_return_buffer_size_in_kbytes; + ip_caps->config_return_buffer_segment_size_in_kbytes = ip_params->config_return_buffer_segment_size_in_kbytes; + ip_caps->meta_fifo_size_in_kentries = ip_params->meta_fifo_size_in_kentries; + ip_caps->compressed_buffer_segment_size_in_kbytes = ip_params->compressed_buffer_segment_size_in_kbytes; + ip_caps->cursor_buffer_size = ip_params->cursor_buffer_size; + ip_caps->max_flip_time_us = ip_params->max_flip_time_us; + ip_caps->max_flip_time_lines = ip_params->max_flip_time_lines; + ip_caps->hostvm_mode = ip_params->hostvm_mode; + ip_caps->vblank_nom_default_us = ip_params->vblank_nom_default_us; + +} + +static void patch_ip_params_with_ip_caps(struct dml2_core_ip_params *ip_params, const struct dml2_ip_capabilities *ip_caps) +{ + ip_params->max_num_dpp = ip_caps->pipe_count; + ip_params->max_num_otg = ip_caps->otg_count; + ip_params->TDLUT_33cube_count = ip_caps->TDLUT_33cube_count; + ip_params->num_dsc = ip_caps->num_dsc; + ip_params->max_num_dp2p0_streams = ip_caps->max_num_dp2p0_streams; + ip_params->max_num_dp2p0_outputs = ip_caps->max_num_dp2p0_outputs; + ip_params->max_num_hdmi_frl_outputs = ip_caps->max_num_hdmi_frl_outputs; + ip_params->max_num_wb = ip_caps->max_num_wb; + ip_params->rob_buffer_size_kbytes = ip_caps->rob_buffer_size_kbytes; + ip_params->config_return_buffer_size_in_kbytes = ip_caps->config_return_buffer_size_in_kbytes; + ip_params->config_return_buffer_segment_size_in_kbytes = ip_caps->config_return_buffer_segment_size_in_kbytes; + ip_params->meta_fifo_size_in_kentries = ip_caps->meta_fifo_size_in_kentries; + ip_params->compressed_buffer_segment_size_in_kbytes = ip_caps->compressed_buffer_segment_size_in_kbytes; + ip_params->cursor_buffer_size = ip_caps->cursor_buffer_size; + ip_params->max_flip_time_us = ip_caps->max_flip_time_us; + ip_params->max_flip_time_lines = ip_caps->max_flip_time_lines; + ip_params->hostvm_mode = ip_caps->hostvm_mode; + ip_params->alt_chan_fw_delay_us = ip_caps->fams2.scheduling_delay_us + + ip_caps->fams2.subvp_programming_delay_us + + ip_caps->fams2.subvp_df_throttle_delay_us + + (ip_caps->fams2.vertical_interrupt_ack_delay_us + + ip_caps->fams2.drr_programming_delay_us > ip_caps->fams2.allow_programming_delay_us ? + ip_caps->fams2.drr_programming_delay_us : ip_caps->fams2.allow_programming_delay_us) * (ip_caps->otg_count - 1); + ip_params->dcn_mrq_present = ip_caps->dcn_mrq_present; + ip_params->fams2_max_allow_delay_us = ip_caps->fams2.max_allow_delay_us; + ip_params->fams2_min_allow_width_us = ip_caps->fams2.min_allow_width_us; + ip_params->ppt_max_allow_delay_us = ip_caps->ppt_max_allow_delay_us; + ip_params->temp_read_max_allow_delay_us = ip_caps->temp_read_max_allow_delay_us; +} + +bool dml2_core_dcn6_funcs_initialize(struct dml2_core_initialize_in_out *in_out) +{ + struct dml2_core_instance *core = in_out->instance; + + DML_LOG_COMP_IF_ENTER(); + if (in_out->explicit_ip_bb && in_out->explicit_ip_bb_size > 0) { + memcpy(&core->clean_me_up.mode_lib.ip, in_out->explicit_ip_bb, in_out->explicit_ip_bb_size); + patch_ip_caps_with_explicit_ip_params(in_out->ip_caps, in_out->explicit_ip_bb); + } else { + memcpy(&core->clean_me_up.mode_lib.ip, &core_dcn6_ip_caps_base, sizeof(struct dml2_core_ip_params)); + patch_ip_params_with_ip_caps(&core->clean_me_up.mode_lib.ip, in_out->ip_caps); + + core->clean_me_up.mode_lib.ip.imall_supported = false; + } + + memcpy(&core->clean_me_up.mode_lib.ip_caps, in_out->ip_caps, sizeof(struct dml2_ip_capabilities)); + core->utm_soc_bb = in_out->utm_soc_bb; + core->clock_adjuster = in_out->clock_adjuster; + + DML_LOG_DEBUG("%s exit with true\n", __func__); + DML_LOG_COMP_IF_EXIT(); + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.h new file mode 100644 index 000000000000..86459cb7553d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_initialize.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN6_FUNCS_INITIALIZE_H__ +#define __DML2_CORE_DCN6_FUNCS_INITIALIZE_H__ +#include "dml2_internal_shared_types.h" + +bool dml2_core_dcn6_funcs_initialize(struct dml2_core_initialize_in_out *in_out); + + +#endif /* __DML2_CORE_DCN6_FUNCS_INITIALIZE_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c new file mode 100644 index 000000000000..39f0cd14ca3e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c @@ -0,0 +1,1389 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#include "dml2_core_dcn6_funcs_mode_programming.h" +#include "dml2_core_dcn5_calcs_dchub.h" +#include "dml2_core_dcn6_calcs_dchub.h" +#include "dml2_core_dcn5_calcs_display_pipe.h" +#include "dml2_core_utils.h" + +static void dcn6_mp_populate_odm_mode(const struct dml2_display_solution *solution, + struct dml2_core_internal_mode_program *outputs) +{ + unsigned int k; + const struct core_display_cfg_support_info *cfg_support_info = + &solution->validation_result.mode_support.cfg_support_info; + const struct dml2_display_cfg *display_cfg = &solution->dispcfg; + unsigned int stream_index; + + for (k = 0; k < solution->dispcfg.num_planes; ++k) { + stream_index = display_cfg->plane_descriptors[k].stream_index; + DML_ASSERT(cfg_support_info->stream_support_info[stream_index].odms_used <= 4); + DML_ASSERT(cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 4 + || cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 2 + || cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 1); + if (cfg_support_info->stream_support_info[stream_index].odms_used > 1) + DML_ASSERT(cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 1); + + switch (cfg_support_info->stream_support_info[stream_index].odms_used) { + case (4): + outputs->ODMMode[k] = dml2_odm_mode_combine_4to1; + break; + case (3): + outputs->ODMMode[k] = dml2_odm_mode_combine_3to1; + break; + case (2): + outputs->ODMMode[k] = dml2_odm_mode_combine_2to1; + break; + default: + if (cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 4) + outputs->ODMMode[k] = dml2_odm_mode_mso_1to4; + else if (cfg_support_info->stream_support_info[stream_index].num_odm_output_segments == 2) + outputs->ODMMode[k] = dml2_odm_mode_mso_1to2; + else + outputs->ODMMode[k] = dml2_odm_mode_bypass; + + break; + } + } +} + +static void dcn6_mp_calculate_dcc_configurations(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + unsigned int k; + + for (k = 0; k < display_cfg->num_planes; ++k) { + DML_LOG_VERBOSE("DML::%s: Calculate DCC configuration for surface k=%u\n", __func__, k); + dcn5_calculate_dcc_configuration( + display_cfg->plane_descriptors[k].surface.dcc.enable, + display_cfg->overrides.dcc_programming_assumes_scan_direction_unknown, + display_cfg->plane_descriptors[k].pixel_format, + display_cfg->plane_descriptors[k].surface.plane0.width, + display_cfg->plane_descriptors[k].surface.plane1.width, + display_cfg->plane_descriptors[k].surface.plane0.height, + display_cfg->plane_descriptors[k].surface.plane1.height, + inputs->NomDETInKByte, + inputs->Read256BlockHeightY[k], + inputs->Read256BlockHeightC[k], + display_cfg->plane_descriptors[k].surface.tiling, + inputs->BytePerPixelY[k], + inputs->BytePerPixelC[k], + inputs->BytePerPixelInDETY[k], + inputs->BytePerPixelInDETC[k], + display_cfg->plane_descriptors[k].composition.rotation_angle, + + /* Output */ + &outputs->RequestLuma[k], + &outputs->RequestChroma[k], + &outputs->DCCYMaxUncompressedBlock[k], + &outputs->DCCCMaxUncompressedBlock[k], + &outputs->DCCYMaxCompressedBlock[k], + &outputs->DCCCMaxCompressedBlock[k], + &outputs->DCCYIndependentBlock[k], + &outputs->DCCCIndependentBlock[k]); + } +} + +static void dcn6_mp_calculate_pixel_delivery_times(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + + //Display Pipeline Delivery Time in Prefetch, Groups + dcn5_calculate_pixel_delivery_times( + display_cfg, + inputs->NoOfDPP, + display_cfg->num_planes, + inputs->VRatioPrefetchY, + inputs->VRatioPrefetchC, + inputs->swath_width_luma_ub, + inputs->swath_width_chroma_ub, + inputs->PSCL_THROUGHPUT, + inputs->PSCL_THROUGHPUT_CHROMA, + inputs->Dppclk, + inputs->DCFCLKDeepSleep, + inputs->BytePerPixelY, + inputs->BytePerPixelC, + inputs->req_per_swath_ub_l, + inputs->req_per_swath_ub_c, + + /* Output */ + outputs->DisplayPipeLineDeliveryTimeLuma, + outputs->DisplayPipeLineDeliveryTimeChroma, + outputs->DisplayPipeLineDeliveryTimeLumaPrefetch, + outputs->DisplayPipeLineDeliveryTimeChromaPrefetch, + outputs->DisplayPipeRequestDeliveryTimeLuma, + outputs->DisplayPipeRequestDeliveryTimeChroma, + outputs->DisplayPipeRequestDeliveryTimeLumaPrefetch, + outputs->DisplayPipeRequestDeliveryTimeChromaPrefetch); +} + +static void dcn6_mp_calculate_meta_and_pte_times(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_scratch *func_params = ctx->func_params; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + struct dml2_core_shared_CalculateMetaAndPTETimes_params *p = &func_params->CalculateMetaAndPTETimes_params; + + p->scratch = func_params; + p->display_cfg = display_cfg; + p->NumberOfActiveSurfaces = display_cfg->num_planes; + p->use_one_row_for_frame = inputs->use_one_row_for_frame; + p->dst_y_per_row_vblank = inputs->dst_y_per_row_vblank; + p->dst_y_per_row_flip = inputs->dst_y_per_row_flip; + p->BytePerPixelY = inputs->BytePerPixelY; + p->BytePerPixelC = inputs->BytePerPixelC; + p->dpte_row_height = inputs->dpte_row_height; + p->dpte_row_height_chroma = inputs->dpte_row_height_chroma; + p->dpte_group_bytes = inputs->dpte_group_bytes; + p->PTERequestSizeY = inputs->PTERequestSizeY; + p->PTERequestSizeC = inputs->PTERequestSizeC; + p->PixelPTEReqWidthY = inputs->PixelPTEReqWidthY; + p->PixelPTEReqHeightY = inputs->PixelPTEReqHeightY; + p->PixelPTEReqWidthC = inputs->PixelPTEReqWidthC; + p->PixelPTEReqHeightC = inputs->PixelPTEReqHeightC; + p->dpte_row_width_luma_ub = inputs->dpte_row_width_luma_ub; + p->dpte_row_width_chroma_ub = inputs->dpte_row_width_chroma_ub; + p->tdlut_groups_per_2row_ub = inputs->tdlut_groups_per_2row_ub; + p->mrq_present = ip->dcn_mrq_present; + + p->MetaChunkSize = ip->meta_chunk_size_kbytes; + p->MinMetaChunkSizeBytes = ip->min_meta_chunk_size_bytes; + p->meta_row_width = inputs->meta_row_width; + p->meta_row_width_chroma = inputs->meta_row_width_chroma; + p->meta_row_height = inputs->meta_row_height; + p->meta_row_height_chroma = inputs->meta_row_height_chroma; + p->meta_req_width = inputs->meta_req_width; + p->meta_req_width_chroma = inputs->meta_req_width_chroma; + p->meta_req_height = inputs->meta_req_height; + p->meta_req_height_chroma = inputs->meta_req_height_chroma; + + p->time_per_tdlut_group = outputs->time_per_tdlut_group; + p->DST_Y_PER_PTE_ROW_NOM_L = outputs->DST_Y_PER_PTE_ROW_NOM_L; + p->DST_Y_PER_PTE_ROW_NOM_C = outputs->DST_Y_PER_PTE_ROW_NOM_C; + p->time_per_pte_group_nom_luma = outputs->time_per_pte_group_nom_luma; + p->time_per_pte_group_vblank_luma = outputs->time_per_pte_group_vblank_luma; + p->time_per_pte_group_flip_luma = outputs->time_per_pte_group_flip_luma; + p->time_per_pte_group_nom_chroma = outputs->time_per_pte_group_nom_chroma; + p->time_per_pte_group_vblank_chroma = outputs->time_per_pte_group_vblank_chroma; + p->time_per_pte_group_flip_chroma = outputs->time_per_pte_group_flip_chroma; + p->DST_Y_PER_META_ROW_NOM_L = outputs->DST_Y_PER_META_ROW_NOM_L; + p->DST_Y_PER_META_ROW_NOM_C = outputs->DST_Y_PER_META_ROW_NOM_C; + p->TimePerMetaChunkNominal = outputs->TimePerMetaChunkNominal; + p->TimePerChromaMetaChunkNominal = outputs->TimePerChromaMetaChunkNominal; + p->TimePerMetaChunkVBlank = outputs->TimePerMetaChunkVBlank; + p->TimePerChromaMetaChunkVBlank = outputs->TimePerChromaMetaChunkVBlank; + p->TimePerMetaChunkFlip = outputs->TimePerMetaChunkFlip; + p->TimePerChromaMetaChunkFlip = outputs->TimePerChromaMetaChunkFlip; + + dcn5_calculate_meta_and_pte_times(p); +} + +static void dcn6_mp_calculate_vm_group_and_request_times(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + + dcn5_calculate_vm_group_and_request_times( + display_cfg, + display_cfg->num_planes, + inputs->BytePerPixelC, + inputs->dst_y_per_vm_vblank, + inputs->dst_y_per_vm_flip, + inputs->dpte_row_width_luma_ub, + inputs->dpte_row_width_chroma_ub, + inputs->vm_group_bytes, + inputs->dpde0_bytes_per_frame_ub_l, + inputs->dpde0_bytes_per_frame_ub_c, + inputs->tdlut_pte_bytes_per_frame, + inputs->meta_pte_bytes_per_frame_ub_l, + inputs->meta_pte_bytes_per_frame_ub_c, + ip->dcn_mrq_present, + + /* Output */ + outputs->TimePerVMGroupVBlank, + outputs->TimePerVMGroupFlip, + outputs->TimePerVMRequestVBlank, + outputs->TimePerVMRequestFlip); +} + +static void dcn6_mp_calculate_vstartup_adjustment(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + unsigned int k; + bool isInterlaceTiming; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + double Tvstartup_margin; + double dlg_vblank_start; + double LSetup; + double blank_lines_remaining; + + // VStartup Adjustment + for (k = 0; k < display_cfg->num_planes; ++k) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + isInterlaceTiming = (stream->timing.interlaced && !ip->ptoi_supported); + + outputs->MinTTUVBlank[k] = inputs->TWait[k] + inputs->ExtraLatency; + if (!display_cfg->plane_descriptors[k].dynamic_meta_data.enable) + outputs->MinTTUVBlank[k] = inputs->TCalc + outputs->MinTTUVBlank[k]; + + DML_LOG_VERBOSE("DML::%s: k=%u, MinTTUVBlank = %f (before vstartup margin)\n", + __func__, k, outputs->MinTTUVBlank[k]); + Tvstartup_margin = (inputs->MaxVStartupLines[k] - inputs->VStartupMin[k]) + * stream->timing.h_total + / ((double)stream->timing.pixel_clock_khz / 1000); + outputs->MinTTUVBlank[k] = outputs->MinTTUVBlank[k] + Tvstartup_margin; + + DML_LOG_VERBOSE("DML::%s: k=%u, Tvstartup_margin = %f\n", __func__, k, Tvstartup_margin); + DML_LOG_VERBOSE("DML::%s: k=%u, MaxVStartupLines = %u\n", __func__, k, inputs->MaxVStartupLines[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, MinTTUVBlank = %f\n", __func__, k, outputs->MinTTUVBlank[k]); + + outputs->Tdmdl[k] = inputs->Tdmdl_raw[k] + Tvstartup_margin; + if (plane->dynamic_meta_data.enable && ip->dynamic_metadata_vm_enabled) + outputs->Tdmdl_vm[k] = inputs->Tdmdl_vm_raw[k] + Tvstartup_margin; + else + outputs->Tdmdl_vm[k] = inputs->Tdmdl_vm_raw[k]; + + // The actual positioning of the vstartup + outputs->VStartup[k] = (isInterlaceTiming ? + (2 * inputs->MaxVStartupLines[k]) : inputs->MaxVStartupLines[k]); + + dlg_vblank_start = + ((isInterlaceTiming ? + math_floor2((stream->timing.v_total - stream->timing.v_front_porch) / 2.0, 1.0) : + stream->timing.v_total) + - stream->timing.v_front_porch); + LSetup = math_floor2( + 4.0 * inputs->TSetup[k] + / ((double)stream->timing.h_total + / ((double)stream->timing.pixel_clock_khz / 1000)), + 1.0) / 4.0; + blank_lines_remaining = (stream->timing.v_total - stream->timing.v_active) - outputs->VStartup[k]; + + if (blank_lines_remaining < 0) { + DML_LOG_VERBOSE("ERROR: Vstartup is larger than vblank!?\n"); + blank_lines_remaining = 0; + DML_ASSERT(0); + } + outputs->MIN_DST_Y_NEXT_START[k] = dlg_vblank_start + blank_lines_remaining + LSetup; + + // debug only + if (((inputs->VUpdateOffsetPix[k] + inputs->VUpdateWidthPix[k] + inputs->VReadyOffsetPix[k]) / (double) stream->timing.h_total) + <= (isInterlaceTiming ? + math_floor2((stream->timing.v_total - stream->timing.v_active - stream->timing.v_front_porch - outputs->VStartup[k]) / 2.0, 1.0) : + (int)(stream->timing.v_total - stream->timing.v_active - stream->timing.v_front_porch - outputs->VStartup[k]))) { + outputs->VREADY_AT_OR_AFTER_VSYNC[k] = true; + } else { + outputs->VREADY_AT_OR_AFTER_VSYNC[k] = false; + } + DML_LOG_VERBOSE("DML::%s: k=%u, VStartup = %u (max)\n", __func__, k, outputs->VStartup[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VStartupMin = %u (max)\n", __func__, k, inputs->VStartupMin[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VUpdateOffsetPix = %u\n", __func__, k, inputs->VUpdateOffsetPix[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VUpdateWidthPix = %u\n", __func__, k, inputs->VUpdateWidthPix[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VReadyOffsetPix = %u\n", __func__, k, inputs->VReadyOffsetPix[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, HTotal = %lu\n", __func__, k, stream->timing.h_total); + DML_LOG_VERBOSE("DML::%s: k=%u, VTotal = %lu\n", __func__, k, stream->timing.v_total); + DML_LOG_VERBOSE("DML::%s: k=%u, VActive = %lu\n", __func__, k, stream->timing.v_active); + DML_LOG_VERBOSE("DML::%s: k=%u, VFrontPorch = %lu\n", __func__, k, stream->timing.v_front_porch); + DML_LOG_VERBOSE("DML::%s: k=%u, TSetup = %f\n", __func__, k, inputs->TSetup[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, MIN_DST_Y_NEXT_START = %f\n", + __func__, k, outputs->MIN_DST_Y_NEXT_START[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, VREADY_AT_OR_AFTER_VSYNC = %u\n", + __func__, k, (unsigned char)outputs->VREADY_AT_OR_AFTER_VSYNC[k]); + } +} + +static void dcn6_mp_calculate_max_bandwidth_used(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + unsigned int j, k; + const struct dml2_stream_parameters *stream; + double WRBandwidth; + + outputs->TotalWRBandwidth = 0; + for (k = 0; k < display_cfg->num_streams; ++k) { + stream = &display_cfg->stream_descriptors[k]; + + for (j = 0; j < stream->writeback.active_writebacks_per_stream; ++j) { + double writeback_bytes_per_pixel; + if (stream->writeback.writeback_stream[j].pixel_format == dml2_444_64) + writeback_bytes_per_pixel = 8.0; + else if (stream->writeback.writeback_stream[j].pixel_format == dml2_444_32) + writeback_bytes_per_pixel = 4.0; + else if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8) + writeback_bytes_per_pixel = 1.5; + else if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) + writeback_bytes_per_pixel = 3.0; + else if (stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_8) + writeback_bytes_per_pixel = 2.0; + else if (stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) + writeback_bytes_per_pixel = 4.0; + else + writeback_bytes_per_pixel = 0.0; + WRBandwidth = stream->writeback.writeback_stream[j].output_height + * stream->writeback.writeback_stream[j].output_width + / (stream->timing.h_total + * stream->writeback.writeback_stream[j].input_height + / ((double)stream->timing.pixel_clock_khz / 1000)) + * writeback_bytes_per_pixel; + outputs->TotalWRBandwidth = outputs->TotalWRBandwidth + WRBandwidth; + } + } + + outputs->TotalDataReadBandwidth = 0; + for (k = 0; k < display_cfg->num_planes; ++k) { + outputs->TotalDataReadBandwidth = + outputs->TotalDataReadBandwidth + + inputs->vactive_sw_bw_l[k] + + inputs->vactive_sw_bw_c[k]; + DML_LOG_VERBOSE("DML::%s: k=%u, TotalDataReadBandwidth = %f\n", + __func__, k, outputs->TotalDataReadBandwidth); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_l = %f\n", __func__, k, inputs->vactive_sw_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_c = %f\n", __func__, k, inputs->vactive_sw_bw_c[k]); + } +} + +static void dcn6_mp_calculate_stutter_efficiency(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_scratch *func_params = ctx->func_params; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + struct dml2_core_calcs_CalculateStutterEfficiency_params *p = + &func_params->CalculateStutterEfficiency_params; + + p->display_cfg = display_cfg; + p->CompressedBufferSizeInkByte = inputs->CompressedBufferSizeInkByte; + p->UnboundedRequestEnabled = inputs->UnboundedRequestEnabled; + p->MetaFIFOSizeInKEntries = ip->meta_fifo_size_in_kentries; + p->ZeroSizeBufferEntries = ip->zero_size_buffer_entries; + p->PixelChunkSizeInKByte = ip->pixel_chunk_size_kbytes; + p->NumberOfActiveSurfaces = display_cfg->num_planes; + p->ROBBufferSizeInKByte = ip->rob_buffer_size_kbytes; + p->TotalDataReadBandwidth = inputs->TotalDataReadBandwidth; + p->DCFCLK = inputs->Dcfclk; + p->ReturnBW = **inputs->urg_bandwidth_available; + p->CompbufReservedSpace64B = inputs->compbuf_reserved_space_64b; + p->CompbufReservedSpaceZs = ip->compbuf_reserved_space_zs; + p->SRExitTime = soc_bb->power_management_parameters.stutter_exit_latency_us; + p->SRExitTimeLowPower = soc_bb->power_management_parameters.low_power_stutter_exit_latency_us; + p->SRExitZ8Time = soc_bb->power_management_parameters.z8_stutter_exit_latency_us; + p->SynchronizeTimings = display_cfg->overrides.synchronize_timings; + p->StutterEnterPlusExitWatermark = inputs->Watermark.StutterEnterPlusExitWatermark; + p->LowPowerStutterEnterPlusExitWatermark = inputs->Watermark.LowPowerStutterEnterPlusExitWatermark; + p->Z8StutterEnterPlusExitWatermark = inputs->Watermark.Z8StutterEnterPlusExitWatermark; + p->ProgressiveToInterlaceUnitInOPP = ip->ptoi_supported; + p->MinTTUVBlank = inputs->MinTTUVBlank; + p->DPPPerSurface = inputs->NoOfDPP; + p->DETBufferSizeY = inputs->DETBufferSizeY; + p->BytePerPixelY = inputs->BytePerPixelY; + p->BytePerPixelDETY = inputs->BytePerPixelInDETY; + p->SwathWidthY = inputs->SwathWidthY; + p->SwathHeightY = inputs->SwathHeightY; + p->SwathHeightC = inputs->SwathHeightC; + p->BlockHeight256BytesY = inputs->Read256BlockHeightY; + p->BlockWidth256BytesY = inputs->Read256BlockWidthY; + p->BlockHeight256BytesC = inputs->Read256BlockHeightC; + p->BlockWidth256BytesC = inputs->Read256BlockWidthC; + p->DCCYMaxUncompressedBlock = inputs->DCCYMaxUncompressedBlock; + p->DCCCMaxUncompressedBlock = inputs->DCCCMaxUncompressedBlock; + p->ReadBandwidthSurfaceLuma = inputs->vactive_sw_bw_l; + p->ReadBandwidthSurfaceChroma = inputs->vactive_sw_bw_c; + p->dpte_row_bw = inputs->dpte_row_bw; + p->meta_row_bw = inputs->meta_row_bw; + p->rob_alloc_compressed = ip->dcn_mrq_present; + + // output + p->StutterEfficiencyNotIncludingVBlank = &outputs->StutterEfficiencyNotIncludingVBlank; + p->StutterEfficiency = &outputs->StutterEfficiency; + p->NumberOfStutterBurstsPerFrame = &outputs->NumberOfStutterBurstsPerFrame; + p->LowPowerStutterEfficiencyNotIncludingVBlank = &outputs->LowPowerStutterEfficiencyNotIncludingVBlank; + p->LowPowerStutterEfficiency = &outputs->LowPowerStutterEfficiency; + p->LowPowerNumberOfStutterBurstsPerFrame = &outputs->LowPowerNumberOfStutterBurstsPerFrame; + p->Z8StutterEfficiencyNotIncludingVBlank = &outputs->Z8StutterEfficiencyNotIncludingVBlank; + p->Z8StutterEfficiency = &outputs->Z8StutterEfficiency; + p->Z8NumberOfStutterBurstsPerFrame = &outputs->Z8NumberOfStutterBurstsPerFrame; + p->StutterPeriod = &outputs->StutterPeriod; + p->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE = &outputs->DCHUBBUB_ARB_CSTATE_MAX_CAP_MODE; + + // Stutter Efficiency + dcn6_calculate_stutter_efficiency(func_params, p); + +#ifdef __DML_VBA_ALLOW_DELTA__ + // Calculate z8 stutter eff assuming 0 reserved space + p->CompbufReservedSpace64B = 0; + p->CompbufReservedSpaceZs = 0; + + p->Z8StutterEfficiencyNotIncludingVBlank = &outputs->Z8StutterEfficiencyNotIncludingVBlankBestCase; + p->Z8StutterEfficiency = &outputs->Z8StutterEfficiencyBestCase; + p->Z8NumberOfStutterBurstsPerFrame = &outputs->Z8NumberOfStutterBurstsPerFrameBestCase; + p->StutterPeriod = &outputs->StutterPeriodBestCase; + + // Stutter Efficiency + dcn6_calculate_stutter_efficiency(func_params, p); +#else + outputs->Z8StutterEfficiencyNotIncludingVBlankBestCase = outputs->Z8StutterEfficiencyNotIncludingVBlank; + outputs->Z8StutterEfficiencyBestCase = outputs->Z8StutterEfficiency; + outputs->Z8NumberOfStutterBurstsPerFrameBestCase = outputs->Z8NumberOfStutterBurstsPerFrame; + outputs->StutterPeriodBestCase = outputs->StutterPeriod; +#endif +} + +static void dcn6_mp_calculate_fraction_of_urgent_bandwidth(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + (void)ctx; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + + outputs->FractionOfUrgentBandwidth = inputs->non_urg_bandwidth_required[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp] + / inputs->min_available_urgent_bandwidth_MBps; + + outputs->FractionOfUrgentBandwidthImmediateFlip = inputs->non_urg_bandwidth_required_flip[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp] + / inputs->min_available_urgent_bandwidth_MBps; + + DML_LOG_DEBUG_DOUBLE(outputs->FractionOfUrgentBandwidth); +} + +static void dcn6_mp_calculate_writeback_allow_clock_change_end_position(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + double line_time_us; + + for (k = 0; k < display_cfg->num_planes; ++k) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + + if (stream->writeback.active_writebacks_per_stream > 0) { + line_time_us = stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000); + outputs->WritebackAllowDRAMClockChangeEndPosition[k] = + math_max2(0, inputs->VStartupMin[k] * line_time_us + - inputs->Watermark.WritebackDRAMClockChangeWatermark); + outputs->WritebackAllowFCLKChangeEndPosition[k] = + math_max2(0, inputs->VStartupMin[k] * line_time_us + - inputs->Watermark.WritebackFCLKChangeWatermark); + } else { + outputs->WritebackAllowDRAMClockChangeEndPosition[k] = 0; + outputs->WritebackAllowFCLKChangeEndPosition[k] = 0; + } + } +} + +static void dcn6_mp_calculate_pstate_keepout_dst_lines(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + + dcn5_calculate_pstate_keepout_dst_lines(display_cfg, &inputs->Watermark, + outputs->pstate_keepout_dst_lines); +} + +static void dcn6_mp_calculate_dcfclk_deep_sleep_hysteresis(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_program *inputs = states; + struct dml2_core_internal_mode_program *outputs = states; + const struct dml2_sop_table *sop_table = &soc_bb->sop_table; + struct dml2_soc_operating_point max_sop; + double min_return_latency_in_DCFCLK_cycles = 0; + const long min_return_uclk_cycles = 83; + const long min_return_fclk_cycles = 75; + + sop_table->get_max_sop(sop_table, &max_sop); + min_return_latency_in_DCFCLK_cycles = + (min_return_uclk_cycles / (max_sop.uclk_khz / 1000.0) + + min_return_fclk_cycles / (max_sop.fclk_khz / 1000.0)) + * inputs->Dcfclk; + + outputs->min_return_latency_in_dcfclk = (unsigned int)min_return_latency_in_DCFCLK_cycles; + outputs->dcfclk_deep_sleep_hysteresis = (unsigned int)math_max2( + 32, + (double)ip->pixel_chunk_size_kbytes * 1024 * 3 / 4 / 64 + - min_return_latency_in_DCFCLK_cycles); +} + +static void dcn6_calculate_mode_programming(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_internal_mode_program *states) +{ + dcn6_mp_calculate_fraction_of_urgent_bandwidth(ctx, states); + + dcn6_mp_calculate_pstate_keepout_dst_lines(ctx, states); + + dcn6_mp_calculate_dcc_configurations(ctx, states); + + dcn6_mp_calculate_writeback_allow_clock_change_end_position(ctx, states); + + dcn6_mp_calculate_pixel_delivery_times(ctx, states); + + dcn6_mp_calculate_meta_and_pte_times(ctx, states); + + dcn6_mp_calculate_vm_group_and_request_times(ctx, states); + + dcn6_mp_calculate_vstartup_adjustment(ctx, states); + + dcn6_mp_calculate_max_bandwidth_used(ctx, states); + + dcn6_mp_calculate_stutter_efficiency(ctx, states); + + dcn6_mp_calculate_dcfclk_deep_sleep_hysteresis(ctx, states); + + DML_LOG_VERBOSE("DML::%s: --- END --- \n", __func__); +} + +static void dcn6_get_global_sync_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, union dml2_global_sync_programming *out, int pipe_index) +{ + out->dcn4x.vready_offset_pixels = mode_lib->mp.VReadyOffsetPix[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.vstartup_lines = mode_lib->mp.VStartup[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.vupdate_offset_pixels = mode_lib->mp.VUpdateOffsetPix[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.vupdate_vupdate_width_pixels = mode_lib->mp.VUpdateWidthPix[mode_lib->mp.pipe_plane[pipe_index]]; + out->dcn4x.pstate_keepout_start_lines = mode_lib->mp.pstate_keepout_dst_lines[mode_lib->mp.pipe_plane[pipe_index]]; +} + +static void dcn6_get_stream_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_per_stream_programming *out, int pipe_index) +{ + dcn6_get_global_sync_programming(mode_lib, &out->global_sync, pipe_index); +} + +/* 0 : 1 byte per pix + * 1 : 2 byte per pix + * 2 : 4 byte per pix + * 3 : 8 byte per pix + */ +static unsigned int get_element_size_idx(unsigned int byte_per_pix) +{ + unsigned int idx = 0; + + while (byte_per_pix > 1) { + byte_per_pix >>= 1; + idx++; + } + return idx; +} + +/* + * Swizzle mode values understandable by hardware. + * Some of sw_swizzle_mode values might not map to existing dml2_swizzle_mode values. + */ +enum sw_swizzle_mode { + SW_LINEAR = 0, + SW_256B_S = 1, // No dml2 equivalent + SW_256B_D = 2, // No dml2 equivalent + SW_256B_R_2D = 3, + SW_4KB_S = 5, // No dml2 equivalent + SW_4KB_D = 6, // No dml2 equivalent + SW_4KB_R_2D = 7, + SW_64KB_S = 9, + SW_64KB_D = 10, // No dml2 equivalent + SW_64KB_R_2D = 11, + SW_256KB_R_2D = 15, + SW_64KB_S_T = 17, + SW_64KB_D_T = 18, + SW_4KB_S_X = 21, // No dml2 equivalent + SW_4KB_D_X = 22, // No dml2 equivalent + SW_64KB_S_X = 25, + SW_64KB_D_X = 26, + SW_64KB_R_X = 27, + SW_VAR_R_X = 31, // No dml2 equivalent +}; + +/* + * This function maps dml2 swizzle mode used in dml to swizzle mode values + * understandable by hardware. It is required to prepare correct LSDMA copy command. + */ +static uint8_t dml2_swizzle_mode_to_sw_swizzle_mode(enum dml2_swizzle_mode tiling) +{ + uint8_t sw_swizzle_mode = 0; + + switch (tiling) { + case dml2_sw_linear: + case dml2_gfx11_sw_linear: + case dml2_sw_256b_2d: + sw_swizzle_mode = SW_256B_R_2D; + break; + case dml2_sw_4kb_2d: + sw_swizzle_mode = SW_4KB_R_2D; + break; + case dml2_sw_64kb_2d: + sw_swizzle_mode = SW_64KB_R_2D; + break; + case dml2_sw_256kb_2d: + case dml2_gfx11_sw_256kb_d_x: + case dml2_gfx11_sw_256kb_r_x: + sw_swizzle_mode = SW_256KB_R_2D; + break; + case dml2_gfx11_sw_64kb_d_t: + sw_swizzle_mode = SW_64KB_D_T; + break; + case dml2_gfx11_sw_64kb_r_x: + case dml2_gfx11_sw_64kb_d_x: + sw_swizzle_mode = SW_64KB_D_X; + break; + case dml2_gfx11_sw_64kb_d: + sw_swizzle_mode = SW_64KB_D; + break; + default: + sw_swizzle_mode = SW_LINEAR; + break; + } + + return sw_swizzle_mode; +} + +static void dcn6_populate_fams2_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming, + unsigned int main_stream_index) +{ + const struct dml2_uclk_pstate_params *uclk_params = &solution->uclk_pstate_params; + union dmub_cmd_fams2_config *fams2_base_programming = &programming->stream_programming[main_stream_index].fams2_base_params; + enum dml2_pstate_method pstate_method = programming->stream_programming[main_stream_index].uclk_pstate_method; + const struct dml2_display_cfg *disp_cfg = &solution->dispcfg; + const struct dml2_stream_parameters *stream_descriptor = &disp_cfg->stream_descriptors[main_stream_index]; + const struct dml2_plane_parameters *plane_descriptor; + const struct dml2_pstate_meta *stream_pstate_meta = &uclk_params->stream_pstate_meta[main_stream_index]; + + struct dmub_fams2_cmd_stream_static_base_state *base_programming = &fams2_base_programming->stream_v1.base; + union dmub_fams2_stream_static_sub_state_v2 *sub_programming = &programming->stream_programming[main_stream_index].fams2_sub_params_v2; + + unsigned int i, vready_offset, per_stream_plane_index = 0; + bool vertical_access; + + if (disp_cfg->overrides.all_streams_blanked) { + /* stream is blanked, so do nothing */ + return; + } + /* from display configuration */ + base_programming->htotal = (uint16_t)stream_descriptor->timing.h_total; + base_programming->vtotal = (uint16_t)stream_descriptor->timing.v_total; + base_programming->vblank_start = (uint16_t)(stream_pstate_meta->nom_vtotal - + stream_descriptor->timing.v_front_porch); + base_programming->vblank_end = (uint16_t)(stream_pstate_meta->nom_vtotal - + stream_descriptor->timing.v_front_porch - + stream_descriptor->timing.v_active); + base_programming->config.bits.is_drr = stream_descriptor->timing.drr_config.enabled; + + /* from meta */ + base_programming->otg_vline_time_ns = + (unsigned int)(stream_pstate_meta->otg_vline_time_us * 1000.0); + base_programming->scheduling_delay_otg_vlines = (uint8_t)stream_pstate_meta->scheduling_delay_otg_vlines; + base_programming->contention_delay_otg_vlines = (uint8_t)stream_pstate_meta->contention_delay_otg_vlines; + base_programming->vline_int_ack_delay_otg_vlines = (uint8_t)stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines; + base_programming->drr_keepout_otg_vline = (uint16_t)(stream_pstate_meta->nom_vtotal - + stream_descriptor->timing.v_front_porch - + stream_pstate_meta->method_drr.programming_delay_otg_vlines); + base_programming->allow_to_target_delay_otg_vlines = (uint8_t)stream_pstate_meta->allow_to_target_delay_otg_vlines; + base_programming->max_vtotal = (uint16_t)stream_pstate_meta->max_vtotal; + + /* from core */ + base_programming->config.bits.min_ttu_vblank_usable = true; + for (i = 0; i < disp_cfg->num_planes; i++) { + /* check if all planes support p-state in blank */ + if (disp_cfg->plane_descriptors[i].stream_index == main_stream_index && + mode_lib->mp.MinTTUVBlank[i] <= mode_lib->mp.Watermark.DRAMClockChangeWatermark) { + base_programming->config.bits.min_ttu_vblank_usable = false; + break; + } + } + + switch (pstate_method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + /* legacy vactive */ + base_programming->type = FAMS2_STREAM_TYPE_VACTIVE; + sub_programming->legacy.vactive_det_fill_delay_otg_vlines = + (uint8_t)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines; + sub_programming->legacy.disallow_time_us = + (uint32_t)(stream_pstate_meta->method_vactive.common.disallow_time_us); + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_vactive.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_vactive.common.allow_end_otg_vline; + base_programming->config.bits.clamp_vtotal_min = true; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + /* legacy vblank */ + base_programming->type = FAMS2_STREAM_TYPE_VBLANK; + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_vblank.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_vblank.common.allow_end_otg_vline; + base_programming->config.bits.clamp_vtotal_min = true; + sub_programming->legacy.disallow_time_us = + (uint32_t)(stream_pstate_meta->method_vblank.common.disallow_time_us); + break; + case dml2_pstate_method_fw_drr: + /* drr */ + base_programming->type = FAMS2_STREAM_TYPE_DRR; + sub_programming->drr.programming_delay_otg_vlines = + (uint8_t)stream_pstate_meta->method_drr.programming_delay_otg_vlines; + sub_programming->drr.nom_stretched_vtotal = + (uint16_t)stream_pstate_meta->method_drr.stretched_vtotal; + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_drr.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_drr.common.allow_end_otg_vline; + /* drr only clamps to vtotal min for single display */ + base_programming->config.bits.clamp_vtotal_min = disp_cfg->num_streams == 1; + sub_programming->drr.only_stretch_if_required = true; + break; + case dml2_pstate_method_alternate: + base_programming->type = FAMS2_STREAM_TYPE_ALTERNATE; + base_programming->allow_start_otg_vline = + (uint16_t)stream_pstate_meta->method_alternate.common.allow_start_otg_vline; + base_programming->allow_end_otg_vline = + (uint16_t)stream_pstate_meta->method_alternate.common.allow_end_otg_vline; + base_programming->config.bits.clamp_vtotal_min = true; + + sub_programming->alternate.total_bytes_to_copy = mode_lib->mp.svp0_max_bytes + mode_lib->mp.svp1_max_bytes; // global + sub_programming->alternate.svp0_dst_lines = (uint16_t)mode_lib->mp.svp0_dst_lines[main_stream_index]; // per stream + sub_programming->alternate.svp1_dst_lines = (uint16_t)mode_lib->mp.svp1_dst_lines[main_stream_index]; // per stream + sub_programming->alternate.svp_req_limit = (uint16_t)mode_lib->mp.svp_req_limit[main_stream_index]; // per stream + sub_programming->alternate.min_lead_dst_lines = (uint16_t)mode_lib->mp.min_lead_dst_lines[main_stream_index]; // per stream + // For now include throttle delay, programming delay, scheduling delay, and contention delay into fw_delays. Can reduce later as needed (this is most likely an overestimate). + sub_programming->alternate.fw_delays = (uint16_t)(stream_pstate_meta->method_alternate.pmfw_throttle_delay_otg_vlines + stream_pstate_meta->method_alternate.programming_delay_otg_vlines + + stream_pstate_meta->scheduling_delay_otg_vlines + stream_pstate_meta->contention_delay_otg_vlines); + sub_programming->alternate.max_cursor_size = 128; // DCN6 limits HW cursor to 128x128 - only matters for alternate channel + sub_programming->alternate.vready_offset_lines = 0; // initialize to 0, assign actual value in inner loop + base_programming->allow_to_target_delay_otg_vlines = (uint16_t)math_max2(mode_lib->mp.svp_req_limit[main_stream_index], mode_lib->mp.max_prefetch_in_lines[main_stream_index]); + for (i = 0; i < programming->display_config.num_planes; i++) { + if (disp_cfg->plane_descriptors[i].stream_index == main_stream_index) { + plane_descriptor = &disp_cfg->plane_descriptors[i]; + + vertical_access = plane_descriptor->composition.rotation_angle == dml2_rotation_90 || plane_descriptor->composition.rotation_angle == dml2_rotation_270; + + sub_programming->alternate.rec_height[per_stream_plane_index] = vertical_access ? (uint16_t)((double)plane_descriptor->composition.viewport.plane0.width / plane_descriptor->composition.scaler_info.plane0.v_ratio) : + (uint16_t)((double)plane_descriptor->composition.viewport.plane0.height / plane_descriptor->composition.scaler_info.plane0.v_ratio); + sub_programming->alternate.vstartup_start = (uint16_t)mode_lib->mp.VStartup[i]; // per stream, but DML populates for every plane + /* For now viewport_size indicates the number of lines in the direction that's perpendicular to the scan direction */ + sub_programming->alternate.viewport_start[per_stream_plane_index] = vertical_access ? (uint16_t)plane_descriptor->composition.viewport.plane0.x_start : (uint16_t)plane_descriptor->composition.viewport.plane0.y_start; + sub_programming->alternate.viewport_size[per_stream_plane_index] = vertical_access ? (uint16_t)plane_descriptor->composition.viewport.plane0.width : (uint16_t)plane_descriptor->composition.viewport.plane0.height; + sub_programming->alternate.viewport_start_c[per_stream_plane_index] = vertical_access ? (uint16_t)plane_descriptor->composition.viewport.plane1.x_start : (uint16_t)plane_descriptor->composition.viewport.plane1.y_start; + sub_programming->alternate.viewport_size_c[per_stream_plane_index] = vertical_access ? (uint16_t)plane_descriptor->composition.viewport.plane1.width : (uint16_t)plane_descriptor->composition.viewport.plane1.height; + sub_programming->alternate.surface_pitch[per_stream_plane_index] = (uint16_t)plane_descriptor->surface.plane0.pitch; + sub_programming->alternate.surface_pitch_c[per_stream_plane_index] = (uint16_t)plane_descriptor->surface.plane1.pitch; + sub_programming->alternate.surface_height[per_stream_plane_index] = (uint16_t)plane_descriptor->surface.plane0.height; + sub_programming->alternate.surface_height_c[per_stream_plane_index] = (uint16_t)plane_descriptor->surface.plane1.height; + sub_programming->alternate.element_size[per_stream_plane_index] = (uint8_t) get_element_size_idx(mode_lib->mp.BytePerPixelY[i]); + sub_programming->alternate.element_size_c[per_stream_plane_index] = (uint8_t) get_element_size_idx(mode_lib->mp.BytePerPixelC[i]); + sub_programming->alternate.swath_height[per_stream_plane_index] = (uint8_t)mode_lib->mp.SwathHeightY[i]; + sub_programming->alternate.swath_height_c[per_stream_plane_index] = (uint8_t)mode_lib->mp.SwathHeightC[i]; + sub_programming->alternate.macro_tile_width[per_stream_plane_index] = (uint16_t)mode_lib->mp.MacroTileWidthY[i]; + sub_programming->alternate.macro_tile_width_c[per_stream_plane_index] = (uint16_t)mode_lib->mp.MacroTileWidthC[i]; + sub_programming->alternate.block_256b_width[per_stream_plane_index] = (uint16_t)mode_lib->mp.Read256BlockWidthY[i]; + sub_programming->alternate.block_256b_height[per_stream_plane_index] = (uint16_t)mode_lib->mp.Read256BlockHeightY[i]; + sub_programming->alternate.block_256b_width_c[per_stream_plane_index] = (uint16_t)mode_lib->mp.Read256BlockWidthC[i]; + sub_programming->alternate.block_256b_height_c[per_stream_plane_index] = (uint16_t)mode_lib->mp.Read256BlockHeightC[i]; + sub_programming->alternate.dst_y_prefetch_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.dst_y_prefetch[i] * 1000); + sub_programming->alternate.total_swaths[per_stream_plane_index] = (uint16_t)mode_lib->mp.total_swaths[i]; + sub_programming->alternate.total_swaths_c[per_stream_plane_index] = (uint16_t)mode_lib->mp.total_swaths_c[i]; + sub_programming->alternate.prefetch_swaths[per_stream_plane_index] = (uint8_t)mode_lib->mp.prefetch_swaths[i]; + sub_programming->alternate.prefetch_swaths_c[per_stream_plane_index] = (uint8_t)mode_lib->mp.prefetch_swaths_c[i]; + sub_programming->alternate.pre_hdl_delta_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.prefetch_hdl_delta[i] * 1000); + sub_programming->alternate.rec_hdl_delta_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.recout_hdl_delta[i] * 1000); + sub_programming->alternate.pre_hdl_delta_c_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.prefetch_hdl_delta_c[i] * 1000); + sub_programming->alternate.rec_hdl_delta_c_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.recout_hdl_delta_c[i] * 1000); + sub_programming->alternate.dst_y_per_vm_vblank_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.dst_y_per_vm_vblank[i] * 1000); + sub_programming->alternate.dst_y_per_row_vblank_x1000[per_stream_plane_index] = (uint16_t)(mode_lib->mp.dst_y_per_row_vblank[i] * 1000); + sub_programming->alternate.dst_y_after_scaler[per_stream_plane_index] = (uint16_t)mode_lib->mp.DSTYAfterScaler[i]; + sub_programming->alternate.vinit_prefill[per_stream_plane_index] = (uint16_t)mode_lib->mp.VInitPreFillY[i]; + sub_programming->alternate.vinit_prefill_c[per_stream_plane_index] = (uint16_t)mode_lib->mp.VInitPreFillC[i]; + sub_programming->alternate.vratio_x1000[per_stream_plane_index] = (uint16_t)(plane_descriptor->composition.scaler_info.plane0.v_ratio * 1000); + sub_programming->alternate.vratio_c_x1000[per_stream_plane_index] = (uint16_t)(plane_descriptor->composition.scaler_info.plane1.v_ratio * 1000); + sub_programming->alternate.swizzle_mode[per_stream_plane_index] = dml2_swizzle_mode_to_sw_swizzle_mode(plane_descriptor->surface.tiling); + // For now round up vready offset to the next line, and add +1 line. It can be more precise if we confirm that vstartup rising edge is always at the beginning of the line. + vready_offset = (unsigned int)(math_ceil(((double)mode_lib->mp.VUpdateOffsetPix[i] + mode_lib->mp.VUpdateWidthPix[i] + mode_lib->mp.VReadyOffsetPix[i] + 1) / stream_descriptor->timing.h_total) + 1); + if (vready_offset > sub_programming->alternate.vready_offset_lines) + sub_programming->alternate.vready_offset_lines = (uint8_t)vready_offset; + sub_programming->alternate.config[per_stream_plane_index].bits.prefetch_relative_vblank = 1; + sub_programming->alternate.config[per_stream_plane_index].bits.dcc = plane_descriptor->surface.dcc.enable; + sub_programming->alternate.config[per_stream_plane_index].bits.is_multi_planar = + plane_descriptor->surface.plane1.height > 0; + sub_programming->alternate.config[per_stream_plane_index].bits.is_yuv420 = + plane_descriptor->pixel_format == dml2_420_8 || + plane_descriptor->pixel_format == dml2_420_10 || + plane_descriptor->pixel_format == dml2_420_12; + sub_programming->alternate.config[per_stream_plane_index].bits.vertical_access = vertical_access ? 1 : 0; + sub_programming->alternate.config[per_stream_plane_index].bits.access_direction = + (plane_descriptor->composition.rotation_angle == dml2_rotation_90 && !plane_descriptor->composition.mirrored) || + (plane_descriptor->composition.rotation_angle == dml2_rotation_270 && plane_descriptor->composition.mirrored) || + (plane_descriptor->composition.rotation_angle == dml2_rotation_180) ? 1 : 0; + + sub_programming->alternate.pipe_copy_max_size[0][per_stream_plane_index] = mode_lib->mp.svp0_max_bytes_per_dpp[i]; + sub_programming->alternate.pipe_copy_max_size[1][per_stream_plane_index] = mode_lib->mp.svp1_max_bytes_per_dpp[i]; + sub_programming->alternate.pipe_copy_max_size_c[0][per_stream_plane_index] = mode_lib->mp.svp0_max_bytes_per_dpp_c[i]; + sub_programming->alternate.pipe_copy_max_size_c[1][per_stream_plane_index] = mode_lib->mp.svp1_max_bytes_per_dpp_c[i]; + + per_stream_plane_index++; + } + } + base_programming->num_planes = (uint8_t)per_stream_plane_index; + break; + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_na: + case dml2_pstate_method_count: + default: + /* this should never happen */ + break; + } +} + +static void dcn6_populate_global_fams2_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_display_solution *solution, + struct dmub_cmd_fams2_global_config *fams2_global_config) +{ + unsigned int i, allow_to_target_delta_us; + double line_time_us; + fams2_global_config->features.bits.enable = solution->uclk_pstate_params.fams2_required; + fams2_global_config->features.bits.legacy_method_no_fams2 = solution->uclk_pstate_params.legacy_pstate_info_for_dmu; + + if (fams2_global_config->features.bits.enable || fams2_global_config->features.bits.legacy_method_no_fams2) { + fams2_global_config->features.bits.enable_stall_recovery = true; + fams2_global_config->features.bits.allow_delay_check_mode = FAMS2_ALLOW_DELAY_CHECK_FROM_START; + + fams2_global_config->max_allow_delay_us = mode_lib->ip_caps.fams2.max_allow_delay_us; + fams2_global_config->lock_wait_time_us = mode_lib->ip_caps.fams2.lock_timeout_us; + fams2_global_config->recovery_timeout_us = mode_lib->ip_caps.fams2.recovery_timeout_us; + fams2_global_config->hwfq_flip_programming_delay_us = mode_lib->ip_caps.fams2.flip_programming_delay_us; + + fams2_global_config->num_streams = solution->dispcfg.num_streams; + + for (i = 0; i < solution->dispcfg.num_streams; i++) { + line_time_us = ((double)solution->dispcfg.stream_descriptors[i].timing.h_total * 1000 / solution->dispcfg.stream_descriptors[i].timing.pixel_clock_khz); + allow_to_target_delta_us = (unsigned int)math_ceil(mode_lib->mp.svp_req_limit[i] * line_time_us); + if (fams2_global_config->max_allow_to_target_delta_us < allow_to_target_delta_us) + fams2_global_config->max_allow_to_target_delta_us = allow_to_target_delta_us; + } + } +} + +static void dcn6_populate_min_clocks(struct dml2_display_cfg_programming *programming, + const struct dml2_display_solution *solution, const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int i; + + programming->min_clocks.dcn4x.dispclk_khz = solution->validation_result.mode_support.global.dispclk_khz; + programming->min_clocks.dcn4x.dpprefclk_khz = solution->validation_result.mode_support.global.dpprefclk_khz; + programming->min_clocks.dcn4x.dtbrefclk_khz = solution->validation_result.mode_support.global.dtbrefclk_khz; + programming->min_clocks.dcn4x.socclk_khz = utm_soc_bb->min_socclk_khz; + if (solution->dispcfg.overrides.hw.dcfclk_mhz > 0) + programming->min_clocks.dcn4x.active.dcfclk_khz = + (unsigned long) math_ceil(solution->dispcfg.overrides.hw.dcfclk_mhz * 1000); + else + programming->min_clocks.dcn4x.active.dcfclk_khz = solution->sop_constraint.dcn5.clocks.dcfclk_khz; + programming->min_clocks.dcn4x.active.fclk_khz = solution->sop_constraint.dcn5.clocks.fclk_khz; + programming->min_clocks.dcn4x.active.uclk_khz = solution->sop_constraint.dcn5.clocks.uclk_khz; + programming->min_sop_index = solution->sop_constraint.dcn5.min_sop_index; + programming->min_clocks.dcn4x.deepsleep_dcfclk_khz = + (unsigned long) math_min2((double)solution->validation_result.mode_support.global.dcfclk_deepsleep_khz, (double)programming->min_clocks.dcn4x.active.dcfclk_khz); + for (i = 0; i < solution->dispcfg.num_planes; i++) + programming->plane_programming[i].min_clocks.dcn4x.dppclk_khz = + solution->validation_result.mode_support.per_plane[i].dppclk_khz; + for (i = 0; i < solution->dispcfg.num_streams; i++) { + programming->stream_programming[i].min_clocks.dcn4x.dscclk_khz = + solution->validation_result.mode_support.per_stream[i].dscclk_khz; + programming->stream_programming[i].min_clocks.dcn4x.dtbclk_khz = + solution->validation_result.mode_support.per_stream[i].dtbclk_khz; + programming->stream_programming[i].min_clocks.dcn4x.phyclk_khz = + solution->validation_result.mode_support.per_stream[i].phyclk_khz; + } +} + +static void dcn6_populate_stutter_support(struct dml2_display_cfg_programming *programming, + const struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + programming->stutter.supported_in_blank = solution->stutter_support_in_vblank; + programming->z8_stutter.supported_in_blank = solution->z8_stutter_support_in_vblank; + programming->z8_stutter.meets_eco = utm_soc_bb->power_management_parameters.z8_min_idle_time > 0 + && mode_lib->mp.StutterPeriod >= utm_soc_bb->power_management_parameters.z8_min_idle_time; + programming->stutter.base_percent_efficiency = (uint8_t)(mode_lib->mp.StutterEfficiency); + programming->stutter.low_power_percent_efficiency = (uint8_t)(mode_lib->mp.LowPowerStutterEfficiency); +} + +static void dcn6_populate_mcache_allocation(struct dml2_display_cfg_programming *programming, + const struct dml2_display_solution *solution) +{ + unsigned int i; + + for (i = 0; i < solution->dispcfg.num_planes; i++) + programming->plane_programming[i].mcache_allocation = solution->mcache_allocations[i]; +} + +static void dcn6_populate_qos_bound(struct dml2_display_cfg_programming *programming, + const struct dml2_display_solution *solution) +{ + programming->qos_bound.latency_ub = solution->sop_constraint.dcn5.latency; + programming->qos_bound.bandwidth_lb = solution->validation_result.mode_support.bandwidth_upper_bound; + programming->qos_bound.lsdma_bandwidth_lb_kbps = solution->validation_result.mode_support.global.lsdma_bw_req_for_alt_kbps; +} + +static void dcn6_populate_mode_programming(struct dml2_display_cfg_programming *programming, + struct dml2_core_internal_scratch *s, + const struct dml2_core_internal_display_mode_lib *mode_lib, + const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + const struct dml2_uclk_pstate_params *uclk_params = &solution->uclk_pstate_params; + const struct core_display_cfg_support_info *cfg_support_info = &solution->validation_result.mode_support.cfg_support_info; + unsigned int pipe_offset; + int dml_internal_pipe_index; + int total_pipe_regs_copied = 0; + int stream_already_populated_mask = 0; + + unsigned int main_stream_index; + unsigned int plane_index; + + memcpy(&programming->display_config, &solution->dispcfg, sizeof(struct dml2_display_cfg)); + dcn6_populate_min_clocks(programming, solution, utm_soc_bb); + dcn5_get_arb_params(&programming->display_config, mode_lib, utm_soc_bb, &programming->global_regs.arb_regs); + programming->global_regs.num_watermark_sets = 1; + dcn6_get_watermarks(&programming->display_config, mode_lib, utm_soc_bb, &programming->global_regs.wm_regs[0]); + dcn6_populate_stutter_support(programming, mode_lib, solution, utm_soc_bb); + dcn6_populate_mcache_allocation(programming, solution); + dcn6_populate_qos_bound(programming, solution); + dml_internal_pipe_index = 0; + + programming->fclk_pstate_supported = solution->fclk_pstate_support; + if (uclk_params) { + programming->uclk_pstate_supported = uclk_params->support; + if (programming->uclk_pstate_supported) { + programming->fams2_required = uclk_params->fams2_required; + programming->legacy_pstate_info_for_dmu = uclk_params->legacy_pstate_info_for_dmu; + dcn6_populate_global_fams2_programming(mode_lib, solution, &programming->fams2_global_config); + } + } + + DML_LOG_VERBOSE("DML_PMO::%s: num_planes=%u\n", __func__, programming->display_config.num_planes); + + for (plane_index = 0; plane_index < programming->display_config.num_planes; plane_index++) { + programming->plane_programming[plane_index].num_dpps_required = mode_lib->mp.NoOfDPP[plane_index]; + + // Setup the appropriate p-state strategy for each plane + if (uclk_params) { + if (programming->uclk_pstate_supported) + programming->plane_programming[plane_index].uclk_pstate_support_method = uclk_params->pstate_switch_modes[plane_index]; + else + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_na; + } else { + if (mode_lib->mp.MaxActiveDRAMClockChangeLatencySupported[plane_index] >= utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us) + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_vactive; + else if (mode_lib->mp.TWait[plane_index] >= utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us) + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_vblank; + else + programming->plane_programming[plane_index].uclk_pstate_support_method = dml2_pstate_method_na; + } + + for (pipe_offset = 0; pipe_offset < programming->plane_programming[plane_index].num_dpps_required; pipe_offset++) { + programming->plane_programming[plane_index].plane_descriptor = &programming->display_config.plane_descriptors[plane_index]; + + // Assign storage for this pipe's register values + programming->plane_programming[plane_index].pipe_regs[pipe_offset] = &programming->pipe_regs[total_pipe_regs_copied]; + memset(programming->plane_programming[plane_index].pipe_regs[pipe_offset], 0, sizeof(struct dml2_dchub_per_pipe_register_set)); + total_pipe_regs_copied++; + + // Populate + dcn6_get_pipe_regs(&programming->display_config, mode_lib, programming->plane_programming[plane_index].pipe_regs[pipe_offset], dml_internal_pipe_index, utm_soc_bb, s); + + main_stream_index = programming->display_config.plane_descriptors[plane_index].stream_index; + + // Multiple planes can refer to the same stream index, so it's only necessary to populate it once + if (!(stream_already_populated_mask & (0x1 << main_stream_index))) { + programming->stream_programming[main_stream_index].uclk_pstate_method = programming->plane_programming[plane_index].uclk_pstate_support_method; + programming->stream_programming[main_stream_index].stream_descriptor = &programming->display_config.stream_descriptors[main_stream_index]; + programming->stream_programming[main_stream_index].num_odms_required = cfg_support_info->stream_support_info[main_stream_index].odms_used; + dcn6_get_stream_programming(mode_lib, &programming->stream_programming[main_stream_index], dml_internal_pipe_index); + + stream_already_populated_mask |= (0x1 << main_stream_index); + } + dml_internal_pipe_index++; + } + } + /* Loop through per stream to populate FAMS programming. The internal function will loop through + * per plane as needed (specifically for alternate scenarios). + */ + for (main_stream_index = 0; main_stream_index < programming->display_config.num_streams; main_stream_index++) { + if (uclk_params) { + dcn6_populate_fams2_programming(mode_lib, + solution, + programming, + main_stream_index); + } + } + // TODO: Add DML_LOG_DEBUG for entire programming structure to log out calculated values +} + +/* + * This function converts display_config and cfg_support_info into mode_lib.mp data. + * + * To move mode support result to mode programming, the data needs to be saved in cfg_support_info in the end of + * mode support, so we can access cfg_support_info and populate mode programming data in this function based on it. + */ +static void dcn6_mp_initialize_from_solution(struct dml2_core_internal_mode_program *outputs, + const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + const struct dml2_display_cfg *display_cfg = &solution->dispcfg; + const struct core_display_cfg_support_info *cfg_support_info = + &solution->validation_result.mode_support.cfg_support_info; + const struct core_plane_support_info *plane_support_info; + const struct core_stream_support_info *stream_support_info; + unsigned int k; + const struct dml2_plane_parameters *plane; + + outputs->num_active_pipes = dml2_core_util_get_num_active_pipes(display_cfg->num_planes, cfg_support_info); + + dml2_core_utils_pipe_plane_mapping(cfg_support_info, outputs->pipe_plane); + + dml2_core_utils_get_stream_output_bpp(outputs->OutputBpp, display_cfg); + + dcn6_mp_populate_odm_mode(solution, outputs); + + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + plane_support_info = &cfg_support_info->plane_support_info[k]; + stream_support_info = &cfg_support_info->stream_support_info[plane->stream_index]; + + outputs->immediate_flip_required = outputs->immediate_flip_required + || plane->immediate_flip; + outputs->NoOfDPP[k] = plane_support_info->dpps_used; + outputs->dsc_enable[k] = stream_support_info->dsc_enable; + outputs->num_dsc_slices[k] = stream_support_info->num_dsc_slices; + } + + /* min clocks */ + outputs->GlobalDPPCLK = solution->validation_result.mode_support.global.dpprefclk_khz / 1000.0; + if (solution->dispcfg.overrides.hw.dcfclk_mhz > 0) + outputs->Dcfclk = solution->dispcfg.overrides.hw.dcfclk_mhz; + else + outputs->Dcfclk = solution->sop_constraint.dcn5.clocks.dcfclk_khz / 1000.0; + outputs->FabricClock = solution->sop_constraint.dcn5.clocks.fclk_khz / 1000.0; + outputs->uclk_freq_mhz = solution->sop_constraint.dcn5.clocks.uclk_khz / 1000.0; + outputs->SOCCLK = utm_soc_bb->min_socclk_khz / 1000.0; + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + outputs->Dppclk[k] = solution->validation_result.mode_support.per_plane[k].dppclk_khz / 1000.0; + outputs->DSCCLK[k] = solution->validation_result.mode_support.per_stream[plane->stream_index].dscclk_khz / 1000.0; + } + + /* qos bound */ + outputs->min_available_urgent_bandwidth_MBps = solution->validation_result.mode_support.bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps / 1000.0; + **outputs->urg_bandwidth_available = math_min2(solution->sop_constraint.dcn5.min_available_urgent_bandwidth_KBps / 1000.0, + outputs->Dcfclk * utm_soc_bb->urgent_sdp_derate_percent / 100.0 * utm_soc_bb->return_bus_width_bytes); + + outputs->UrgentLatency = solution->sop_constraint.dcn5.latency.dcn5.urgent_ramp; + outputs->TripToMemory = math_max2(solution->sop_constraint.dcn5.latency.dcn5.urgent_ramp, + solution->sop_constraint.dcn5.latency.dcn5.t_trip); + outputs->MetaTripToMemory = solution->sop_constraint.dcn5.latency.dcn5.meta_trip_to_mem; + outputs->max_urgent_latency_us = solution->sop_constraint.dcn5.latency.dcn5.max_req_latency_urg; + outputs->df_response_time_us = solution->sop_constraint.dcn5.latency.dcn5.df_response_time_us; + + /* pstate */ + memcpy(outputs->uclk_pstate_switch_modes, + solution->uclk_pstate_params.pstate_switch_modes, + sizeof(solution->uclk_pstate_params.pstate_switch_modes)); +} + +static void dcn6_mp_build_calculate_mp_context(struct dml2_core_calculate_mp_context *ctx, + struct dml2_core_instance *core, + const struct dml2_display_solution *solution) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = &core->clean_me_up.mode_lib; + + ctx->display_cfg = &solution->dispcfg; + ctx->ip = &mode_lib->ip; + ctx->soc_bb = core->utm_soc_bb; + ctx->ms = &mode_lib->ms; + ctx->dummies = &mode_lib->scratch.dml_core_mode_programming_locals; + ctx->func_params = &mode_lib->scratch; +} + +static void dcn6_mp_initialize_from_ms(struct dml2_core_internal_mode_program *outputs, + const struct dml2_core_internal_mode_support *ms) +{ + outputs->DCFCLKDeepSleep = ms->dcfclk_deepsleep; + outputs->Dispclk = ms->RequiredDISPCLK; + outputs->MaxTotalDETInKByte = ms->MaxTotalDETInKByte; + outputs->NomDETInKByte = ms->NomDETInKByte; + outputs->MinCompressedBufferSizeInKByte = ms->MinCompressedBufferSizeInKByte; + memcpy(outputs->PixelClockBackEnd, ms->PixelClockBackEnd, sizeof(outputs->PixelClockBackEnd)); + memcpy(outputs->DPPCLKUsingSingleDPP, ms->MinDPPCLKUsingSingleDPP, sizeof(outputs->DPPCLKUsingSingleDPP)); + memcpy(outputs->PSCL_THROUGHPUT, ms->PSCL_FACTOR, sizeof(outputs->PSCL_THROUGHPUT)); + memcpy(outputs->PSCL_THROUGHPUT_CHROMA, ms->PSCL_FACTOR_CHROMA, sizeof(outputs->PSCL_THROUGHPUT_CHROMA)); + memcpy(outputs->BytePerPixelY, ms->BytePerPixelY, sizeof(outputs->BytePerPixelY)); + memcpy(outputs->BytePerPixelC, ms->BytePerPixelC, sizeof(outputs->BytePerPixelC)); + memcpy(outputs->BytePerPixelInDETY, ms->BytePerPixelInDETY, sizeof(outputs->BytePerPixelInDETY)); + memcpy(outputs->BytePerPixelInDETC, ms->BytePerPixelInDETC, sizeof(outputs->BytePerPixelInDETC)); + memcpy(outputs->Read256BlockHeightY, ms->Read256BlockHeightY, sizeof(outputs->Read256BlockHeightY)); + memcpy(outputs->Read256BlockHeightC, ms->Read256BlockHeightC, sizeof(outputs->Read256BlockHeightC)); + memcpy(outputs->Read256BlockWidthY, ms->Read256BlockWidthY, sizeof(outputs->Read256BlockWidthY)); + memcpy(outputs->Read256BlockWidthC, ms->Read256BlockWidthC, sizeof(outputs->Read256BlockWidthC)); + memcpy(outputs->MacroTileHeightY, ms->MacroTileHeightY, sizeof(outputs->MacroTileHeightY)); + memcpy(outputs->MacroTileHeightC, ms->MacroTileHeightC, sizeof(outputs->MacroTileHeightC)); + memcpy(outputs->MacroTileWidthY, ms->MacroTileWidthY, sizeof(outputs->MacroTileWidthY)); + memcpy(outputs->MacroTileWidthC, ms->MacroTileWidthC, sizeof(outputs->MacroTileWidthC)); + memcpy(outputs->surf_linear128_l, ms->surf_linear128_l, sizeof(outputs->surf_linear128_l)); + memcpy(outputs->surf_linear128_c, ms->surf_linear128_c, sizeof(outputs->surf_linear128_c)); + memcpy(outputs->MaximumSwathWidthChroma, ms->MaximumSwathWidthChroma, sizeof(outputs->MaximumSwathWidthChroma)); + outputs->MaximumSwathWidthInLineBufferChroma = ms->MaximumSwathWidthInLineBufferChroma; + memcpy(outputs->MaximumSwathWidthLuma, ms->MaximumSwathWidthLuma, sizeof(outputs->MaximumSwathWidthLuma)); + outputs->MaximumSwathWidthInLineBufferLuma = ms->MaximumSwathWidthInLineBufferLuma; + memcpy(outputs->cursor_bw, ms->cursor_bw, sizeof(outputs->cursor_bw)); + memcpy(outputs->vactive_sw_bw_c, ms->vactive_sw_bw_c, sizeof(outputs->vactive_sw_bw_c)); + memcpy(outputs->vactive_sw_bw_l, ms->vactive_sw_bw_l, sizeof(outputs->vactive_sw_bw_l)); + memcpy(outputs->SwathWidthSingleDPPC, ms->SwathWidthCSingleDPP, sizeof(outputs->SwathWidthSingleDPPC)); + memcpy(outputs->SwathWidthSingleDPPY, ms->SwathWidthYSingleDPP, sizeof(outputs->SwathWidthSingleDPPY)); + memcpy(outputs->req_per_swath_ub_l, ms->req_per_swath_ub_l, sizeof(outputs->req_per_swath_ub_l)); + memcpy(outputs->req_per_swath_ub_c, ms->req_per_swath_ub_c, sizeof(outputs->req_per_swath_ub_c)); + memcpy(outputs->swath_width_luma_ub, ms->swath_width_luma_ub, sizeof(outputs->swath_width_luma_ub)); + memcpy(outputs->swath_width_chroma_ub, ms->swath_width_chroma_ub, sizeof(outputs->swath_width_chroma_ub)); + memcpy(outputs->SwathWidthY, ms->SwathWidthY, sizeof(outputs->SwathWidthY)); + memcpy(outputs->SwathWidthC, ms->SwathWidthC, sizeof(outputs->SwathWidthC)); + memcpy(outputs->SwathHeightY, ms->SwathHeightY, sizeof(outputs->SwathHeightY)); + memcpy(outputs->SwathHeightC, ms->SwathHeightC, sizeof(outputs->SwathHeightC)); + memcpy(outputs->request_size_bytes_luma, ms->support.request_size_bytes_luma, sizeof(outputs->request_size_bytes_luma)); + memcpy(outputs->request_size_bytes_chroma, ms->support.request_size_bytes_chroma, sizeof(outputs->request_size_bytes_chroma)); + memcpy(outputs->DETBufferSizeInKByte, ms->DETBufferSizeInKByte, sizeof(outputs->DETBufferSizeInKByte)); + memcpy(outputs->DETBufferSizeY, ms->DETBufferSizeY, sizeof(outputs->DETBufferSizeY)); + memcpy(outputs->DETBufferSizeC, ms->DETBufferSizeC, sizeof(outputs->DETBufferSizeC)); + memcpy(outputs->full_swath_bytes_l, ms->full_swath_bytes_l, sizeof(outputs->full_swath_bytes_l)); + memcpy(outputs->full_swath_bytes_c, ms->full_swath_bytes_c, sizeof(outputs->full_swath_bytes_c)); + outputs->UnboundedRequestEnabled = ms->UnboundedRequestEnabled; + outputs->compbuf_reserved_space_64b = ms->compbuf_reserved_space_64b; + outputs->hw_debug5 = ms->hw_debug5; + outputs->CompressedBufferSizeInkByte = ms->CompressedBufferSizeInkByte; + memcpy(outputs->DSCDelay, ms->DSCDelay, sizeof(outputs->DSCDelay)); + memcpy(outputs->vmpg_height_y, ms->vmpg_height_y, sizeof(outputs->vmpg_height_y)); + memcpy(outputs->VInitPreFillC, ms->PrefillC, sizeof(outputs->VInitPreFillC)); + memcpy(outputs->is_using_mall_for_ss, ms->is_using_mall_for_ss, sizeof(outputs->is_using_mall_for_ss)); + memcpy(outputs->meta_row_width_chroma, ms->meta_row_width_chroma, sizeof(outputs->meta_row_width_chroma)); + memcpy(outputs->dpte_row_bytes_per_row_c, ms->dpte_row_bytes_per_row_c, sizeof(outputs->dpte_row_bytes_per_row_c)); + memcpy(outputs->PixelPTEReqHeightC, ms->PixelPTEReqHeightC, sizeof(outputs->PixelPTEReqHeightC)); + memcpy(outputs->meta_row_height, ms->meta_row_height_luma, sizeof(outputs->meta_row_height)); + memcpy(outputs->MaxNumSwathY, ms->MaxNumSwathY, sizeof(outputs->MaxNumSwathY)); + memcpy(outputs->meta_row_bytes_per_row_ub_l, ms->meta_row_bytes_per_row_ub_l, sizeof(outputs->meta_row_bytes_per_row_ub_l)); + memcpy(outputs->PTE_BUFFER_MODE, ms->PTE_BUFFER_MODE, sizeof(outputs->PTE_BUFFER_MODE)); + memcpy(outputs->dpte_row_bytes_per_row_l, ms->dpte_row_bytes_per_row_l, sizeof(outputs->dpte_row_bytes_per_row_l)); + memcpy(outputs->dpte_row_height, ms->dpte_row_height, sizeof(outputs->dpte_row_height)); + memcpy(outputs->meta_req_height_chroma, ms->meta_req_height_chroma, sizeof(outputs->meta_req_height_chroma)); + memcpy(outputs->PixelPTEBytesPerRow, ms->DPTEBytesPerRow, sizeof(outputs->PixelPTEBytesPerRow)); + memcpy(outputs->vmpg_width_y, ms->vmpg_width_y, sizeof(outputs->vmpg_width_y)); + memcpy(outputs->meta_pte_bytes_per_frame_ub_c, ms->meta_pte_bytes_per_frame_ub_c, sizeof(outputs->meta_pte_bytes_per_frame_ub_c)); + memcpy(outputs->dpde0_bytes_per_frame_ub_c, ms->dpde0_bytes_per_frame_ub_c, sizeof(outputs->dpde0_bytes_per_frame_ub_c)); + memcpy(outputs->dpte_group_bytes, ms->dpte_group_bytes, sizeof(outputs->dpte_group_bytes)); + memcpy(outputs->dpte_row_width_luma_ub, ms->dpte_row_width_luma_ub, sizeof(outputs->dpte_row_width_luma_ub)); + memcpy(outputs->meta_req_width, ms->meta_req_width, sizeof(outputs->meta_req_width)); + memcpy(outputs->PrefetchSourceLinesY, ms->PrefetchLinesY, sizeof(outputs->PrefetchSourceLinesY)); + memcpy(outputs->vmpg_width_c, ms->vmpg_width_c, sizeof(outputs->vmpg_width_c)); + memcpy(outputs->meta_row_bytes, ms->meta_row_bytes, sizeof(outputs->meta_row_bytes)); + memcpy(outputs->PrefetchSourceLinesC, ms->PrefetchLinesC, sizeof(outputs->PrefetchSourceLinesC)); + memcpy(outputs->meta_row_bw, ms->meta_row_bw, sizeof(outputs->meta_row_bw)); + memcpy(outputs->meta_row_width, ms->meta_row_width, sizeof(outputs->meta_row_width)); + memcpy(outputs->PixelPTEReqWidthY, ms->PixelPTEReqWidthY, sizeof(outputs->PixelPTEReqWidthY)); + memcpy(outputs->dpte_row_bw, ms->dpte_row_bw, sizeof(outputs->dpte_row_bw)); + memcpy(outputs->dpte_row_height_linear, ms->dpte_row_height_linear, sizeof(outputs->dpte_row_height_linear)); + memcpy(outputs->PTERequestSizeY, ms->PTERequestSizeY, sizeof(outputs->PTERequestSizeY)); + memcpy(outputs->dpte_row_height_chroma, ms->dpte_row_height_chroma, sizeof(outputs->dpte_row_height_chroma)); + memcpy(outputs->VInitPreFillY, ms->PrefillY, sizeof(outputs->VInitPreFillY)); + memcpy(outputs->use_one_row_for_frame, ms->use_one_row_for_frame, sizeof(outputs->use_one_row_for_frame)); + memcpy(outputs->vmpg_height_c, ms->vmpg_height_c, sizeof(outputs->vmpg_height_c)); + memcpy(outputs->dpte_row_width_chroma_ub, ms->dpte_row_width_chroma_ub, sizeof(outputs->dpte_row_width_chroma_ub)); + memcpy(outputs->PixelPTEReqWidthC, ms->PixelPTEReqWidthC, sizeof(outputs->PixelPTEReqWidthC)); + memcpy(outputs->meta_pte_bytes_per_frame_ub_l, ms->meta_pte_bytes_per_frame_ub_l, sizeof(outputs->meta_pte_bytes_per_frame_ub_l)); + memcpy(outputs->meta_row_height_chroma, ms->meta_row_height_chroma, sizeof(outputs->meta_row_height_chroma)); + memcpy(outputs->MaxNumSwathC, ms->MaxNumSwathC, sizeof(outputs->MaxNumSwathC)); + memcpy(outputs->dpte_row_height_linear_chroma, ms->dpte_row_height_linear_chroma, sizeof(outputs->dpte_row_height_linear_chroma)); + memcpy(outputs->PTERequestSizeC, ms->PTERequestSizeC, sizeof(outputs->PTERequestSizeC)); + memcpy(outputs->meta_req_height, ms->meta_req_height, sizeof(outputs->meta_req_height)); + memcpy(outputs->vm_bytes, ms->vm_bytes, sizeof(outputs->vm_bytes)); + memcpy(outputs->meta_row_bytes_per_row_ub_c, ms->meta_row_bytes_per_row_ub_c, sizeof(outputs->meta_row_bytes_per_row_ub_c)); + memcpy(outputs->dpde0_bytes_per_frame_ub_l, ms->dpde0_bytes_per_frame_ub_l, sizeof(outputs->dpde0_bytes_per_frame_ub_l)); + memcpy(outputs->meta_req_width_chroma, ms->meta_req_width_chroma, sizeof(outputs->meta_req_width_chroma)); + memcpy(outputs->PixelPTEReqHeightY, ms->PixelPTEReqHeightY, sizeof(outputs->PixelPTEReqHeightY)); + memcpy(outputs->BIGK_FRAGMENT_SIZE, ms->BIGK_FRAGMENT_SIZE, sizeof(outputs->BIGK_FRAGMENT_SIZE)); + memcpy(outputs->vm_group_bytes, ms->vm_group_bytes, sizeof(outputs->vm_group_bytes)); + memcpy(outputs->use_one_row_for_frame_flip, ms->use_one_row_for_frame_flip, sizeof(outputs->use_one_row_for_frame_flip)); + memcpy(outputs->mall_comb_mcache_l, ms->mall_comb_mcache_l, sizeof(outputs->mall_comb_mcache_l)); + memcpy(outputs->dcc_dram_bw_nom_overhead_factor_p0, ms->dcc_dram_bw_nom_overhead_factor_p0, sizeof(outputs->dcc_dram_bw_nom_overhead_factor_p0)); + memcpy(outputs->mcache_row_bytes_l, ms->mcache_row_bytes_l, sizeof(outputs->mcache_row_bytes_l)); + memcpy(outputs->dcc_dram_bw_pref_overhead_factor_p1, ms->dcc_dram_bw_pref_overhead_factor_p1, sizeof(outputs->dcc_dram_bw_pref_overhead_factor_p1)); + memcpy(outputs->mcache_offsets_l, ms->mcache_offsets_l, sizeof(outputs->mcache_offsets_l)); + memcpy(outputs->mcache_shift_granularity_l, ms->mcache_shift_granularity_l, sizeof(outputs->mcache_shift_granularity_l)); + memcpy(outputs->mcache_offsets_c, ms->mcache_offsets_c, sizeof(outputs->mcache_offsets_c)); + memcpy(outputs->lc_comb_mcache, ms->lc_comb_mcache, sizeof(outputs->lc_comb_mcache)); + memcpy(outputs->num_mcaches_c, ms->num_mcaches_c, sizeof(outputs->num_mcaches_c)); + memcpy(outputs->mcache_row_bytes_per_channel_l, ms->mcache_row_bytes_per_channel_l, sizeof(outputs->mcache_row_bytes_per_channel_l)); + memcpy(outputs->mcache_shift_granularity_c, ms->mcache_shift_granularity_c, sizeof(outputs->mcache_shift_granularity_c)); + memcpy(outputs->mcache_row_bytes_per_channel_c, ms->mcache_row_bytes_per_channel_c, sizeof(outputs->mcache_row_bytes_per_channel_c)); + memcpy(outputs->mcache_row_bytes_c, ms->mcache_row_bytes_c, sizeof(outputs->mcache_row_bytes_c)); + memcpy(outputs->num_mcaches_l, ms->num_mcaches_l, sizeof(outputs->num_mcaches_l)); + memcpy(outputs->mall_comb_mcache_c, ms->mall_comb_mcache_c, sizeof(outputs->mall_comb_mcache_c)); + memcpy(outputs->dcc_dram_bw_nom_overhead_factor_p1, ms->dcc_dram_bw_nom_overhead_factor_p1, sizeof(outputs->dcc_dram_bw_nom_overhead_factor_p1)); + memcpy(outputs->dcc_dram_bw_pref_overhead_factor_p0, ms->dcc_dram_bw_pref_overhead_factor_p0, sizeof(outputs->dcc_dram_bw_pref_overhead_factor_p0)); + outputs->TCalc = ms->TimeCalc; + outputs->HostVMInefficiencyFactor = ms->HostVMInefficiencyFactor; + outputs->HostVMInefficiencyFactorPrefetch = ms->HostVMInefficiencyFactorPrefetch; + memcpy(outputs->tdlut_pte_bytes_per_frame, ms->tdlut_pte_bytes_per_frame, sizeof(outputs->tdlut_pte_bytes_per_frame)); + memcpy(outputs->tdlut_bytes_per_frame, ms->tdlut_bytes_per_frame, sizeof(outputs->tdlut_bytes_per_frame)); + memcpy(outputs->tdlut_drain_time, ms->tdlut_drain_time, sizeof(outputs->tdlut_drain_time)); + memcpy(outputs->tdlut_opt_time, ms->tdlut_opt_time, sizeof(outputs->tdlut_opt_time)); + memcpy(outputs->tdlut_bytes_per_group, ms->tdlut_bytes_per_group, sizeof(outputs->tdlut_bytes_per_group)); + memcpy(outputs->tdlut_groups_per_2row_ub, ms->tdlut_groups_per_2row_ub, sizeof(outputs->tdlut_groups_per_2row_ub)); + outputs->ExtraLatencyPrefetch = ms->ExtraLatencyPrefetch; + outputs->ExtraLatency = ms->ExtraLatency; + outputs->ExtraLatency_sr = ms->ExtraLatency_sr; + memcpy(outputs->WritebackDelay, ms->WritebackDelayTime, sizeof(outputs->WritebackDelay)); + memcpy(outputs->excess_vactive_fill_bw_c, ms->excess_vactive_fill_bw_c, sizeof(outputs->excess_vactive_fill_bw_c)); + memcpy(outputs->excess_vactive_fill_bw_l, ms->excess_vactive_fill_bw_l, sizeof(outputs->excess_vactive_fill_bw_l)); + memcpy(outputs->NotEnoughUrgentLatencyHiding, ms->NotEnoughUrgentLatencyHiding, sizeof(outputs->NotEnoughUrgentLatencyHiding)); + memcpy(outputs->UrgentBurstFactorCursor, ms->UrgentBurstFactorCursor, sizeof(outputs->UrgentBurstFactorCursor)); + memcpy(outputs->UrgentBurstFactorChroma, ms->UrgentBurstFactorChroma, sizeof(outputs->UrgentBurstFactorChroma)); + memcpy(outputs->cursor_bytes_per_chunk, ms->cursor_bytes_per_chunk, sizeof(outputs->cursor_bytes_per_chunk)); + memcpy(outputs->cursor_bytes_per_line, ms->cursor_bytes_per_line, sizeof(outputs->cursor_bytes_per_line)); + memcpy(outputs->UrgentBurstFactorCursorPre, ms->UrgentBurstFactorCursorPre, sizeof(outputs->UrgentBurstFactorCursorPre)); + memcpy(outputs->UrgentBurstFactorLuma, ms->UrgentBurstFactorLuma, sizeof(outputs->UrgentBurstFactorLuma)); + memcpy(outputs->MaxVStartupLines, ms->MaxVStartupLines, sizeof(outputs->MaxVStartupLines)); + memcpy(outputs->Tr0_trips_flip_rounded, ms->Tr0_trips_flip_rounded, sizeof(outputs->Tr0_trips_flip_rounded)); + memcpy(outputs->Tno_bw_flip, ms->Tno_bw_flip, sizeof(outputs->Tno_bw_flip)); + memcpy(outputs->dst_y_prefetch, ms->dst_y_prefetch, sizeof(outputs->dst_y_prefetch)); + memcpy(outputs->DSTXAfterScaler, ms->DSTXAfterScaler, sizeof(outputs->DSTXAfterScaler)); + memcpy(outputs->DSTYAfterScaler, ms->DSTYAfterScaler, sizeof(outputs->DSTYAfterScaler)); + memcpy(outputs->Tr0_trips_flip, ms->Tr0_trips_flip, sizeof(outputs->Tr0_trips_flip)); + memcpy(outputs->VReadyOffsetPix, ms->VReadyOffsetPix, sizeof(outputs->VReadyOffsetPix)); + memcpy(outputs->prefetch_vmrow_bw, ms->prefetch_vmrow_bw, sizeof(outputs->prefetch_vmrow_bw)); + memcpy(outputs->dst_y_per_vm_vblank, ms->LinesForVM, sizeof(outputs->dst_y_per_vm_vblank)); + memcpy(outputs->VUpdateOffsetPix, ms->VUpdateOffsetPix, sizeof(outputs->VUpdateOffsetPix)); + memcpy(outputs->RequiredPrefetchPixelDataBWLuma, ms->RequiredPrefetchPixelDataBWLuma, sizeof(outputs->RequiredPrefetchPixelDataBWLuma)); + memcpy(outputs->Tno_bw, ms->Tno_bw, sizeof(outputs->Tno_bw)); + memcpy(outputs->TSetup, ms->TSetup, sizeof(outputs->TSetup)); + memcpy(outputs->Tdmdl_vm_raw, ms->Tdmdl_vm_raw, sizeof(outputs->Tdmdl_vm_raw)); + memcpy(outputs->VRatioPrefetchY, ms->VRatioPreY, sizeof(outputs->VRatioPrefetchY)); + memcpy(outputs->prefetch_cursor_bw, ms->prefetch_cursor_bw, sizeof(outputs->prefetch_cursor_bw)); + memcpy(outputs->NotEnoughTimeForDynamicMetadata, ms->NoTimeForDynamicMetadata, sizeof(outputs->NotEnoughTimeForDynamicMetadata)); + memcpy(outputs->RequiredPrefetchPixelDataBWChroma, ms->RequiredPrefetchPixelDataBWChroma, sizeof(outputs->RequiredPrefetchPixelDataBWChroma)); + memcpy(outputs->VUpdateWidthPix, ms->VUpdateWidthPix, sizeof(outputs->VUpdateWidthPix)); + memcpy(outputs->NoTimeToPrefetch, ms->NoTimeForPrefetch, sizeof(outputs->NoTimeToPrefetch)); + memcpy(outputs->dst_y_per_row_vblank, ms->LinesForDPTERow, sizeof(outputs->dst_y_per_row_vblank)); + memcpy(outputs->Tdmdl_raw, ms->Tdmdl_raw, sizeof(outputs->Tdmdl_raw)); + memcpy(outputs->Tvm_trips_flip, ms->Tvm_trips_flip, sizeof(outputs->Tvm_trips_flip)); + memcpy(outputs->TWait, ms->TWait, sizeof(outputs->TWait)); + memcpy(outputs->VRatioPrefetchC, ms->VRatioPreC, sizeof(outputs->VRatioPrefetchC)); + memcpy(outputs->Tvm_trips_flip_rounded, ms->Tvm_trips_flip_rounded, sizeof(outputs->Tvm_trips_flip_rounded)); + memcpy(outputs->VStartupMin, ms->VStartupMin, sizeof(outputs->VStartupMin)); + outputs->PrefetchScheduleSupported = ms->support.PrefetchScheduleSupported; + memcpy(outputs->UrgentBurstFactorLumaPre, ms->UrgentBurstFactorLumaPre, sizeof(outputs->UrgentBurstFactorLumaPre)); + memcpy(outputs->UrgentBurstFactorChromaPre, ms->UrgentBurstFactorChromaPre, sizeof(outputs->UrgentBurstFactorChromaPre)); + memcpy(outputs->NotEnoughUrgentLatencyHidingPre, ms->NotEnoughUrgentLatencyHidingPre, sizeof(outputs->NotEnoughUrgentLatencyHidingPre)); + memcpy(outputs->urg_vactive_bandwidth_required, ms->support.urg_vactive_bandwidth_required, sizeof(outputs->urg_vactive_bandwidth_required)); + memcpy(outputs->urg_bandwidth_required, ms->support.urg_bandwidth_required, sizeof(outputs->urg_bandwidth_required)); + memcpy(outputs->non_urg_bandwidth_required, ms->support.non_urg_bandwidth_required, sizeof(outputs->non_urg_bandwidth_required)); + memcpy(outputs->urg_bandwidth_required_flip, ms->support.urg_bandwidth_required_flip, sizeof(outputs->urg_bandwidth_required_flip)); + memcpy(outputs->non_urg_bandwidth_required_flip, ms->support.non_urg_bandwidth_required_flip, sizeof(outputs->non_urg_bandwidth_required_flip)); + outputs->PrefetchModeSupported = ms->support.PrefetchSupported; + memcpy(outputs->final_flip_bw, ms->final_flip_bw, sizeof(outputs->final_flip_bw)); + memcpy(outputs->ImmediateFlipSupportedForPipe, ms->ImmediateFlipSupportedForPipe, sizeof(outputs->ImmediateFlipSupportedForPipe)); + memcpy(outputs->dst_y_per_vm_flip, ms->dst_y_per_vm_flip, sizeof(outputs->dst_y_per_vm_flip)); + memcpy(outputs->dst_y_per_row_flip, ms->dst_y_per_row_flip, sizeof(outputs->dst_y_per_row_flip)); + outputs->Watermark = ms->support.watermarks; + memcpy(outputs->DRAMClockChangeSupport, ms->support.DRAMClockChangeSupport, sizeof(outputs->DRAMClockChangeSupport)); + outputs->global_dram_clock_change_supported = ms->support.global_dram_clock_change_supported; + memcpy(outputs->MaxActiveDRAMClockChangeLatencySupported, ms->MaxActiveDRAMClockChangeLatencySupported, sizeof(outputs->MaxActiveDRAMClockChangeLatencySupported)); + memcpy(outputs->SubViewportLinesNeededInMALL, ms->SubViewportLinesNeededInMALL, sizeof(outputs->SubViewportLinesNeededInMALL)); + memcpy(outputs->FCLKChangeSupport, ms->support.FCLKChangeSupport, sizeof(outputs->FCLKChangeSupport)); + outputs->global_fclk_change_supported = ms->support.global_fclk_change_supported; + outputs->MaxActiveFCLKChangeLatencySupported = ms->MaxActiveFCLKChangeLatencySupported; + outputs->USRRetrainingSupport = ms->support.USRRetrainingSupport; + outputs->global_temp_read_or_ppt_supported = ms->support.global_temp_read_or_ppt_supported; + memcpy(outputs->VActiveLatencyHidingUs, ms->VActiveLatencyHidingUs, sizeof(outputs->VActiveLatencyHidingUs)); + outputs->ImmediateFlipSupported = ms->support.ImmediateFlipSupport; + + outputs->svp0_max_bytes = ms->svp0_max_bytes; + outputs->svp1_max_bytes = ms->svp1_max_bytes; + memcpy(outputs->svp0_max_bytes_per_dpp, ms->svp0_max_bytes_per_dpp, sizeof(outputs->svp0_max_bytes_per_dpp)); + memcpy(outputs->svp0_max_bytes_per_dpp_c, ms->svp0_max_bytes_per_dpp_c, sizeof(outputs->svp0_max_bytes_per_dpp_c)); + memcpy(outputs->svp1_max_bytes_per_dpp, ms->svp1_max_bytes_per_dpp, sizeof(outputs->svp1_max_bytes_per_dpp)); + memcpy(outputs->svp1_max_bytes_per_dpp_c, ms->svp1_max_bytes_per_dpp_c, sizeof(outputs->svp1_max_bytes_per_dpp_c)); + memcpy(outputs->svp0_dst_lines, ms->svp0_dst_lines, sizeof(outputs->svp0_dst_lines)); + memcpy(outputs->svp1_dst_lines, ms->svp1_dst_lines, sizeof(outputs->svp1_dst_lines)); + memcpy(outputs->svp_req_limit, ms->svp_req_limit, sizeof(outputs->svp_req_limit)); + memcpy(outputs->nom_req_limit_alt, ms->nom_req_limit_alt, sizeof(outputs->nom_req_limit_alt)); + memcpy(outputs->min_lead_dst_lines, ms->min_lead_dst_lines, sizeof(outputs->min_lead_dst_lines)); + memcpy(outputs->total_swaths, ms->total_swaths, sizeof(outputs->total_swaths)); + memcpy(outputs->total_swaths_c, ms->total_swaths_c, sizeof(outputs->total_swaths_c)); + memcpy(outputs->prefetch_swaths, ms->prefetch_swaths, sizeof(outputs->prefetch_swaths)); + memcpy(outputs->prefetch_swaths_c, ms->prefetch_swaths_c, sizeof(outputs->prefetch_swaths_c)); + memcpy(outputs->prefetch_hdl_delta, ms->prefetch_hdl_delta, sizeof(outputs->prefetch_hdl_delta)); + memcpy(outputs->recout_hdl_delta, ms->recout_hdl_delta, sizeof(outputs->recout_hdl_delta)); + memcpy(outputs->prefetch_hdl_delta_c, ms->prefetch_hdl_delta_c, sizeof(outputs->prefetch_hdl_delta_c)); + memcpy(outputs->recout_hdl_delta_c, ms->recout_hdl_delta_c, sizeof(outputs->recout_hdl_delta_c)); + memcpy(outputs->max_prefetch_in_lines, ms->max_prefetch_in_lines, sizeof(outputs->max_prefetch_in_lines)); +} + +enum dml2_status dml2_core_dcn6_funcs_populate_programming(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming) +{ + struct dml2_core_internal_display_mode_lib *mode_lib = &core->clean_me_up.mode_lib; + struct dml2_core_calculate_mp_context *calc_mp_ctx = &core->scratch.mode_programming_locals.calc_mp_ctx; + enum dml2_status status; + + DML_LOG_COMP_IF_ENTER(); + memset(&mode_lib->scratch, 0, sizeof(struct dml2_core_internal_scratch)); + memset(&mode_lib->mp, 0, sizeof(struct dml2_core_internal_mode_program)); + memset(&core->scratch.mode_programming_locals.temp_result, 0, sizeof(struct dml2_validation_result)); + + status = core->validate_solution(core, solution, &core->scratch.mode_programming_locals.temp_result); + DML_ASSERT_MSG(status == DML2_STATUS_OK, + "an invalid solution is passed into populate_programming interface! (status = %s)\n", + dml2_status_str(status)); + + dcn6_mp_initialize_from_ms(&mode_lib->mp, &mode_lib->ms); + dcn6_mp_initialize_from_solution(&mode_lib->mp, solution, core->utm_soc_bb); + dcn6_mp_build_calculate_mp_context(calc_mp_ctx, core, solution); + dcn6_calculate_mode_programming(calc_mp_ctx, &mode_lib->mp); + dcn6_populate_mode_programming(programming, &mode_lib->scratch, mode_lib, solution, core->utm_soc_bb); + + DML_LOG_DEBUG("%s exit\n", __func__); + DML_LOG_COMP_IF_EXIT(); + return status; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.h new file mode 100644 index 000000000000..f182c27aea7b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN6_FUNCS_MODE_PROGRAMMING_H__ +#define __DML2_CORE_DCN6_FUNCS_MODE_PROGRAMMING_H__ +#include "dml2_internal_shared_types.h" +enum dml2_status dml2_core_dcn6_funcs_populate_programming(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming); +#endif /* __DML2_CORE_DCN6_FUNCS_MODE_PROGRAMMING_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c new file mode 100644 index 000000000000..d83c7eea462b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c @@ -0,0 +1,4277 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#include "dml2_core_dcn6_funcs_mode_support.h" +#include "dml2_core_utils.h" +#include "dml2_core_dcn5_calcs_dchub.h" +#include "dml2_core_dcn5_calcs_display_pipe.h" +#include "dml2_core_dcn6_calcs_dchub.h" +#include "dml_top_types.h" + +static void dcn6_ms_check_input_sanity( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int k; + const struct dml2_plane_parameters *plane; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.ViewportExceedsSurface = false; + if (!ctx->display_cfg->overrides.hw.surface_viewport_size_check_disable) { + for (k = 0; k < ctx->display_cfg->num_planes; k++) { + plane = &ctx->display_cfg->plane_descriptors[k]; + if (plane->composition.viewport.plane0.width > plane->surface.plane0.width + || plane->composition.viewport.plane0.height > plane->surface.plane0.height) { + outputs->support.ViewportExceedsSurface = true; + DML_LOG_VERBOSE("DML::%s: k=%u ViewportWidth = %ld\n", __func__, k, plane->composition.viewport.plane0.width); + DML_LOG_VERBOSE("DML::%s: k=%u SurfaceWidthY = %ld\n", __func__, k, plane->surface.plane0.width); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportHeight = %ld\n", __func__, k, plane->composition.viewport.plane0.height); + DML_LOG_VERBOSE("DML::%s: k=%u SurfaceHeightY = %ld\n", __func__, k, plane->surface.plane0.height); + DML_LOG_VERBOSE("DML::%s: k=%u ViewportExceedsSurface = %d\n", __func__, k, outputs->support.ViewportExceedsSurface); + } + if (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) + || plane->pixel_format == dml2_rgbe_alpha) { + if (plane->composition.viewport.plane1.width > plane->surface.plane1.width + || plane->composition.viewport.plane1.height > plane->surface.plane1.height) { + outputs->support.ViewportExceedsSurface = true; + } + } + } + } + + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_desired_output_bpp( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + dml2_core_utils_get_stream_output_bpp(outputs->DesiredOutputBpp, display_cfg); + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->DesiredOutputBpp, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_max_det_and_min_compressed_buffer_size( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + dcn5_calculate_max_det_and_min_compressed_buffer_size( + ip->config_return_buffer_size_in_kbytes, + ip->config_return_buffer_segment_size_in_kbytes, + ip->rob_buffer_size_kbytes, + ip->max_num_dpp, + display_cfg->overrides.hw.force_nom_det_size_kbytes.enable, + display_cfg->overrides.hw.force_nom_det_size_kbytes.value, + ip->dcn_mrq_present, + + /* Output */ + &outputs->MaxTotalDETInKByte, + &outputs->NomDETInKByte, + &outputs->MinCompressedBufferSizeInKByte); + + DML_LOG_DEBUG_UINT(outputs->MaxTotalDETInKByte); + DML_LOG_DEBUG_UINT(outputs->MinCompressedBufferSizeInKByte); + DML_LOG_DEBUG_UINT(outputs->NomDETInKByte); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_effective_pixel_clock( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + /* + * This function should probably be removed since ptoi is never true, so the function is a noop; not really + * obvious if the comment means DML2.1 doesn't support interlace today. + */ + dcn5_adjust_pixel_clock_for_progressive_to_interlace_unit(display_cfg, ip->ptoi_supported, outputs->PixelClockBackEnd); + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->PixelClockBackEnd, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_scaler_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + /*Scale Ratio, taps Support Check*/ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + outputs->support.ScaleRatioAndTapsSupport = true; + // Many core tests are still setting scaling parameters "incorrectly" + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->composition.scaler_info.enabled == false + && (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) || dml2_core_utils_is_422_packed(plane->pixel_format) + || plane->composition.scaler_info.plane0.h_ratio != 1.0 + || plane->composition.scaler_info.plane0.h_taps != 1.0 + || plane->composition.scaler_info.plane0.v_ratio != 1.0 + || plane->composition.scaler_info.plane0.v_taps != 1.0)) { + outputs->support.ScaleRatioAndTapsSupport = false; + } else if (plane->composition.scaler_info.plane0.v_taps < 1.0 + || plane->composition.scaler_info.plane0.v_taps > 8.0 + || plane->composition.scaler_info.plane0.h_taps < 1.0 + || plane->composition.scaler_info.plane0.h_taps > 8.0 + || (plane->composition.scaler_info.plane0.h_taps > 1.0 + && (plane->composition.scaler_info.plane0.h_taps % 2) == 1) + || plane->composition.scaler_info.plane0.h_ratio > ip->max_hscl_ratio + || plane->composition.scaler_info.plane0.v_ratio > ip->max_vscl_ratio + || plane->composition.scaler_info.plane0.h_ratio > plane->composition.scaler_info.plane0.h_taps + || plane->composition.scaler_info.plane0.v_ratio > plane->composition.scaler_info.plane0.v_taps + || ((dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format)) + && (plane->composition.scaler_info.plane1.v_taps < 1 + || plane->composition.scaler_info.plane1.v_taps > 8 + || plane->composition.scaler_info.plane1.h_taps < 1 + || plane->composition.scaler_info.plane1.h_taps > 8 + || (plane->composition.scaler_info.plane1.h_taps > 1 + && plane->composition.scaler_info.plane1.h_taps % 2 == 1) + || plane->composition.scaler_info.plane1.h_ratio > ip->max_hscl_ratio + || plane->composition.scaler_info.plane1.v_ratio > ip->max_vscl_ratio + || plane->composition.scaler_info.plane1.h_ratio > plane->composition.scaler_info.plane1.h_taps + || plane->composition.scaler_info.plane1.v_ratio > plane->composition.scaler_info.plane1.v_taps))) { + outputs->support.ScaleRatioAndTapsSupport = false; + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.ScaleRatioAndTapsSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.ScaleRatioAndTapsSupport; +} + +static bool dcn6_ms_check_source_format_and_scan_direction( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + /*Source Format, Pixel Format and Scan Support Check*/ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + outputs->support.SourceFormatPixelAndScanSupport = true; + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->surface.tiling == dml2_sw_linear + && dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle)) { + outputs->support.SourceFormatPixelAndScanSupport = false; + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.SourceFormatPixelAndScanSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.SourceFormatPixelAndScanSupport; +} + +static void dcn6_ms_calculate_byte_per_pixel_and_block_sizes( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + dcn5_calculate_byte_per_pixel_and_block_sizes( + plane->pixel_format, + plane->surface.tiling, + plane->surface.plane0.pitch, + plane->surface.plane1.pitch, + /* Output */ + &outputs->BytePerPixelY[k], + &outputs->BytePerPixelC[k], + &outputs->BytePerPixelInDETY[k], + &outputs->BytePerPixelInDETC[k], + &outputs->Read256BlockHeightY[k], + &outputs->Read256BlockHeightC[k], + &outputs->Read256BlockWidthY[k], + &outputs->Read256BlockWidthC[k], + &outputs->MacroTileHeightY[k], + &outputs->MacroTileHeightC[k], + &outputs->MacroTileWidthY[k], + &outputs->MacroTileWidthC[k], + &outputs->surf_linear128_l[k], + &outputs->surf_linear128_c[k]); + } + + DML_LOG_DEBUG_ARRAY_BOOL(outputs->surf_linear128_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MacroTileWidthY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->BytePerPixelInDETY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->Read256BlockWidthY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->Read256BlockHeightC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->BytePerPixelInDETC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->BytePerPixelY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->Read256BlockWidthC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MacroTileHeightY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->BytePerPixelC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MacroTileHeightC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->surf_linear128_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->Read256BlockHeightY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MacroTileWidthC, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_read_bandwidth( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_internal_mode_support *inputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + /* Bandwidth Support Check */ + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle)) { + outputs->SwathWidthYSingleDPP[k] = plane->composition.viewport.plane0.width; + outputs->SwathWidthCSingleDPP[k] = plane->composition.viewport.plane1.width; + } else { + outputs->SwathWidthYSingleDPP[k] = plane->composition.viewport.plane0.height; + outputs->SwathWidthCSingleDPP[k] = plane->composition.viewport.plane1.height; + } + } + for (k = 0; k < display_cfg->num_planes ; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + outputs->vactive_sw_bw_l[k] = outputs->SwathWidthYSingleDPP[k] + * math_ceil2(inputs->BytePerPixelY[k], 1.0) + / (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)) + * plane->composition.scaler_info.plane0.v_ratio; + outputs->vactive_sw_bw_c[k] = outputs->SwathWidthCSingleDPP[k] + * math_ceil2(inputs->BytePerPixelC[k], 2.0) + / (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)) + * plane->composition.scaler_info.plane1.v_ratio; + outputs->cursor_bw[k] = plane->cursor.num_cursors + * plane->cursor.cursor_width + * plane->cursor.cursor_bpp + / 8.0 + / (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_l = %f\n", __func__, k, + outputs->vactive_sw_bw_l[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, vactive_sw_bw_c = %f\n", __func__, k, + outputs->vactive_sw_bw_c[k]); + } + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->vactive_sw_bw_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->SwathWidthYSingleDPP, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->vactive_sw_bw_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->cursor_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->SwathWidthCSingleDPP, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_writeback_bandwidth( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k, j; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + // Writeback bandwidth + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + outputs->WriteBandwidth[k][j] = stream->writeback.writeback_stream[j].output_height * stream->writeback.writeback_stream[j].output_width + / (stream->writeback.writeback_stream[j].input_height * stream->timing.h_total / ((double)stream->timing.pixel_clock_khz / 1000)); + if (stream->writeback.writeback_stream[j].pixel_format == dml2_444_64) { + outputs->WriteBandwidth[k][j] *= 8.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_444_32) { + outputs->WriteBandwidth[k][j] *= 4.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8) { + outputs->WriteBandwidth[k][j] *= 1.5; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) { + outputs->WriteBandwidth[k][j] *= 3.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_8) { + outputs->WriteBandwidth[k][j] *= 2.0; + } else if (stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) { + outputs->WriteBandwidth[k][j] *= 4.0; + } else { + outputs->WriteBandwidth[k][j] = 0.0; + } + } + } + + DML_LOG_DEBUG_2D_ARRAY_DOUBLE(outputs->WriteBandwidth, display_cfg->num_planes, DML2_MAX_WRITEBACK);; + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_writeback_bandwidth_latency_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + /*Writeback Latency support check*/ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k, j; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + outputs->support.WritebackLatencySupport = true; + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8 || stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) {// In planar mode just check luma bw does not exceed half latency hiding buffer + if ((inputs->WriteBandwidth[k][j] / 1.5 > + ip->writeback_interface_buffer_size_kbytes * 1024 / 2.0 // half buffer for luma + * ((stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) ? 1.6 : 1.0) // 16 bit frame buffer to 10 bit buffer packing + / soc_bb->writeback_base_latency_us)) + outputs->support.WritebackLatencySupport = false; + else + if ((inputs->WriteBandwidth[k][j] > + ip->writeback_interface_buffer_size_kbytes * 1024 + * ((stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) ? 1.6 : 1.0) + / soc_bb->writeback_base_latency_us)) + outputs->support.WritebackLatencySupport = false; + } + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.WritebackLatencySupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.WritebackLatencySupport; +} + +static bool dcn6_ms_check_writeback_scale_ratio_and_taps_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + /* Writeback Scale Ratio and Taps Support Check */ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + unsigned int j; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + outputs->support.WritebackScaleRatioAndTapsSupport = true; + for (k = 0; k <= display_cfg->num_planes - 1; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + + double h_ratio_chroma; + double output_width_chroma; + + if (stream->writeback.writeback_stream[j].pixel_format == dml2_420_8 || stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_8 + || stream->writeback.writeback_stream[j].pixel_format == dml2_420_10 || stream->writeback.writeback_stream[j].pixel_format == dml2_422_packed_10) { + h_ratio_chroma = 2.0 * stream->writeback.writeback_stream[j].h_ratio; + output_width_chroma = 0.5 * stream->writeback.writeback_stream[j].output_width; + } else { + h_ratio_chroma = stream->writeback.writeback_stream[j].h_ratio; + output_width_chroma = stream->writeback.writeback_stream[j].output_width; + } + + double v_ratio_chroma = ((stream->writeback.writeback_stream[j].pixel_format == dml2_420_8 || stream->writeback.writeback_stream[j].pixel_format == dml2_420_10) ? 2.0 : 1.0) + * stream->writeback.writeback_stream[j].v_ratio; + + if (stream->writeback.writeback_stream[j].h_ratio > ip->writeback_max_hscl_ratio + || stream->writeback.writeback_stream[j].v_ratio > ip->writeback_max_vscl_ratio + || stream->writeback.writeback_stream[j].h_ratio < ip->writeback_min_hscl_ratio + || stream->writeback.writeback_stream[j].v_ratio < ip->writeback_min_vscl_ratio + || stream->writeback.writeback_stream[j].h_taps > (unsigned int) ip->writeback_max_hscl_taps + || stream->writeback.writeback_stream[j].v_taps > (unsigned int) ip->writeback_max_vscl_taps + || stream->writeback.writeback_stream[j].h_taps_chroma > (unsigned int)ip->writeback_max_hscl_taps + || stream->writeback.writeback_stream[j].v_taps_chroma > (unsigned int)ip->writeback_max_vscl_taps + || stream->writeback.writeback_stream[j].h_ratio > (unsigned int)stream->writeback.writeback_stream[j].h_taps + || stream->writeback.writeback_stream[j].v_ratio > (unsigned int)stream->writeback.writeback_stream[j].v_taps + || h_ratio_chroma > (unsigned int)stream->writeback.writeback_stream[j].h_taps_chroma + || v_ratio_chroma > (unsigned int)stream->writeback.writeback_stream[j].v_taps_chroma + || (stream->writeback.writeback_stream[j].h_taps > 2.0 && ((stream->writeback.writeback_stream[j].h_taps % 2) == 1)) + || (stream->writeback.writeback_stream[j].h_taps_chroma > 2.0 && ((stream->writeback.writeback_stream[j].h_taps_chroma % 2) == 1))) { + DML_LOG_VERBOSE("DML::%s: k=%d, j=%d, not support (%d)\n", __func__, k, j, __LINE__); + outputs->support.WritebackScaleRatioAndTapsSupport = false; + } + + double writeback_luma_vextra = (stream->writeback.writeback_stream[j].v_ratio < 1) ? + math_max2(1 - 2.0 / math_ceil2(1 / stream->writeback.writeback_stream[j].v_ratio, 1.0), 0.0) : -1.0; + + if (stream->writeback.writeback_stream[j].output_width * (stream->writeback.writeback_stream[j].v_taps + writeback_luma_vextra) + > ip->writeback_line_buffer_buffer_size / 3.0 / 10.0) { // One third of the buffer per each component Y Cb Cr + DML_LOG_VERBOSE("DML::%s: k=%d, j=%d, not support (%d)\n", __func__, k, j, __LINE__); + outputs->support.WritebackScaleRatioAndTapsSupport = false; + } + + double writeback_chroma_vextra = (v_ratio_chroma < 1) ? math_max2(1 - 2.0 / math_ceil2(1 / v_ratio_chroma, 1.0), 0.0) : -1.0; + + if (output_width_chroma * (stream->writeback.writeback_stream[j].v_taps_chroma + writeback_chroma_vextra) + > ip->writeback_line_buffer_buffer_size / 3.0 / 10.0) { // One third of the buffer per each component Y Cb Cr + DML_LOG_VERBOSE("DML::%s: k=%d, j=%d, not support (%d)\n", __func__, k, j, __LINE__); + outputs->support.WritebackScaleRatioAndTapsSupport = false; + } + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.WritebackScaleRatioAndTapsSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.WritebackScaleRatioAndTapsSupport; +} + +static void dcn6_ms_calculate_single_pipe_dppclk_and_pscl_factor( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + dcn5_calculate_single_pipe_dppclk_and_scl_throughput( + plane->composition.scaler_info.plane0.h_ratio, + plane->composition.scaler_info.plane1.h_ratio, + plane->composition.scaler_info.plane0.v_ratio, + plane->composition.scaler_info.plane1.v_ratio, + ip->max_dchub_pscl_bw_pix_per_clk, + ip->max_pscl_lb_bw_pix_per_clk, + ((double) stream->timing.pixel_clock_khz / 1000), + plane->pixel_format, + plane->composition.scaler_info.plane0.h_taps, + plane->composition.scaler_info.plane1.h_taps, + plane->composition.scaler_info.plane0.v_taps, + plane->composition.scaler_info.plane1.v_taps, + + /* Output */ + &outputs->PSCL_FACTOR[k], + &outputs->PSCL_FACTOR_CHROMA[k], + &outputs->MinDPPCLKUsingSingleDPP[k]); + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->MinDPPCLKUsingSingleDPP, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->PSCL_FACTOR_CHROMA, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->PSCL_FACTOR, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_max_swath_widths( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + // Max Viewport Size support + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + unsigned int lb_buffer_size_bits_luma; + unsigned int lb_buffer_size_bits_chroma; + unsigned int maximumSwathWidthSupportLuma; + unsigned int maximumSwathWidthSupportChroma; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->surface.tiling == dml2_sw_linear) + maximumSwathWidthSupportLuma = 15360; + else if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) + && inputs->BytePerPixelC[k] > 0 + && plane->pixel_format != dml2_rgbe_alpha) + // horz video + maximumSwathWidthSupportLuma = 7680 + 16; + else if (dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) + && inputs->BytePerPixelC[k] > 0 + && plane->pixel_format != dml2_rgbe_alpha) + // vert video + maximumSwathWidthSupportLuma = 4320 + 16; + else if (plane->pixel_format == dml2_rgbe_alpha) + // rgbe + alpha + maximumSwathWidthSupportLuma = 5120 + 16; + else if (dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) + && inputs->BytePerPixelY[k] == 8 + && plane->surface.dcc.enable == true) + // vert 64bpp + maximumSwathWidthSupportLuma = 3072 + 16; + else + maximumSwathWidthSupportLuma = 6144 + 16; + + if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) && dml2_core_utils_is_420(plane->pixel_format)) + maximumSwathWidthSupportChroma = (unsigned int) (maximumSwathWidthSupportLuma / 2.0); + else if (!dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) && dml2_core_utils_is_422_planar(plane->pixel_format)) + maximumSwathWidthSupportChroma = (unsigned int)(maximumSwathWidthSupportLuma / 2.0); + else if (dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle) && dml2_core_utils_is_420(plane->pixel_format)) + maximumSwathWidthSupportChroma = (unsigned int)(maximumSwathWidthSupportLuma / 2.0); + else + maximumSwathWidthSupportChroma = maximumSwathWidthSupportLuma; + + lb_buffer_size_bits_luma = ip->line_buffer_size_bits; + lb_buffer_size_bits_chroma = ip->line_buffer_size_bits; + + outputs->MaximumSwathWidthInLineBufferLuma = lb_buffer_size_bits_luma + * math_max2(plane->composition.scaler_info.plane0.h_ratio, 1.0) + / 57 + / (plane->composition.scaler_info.plane0.v_taps + + math_max2(math_ceil2(plane->composition.scaler_info.plane0.v_ratio, 1.0) - 2, 0.0)); + if (inputs->BytePerPixelC[k] == 0.0) + outputs->MaximumSwathWidthInLineBufferChroma = 0; + else + outputs->MaximumSwathWidthInLineBufferChroma = lb_buffer_size_bits_chroma + * math_max2(plane->composition.scaler_info.plane1.h_ratio, 1.0) + / 57 + / (plane->composition.scaler_info.plane1.v_taps + + math_max2(math_ceil2(plane->composition.scaler_info.plane1.v_ratio, 1.0) - 2, 0.0)); + outputs->MaximumSwathWidthLuma[k] = math_min2(maximumSwathWidthSupportLuma, outputs->MaximumSwathWidthInLineBufferLuma); + outputs->MaximumSwathWidthChroma[k] = math_min2(maximumSwathWidthSupportChroma, outputs->MaximumSwathWidthInLineBufferChroma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthLuma=%f\n", __func__, k, outputs->MaximumSwathWidthLuma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthSupportLuma=%u\n", __func__, k, maximumSwathWidthSupportLuma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthInLineBufferLuma=%f\n", __func__, k, outputs->MaximumSwathWidthInLineBufferLuma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthChroma=%f\n", __func__, k, outputs->MaximumSwathWidthChroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthSupportChroma=%u\n", __func__, k, maximumSwathWidthSupportChroma); + DML_LOG_VERBOSE("DML::%s: k=%u MaximumSwathWidthInLineBufferChroma=%f\n", __func__, k, outputs->MaximumSwathWidthInLineBufferChroma); + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->MaximumSwathWidthLuma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->MaximumSwathWidthChroma, display_cfg->num_planes); + DML_LOG_DEBUG_DOUBLE(outputs->MaximumSwathWidthInLineBufferChroma); + DML_LOG_DEBUG_DOUBLE(outputs->MaximumSwathWidthInLineBufferLuma); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_cursor_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + /* Cursor Support Check */ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + outputs->support.CursorSupport = true; + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->cursor.cursor_width > 0.0) { + if (plane->cursor.cursor_bpp == 64 + && ip->cursor_64bpp_support == false) { + outputs->support.CursorSupport = false; + } + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.CursorSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.CursorSupport; +} + +static bool dcn6_ms_check_surface_alginment_requirements( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + outputs->support.PitchSupport = true; + /* Valid Pitch Check */ + for (k = 0; k < display_cfg->num_planes; k++) { + // data pitch + + plane = &display_cfg->plane_descriptors[k]; + unsigned int pixel_per_element = dml2_core_utils_is_422_packed(plane->pixel_format) ? 2 : 1; + unsigned int alignment_l = inputs->MacroTileWidthY[k]; + if (inputs->surf_linear128_l[k]) + alignment_l = alignment_l / 2; + DML_LOG_VERBOSE("DML::%s: alignment_l = %d\n", __func__, alignment_l); + + outputs->support.AlignedYPitch[k] = (unsigned int)math_ceil2(math_max2(plane->surface.plane0.pitch, plane->surface.plane0.width / pixel_per_element), alignment_l / pixel_per_element); + if (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) || plane->pixel_format == dml2_rgbe_alpha) { + unsigned int alignment_c = inputs->MacroTileWidthC[k]; + + if (inputs->surf_linear128_c[k]) + alignment_c = alignment_c / 2; + outputs->support.AlignedCPitch[k] = (unsigned int)math_ceil2(math_max2(plane->surface.plane1.pitch, plane->surface.plane1.width), alignment_c); + } else { + outputs->support.AlignedCPitch[k] = plane->surface.plane1.pitch; + } + + if (outputs->support.AlignedYPitch[k] > plane->surface.plane0.pitch || + outputs->support.AlignedCPitch[k] > plane->surface.plane1.pitch) { + outputs->support.PitchSupport = false; + DML_LOG_VERBOSE("DML::%s: k=%u AlignedYPitch = %d\n", __func__, k, outputs->support.AlignedYPitch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u PitchY = %ld\n", __func__, k, plane->surface.plane0.pitch); + DML_LOG_VERBOSE("DML::%s: k=%u AlignedCPitch = %d\n", __func__, k, outputs->support.AlignedCPitch[k]); + DML_LOG_VERBOSE("DML::%s: k=%u PitchC = %ld\n", __func__, k, plane->surface.plane1.pitch); + DML_LOG_VERBOSE("DML::%s: k=%u PitchSupport = %d\n", __func__, k, outputs->support.PitchSupport); + } + + // meta pitch + if (ip->dcn_mrq_present && plane->surface.dcc.enable) { + outputs->support.AlignedDCCMetaPitchY[k] = (unsigned int)math_ceil2(math_max2(plane->surface.dcc.plane0.pitch, + plane->surface.plane0.width), 64.0 * inputs->Read256BlockWidthY[k]); + + if (outputs->support.AlignedDCCMetaPitchY[k] > plane->surface.dcc.plane0.pitch) + outputs->support.PitchSupport = false; + + if (dml2_core_utils_is_420(plane->pixel_format) || dml2_core_utils_is_422_planar(plane->pixel_format) || plane->pixel_format == dml2_rgbe_alpha) { + outputs->support.AlignedDCCMetaPitchC[k] = (unsigned int)math_ceil2(math_max2(plane->surface.dcc.plane1.pitch, + plane->surface.plane1.width), 64.0 * inputs->Read256BlockWidthC[k]); + + if (outputs->support.AlignedDCCMetaPitchC[k] > plane->surface.dcc.plane1.pitch) + outputs->support.PitchSupport = false; + } + } else { + outputs->support.AlignedDCCMetaPitchY[k] = 0; + outputs->support.AlignedDCCMetaPitchC[k] = 0; + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.PitchSupport); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.AlignedDCCMetaPitchY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.AlignedDCCMetaPitchC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.AlignedYPitch, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.AlignedCPitch, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); + + return outputs->support.PitchSupport; +} + +static void dcn6_ms_calculate_swath_and_det_configuration_for_single_dpp( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + /* + * FIXME - The whole point of this call seems to be to figure out SingleDPPViewportSizeSupportPerSurface, which + * if 0, means you need 2 DPPs. + */ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *p = + &ctx->func_params->CalculateSwathAndDETConfiguration_params; + + DML_LOG_FUNC_ENTER(); + p->display_cfg = display_cfg; + p->ConfigReturnBufferSizeInKByte = ip->config_return_buffer_size_in_kbytes; + p->MaxTotalDETInKByte = inputs->MaxTotalDETInKByte; + p->MinCompressedBufferSizeInKByte = inputs->MinCompressedBufferSizeInKByte; + p->rob_buffer_size_kbytes = ip->rob_buffer_size_kbytes; + p->pixel_chunk_size_kbytes = ip->pixel_chunk_size_kbytes; + p->rob_buffer_size_kbytes = ip->rob_buffer_size_kbytes; + p->pixel_chunk_size_kbytes = ip->pixel_chunk_size_kbytes; + p->ForceSingleDPP = 1; + p->NumberOfActiveSurfaces = display_cfg->num_planes; + p->nomDETInKByte = inputs->NomDETInKByte; + p->ConfigReturnBufferSegmentSizeInkByte = ip->config_return_buffer_segment_size_in_kbytes; + p->CompressedBufferSegmentSizeInkByte = ip->compressed_buffer_segment_size_in_kbytes; + p->ReadBandwidthLuma = inputs->vactive_sw_bw_l; + p->ReadBandwidthChroma = inputs->vactive_sw_bw_c; + p->MaximumSwathWidthLuma = inputs->MaximumSwathWidthLuma; + p->MaximumSwathWidthChroma = inputs->MaximumSwathWidthChroma; + p->Read256BytesBlockHeightY = inputs->Read256BlockHeightY; + p->Read256BytesBlockHeightC = inputs->Read256BlockHeightC; + p->Read256BytesBlockWidthY = inputs->Read256BlockWidthY; + p->Read256BytesBlockWidthC = inputs->Read256BlockWidthC; + p->surf_linear128_l = inputs->surf_linear128_l; + p->surf_linear128_c = inputs->surf_linear128_c; + p->ODMMode = dummies->dummy_odm_mode; + p->BytePerPixY = inputs->BytePerPixelY; + p->BytePerPixC = inputs->BytePerPixelC; + p->BytePerPixDETY = inputs->BytePerPixelInDETY; + p->BytePerPixDETC = inputs->BytePerPixelInDETC; + p->DPPPerSurface = dummies->dummy_integer_array[2]; + p->mrq_present = ip->dcn_mrq_present; + // output + p->req_per_swath_ub_l = dummies->dummy_integer_array[0]; + p->req_per_swath_ub_c = dummies->dummy_integer_array[1]; + p->swath_width_luma_ub = dummies->dummy_integer_array[3]; + p->swath_width_chroma_ub = dummies->dummy_integer_array[4]; + p->SwathWidth = dummies->dummy_integer_array[5]; + p->SwathWidthChroma = dummies->dummy_integer_array[6]; + p->SwathHeightY = dummies->dummy_integer_array[7]; + p->SwathHeightC = dummies->dummy_integer_array[8]; + p->request_size_bytes_luma = dummies->dummy_integer_array[26]; + p->request_size_bytes_chroma = dummies->dummy_integer_array[27]; + p->DETBufferSizeInKByte = dummies->dummy_integer_array[9]; + p->DETBufferSizeY = dummies->dummy_integer_array[10]; + p->DETBufferSizeC = dummies->dummy_integer_array[11]; + p->full_swath_bytes_l = dummies->dummy_integer_array[12]; + p->full_swath_bytes_c = dummies->dummy_integer_array[13]; + p->full_swath_bytes_single_dpp_l = dummies->dummy_integer_array[14]; + p->full_swath_bytes_single_dpp_c = dummies->dummy_integer_array[15]; + p->UnboundedRequestEnabled = &dummies->dummy_boolean[0]; + p->compbuf_reserved_space_64b = &dummies->dummy_integer[1]; + p->hw_debug5 = &dummies->dummy_boolean[2]; + p->CompressedBufferSizeInkByte = &dummies->dummy_integer[0]; + p->ViewportSizeSupportPerSurface = outputs->SingleDPPViewportSizeSupportPerSurface; + p->ViewportSizeSupport = &dummies->dummy_boolean[1]; + // This calls is just to find out if there is enough DET space to support full vp in 1 pipe. + dcn5_calculate_swath_and_det_configuration(ctx->func_params, p); + + DML_LOG_DEBUG_ARRAY_BOOL(outputs->SingleDPPViewportSizeSupportPerSurface, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); + +} + +static void dcn6_ms_calculate_estimated_num_of_dsc_slices( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_stream_parameters *stream; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + /*Number Of DSC Slices*/ + if (stream->timing.dsc.enable == dml2_dsc_enable + || stream->timing.dsc.enable == dml2_dsc_enable_if_necessary) { + if (stream->timing.dsc.overrides.num_slices != 0) + outputs->EstimatedNumberOfDSCSlices[k] = stream->timing.dsc.overrides.num_slices; + else { + if (inputs->PixelClockBackEnd[k] > 7200) { + outputs->EstimatedNumberOfDSCSlices[k] = 16; + } else if (inputs->PixelClockBackEnd[k] > 3200) { + outputs->EstimatedNumberOfDSCSlices[k] = 12; + } else if (inputs->PixelClockBackEnd[k] > 1360) { + outputs->EstimatedNumberOfDSCSlices[k] = 8; + } else if (inputs->PixelClockBackEnd[k] > 680) { + outputs->EstimatedNumberOfDSCSlices[k] = 4; + } else if (inputs->PixelClockBackEnd[k] > 340) { + outputs->EstimatedNumberOfDSCSlices[k] = 2; + } else { + outputs->EstimatedNumberOfDSCSlices[k] = 1; + } + } + } + } + + DML_LOG_DEBUG_ARRAY_UINT(outputs->EstimatedNumberOfDSCSlices, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_output_link( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + const struct dml2_stream_parameters *stream; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + dcn5_calculate_output_link( + ctx->func_params, + ((double) soc_bb->max_phyclk_khz / 1000), + ((double) soc_bb->max_phyclk_d18_khz / 1000), + ((double) soc_bb->max_phyclk_d32_khz / 1000), + soc_bb->phy_downspread_percent, + stream->output.output_encoder, + stream->output.output_format, + stream->timing.h_total, + stream->timing.h_active, + inputs->PixelClockBackEnd[k], + inputs->DesiredOutputBpp[k], + ip->maximum_dsc_bits_per_component, + inputs->EstimatedNumberOfDSCSlices[k], + stream->output.audio_sample_rate, + stream->output.audio_sample_layout, + stream->overrides.odm_mode, + stream->overrides.odm_mode, + stream->timing.dsc.enable, + stream->output.output_dp_lane_count, + stream->output.output_dp_link_rate, + /* Output */ + &outputs->RequiresDSC[k], + &outputs->RequiresFEC[k], + &outputs->OutputBpp[k], + &outputs->OutputType[k], + &outputs->OutputRate[k], + &outputs->RequiredSlots[k]); + DML_LOG_VERBOSE("DML::%s: k=%d RequiresDSC = %d\n", __func__, k, outputs->RequiresDSC[k]); + } + + DML_LOG_DEBUG_ARRAY_UINT(outputs->RequiredSlots, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->OutputBpp, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_INT(outputs->OutputType, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_INT(outputs->OutputRate, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->RequiresDSC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->RequiresFEC, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_odm_mode( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_stream_parameters *stream; + unsigned int k; + unsigned int totalNumberOfActiveDPP = 0; + unsigned int numOfDPP = 0; + bool ODMSupport = true; + + DML_LOG_FUNC_ENTER(); + outputs->support.ODMSupport = true; + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + dcn5_calculate_odm_mode( + ip->maximum_pixels_per_line_per_dsc_unit, + stream->timing.h_active, + stream->output.output_format, + stream->output.output_encoder, + stream->overrides.odm_mode, + ((double) soc_bb->max_dispclk_khz / 1000), + inputs->RequiresDSC[k], // DSCEnable + totalNumberOfActiveDPP, + ip->max_num_dpp, + ((double) stream->timing.pixel_clock_khz / 1000), + ip->maximum_dsc_slices_per_pipe, + inputs->EstimatedNumberOfDSCSlices[k], + ip->odm_combine_support_mask, + + /* Output */ + &ODMSupport, + &numOfDPP, + &outputs->ODMMode[k], + &outputs->RequiredDISPCLKPerSurface[k]); + + totalNumberOfActiveDPP += numOfDPP; + if (!ODMSupport) + outputs->support.ODMSupport = false; + DML_LOG_VERBOSE("DML::%s: k=%d ODMMode = %d\n", __func__, k, outputs->ODMMode[k]); + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->RequiredDISPCLKPerSurface, display_cfg->num_planes); + DML_LOG_DEBUG_BOOL(outputs->support.ODMSupport); + DML_LOG_DEBUG_ARRAY_INT(outputs->ODMMode, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_num_of_dsc_slices( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_stream_parameters *stream; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + // ensure the number dsc slices is integer multiple based on ODM mode + if (inputs->RequiresDSC[k]) { + outputs->support.NumberOfDSCSlices[k] = inputs->EstimatedNumberOfDSCSlices[k]; + // fail a ms check if the override num_slices doesn't align with odm mode setting + if (stream->timing.dsc.overrides.num_slices == 0) { + // safe guard to ensure the dml derived dsc slices and odm setting are compatible + if (inputs->ODMMode[k] == dml2_odm_mode_combine_2to1) + outputs->support.NumberOfDSCSlices[k] = 2 * (unsigned int) math_ceil2(outputs->support.NumberOfDSCSlices[k] / 2.0, 1.0); + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_3to1) + outputs->support.NumberOfDSCSlices[k] = 12; + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_4to1) + outputs->support.NumberOfDSCSlices[k] = 4 * (unsigned int) math_ceil2(outputs->support.NumberOfDSCSlices[k] / 4.0, 1.0); + } + } + } + + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.NumberOfDSCSlices, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_num_of_dsc_slices_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.DSCSlicesODMModeSupported = true; + for (unsigned int k = 0; k < ctx->display_cfg->num_planes; k++) { + if (inputs->RequiresDSC[k]) { + if (inputs->support.NumberOfDSCSlices[k] == 0) + outputs->support.DSCSlicesODMModeSupported = false; + + if (inputs->ODMMode[k] == dml2_odm_mode_combine_2to1) + outputs->support.DSCSlicesODMModeSupported = + ((inputs->support.NumberOfDSCSlices[k] % 2) == 0); + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_3to1) + outputs->support.DSCSlicesODMModeSupported = + (inputs->support.NumberOfDSCSlices[k] == 12); + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_4to1) + outputs->support.DSCSlicesODMModeSupported = + ((inputs->support.NumberOfDSCSlices[k] % 4) == 0); + if (!outputs->support.DSCSlicesODMModeSupported) { + DML_LOG_VERBOSE("DML::%s: k=%d Invalid dsc num_slices and ODM mode setting\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: k=%d num_slices = %d\n", __func__, k, inputs->support.NumberOfDSCSlices[k]); + DML_LOG_VERBOSE("DML::%s: k=%d ODMMode = %d\n", __func__, k, inputs->ODMMode[k]); + break; + } + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.DSCSlicesODMModeSupported); + DML_LOG_FUNC_EXIT(); + + return outputs->support.DSCSlicesODMModeSupported; +} + +static void dcn6_ms_calculate_num_of_dpp_required( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + outputs->NoOfDPP[k] = 1; + if (inputs->ODMMode[k] == dml2_odm_mode_combine_4to1) { + outputs->NoOfDPP[k] = 4; + } else if (inputs->ODMMode[k] == dml2_odm_mode_combine_3to1) { + outputs->NoOfDPP[k] = 3; + } else if (inputs->ODMMode[k] == dml2_odm_mode_combine_2to1) { + outputs->NoOfDPP[k] = 2; + } else if (plane->overrides.mpcc_combine_factor == 2) { + outputs->MPCCombine[k] = true; + outputs->NoOfDPP[k] = 2; + } else if (plane->overrides.mpcc_combine_factor == 1) { + outputs->NoOfDPP[k] = 1; + if (!inputs->SingleDPPViewportSizeSupportPerSurface[k]) { + DML_LOG_VERBOSE("WARNING: DML::%s: MPCC is override to disable but viewport is too large to be supported with single pipe!\n", __func__); + } + } else { + if ((inputs->MinDPPCLKUsingSingleDPP[k] > ((double) soc_bb->max_dppclk_khz / 1000)) + || !inputs->SingleDPPViewportSizeSupportPerSurface[k]) { + outputs->MPCCombine[k] = true; + outputs->NoOfDPP[k] = 2; + } + } + DML_LOG_VERBOSE("DML::%s: k=%d, NoOfDPP = %d\n", __func__, k, + outputs->NoOfDPP[k]); + } + + DML_LOG_DEBUG_ARRAY_UINT(outputs->NoOfDPP, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->MPCCombine, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_total_available_pipes_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int totalNumOfActiveDPP = 0; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) + totalNumOfActiveDPP += inputs->NoOfDPP[k]; + outputs->support.TotalAvailablePipesSupport = totalNumOfActiveDPP <= (unsigned int)ip->max_num_dpp; + + DML_LOG_DEBUG_BOOL(outputs->support.TotalAvailablePipesSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.TotalAvailablePipesSupport; +} + +static bool dcn6_ms_check_total_available_TDLUT_33cube_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int total_TDLUT_33cube = 0; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) + if ((display_cfg->plane_descriptors[k].tdlut.tdlut_width_mode == dml2_tdlut_width_33_cube) && display_cfg->plane_descriptors[k].tdlut.setup_for_tdlut) + total_TDLUT_33cube += inputs->NoOfDPP[k]; + outputs->support.NumberOfTDLUT33cubeSupport = total_TDLUT_33cube <= (unsigned int)ip->TDLUT_33cube_count; + + DML_LOG_DEBUG_BOOL(outputs->support.NumberOfTDLUT33cubeSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.NumberOfTDLUT33cubeSupport; +} + +static void dcn6_ms_calculate_total_num_of_single_dpp_surfaces( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < ctx->display_cfg->num_planes; k++) { + if (inputs->NoOfDPP[k] == 1) + outputs->TotalNumberOfSingleDPPSurfaces = + outputs->TotalNumberOfSingleDPPSurfaces + 1; + } + + DML_LOG_DEBUG_UINT(outputs->TotalNumberOfSingleDPPSurfaces); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_dispclk_and_dppclk_required( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_clock_granularity_adjuster *clock_adjuster = ctx->clock_adjuster; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k, j; + const struct dml2_stream_parameters *stream; + double writeback_required_dispclk; + + DML_LOG_FUNC_ENTER(); + //DISPCLK/DPPCLK + outputs->WritebackRequiredDISPCLK = 0; + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; ++j) { + writeback_required_dispclk = dcn5_calculate_write_back_dispclk( + stream->writeback.writeback_stream[j].pixel_format, + ((double)stream->timing.pixel_clock_khz / 1000), + inputs->ODMMode[k], + stream->writeback.writeback_stream[j].h_ratio, + stream->writeback.writeback_stream[j].v_ratio, + stream->writeback.writeback_stream[j].h_taps, + stream->writeback.writeback_stream[j].v_taps, + stream->writeback.writeback_stream[j].h_taps_chroma, + stream->writeback.writeback_stream[j].v_taps_chroma, + stream->writeback.writeback_stream[j].input_width, + stream->writeback.writeback_stream[j].output_width, + stream->timing.h_total, + ip->writeback_line_buffer_buffer_size); + outputs->WritebackRequiredDISPCLK = math_max2( + outputs->WritebackRequiredDISPCLK, + writeback_required_dispclk); + } + } + + outputs->RequiredDISPCLK = outputs->WritebackRequiredDISPCLK; + for (k = 0; k < display_cfg->num_planes; k++) { + outputs->RequiredDISPCLK = math_max2( + outputs->RequiredDISPCLK, + inputs->RequiredDISPCLKPerSurface[k]); + } + outputs->RequiredDISPCLK = clock_adjuster->adjust_dispclk_mhz(clock_adjuster, outputs->RequiredDISPCLK); + + for (k = 0; k < display_cfg->num_planes; k++) + outputs->RequiredDPPCLK[k] = inputs->MinDPPCLKUsingSingleDPP[k] / inputs->NoOfDPP[k]; + clock_adjuster->adjust_dppclks_mhz(clock_adjuster, display_cfg->num_planes, outputs->RequiredDPPCLK, + outputs->RequiredDPPCLK, &outputs->GlobalDPPCLK); + + DML_LOG_DEBUG_DOUBLE(outputs->GlobalDPPCLK); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->RequiredDPPCLK, display_cfg->num_planes); + DML_LOG_DEBUG_DOUBLE(outputs->RequiredDISPCLK); + DML_LOG_DEBUG_DOUBLE(outputs->WritebackRequiredDISPCLK); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_dispclk_and_dppclk_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.DISPCLK_DPPCLK_Support = (inputs->RequiredDISPCLK <= ((double) soc_bb->max_dispclk_khz / 1000)) + && (inputs->GlobalDPPCLK <= ((double) soc_bb->max_dppclk_khz / 1000)); + + DML_LOG_DEBUG_BOOL(outputs->support.DISPCLK_DPPCLK_Support); + DML_LOG_FUNC_EXIT(); + + return outputs->support.DISPCLK_DPPCLK_Support; +} + +static bool dcn6_ms_check_otg_count_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int TotalNumberOfActiveOTG = 0; + unsigned int stream_visited_bit_map = 0; + + DML_LOG_FUNC_ENTER(); + for (unsigned int k = 0; k < display_cfg->num_planes; k++) { + /* Check if stream has been visited */ + if (stream_visited_bit_map & (1 << display_cfg->plane_descriptors[k].stream_index)) + continue; + /* Mark stream as visited */ + stream_visited_bit_map |= (1 << display_cfg->plane_descriptors[k].stream_index); + + TotalNumberOfActiveOTG = TotalNumberOfActiveOTG + 1; + } + outputs->support.NumberOfOTGSupport = TotalNumberOfActiveOTG <= ip->max_num_otg; + + DML_LOG_DEBUG_BOOL(outputs->support.NumberOfOTGSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.NumberOfOTGSupport; +} + +static bool dcn6_ms_check_hpo_frl_encoder_count_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int stream_visited_bit_map = 0; + unsigned int totalNumberOfActiveHDMIFRL = 0; + + DML_LOG_FUNC_ENTER(); + for (unsigned int k = 0; k < display_cfg->num_planes; k++) { + /* Check if stream has been visited */ + if (stream_visited_bit_map & (1 << display_cfg->plane_descriptors[k].stream_index)) + continue; + /* Mark stream as visited */ + stream_visited_bit_map |= (1 << display_cfg->plane_descriptors[k].stream_index); + + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_encoder == dml2_hdmifrl) + totalNumberOfActiveHDMIFRL++; + } + outputs->support.NumberOfHDMIFRLSupport = totalNumberOfActiveHDMIFRL <= ip->max_num_hdmi_frl_outputs; + + DML_LOG_DEBUG_BOOL(outputs->support.NumberOfHDMIFRLSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.NumberOfHDMIFRLSupport; +} + +static bool dcn6_ms_check_hpo_dp_encoder_count_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int stream_visited_bit_map = 0; + unsigned int totalNumberOfActiveDP2p0 = 0; + unsigned int totalNumberOfActiveDP2p0Outputs = 0; + + DML_LOG_FUNC_ENTER(); + for (unsigned int k = 0; k < display_cfg->num_planes; k++) { + /* Check if stream has been visited */ + if (stream_visited_bit_map & (1 << display_cfg->plane_descriptors[k].stream_index)) + continue; + /* Mark stream as visited */ + stream_visited_bit_map |= (1 << display_cfg->plane_descriptors[k].stream_index); + + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_encoder == dml2_dp2p0) { + totalNumberOfActiveDP2p0++; + // FIXME_STAGE2: SW not using backend related stuff, need mapping for mst setup + //if (display_cfg->output.OutputMultistreamId[k] == k || display_cfg->output.OutputMultistreamEn[k] == false) { + totalNumberOfActiveDP2p0Outputs++; + } + } + outputs->support.NumberOfDP2p0Support = (totalNumberOfActiveDP2p0 <= ip->max_num_dp2p0_streams) + && (totalNumberOfActiveDP2p0Outputs <= ip->max_num_dp2p0_outputs); + + DML_LOG_DEBUG_BOOL(outputs->support.NumberOfDP2p0Support); + DML_LOG_FUNC_EXIT(); + + return outputs->support.NumberOfDP2p0Support; +} + +static bool dcn6_ms_check_writeback_count_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int stream_visited_bit_map = 0; + unsigned int totalNumberOfActiveWriteback = 0; + bool writeback_per_stream_supported = true; + + DML_LOG_FUNC_ENTER(); + for (unsigned int k = 0; k < display_cfg->num_planes; k++) { + /* Check if stream has been visited */ + if (stream_visited_bit_map & (1 << display_cfg->plane_descriptors[k].stream_index)) + continue; + /* Mark stream as visited */ + stream_visited_bit_map |= (1 << display_cfg->plane_descriptors[k].stream_index); + + totalNumberOfActiveWriteback += + display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream; + + /* >1 writeback per stream is currently not supported */ + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream > 1) + writeback_per_stream_supported = false; + } + outputs->support.EnoughWritebackUnits = writeback_per_stream_supported && + totalNumberOfActiveWriteback <= ip->max_num_wb; + + DML_LOG_DEBUG_BOOL(outputs->support.EnoughWritebackUnits); + DML_LOG_FUNC_EXIT(); + + return outputs->support.EnoughWritebackUnits; +} + +static bool dcn6_ms_check_link_bandwidth_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + unsigned int k; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + outputs->support.LinkCapacitySupport = true; + + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + if (!dml2_core_utils_is_stream_encoder_required(stream) + || stream->output.output_disabled || !stream->output.validate_output) + continue; + + outputs->support.LinkCapacitySupport &= (inputs->OutputBpp[k] > 0); + } + + DML_LOG_DEBUG_BOOL(outputs->support.LinkCapacitySupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.LinkCapacitySupport; +} + +static void dcn6_ms_check_misc_link_supports( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + + if (!dml2_core_utils_is_stream_encoder_required(stream)) + continue; + + if (stream->output.output_format == dml2_420 + && stream->timing.interlaced + && ip->ptoi_supported) + outputs->support.P2IWith420 = true; + + if (stream->timing.dsc.enable == dml2_dsc_enable + || stream->timing.dsc.enable == dml2_dsc_enable_if_necessary) { + if (stream->output.output_format == dml2_n422 + && !ip->dsc422_native_support) + outputs->support.DSC422NativeNotSupported = true; + } + + if (dml2_core_utils_is_dp_8b_10b_link_rate(stream->output.output_dp_link_rate) + && !dml2_core_utils_is_dio_dp_encoder(stream)) + outputs->support.LinkRateDoesNotMatchDPVersion = true; + else if (dml2_core_utils_is_dp_128b_132b_link_rate(stream->output.output_dp_link_rate) + && !dml2_core_utils_is_hpo_dp_encoder(stream)) + outputs->support.LinkRateDoesNotMatchDPVersion = true; + + if (dml2_core_utils_is_odm_split(stream->overrides.odm_mode) + && !dml2_core_utils_is_dp_encoder(stream)) + outputs->support.MSOOrODMSplitWithNonDPLink = true; + + if (stream->overrides.odm_mode == dml2_odm_mode_mso_1to2 + && stream->output.output_dp_lane_count < 2) + outputs->support.NotEnoughLanesForMSO = true; + else if (stream->overrides.odm_mode == dml2_odm_mode_mso_1to4 + && stream->output.output_dp_lane_count < 4) + outputs->support.NotEnoughLanesForMSO = true; + } + + DML_LOG_DEBUG_BOOL(outputs->support.P2IWith420); + DML_LOG_DEBUG_BOOL(outputs->support.NotEnoughLanesForMSO); + DML_LOG_DEBUG_BOOL(outputs->support.DSC422NativeNotSupported); + DML_LOG_DEBUG_BOOL(outputs->support.MSOOrODMSplitWithNonDPLink); + DML_LOG_DEBUG_BOOL(outputs->support.LinkRateDoesNotMatchDPVersion); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_dtbclk_required( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_clock_granularity_adjuster *clock_adjuster = ctx->clock_adjuster; + unsigned int k; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + if (stream->output.output_encoder == dml2_hdmifrl) { + outputs->RequiredDTBCLK[k] = dcn5_calculate_required_dtbclk( + inputs->RequiresDSC[k], + inputs->PixelClockBackEnd[k], + stream->output.output_format, + inputs->OutputBpp[k], + inputs->support.NumberOfDSCSlices[k], + stream->timing.h_total, + stream->timing.h_active, + stream->output.audio_sample_rate, + stream->output.audio_sample_layout); + } + } + + clock_adjuster->adjust_dtbclks_mhz(clock_adjuster, display_cfg->num_planes, outputs->RequiredDTBCLK, + outputs->RequiredDTBCLK, &outputs->GlobalDTBCLK); + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->RequiredDTBCLK, display_cfg->num_planes); + DML_LOG_DEBUG_DOUBLE(outputs->GlobalDTBCLK); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_dtbclk_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + bool support = true; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) + if (inputs->RequiredDTBCLK[k] > ((double)soc_bb->max_dtbclk_khz / 1000)) + support = false; + + outputs->support.DTBCLKRequiredMoreThanSupported = !support; + + DML_LOG_DEBUG_BOOL(outputs->support.DTBCLKRequiredMoreThanSupported); + DML_LOG_FUNC_EXIT(); + + return support; +} + +static void dcn6_ms_calculate_dscclk_required( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + unsigned int k; + const struct dml2_stream_parameters *stream; + unsigned int DSCFormatFactor; + double pixelClockBackEndFactor; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + if (!dml2_core_utils_is_encoder_dsc_capable(stream)) + continue; + + if (stream->output.output_format == dml2_420 + || stream->output.output_format == dml2_n422) + DSCFormatFactor = 2; + else + DSCFormatFactor = 1; + + DML_LOG_VERBOSE("DML::%s: k=%u, RequiresDSC = %u\n", __func__, k, inputs->RequiresDSC[k]); + if (!inputs->RequiresDSC[k]) + continue; + + unsigned int num_dsc_units; + + if (inputs->ODMMode[k] == dml2_odm_mode_combine_4to1) + num_dsc_units = 4; + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_3to1) + num_dsc_units = 3; + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_2to1) + num_dsc_units = 2; + else + num_dsc_units = 1; + + pixelClockBackEndFactor = 3.0 * num_dsc_units; + + if (inputs->support.NumberOfDSCSlices[k] > num_dsc_units) + pixelClockBackEndFactor *= 2; + + outputs->required_dscclk_freq_mhz[k] = inputs->PixelClockBackEnd[k] / pixelClockBackEndFactor / (double) DSCFormatFactor; + DML_LOG_VERBOSE("DML::%s: k=%u, PixelClockBackEnd = %f\n", __func__, k, inputs->PixelClockBackEnd[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, required_dscclk_freq_mhz = %f\n", __func__, k, outputs->required_dscclk_freq_mhz[k]); + DML_LOG_VERBOSE("DML::%s: k=%u, DSCFormatFactor = %u\n", __func__, k, DSCFormatFactor); + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->required_dscclk_freq_mhz, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_dscclk_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) + if (inputs->required_dscclk_freq_mhz[k] > (double) soc_bb->max_dscclk_khz / 1000) { + outputs->support.DSCCLKRequiredMoreThanSupported = true; + DML_LOG_VERBOSE("DML::%s: k=%u, DSCCLKRequiredMoreThanSupported = %u\n", + __func__, k, outputs->support.DSCCLKRequiredMoreThanSupported); + break; + } + + DML_LOG_DEBUG_BOOL(outputs->support.DSCCLKRequiredMoreThanSupported); + DML_LOG_FUNC_EXIT(); + + return !outputs->support.DSCCLKRequiredMoreThanSupported; +} + +static void dcn6_ms_check_dsc_engine_supports( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + unsigned int k; + const struct dml2_stream_parameters *stream; + unsigned int totalDSCUnitsRequired = 0; + unsigned int numDSCUnitRequired; + unsigned int stream_visited_bit_map = 0; + unsigned int stream_index; + + DML_LOG_FUNC_ENTER(); + outputs->support.PixelsPerLinePerDSCUnitSupport = true; + for (k = 0; k < display_cfg->num_planes; k++) { + stream_index = display_cfg->plane_descriptors[k].stream_index; + + /* Check if stream has been visited */ + if (stream_visited_bit_map & (1 << stream_index)) + continue; + + /* Mark stream as visited */ + stream_visited_bit_map |= (1 << stream_index); + + if (!inputs->RequiresDSC[k]) + continue; + + if (inputs->ODMMode[k] == dml2_odm_mode_combine_4to1) + numDSCUnitRequired = 4; + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_3to1) + numDSCUnitRequired = 3; + else if (inputs->ODMMode[k] == dml2_odm_mode_combine_2to1) + numDSCUnitRequired = 2; + else + numDSCUnitRequired = 1; + + stream = &display_cfg->stream_descriptors[stream_index]; + + if (stream->timing.h_active > numDSCUnitRequired * ip->maximum_pixels_per_line_per_dsc_unit) + outputs->support.PixelsPerLinePerDSCUnitSupport = false; + totalDSCUnitsRequired += numDSCUnitRequired; + + if (inputs->support.NumberOfDSCSlices[k] > numDSCUnitRequired * ip->maximum_dsc_slices_per_pipe) + outputs->support.NotEnoughDSCSlices = true; + } + + if (totalDSCUnitsRequired > ip->num_dsc) + outputs->support.NotEnoughDSCUnits = true; + + DML_LOG_DEBUG_BOOL(outputs->support.NotEnoughDSCUnits); + DML_LOG_DEBUG_BOOL(outputs->support.NotEnoughDSCSlices); + DML_LOG_DEBUG_BOOL(outputs->support.PixelsPerLinePerDSCUnitSupport); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_dsc_delay( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + /*DSC Delay per state*/ + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + outputs->DSCDelay[k] = dcn5_calculate_dsc_delay_requirement( + inputs->RequiresDSC[k], + inputs->ODMMode[k], + ip->maximum_dsc_bits_per_component, + inputs->DesiredOutputBpp[k] > 0 ? inputs->DesiredOutputBpp[k] : inputs->OutputBpp[k], + stream->timing.h_active, + stream->timing.h_total, + inputs->support.NumberOfDSCSlices[k], + stream->output.output_format, + stream->output.output_encoder, + ((double) stream->timing.pixel_clock_khz / 1000), + inputs->PixelClockBackEnd[k], + inputs->use_legacy_dsc_delay_formula); + } + + DML_LOG_DEBUG_ARRAY_UINT(outputs->DSCDelay, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_swath_and_det_configuration( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params *p = + &ctx->func_params->CalculateSwathAndDETConfiguration_params; + + DML_LOG_FUNC_ENTER(); + // Figure out the swath and DET configuration after the num dpp per plane is figured out + p->display_cfg = display_cfg; + p->ConfigReturnBufferSizeInKByte = ip->config_return_buffer_size_in_kbytes; + p->MaxTotalDETInKByte = inputs->MaxTotalDETInKByte; + p->MinCompressedBufferSizeInKByte = inputs->MinCompressedBufferSizeInKByte; + p->rob_buffer_size_kbytes = ip->rob_buffer_size_kbytes; + p->pixel_chunk_size_kbytes = ip->pixel_chunk_size_kbytes; + p->rob_buffer_size_kbytes = ip->rob_buffer_size_kbytes; + p->pixel_chunk_size_kbytes = ip->pixel_chunk_size_kbytes; + p->ForceSingleDPP = false; + p->NumberOfActiveSurfaces = display_cfg->num_planes; + p->nomDETInKByte = inputs->NomDETInKByte; + p->ConfigReturnBufferSegmentSizeInkByte = ip->config_return_buffer_segment_size_in_kbytes; + p->CompressedBufferSegmentSizeInkByte = ip->compressed_buffer_segment_size_in_kbytes; + p->ReadBandwidthLuma = inputs->vactive_sw_bw_l; + p->ReadBandwidthChroma = inputs->vactive_sw_bw_c; + p->MaximumSwathWidthLuma = inputs->MaximumSwathWidthLuma; + p->MaximumSwathWidthChroma = inputs->MaximumSwathWidthChroma; + p->Read256BytesBlockHeightY = inputs->Read256BlockHeightY; + p->Read256BytesBlockHeightC = inputs->Read256BlockHeightC; + p->Read256BytesBlockWidthY = inputs->Read256BlockWidthY; + p->Read256BytesBlockWidthC = inputs->Read256BlockWidthC; + p->surf_linear128_l = inputs->surf_linear128_l; + p->surf_linear128_c = inputs->surf_linear128_c; + p->ODMMode = inputs->ODMMode; + p->DPPPerSurface = inputs->NoOfDPP; + p->BytePerPixY = inputs->BytePerPixelY; + p->BytePerPixC = inputs->BytePerPixelC; + p->BytePerPixDETY = inputs->BytePerPixelInDETY; + p->BytePerPixDETC = inputs->BytePerPixelInDETC; + p->mrq_present = ip->dcn_mrq_present; + // output + p->req_per_swath_ub_l = outputs->req_per_swath_ub_l; + p->req_per_swath_ub_c = outputs->req_per_swath_ub_c; + p->swath_width_luma_ub = outputs->swath_width_luma_ub; + p->swath_width_chroma_ub = outputs->swath_width_chroma_ub; + p->SwathWidth = outputs->SwathWidthY; + p->SwathWidthChroma = outputs->SwathWidthC; + p->SwathHeightY = outputs->SwathHeightY; + p->SwathHeightC = outputs->SwathHeightC; + p->request_size_bytes_luma = outputs->support.request_size_bytes_luma; + p->request_size_bytes_chroma = outputs->support.request_size_bytes_chroma; + p->DETBufferSizeInKByte = outputs->DETBufferSizeInKByte; // FIXME: This is per pipe but the pipes in plane will use that + p->DETBufferSizeY = outputs->DETBufferSizeY; + p->DETBufferSizeC = outputs->DETBufferSizeC; + p->full_swath_bytes_l = dummies->dummy_integer_array[2]; + p->full_swath_bytes_c = dummies->dummy_integer_array[3]; + p->full_swath_bytes_single_dpp_l = outputs->full_swath_bytes_l; + p->full_swath_bytes_single_dpp_c = outputs->full_swath_bytes_c; + p->UnboundedRequestEnabled = &outputs->UnboundedRequestEnabled; + p->compbuf_reserved_space_64b = &outputs->compbuf_reserved_space_64b; + p->hw_debug5 = &outputs->hw_debug5; + p->CompressedBufferSizeInkByte = &outputs->CompressedBufferSizeInkByte; + p->ViewportSizeSupportPerSurface = dummies->dummy_boolean_array[0]; + p->ViewportSizeSupport = &outputs->support.ViewportSizeSupport; + dcn5_calculate_swath_and_det_configuration(ctx->func_params, p); + + DML_LOG_DEBUG_ARRAY_UINT(outputs->SwathWidthY, display_cfg->num_planes); + DML_LOG_DEBUG_UINT(outputs->CompressedBufferSizeInkByte); + DML_LOG_DEBUG_BOOL(outputs->UnboundedRequestEnabled); + DML_LOG_DEBUG_ARRAY_UINT(outputs->SwathHeightY, display_cfg->num_planes); + DML_LOG_DEBUG_BOOL(outputs->support.ViewportSizeSupport); + DML_LOG_DEBUG_ARRAY_UINT(outputs->SwathWidthC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->DETBufferSizeC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.request_size_bytes_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->swath_width_luma_ub, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->swath_width_chroma_ub, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->SwathHeightC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->DETBufferSizeInKByte, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->DETBufferSizeY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.request_size_bytes_luma, display_cfg->num_planes); + DML_LOG_DEBUG_UINT(outputs->compbuf_reserved_space_64b); + DML_LOG_DEBUG_BOOL(outputs->hw_debug5); + DML_LOG_DEBUG_ARRAY_UINT(outputs->req_per_swath_ub_l, display_cfg->num_planes); + + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_total_num_of_dcc_active_dpp( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + unsigned int k; + const struct dml2_plane_parameters *plane; + + DML_LOG_FUNC_ENTER(); + outputs->TotalNumberOfDCCActiveDPP = 0; + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->surface.dcc.enable == true) { + outputs->TotalNumberOfDCCActiveDPP = + outputs->TotalNumberOfDCCActiveDPP + inputs->NoOfDPP[k]; + } + } + + DML_LOG_DEBUG_UINT(outputs->TotalNumberOfDCCActiveDPP); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_vm_row_and_swath_and_calculate_dcc_meta_cache_requirements( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + struct dml2_core_calcs_CalculateVMRowAndSwath_params *p; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + struct dml2_core_internal_DmlPipe *surfParameters; + + DML_LOG_FUNC_ENTER(); + memset(dummies->dummy_integer_array[0], 0, sizeof(dummies->dummy_integer_array[0])); + + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + surfParameters = &dummies->SurfParameters[k]; + + surfParameters->PixelClock = ((double) stream->timing.pixel_clock_khz / 1000); + surfParameters->DPPPerSurface = inputs->NoOfDPP[k]; + surfParameters->RotationAngle = plane->composition.rotation_angle; + surfParameters->ViewportHeight = plane->composition.viewport.plane0.height; + surfParameters->ViewportHeightC = plane->composition.viewport.plane1.height; + surfParameters->BlockWidth256BytesY = inputs->Read256BlockWidthY[k]; + surfParameters->BlockHeight256BytesY = inputs->Read256BlockHeightY[k]; + surfParameters->BlockWidth256BytesC = inputs->Read256BlockWidthC[k]; + surfParameters->BlockHeight256BytesC = inputs->Read256BlockHeightC[k]; + surfParameters->BlockWidthY = inputs->MacroTileWidthY[k]; + surfParameters->BlockHeightY = inputs->MacroTileHeightY[k]; + surfParameters->BlockWidthC = inputs->MacroTileWidthC[k]; + surfParameters->BlockHeightC = inputs->MacroTileHeightC[k]; + surfParameters->InterlaceEnable = stream->timing.interlaced; + surfParameters->HTotal = stream->timing.h_total; + surfParameters->DCCEnable = plane->surface.dcc.enable; + surfParameters->SourcePixelFormat = plane->pixel_format; + surfParameters->SurfaceTiling = plane->surface.tiling; + surfParameters->BytePerPixelY = inputs->BytePerPixelY[k]; + surfParameters->BytePerPixelC = inputs->BytePerPixelC[k]; + surfParameters->ProgressiveToInterlaceUnitInOPP = ip->ptoi_supported; + surfParameters->VRatio = plane->composition.scaler_info.plane0.v_ratio; + surfParameters->VRatioChroma = plane->composition.scaler_info.plane1.v_ratio; + surfParameters->VTaps = plane->composition.scaler_info.plane0.v_taps; + surfParameters->VTapsChroma = plane->composition.scaler_info.plane1.v_taps; + surfParameters->PitchY = plane->surface.plane0.pitch; + surfParameters->PitchC = plane->surface.plane1.pitch; + surfParameters->ViewportStationary = plane->composition.viewport.stationary; + surfParameters->ViewportXStart = plane->composition.viewport.plane0.x_start; + surfParameters->ViewportYStart = plane->composition.viewport.plane0.y_start; + surfParameters->ViewportXStartC = plane->composition.viewport.plane1.y_start; + surfParameters->ViewportYStartC = plane->composition.viewport.plane1.y_start; + surfParameters->FORCE_ONE_ROW_FOR_FRAME = plane->overrides.hw.force_one_row_for_frame; + surfParameters->SwathHeightY = inputs->SwathHeightY[k]; + surfParameters->SwathHeightC = inputs->SwathHeightC[k]; + surfParameters->DCCMetaPitchY = plane->surface.dcc.plane0.pitch; + surfParameters->DCCMetaPitchC = plane->surface.dcc.plane1.pitch; + surfParameters->UPSPEnabled = plane->composition.scaler_info.upsp_enabled; + surfParameters->UPSPVTaps = plane->composition.scaler_info.upsp_vtaps; + surfParameters->UPSPSamplePositioning = plane->composition.scaler_info.upsp_sample_positioning; + } + + p = &ctx->func_params->CalculateVMRowAndSwath_params; + p->display_cfg = display_cfg; + p->uclk_pstate_switch_modes = inputs->uclk_pstate_switch_modes; + p->NumberOfActiveSurfaces = display_cfg->num_planes; + p->myPipe = dummies->SurfParameters; + p->PTEBufferSizeInRequestsLuma = ip->dpte_buffer_size_in_pte_reqs_luma; + p->PTEBufferSizeInRequestsChroma = ip->dpte_buffer_size_in_pte_reqs_chroma; + p->SwathWidthY = inputs->SwathWidthY; + p->SwathWidthC = inputs->SwathWidthC; + p->DCCMetaBufferSizeBytes = ip->dcc_meta_buffer_size_bytes; + p->mrq_present = ip->dcn_mrq_present; + // output + p->PTEBufferSizeNotExceeded = outputs->PTEBufferSizeNotExceeded; + p->dpte_row_width_luma_ub = outputs->dpte_row_width_luma_ub; + p->dpte_row_width_chroma_ub = outputs->dpte_row_width_chroma_ub; + p->dpte_row_height_luma = outputs->dpte_row_height; + p->dpte_row_height_chroma = outputs->dpte_row_height_chroma; + p->dpte_row_height_linear_luma = outputs->dpte_row_height_linear; // VBA_DELTA + p->dpte_row_height_linear_chroma = outputs->dpte_row_height_linear_chroma; // VBA_DELTA + p->vm_group_bytes = outputs->vm_group_bytes; + p->dpte_group_bytes = outputs->dpte_group_bytes; + p->PixelPTEReqWidthY = outputs->PixelPTEReqWidthY; + p->PixelPTEReqHeightY = outputs->PixelPTEReqHeightY; + p->PTERequestSizeY = outputs->PTERequestSizeY; + p->PixelPTEReqWidthC = outputs->PixelPTEReqWidthC; + p->PixelPTEReqHeightC = outputs->PixelPTEReqHeightC; + p->PTERequestSizeC = outputs->PTERequestSizeC; + p->vmpg_width_y = outputs->vmpg_width_y; + p->vmpg_height_y = outputs->vmpg_height_y; + p->vmpg_width_c = outputs->vmpg_width_c; + p->vmpg_height_c = outputs->vmpg_height_c; + p->dpde0_bytes_per_frame_ub_l = outputs->dpde0_bytes_per_frame_ub_l; + p->dpde0_bytes_per_frame_ub_c = outputs->dpde0_bytes_per_frame_ub_c; + p->PrefetchSourceLinesY = outputs->PrefetchLinesY; + p->PrefetchSourceLinesC = outputs->PrefetchLinesC; + p->VInitPreFillY = outputs->PrefillY; + p->VInitPreFillC = outputs->PrefillC; + p->MaxNumSwathY = outputs->MaxNumSwathY; + p->MaxNumSwathC = outputs->MaxNumSwathC; + p->dpte_row_bw = outputs->dpte_row_bw; + p->PixelPTEBytesPerRow = outputs->DPTEBytesPerRow; + p->dpte_row_bytes_per_row_l = outputs->dpte_row_bytes_per_row_l; + p->dpte_row_bytes_per_row_c = outputs->dpte_row_bytes_per_row_c; + p->vm_bytes = outputs->vm_bytes; + p->use_one_row_for_frame = outputs->use_one_row_for_frame; + p->use_one_row_for_frame_flip = outputs->use_one_row_for_frame_flip; + p->PTE_BUFFER_MODE = outputs->PTE_BUFFER_MODE; + p->BIGK_FRAGMENT_SIZE = outputs->BIGK_FRAGMENT_SIZE; + p->DCCMetaBufferSizeNotExceeded = outputs->DCCMetaBufferSizeNotExceeded; + p->meta_row_bw = outputs->meta_row_bw; + p->meta_row_bytes = outputs->meta_row_bytes; + p->meta_row_bytes_per_row_ub_l = outputs->meta_row_bytes_per_row_ub_l; + p->meta_row_bytes_per_row_ub_c = outputs->meta_row_bytes_per_row_ub_c; + p->meta_req_width_luma = outputs->meta_req_width; + p->meta_req_height_luma = outputs->meta_req_height; + p->meta_row_width_luma = outputs->meta_row_width; + p->meta_row_height_luma = outputs->meta_row_height_luma; + p->meta_pte_bytes_per_frame_ub_l = outputs->meta_pte_bytes_per_frame_ub_l; + p->meta_req_width_chroma = outputs->meta_req_width_chroma; + p->meta_req_height_chroma = outputs->meta_req_height_chroma; + p->meta_row_width_chroma = outputs->meta_row_width_chroma; + p->meta_row_height_chroma = outputs->meta_row_height_chroma; + p->meta_pte_bytes_per_frame_ub_c = outputs->meta_pte_bytes_per_frame_ub_c; + dcn5_calculate_vm_row_and_swath(ctx->func_params, p); + + DML_LOG_DEBUG_ARRAY_BOOL(outputs->PTEBufferSizeNotExceeded, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_width_luma_ub, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_width_chroma_ub, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_height, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_height_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_height_linear, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_height_linear_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->vm_group_bytes, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_group_bytes, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PixelPTEReqWidthY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PixelPTEReqHeightY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PTERequestSizeY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PixelPTEReqWidthC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PixelPTEReqHeightC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PTERequestSizeC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->vmpg_width_y, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->vmpg_height_y, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->vmpg_width_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->vmpg_height_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpde0_bytes_per_frame_ub_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpde0_bytes_per_frame_ub_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PrefetchLinesY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PrefetchLinesC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PrefillY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->PrefillC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MaxNumSwathY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MaxNumSwathC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dpte_row_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->DPTEBytesPerRow, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_bytes_per_row_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->dpte_row_bytes_per_row_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->vm_bytes, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->use_one_row_for_frame, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->use_one_row_for_frame_flip, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->is_using_mall_for_ss, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->PTE_BUFFER_MODE, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->BIGK_FRAGMENT_SIZE, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->DCCMetaBufferSizeNotExceeded, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->meta_row_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_bytes, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_bytes_per_row_ub_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_bytes_per_row_ub_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_req_width, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_req_height, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_width, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_height_luma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_pte_bytes_per_frame_ub_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_req_width_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_req_height_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_width_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_row_height_chroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->meta_pte_bytes_per_frame_ub_c, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_pte_buffer_size_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + outputs->support.PTEBufferSizeNotExceeded = true; + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->PTEBufferSizeNotExceeded[k] == false) + outputs->support.PTEBufferSizeNotExceeded = false; + + DML_LOG_VERBOSE("DML::%s: k=%u, PTEBufferSizeNotExceeded = %u\n", __func__, k, + inputs->PTEBufferSizeNotExceeded[k]); + } + DML_LOG_VERBOSE("DML::%s: PTEBufferSizeNotExceeded = %u\n", __func__, outputs->support.PTEBufferSizeNotExceeded); + DML_LOG_DEBUG_BOOL(outputs->support.PTEBufferSizeNotExceeded); + DML_LOG_FUNC_EXIT(); + + return outputs->support.PTEBufferSizeNotExceeded; +} + +static bool dcn6_ms_check_dcc_meta_cache_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + + DML_LOG_FUNC_ENTER(); + outputs->support.DCCMetaBufferSizeNotExceeded = true; + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->DCCMetaBufferSizeNotExceeded[k] == false) + outputs->support.DCCMetaBufferSizeNotExceeded = false; + + DML_LOG_VERBOSE("DML::%s: k=%u, DCCMetaBufferSizeNotExceeded = %u\n", __func__, k, + inputs->DCCMetaBufferSizeNotExceeded[k]); + } + DML_LOG_VERBOSE("DML::%s: DCCMetaBufferSizeNotExceeded = %u\n", __func__, + outputs->support.DCCMetaBufferSizeNotExceeded); + + DML_LOG_DEBUG_BOOL(outputs->support.DCCMetaBufferSizeNotExceeded); + DML_LOG_FUNC_EXIT(); + + return outputs->support.DCCMetaBufferSizeNotExceeded; +} + +static double dcn6_ms_pstate_type_to_blackout_us( + const struct dml2_core_calculate_ms_context *ctx, + enum dml2_pstate_type pstate_type) +{ + double blackout_us = 0.0; + + switch (pstate_type) { + case dml2_pstate_type_uclk: + blackout_us = ctx->soc_bb->power_management_parameters.dram_clk_change_blackout_us; + break; + case dml2_pstate_type_fclk: + blackout_us = ctx->soc_bb->power_management_parameters.fclk_change_blackout_us; + break; + case dml2_pstate_type_ppt: + blackout_us = ctx->soc_bb->power_management_parameters.g7_ppt_blackout_us; + break; + case dml2_pstate_type_temp_read: + blackout_us = ctx->soc_bb->power_management_parameters.g7_temperature_read_blackout_us; + break; + case dml2_pstate_type_dummy_pstate: + case dml2_pstate_type_count: + default: + blackout_us = 0.0; + } + + return blackout_us; +} + +static void dcn6_ms_calculate_vactive_pstate_requirements( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_calcs_calculate_bytes_to_fetch_required_to_hide_latency_params *p = + &ctx->func_params->calculate_bytes_to_fetch_required_to_hide_latency_params; + unsigned int plane_index; + enum dml2_pstate_type pstate_type; + double blackout_us; + + DML_LOG_FUNC_ENTER(); + /* Max VActive bytes to fetch for any P-State (PPT or UCLK) */ + p->display_cfg = display_cfg; + p->mrq_present = ip->dcn_mrq_present; + p->num_active_planes = display_cfg->num_planes; + p->num_of_dpp = inputs->NoOfDPP; + p->meta_row_height_l = inputs->meta_row_height_luma; + p->meta_row_height_c = inputs->meta_row_height_chroma; + p->meta_row_bytes_per_row_ub_l = inputs->meta_row_bytes_per_row_ub_l; + p->meta_row_bytes_per_row_ub_c = inputs->meta_row_bytes_per_row_ub_c; + p->dpte_row_height_l = inputs->dpte_row_height; + p->dpte_row_height_c = inputs->dpte_row_height_chroma; + p->dpte_bytes_per_row_l = inputs->dpte_row_bytes_per_row_l; + p->dpte_bytes_per_row_c = inputs->dpte_row_bytes_per_row_c; + p->byte_per_pix_l = inputs->BytePerPixelY; + p->byte_per_pix_c = inputs->BytePerPixelC; + p->swath_width_l = inputs->SwathWidthY; + p->swath_width_c = inputs->SwathWidthC; + p->swath_height_l = inputs->SwathHeightY; + p->swath_height_c = inputs->SwathHeightC; + + for (pstate_type = 0; pstate_type < dml2_pstate_type_count; pstate_type++) { + blackout_us = dcn6_ms_pstate_type_to_blackout_us(ctx, pstate_type); + + /* skip unused pstates */ + if (blackout_us <= 0.0) { + continue; + } + + /* determine per plane latency */ + for (plane_index = 0; plane_index < display_cfg->num_planes; plane_index++) { + p->latency_to_hide_us[plane_index] = dcn6_ms_pstate_type_to_blackout_us(ctx, pstate_type); + + /* Only need to consider DRAM blackout time if the plane is using some form of vactive */ + if (pstate_type == dml2_pstate_type_uclk && + !(inputs->uclk_pstate_switch_modes[plane_index] == dml2_pstate_method_vactive || + inputs->uclk_pstate_switch_modes[plane_index] == dml2_pstate_method_fw_vactive_drr)) { + p->latency_to_hide_us[plane_index] = 0.0; + } + } + + /* outputs */ + p->bytes_required_l = outputs->pstate_bytes_required_l[pstate_type]; + p->bytes_required_c = outputs->pstate_bytes_required_c[pstate_type]; + + dcn5_calculate_bytes_to_fetch_required_to_hide_latency(p); + } + + /* Excess VActive bandwidth required to fill DET */ + dcn6_calculate_excess_vactive_bandwidth_required(display_cfg, + outputs->pstate_bytes_required_l, + outputs->pstate_bytes_required_c, + + /* outputs */ + outputs->excess_vactive_fill_bw_l, + outputs->excess_vactive_fill_bw_c); + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->excess_vactive_fill_bw_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->excess_vactive_fill_bw_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_l[dml2_pstate_type_uclk], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_c[dml2_pstate_type_uclk], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_l[dml2_pstate_type_fclk], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_c[dml2_pstate_type_fclk], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_l[dml2_pstate_type_ppt], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_c[dml2_pstate_type_ppt], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_l[dml2_pstate_type_temp_read], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->pstate_bytes_required_c[dml2_pstate_type_temp_read], display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +/* FIXME - break it down according the function name */ +static void dcn6_ms_calculate_det_buffer_time_value_urgent_burst_factor_and_urgent_latency_hiding( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + double line_time_us; + bool cursor_not_enough_urgent_latency_hiding; + unsigned int cursor_lines_per_chunk; + unsigned int cursor_bytes; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + line_time_us = stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000); + cursor_not_enough_urgent_latency_hiding = 0; + dcn5_calculate_cursor_req_attributes( + plane->cursor.cursor_width, + plane->cursor.cursor_bpp, + // output + &cursor_lines_per_chunk, + &outputs->cursor_bytes_per_line[k], + &outputs->cursor_bytes_per_chunk[k], + &cursor_bytes); + dcn5_calculate_cursor_urgent_burst_factor( + ip->cursor_buffer_size, + plane->cursor.cursor_width, + outputs->cursor_bytes_per_chunk[k], + cursor_lines_per_chunk, line_time_us, + inputs->UrgLatency, + + // output + &outputs->UrgentBurstFactorCursor[k], + &cursor_not_enough_urgent_latency_hiding); + outputs->UrgentBurstFactorCursorPre[k] = outputs->UrgentBurstFactorCursor[k]; + DML_LOG_VERBOSE("DML::%s: k=%d, Calling CalculateUrgentBurstFactor\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatio=%f\n", __func__, k, + plane->composition.scaler_info.plane0.v_ratio); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioChroma=%f\n", __func__, k, + plane->composition.scaler_info.plane1.v_ratio); + dcn5_calculate_urgent_burst_factor( + &display_cfg->plane_descriptors[k], + inputs->swath_width_luma_ub[k], + inputs->swath_width_chroma_ub[k], + inputs->SwathHeightY[k], + inputs->SwathHeightC[k], + line_time_us, + inputs->UrgLatency, + plane->composition.scaler_info.plane0.v_ratio, + plane->composition.scaler_info.plane1.v_ratio, + inputs->BytePerPixelInDETY[k], + inputs->BytePerPixelInDETC[k], + inputs->DETBufferSizeY[k], + inputs->DETBufferSizeC[k], + + // Output + &outputs->UrgentBurstFactorLuma[k], + &outputs->UrgentBurstFactorChroma[k], + &outputs->NotEnoughUrgentLatencyHiding[k]); + + outputs->NotEnoughUrgentLatencyHiding[k] = outputs->NotEnoughUrgentLatencyHiding[k] + || cursor_not_enough_urgent_latency_hiding; + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->UrgentBurstFactorCursorPre, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->cursor_bytes_per_chunk, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->UrgentBurstFactorCursor, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->cursor_bytes_per_line, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->UrgentBurstFactorChroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->UrgentBurstFactorLuma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->NotEnoughUrgentLatencyHiding, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_min_dcfclk_deepsleep_clock( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + const struct dml2_clock_granularity_adjuster *clock_adjuster = ctx->clock_adjuster; + double raw_dcfclk_deepsleep_mhz = 0; + + DML_LOG_FUNC_ENTER(); + dcn5_calculate_dcfclk_deep_sleep( + display_cfg, + display_cfg->num_planes, + inputs->BytePerPixelY, + inputs->BytePerPixelC, + inputs->SwathWidthY, + inputs->SwathWidthC, + inputs->NoOfDPP, + inputs->PSCL_FACTOR, + inputs->PSCL_FACTOR_CHROMA, + inputs->RequiredDPPCLK, + inputs->vactive_sw_bw_l, + inputs->vactive_sw_bw_c, + soc_bb->return_bus_width_bytes, + /* Output */ + &raw_dcfclk_deepsleep_mhz); + outputs->dcfclk_deepsleep = clock_adjuster->adjust_dcfclk_deepsleep_mhz( + clock_adjuster, raw_dcfclk_deepsleep_mhz); + DML_LOG_DEBUG_DOUBLE(outputs->dcfclk_deepsleep); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_writeback_delay( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + unsigned int k; + unsigned int j; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + outputs->WritebackDelayTime[k] = 0.0; + for (j = 0; j < stream->writeback.active_writebacks_per_stream; j++) { + outputs->WritebackDelayTime[k] = math_max2(outputs->WritebackDelayTime[k], + soc_bb->writeback_base_latency_us + + dcn5_calculate_write_back_delay( + stream->writeback.writeback_stream[j].pixel_format, + stream->writeback.writeback_stream[j].h_ratio, + stream->writeback.writeback_stream[j].v_ratio, + stream->writeback.writeback_stream[j].v_taps, + stream->writeback.writeback_stream[j].v_taps_chroma, + stream->writeback.writeback_stream[j].output_width, + stream->writeback.writeback_stream[j].output_height, + stream->writeback.writeback_stream[j].input_width, + stream->writeback.writeback_stream[j].input_height, + stream->timing.h_total) + / inputs->RequiredDISPCLK); + } + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->WritebackDelayTime, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_alternate_params(const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_scratch *func_params = ctx->func_params; + struct dml2_core_calcs_calculate_alternate_params *p = &func_params->calculate_alternate_params; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + p->display_cfg = ctx->display_cfg; + p->dst_y_prefetch = inputs->dst_y_prefetch; + p->SwathHeightY = inputs->SwathHeightY; + p->SwathHeightC = inputs->SwathHeightC; + p->SwathWidthY = inputs->SwathWidthY; + p->SwathWidthC = inputs->SwathWidthC; + p->DETBufferSizeY = inputs->DETBufferSizeY; + p->DETBufferSizeC = inputs->DETBufferSizeC; + p->BytePerPixelY = inputs->BytePerPixelY; + p->BytePerPixelC = inputs->BytePerPixelC; + p->BytePerPixelInDETY = inputs->BytePerPixelInDETY; + p->BytePerPixelInDETC = inputs->BytePerPixelInDETC; + p->Read256BlockWidthY = inputs->Read256BlockWidthY; + p->Read256BlockHeightY = inputs->Read256BlockHeightY; + p->Read256BlockWidthC = inputs->Read256BlockWidthC; + p->Read256BlockHeightC = inputs->Read256BlockHeightC; + p->MacroTileWidthY = inputs->MacroTileWidthY; + p->MacroTileWidthC = inputs->MacroTileWidthC; + p->VInitPrefillY = inputs->PrefillY; + p->VInitPrefillC = inputs->PrefillC; + p->VRatioPrefetchY = inputs->VRatioPreY; + p->VRatioPrefetchC = inputs->VRatioPreC; + p->NoOfDPP = inputs->NoOfDPP; + p->max_num_dpp = ctx->ip->max_num_dpp; + p->dram_blackout_us = soc_bb->power_management_parameters.dram_clk_change_blackout_us; + p->VActiveLatencyHidingUs = inputs->VActiveLatencyHidingUs; + p->svp0_dst_lines = inputs->svp0_dst_lines; + p->svp1_dst_lines = inputs->svp1_dst_lines; + p->svp_req_limit = inputs->svp_req_limit; + p->dcn_non_urgent_bandwidth_kbps = inputs->support.bandwidth_upper_bound.dcn5.non_urgent_bandwidth_kbps; + p->alt_chan_fw_delay_us = ctx->ip->alt_chan_fw_delay_us; + p->dst_y_per_vm_vblank = inputs->LinesForVM; + p->dst_y_per_row_vblank = inputs->LinesForDPTERow; + p->DSTYAfterScaler = inputs->DSTYAfterScaler; + p->ODMMode = inputs->ODMMode; + + p->svp0_max_bytes = &outputs->svp0_max_bytes; + p->svp1_max_bytes = &outputs->svp1_max_bytes; + p->svp0_max_bytes_per_dpp = outputs->svp0_max_bytes_per_dpp; + p->svp0_max_bytes_per_dpp_c = outputs->svp0_max_bytes_per_dpp_c; + p->svp1_max_bytes_per_dpp = outputs->svp1_max_bytes_per_dpp; + p->svp1_max_bytes_per_dpp_c = outputs->svp1_max_bytes_per_dpp_c; + p->nom_req_limit_alt = outputs->nom_req_limit_alt; + p->min_lead_dst_lines = outputs->min_lead_dst_lines; + p->total_swaths = outputs->total_swaths; + p->total_swaths_c = outputs->total_swaths_c; + p->prefetch_swaths = outputs->prefetch_swaths; + p->prefetch_swaths_c = outputs->prefetch_swaths_c; + p->prefetch_hdl_delta = outputs->prefetch_hdl_delta; + p->recout_hdl_delta = outputs->recout_hdl_delta; + p->prefetch_hdl_delta_c = outputs->prefetch_hdl_delta_c; + p->recout_hdl_delta_c = outputs->recout_hdl_delta_c; + p->max_prefetch_in_lines = outputs->max_prefetch_in_lines; + p->lsdma_bw_req_for_alt_kbps = &outputs->lsdma_bw_req_for_alt_kbps; + + dcn6_calculate_alternate_params(p); + + DML_LOG_DEBUG_UINT(outputs->svp0_max_bytes); + DML_LOG_DEBUG_UINT(outputs->svp1_max_bytes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp0_max_bytes_per_dpp, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp0_max_bytes_per_dpp_c, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp1_max_bytes_per_dpp, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp1_max_bytes_per_dpp_c, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->nom_req_limit_alt, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->min_lead_dst_lines, ctx->display_cfg->num_streams); + DML_LOG_DEBUG_ARRAY_UINT(outputs->total_swaths, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->total_swaths_c, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->prefetch_swaths, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->prefetch_swaths_c, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->prefetch_hdl_delta, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->recout_hdl_delta, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->prefetch_hdl_delta_c, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->recout_hdl_delta_c, ctx->display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->max_prefetch_in_lines, ctx->display_cfg->num_streams); + DML_LOG_DEBUG_DOUBLE(outputs->lsdma_bw_req_for_alt_kbps); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_alternate_svp_lines(const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_scratch *func_params = ctx->func_params; + struct dml2_core_calcs_calculate_alternate_svp_lines *p = &func_params->calculate_alternate_svp_lines; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + p->display_cfg = ctx->display_cfg; + p->SwathHeightY = inputs->SwathHeightY; + p->SwathHeightC = inputs->SwathHeightC; + p->BytePerPixelInDETC = inputs->BytePerPixelInDETC; + p->dram_blackout_us = soc_bb->power_management_parameters.dram_clk_change_blackout_us; + + p->svp0_dst_lines = outputs->svp0_dst_lines; + p->svp1_dst_lines = outputs->svp1_dst_lines; + p->svp_req_limit = outputs->svp_req_limit; + + dcn6_calculate_alternate_svp_lines(p); + + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp0_dst_lines, ctx->display_cfg->num_streams); + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp1_dst_lines, ctx->display_cfg->num_streams); + DML_LOG_DEBUG_ARRAY_UINT(outputs->svp_req_limit, ctx->display_cfg->num_streams); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_max_vstartup( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + unsigned int k, stream_index; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + // MaximumVStartup is actually Tvstartup_min in DCN4 programming guide + for (k = 0; k < display_cfg->num_planes; k++) { + stream_index = display_cfg->plane_descriptors[k].stream_index; + stream = &display_cfg->stream_descriptors[stream_index]; + outputs->MaximumVStartup[k] = dcn6_calculate_max_vstartup( + ip->ptoi_supported, + ip->vblank_nom_default_us, + &stream->timing, + display_cfg->plane_descriptors[k].overrides.uclk_pstate_change_strategy, + inputs->WritebackDelayTime[k], + inputs->svp0_dst_lines[stream_index] + inputs->svp1_dst_lines[stream_index]); + outputs->MaxVStartupLines[k] = outputs->MaximumVStartup[k]; + } + DML_LOG_VERBOSE("DML::%s: k=%u, MaximumVStartup = %u\n", __func__, k, outputs->MaximumVStartup[k]); + + DML_LOG_DEBUG_ARRAY_UINT(outputs->MaximumVStartup, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->MaxVStartupLines, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_check_average_latency_supports( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + double outstanding_latency_us = 0; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + outputs->support.OutstandingRequestsSupport = true; + outputs->support.OutstandingRequestsUrgencyAvoidance = true; + for (k = 0; k < display_cfg->num_planes; k++) { + outstanding_latency_us = soc_bb->max_outstanding_reqs + * inputs->support.request_size_bytes_luma[k] + / (inputs->DCFCLK * soc_bb->return_bus_width_bytes); + if (outstanding_latency_us < inputs->support.avg_urgent_latency_us) { + outputs->support.OutstandingRequestsSupport = false; + DML_LOG_VERBOSE("DML::%s: DCFCLK = %f\n", __func__, inputs->DCFCLK); + } + if (outstanding_latency_us < inputs->support.avg_non_urgent_latency_us) { + outputs->support.OutstandingRequestsUrgencyAvoidance = false; + } + DML_LOG_VERBOSE("DML::%s: avg_urgent_latency_us = %f\n", __func__, inputs->support.avg_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: avg_non_urgent_latency_us = %f\n", __func__, inputs->support.avg_non_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: k=%d, request_size_bytes_luma = %d\n", __func__, k, inputs->support.request_size_bytes_luma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, outstanding_latency_us = %f (luma)\n", __func__, k, outstanding_latency_us); + if (inputs->BytePerPixelC[k] > 0) { + outstanding_latency_us = soc_bb->max_outstanding_reqs + * inputs->support.request_size_bytes_chroma[k] + / (inputs->DCFCLK * soc_bb->return_bus_width_bytes); + if (outstanding_latency_us < inputs->support.avg_urgent_latency_us) { + outputs->support.OutstandingRequestsSupport = false; + } + if (outstanding_latency_us < inputs->support.avg_non_urgent_latency_us) { + outputs->support.OutstandingRequestsUrgencyAvoidance = false; + } + DML_LOG_VERBOSE("DML::%s: k=%d, request_size_bytes_chroma = %d\n", __func__, k, inputs->support.request_size_bytes_chroma[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, outstanding_latency_us = %f (chroma)\n", __func__, k, outstanding_latency_us); + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.OutstandingRequestsUrgencyAvoidance); + DML_LOG_DEBUG_BOOL(outputs->support.OutstandingRequestsSupport); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_mcache_setting( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + struct dml2_core_calcs_calculate_mcache_setting_params *p; + + DML_LOG_FUNC_ENTER(); + if (soc_bb->mcache_size_bytes == 0) { + for (k = 0; k < display_cfg->num_planes; k++) { + outputs->dcc_dram_bw_nom_overhead_factor_p0[k] = 1.0; + outputs->dcc_dram_bw_pref_overhead_factor_p0[k] = 1.0; + outputs->dcc_dram_bw_nom_overhead_factor_p1[k] = 1.0; + outputs->dcc_dram_bw_pref_overhead_factor_p1[k] = 1.0; + } + } else { + p = &ctx->func_params->calculate_mcache_setting_params; + memset(p, 0, sizeof(struct dml2_core_calcs_calculate_mcache_setting_params)); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + p->dcc_enable = plane->surface.dcc.enable; + p->num_chans = soc_bb->dram_config.channel_count; + p->mem_word_bytes = soc_bb->mem_word_bytes; + p->mcache_size_bytes = soc_bb->mcache_size_bytes; + p->mcache_line_size_bytes = soc_bb->mcache_line_size_bytes; + p->gpuvm_enable = display_cfg->gpuvm_enable; + p->gpuvm_page_size_kbytes = plane->overrides.gpuvm_min_page_size_kbytes; + p->source_format = plane->pixel_format; + p->surf_vert = dml2_core_utils_is_vertical_rotation(plane->composition.rotation_angle); + p->vp_stationary = plane->composition.viewport.stationary; + p->tiling_mode = plane->surface.tiling; + p->imall_enable = ip->imall_supported; + p->vp_start_x_l = plane->composition.viewport.plane0.x_start; + p->vp_start_y_l = plane->composition.viewport.plane0.y_start; + p->full_vp_width_l = plane->composition.viewport.plane0.width; + p->full_vp_height_l = plane->composition.viewport.plane0.height; + p->blk_width_l = inputs->MacroTileWidthY[k]; + p->blk_height_l = inputs->MacroTileHeightY[k]; + p->vmpg_width_l = inputs->vmpg_width_y[k]; + p->vmpg_height_l = inputs->vmpg_height_y[k]; + p->full_swath_bytes_l = inputs->full_swath_bytes_l[k]; + p->bytes_per_pixel_l = inputs->BytePerPixelY[k]; + p->vp_start_x_c = plane->composition.viewport.plane1.x_start; + p->vp_start_y_c = plane->composition.viewport.plane1.y_start; + p->full_vp_width_c = plane->composition.viewport.plane1.width; + p->full_vp_height_c = plane->composition.viewport.plane1.height; + p->blk_width_c = inputs->MacroTileWidthC[k]; + p->blk_height_c = inputs->MacroTileHeightC[k]; + p->vmpg_width_c = inputs->vmpg_width_c[k]; + p->vmpg_height_c = inputs->vmpg_height_c[k]; + p->full_swath_bytes_c = inputs->full_swath_bytes_c[k]; + p->bytes_per_pixel_c = inputs->BytePerPixelC[k]; + // output + p->dcc_dram_bw_nom_overhead_factor_l = &outputs->dcc_dram_bw_nom_overhead_factor_p0[k]; + p->dcc_dram_bw_pref_overhead_factor_l = &outputs->dcc_dram_bw_pref_overhead_factor_p0[k]; + p->dcc_dram_bw_nom_overhead_factor_c = &outputs->dcc_dram_bw_nom_overhead_factor_p1[k]; + p->dcc_dram_bw_pref_overhead_factor_c = &outputs->dcc_dram_bw_pref_overhead_factor_p1[k]; + p->num_mcaches_l = &outputs->num_mcaches_l[k]; + p->mcache_row_bytes_l = &outputs->mcache_row_bytes_l[k]; + p->mcache_row_bytes_per_channel_l = &outputs->mcache_row_bytes_per_channel_l[k]; + p->mcache_offsets_l = outputs->mcache_offsets_l[k]; + p->mcache_shift_granularity_l = &outputs->mcache_shift_granularity_l[k]; + p->num_mcaches_c = &outputs->num_mcaches_c[k]; + p->mcache_row_bytes_c = &outputs->mcache_row_bytes_c[k]; + p->mcache_row_bytes_per_channel_c = &outputs->mcache_row_bytes_per_channel_c[k]; + p->mcache_offsets_c = outputs->mcache_offsets_c[k]; + p->mcache_shift_granularity_c = &outputs->mcache_shift_granularity_c[k]; + p->mall_comb_mcache_l = &outputs->mall_comb_mcache_l[k]; + p->mall_comb_mcache_c = &outputs->mall_comb_mcache_c[k]; + p->lc_comb_mcache = &outputs->lc_comb_mcache[k]; + dcn5_calculate_mcache_setting(ctx->func_params, p); + } + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dcc_dram_bw_pref_overhead_factor_p1, display_cfg->num_planes); + DML_LOG_DEBUG_2D_ARRAY_UINT(outputs->mcache_offsets_l, display_cfg->num_planes, DML2_MAX_MCACHES + 1); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->mall_prefetch_dram_overhead_factor, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->mcache_row_bytes_per_channel_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->lc_comb_mcache, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->mall_comb_mcache_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->mall_prefetch_sdp_overhead_factor, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->mcache_row_bytes_per_channel_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->mcache_shift_granularity_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->mcache_row_bytes_l, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dcc_dram_bw_pref_overhead_factor_p0, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->num_mcaches_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->mall_comb_mcache_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->mcache_shift_granularity_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->num_mcaches_l, display_cfg->num_planes); + DML_LOG_DEBUG_2D_ARRAY_UINT(outputs->mcache_offsets_c, display_cfg->num_planes, DML2_MAX_MCACHES + 1); + DML_LOG_DEBUG_ARRAY_UINT(outputs->mcache_row_bytes_c, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dcc_dram_bw_nom_overhead_factor_p0, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dcc_dram_bw_nom_overhead_factor_p1, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_avg_bandwidth_and_dcfclk_lb_required( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_internal_mode_support *inputs = states; + + DML_LOG_FUNC_ENTER(); + // Average BW support check + dcn5_calculate_avg_bandwidth_required( + *outputs->support.avg_bandwidth_required, + // input + display_cfg->num_planes, + inputs->vactive_sw_bw_l, + inputs->vactive_sw_bw_c, + inputs->cursor_bw, + inputs->dcc_dram_bw_nom_overhead_factor_p0, + inputs->dcc_dram_bw_nom_overhead_factor_p1); + + DML_LOG_VERBOSE("DML::%s: avg_bandwidth_required=%f\n", __func__, outputs->support.avg_bandwidth_required[dml2_core_internal_soc_state_sys_active][dml2_core_internal_bw_sdp]); + + DML_LOG_DEBUG_2D_ARRAY_DOUBLE(outputs->support.avg_bandwidth_required, + dml2_core_internal_soc_state_max, dml2_core_internal_bw_max); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_urgent_latency_hiding_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int k; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.EnoughUrgentLatencyHidingSupport = true; + + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->NotEnoughUrgentLatencyHiding[k]) { + outputs->support.EnoughUrgentLatencyHidingSupport = false; + DML_LOG_VERBOSE("DML::%s: k=%u NotEnoughUrgentLatencyHiding set\n", __func__, k); + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.EnoughUrgentLatencyHidingSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.EnoughUrgentLatencyHidingSupport; +} + +static void dcn6_ms_calculate_t_calc( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + (void)ctx; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->TimeCalc = 24 / inputs->dcfclk_deepsleep; + + DML_LOG_DEBUG_DOUBLE(outputs->TimeCalc); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_hostvm_inefficiency_factor( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + dcn5_calculate_hostvm_inefficiency_factor( + &outputs->HostVMInefficiencyFactor, + &outputs->HostVMInefficiencyFactorPrefetch, + display_cfg->gpuvm_enable, + display_cfg->hostvm_enable, + ip->remote_iommu_outstanding_translations, + soc_bb->max_outstanding_reqs, + 1.0, + 0.5); + + DML_LOG_DEBUG_DOUBLE(outputs->HostVMInefficiencyFactorPrefetch); + DML_LOG_DEBUG_DOUBLE(outputs->HostVMInefficiencyFactor); + DML_LOG_FUNC_EXIT(); +} + +// Using approximate ratio for VM bandwidth + +static void dcn6_ms_calculate_3dlut_settings( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + struct dml2_core_calcs_calculate_tdlut_setting_params *p = &ctx->func_params->calculate_tdlut_setting_params; + + DML_LOG_FUNC_ENTER(); + outputs->Total3dlutActive = 0; + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + if (plane->tdlut.setup_for_tdlut) + outputs->Total3dlutActive = outputs->Total3dlutActive + 1; + + // Calculate tdlut schedule related terms + p->dispclk_mhz = inputs->RequiredDISPCLK; + p->setup_for_tdlut = plane->tdlut.setup_for_tdlut; + p->tdlut_width_mode = plane->tdlut.tdlut_width_mode; + p->tdlut_addressing_mode = plane->tdlut.tdlut_addressing_mode; + p->cursor_buffer_size = ip->cursor_buffer_size; + p->gpuvm_enable = display_cfg->gpuvm_enable; + p->gpuvm_page_size_kbytes = plane->overrides.gpuvm_min_page_size_kbytes; + p->tdlut_mpc_width_flag = plane->tdlut.tdlut_mpc_width_flag; + p->is_gfx11 = dml2_core_utils_get_gfx_version(plane->surface.tiling) == 11; + // output + p->tdlut_pte_bytes_per_frame = &outputs->tdlut_pte_bytes_per_frame[k]; + p->tdlut_bytes_per_frame = &outputs->tdlut_bytes_per_frame[k]; + p->tdlut_groups_per_2row_ub = &outputs->tdlut_groups_per_2row_ub[k]; + p->tdlut_opt_time = &outputs->tdlut_opt_time[k]; + p->tdlut_drain_time = &outputs->tdlut_drain_time[k]; + p->tdlut_bytes_per_group = &outputs->tdlut_bytes_per_group[k]; + dcn5_calculate_tdlut_setting(ctx->func_params, p); + } + + DML_LOG_DEBUG_ARRAY_UINT(outputs->tdlut_bytes_per_group, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->tdlut_bytes_per_frame, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->tdlut_drain_time, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->tdlut_opt_time, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->tdlut_pte_bytes_per_frame, display_cfg->num_planes); + DML_LOG_DEBUG_UINT(outputs->Total3dlutActive); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_urgent_latency( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + dcn5_calculate_extra_latency( + display_cfg, + ip->rob_buffer_size_kbytes, + 0, + 0, + inputs->DCFCLK, + 0.0, + ip->pixel_chunk_size_kbytes, + inputs->min_available_urgent_bandwidth_MBps, + display_cfg->num_planes, + inputs->NoOfDPP, + inputs->dpte_group_bytes, + inputs->tdlut_bytes_per_group, + inputs->HostVMInefficiencyFactor, + inputs->HostVMInefficiencyFactorPrefetch, + dml2_qos_param_type_dcn4x, + !(display_cfg->overrides.max_outstanding_when_urgent_expected_disable), + soc_bb->max_outstanding_reqs, + inputs->support.request_size_bytes_luma, + inputs->support.request_size_bytes_chroma, + ip->meta_chunk_size_kbytes, + ip->dchub_arb_to_ret_delay, + inputs->TripToMemory, + ip->hostvm_mode, + // output + &outputs->ExtraLatency, + &outputs->ExtraLatency_sr, + &outputs->ExtraLatencyPrefetch); + + DML_LOG_DEBUG_DOUBLE(outputs->ExtraLatencyPrefetch); + DML_LOG_DEBUG_DOUBLE(outputs->ExtraLatency_sr); + DML_LOG_DEBUG_DOUBLE(outputs->ExtraLatency); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_prefetch_schedule( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + struct dml2_core_internal_DmlPipe *pipe = &dummies->myPipe; + struct dml2_core_calcs_CalculatePrefetchSchedule_params *p = &ctx->func_params->CalculatePrefetchSchedule_params; + double Tvm_trip; + double Tr0_trip; + double prefetch_sw_bytes; + double Tpre_rounded; + double Tpre_oto; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + outputs->TWait[k] = dcn5_calculate_t_wait( + plane->overrides.reserved_vblank_time_ns, + inputs->UrgLatency, + inputs->TripToMemory, + math_max2(soc_bb->power_management_parameters.g7_ppt_blackout_us, soc_bb->power_management_parameters.g7_temperature_read_blackout_us), + display_cfg->stream_descriptors->timing.drr_config.enabled); + pipe->Dppclk = inputs->RequiredDPPCLK[k]; + pipe->Dispclk = inputs->RequiredDISPCLK; + pipe->PixelClock = ((double) stream->timing.pixel_clock_khz / 1000); + pipe->DCFClkDeepSleep = inputs->dcfclk_deepsleep; + pipe->DPPPerSurface = inputs->NoOfDPP[k]; + pipe->ScalerEnabled = plane->composition.scaler_info.enabled; + pipe->VRatio = plane->composition.scaler_info.plane0.v_ratio; + pipe->VRatioChroma = plane->composition.scaler_info.plane1.v_ratio; + pipe->VTaps = plane->composition.scaler_info.plane0.v_taps; + pipe->VTapsChroma = plane->composition.scaler_info.plane1.v_taps; + pipe->RotationAngle = plane->composition.rotation_angle; + pipe->mirrored = plane->composition.mirrored; + pipe->BlockWidth256BytesY = inputs->Read256BlockWidthY[k]; + pipe->BlockHeight256BytesY = inputs->Read256BlockHeightY[k]; + pipe->BlockWidth256BytesC = inputs->Read256BlockWidthC[k]; + pipe->BlockHeight256BytesC = inputs->Read256BlockHeightC[k]; + pipe->InterlaceEnable = stream->timing.interlaced; + pipe->NumberOfCursors = plane->cursor.num_cursors; + pipe->VBlank = stream->timing.v_total - stream->timing.v_active; + pipe->HTotal = stream->timing.h_total; + pipe->HActive = stream->timing.h_active; + pipe->DCCEnable = plane->surface.dcc.enable; + pipe->ODMMode = inputs->ODMMode[k]; + pipe->SourcePixelFormat = plane->pixel_format; + pipe->BytePerPixelY = inputs->BytePerPixelY[k]; + pipe->BytePerPixelC = inputs->BytePerPixelC[k]; + pipe->ProgressiveToInterlaceUnitInOPP = ip->ptoi_supported; + DML_LOG_VERBOSE("DML::%s: Calling CalculatePrefetchSchedule for k=%u\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: MaximumVStartup = %u\n", __func__, inputs->MaximumVStartup[k]); + p->display_cfg = display_cfg; + p->HostVMInefficiencyFactor = inputs->HostVMInefficiencyFactorPrefetch; + p->myPipe = pipe; + p->DSCDelay = inputs->DSCDelay[k]; + p->DPPCLKDelaySubtotalPlusCNVCFormater = ip->dppclk_delay_subtotal + ip->dppclk_delay_cnvc_formatter; + p->DPPCLKDelaySCL = ip->dppclk_delay_scl; + p->DPPCLKDelaySCLLBOnly = ip->dppclk_delay_scl_lb_only; + p->DPPCLKDelayCNVCCursor = ip->dppclk_delay_cnvc_cursor; + p->DISPCLKDelaySubtotal = ip->dispclk_delay_subtotal; + p->DPP_RECOUT_WIDTH = (unsigned int) (inputs->SwathWidthY[k] / plane->composition.scaler_info.plane0.h_ratio); + p->OutputFormat = stream->output.output_format; + p->MaxInterDCNTileRepeaters = ip->max_inter_dcn_tile_repeaters; + p->VStartup = inputs->MaxVStartupLines[k]; + p->HostVMMinPageSize = plane->overrides.hostvm_min_page_size_kbytes; + p->DynamicMetadataEnable = plane->dynamic_meta_data.enable; + p->DynamicMetadataVMEnabled = ip->dynamic_metadata_vm_enabled; + p->DynamicMetadataLinesBeforeActiveRequired = plane->dynamic_meta_data.lines_before_active_required; + p->DynamicMetadataTransmittedBytes = plane->dynamic_meta_data.transmitted_bytes; + p->ExtraLatencyPrefetch = inputs->ExtraLatencyPrefetch; + p->TCalc = inputs->TimeCalc; + p->vm_bytes = inputs->vm_bytes[k]; + p->PixelPTEBytesPerRow = inputs->DPTEBytesPerRow[k]; + p->PrefetchSourceLinesY = inputs->PrefetchLinesY[k]; + p->VInitPreFillY = inputs->PrefillY[k]; + p->MaxNumSwathY = inputs->MaxNumSwathY[k]; + p->PrefetchSourceLinesC = inputs->PrefetchLinesC[k]; + p->VInitPreFillC = inputs->PrefillC[k]; + p->MaxNumSwathC = inputs->MaxNumSwathC[k]; + p->swath_width_luma_ub = inputs->swath_width_luma_ub[k]; + p->swath_width_chroma_ub = inputs->swath_width_chroma_ub[k]; + p->SwathHeightY = inputs->SwathHeightY[k]; + p->SwathHeightC = inputs->SwathHeightC[k]; + p->TWait = outputs->TWait[k]; + p->Ttrip = inputs->TripToMemory; + p->Turg = inputs->UrgLatency; + p->setup_for_tdlut = plane->tdlut.setup_for_tdlut; + p->use_max_lsw = plane->overrides.use_max_lsw; + p->tdlut_pte_bytes_per_frame = inputs->tdlut_pte_bytes_per_frame[k]; + p->tdlut_bytes_per_frame = inputs->tdlut_bytes_per_frame[k]; + p->tdlut_opt_time = inputs->tdlut_opt_time[k]; + p->tdlut_drain_time = inputs->tdlut_drain_time[k]; + p->num_cursors = (plane->cursor.cursor_width > 0); + p->cursor_bytes_per_chunk = inputs->cursor_bytes_per_chunk[k]; + p->cursor_bytes_per_line = inputs->cursor_bytes_per_line[k]; + p->dcc_enable = plane->surface.dcc.enable; + p->mrq_present = ip->dcn_mrq_present; + p->meta_row_bytes = inputs->meta_row_bytes[k]; + // output + p->DSTXAfterScaler = &outputs->DSTXAfterScaler[k]; + p->DSTYAfterScaler = &outputs->DSTYAfterScaler[k]; + p->dst_y_prefetch = &outputs->dst_y_prefetch[k]; + p->dst_y_per_vm_vblank = &outputs->LinesForVM[k]; + p->dst_y_per_row_vblank = &outputs->LinesForDPTERow[k]; + p->VRatioPrefetchY = &outputs->VRatioPreY[k]; + p->VRatioPrefetchC = &outputs->VRatioPreC[k]; + p->RequiredPrefetchPixelDataBWLuma = &outputs->RequiredPrefetchPixelDataBWLuma[k]; // prefetch_sw_bw_l + p->RequiredPrefetchPixelDataBWChroma = &outputs->RequiredPrefetchPixelDataBWChroma[k]; // prefetch_sw_bw_c + p->NotEnoughTimeForDynamicMetadata = &outputs->NoTimeForDynamicMetadata[k]; + p->Tno_bw = &outputs->Tno_bw[k]; + p->Tno_bw_flip = &outputs->Tno_bw_flip[k]; + p->prefetch_vmrow_bw = &outputs->prefetch_vmrow_bw[k]; + p->Tdmdl_vm = &outputs->Tdmdl_vm_raw[k]; + p->Tdmdl = &outputs->Tdmdl_raw[k]; + p->TSetup = &outputs->TSetup[k]; + p->Tvm_trips = &Tvm_trip; + p->Tr0_trips = &Tr0_trip; + p->Tvm_trips_flip = &outputs->Tvm_trips_flip[k]; + p->Tr0_trips_flip = &outputs->Tr0_trips_flip[k]; + p->Tvm_trips_flip_rounded = &outputs->Tvm_trips_flip_rounded[k]; + p->Tr0_trips_flip_rounded = &outputs->Tr0_trips_flip_rounded[k]; + p->VUpdateOffsetPix = &outputs->VUpdateOffsetPix[k]; + p->VUpdateWidthPix = &outputs->VUpdateWidthPix[k]; + p->VReadyOffsetPix = &outputs->VReadyOffsetPix[k]; + p->prefetch_cursor_bw = &outputs->prefetch_cursor_bw[k]; + p->prefetch_sw_bytes = &prefetch_sw_bytes; + p->Tpre_rounded = &Tpre_rounded; + p->Tpre_oto = &Tpre_oto; + + outputs->NoTimeForPrefetch[k] = dcn5_calculate_prefetch_schedule(ctx->func_params, p); + DML_LOG_VERBOSE("DML::%s: k=%d, dst_y_per_vm_vblank = %f\n", __func__, k, *p->dst_y_per_vm_vblank); + DML_LOG_VERBOSE("DML::%s: k=%d, dst_y_per_row_vblank = %f\n", __func__, k, *p->dst_y_per_row_vblank); + outputs->VStartupMin[k] = inputs->MaxVStartupLines[k]; + } + + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->prefetch_vmrow_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->DSTYAfterScaler, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tr0_trips_flip_rounded, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tno_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dst_y_prefetch, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->DSTXAfterScaler, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tr0_trips_flip, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->prefetch_cursor_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->LinesForDPTERow, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->LinesForVM, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->NoTimeForPrefetch, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->NoTimeForDynamicMetadata, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->RequiredPrefetchPixelDataBWLuma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->RequiredPrefetchPixelDataBWChroma, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tvm_trips_flip, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->TWait, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->VRatioPreC, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tvm_trips_flip_rounded, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->VRatioPreY, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tno_bw_flip, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->VReadyOffsetPix, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->TSetup, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tdmdl_vm_raw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->Tdmdl_raw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->VUpdateWidthPix, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->VStartupMin, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_prefetch_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int k; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.PrefetchScheduleSupported = true; + + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->dst_y_prefetch[k] < 2.0 + || inputs->LinesForVM[k] >= 32.0 + || inputs->LinesForDPTERow[k] >= 16.0 + || inputs->NoTimeForPrefetch[k] == true + || inputs->DSTYAfterScaler[k] > 8) { + outputs->support.PrefetchScheduleSupported = false; + DML_LOG_VERBOSE("DML::%s: k=%d, dst_y_prefetch=%f (should not be < 2)\n", __func__, k, inputs->dst_y_prefetch[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, LinesForVM=%f (should not be >= 32)\n", __func__, k, inputs->LinesForVM[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, LinesForDPTERow=%f (should not be >= 16)\n", __func__, k, inputs->LinesForDPTERow[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, DSTYAfterScaler=%d (should be <= 8)\n", __func__, k, inputs->DSTYAfterScaler[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, NoTimeForPrefetch=%d\n", __func__, k, inputs->NoTimeForPrefetch[k]); + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.PrefetchScheduleSupported); + DML_LOG_FUNC_EXIT(); + + return outputs->support.PrefetchScheduleSupported; +} + +static bool dcn6_ms_check_dynamic_metadata_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int k; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.DynamicMetadataSupported = true; + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->NoTimeForDynamicMetadata[k] == true) { + outputs->support.DynamicMetadataSupported = false; + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.DynamicMetadataSupported); + DML_LOG_FUNC_EXIT(); + + return outputs->support.DynamicMetadataSupported; +} + +static bool dcn6_ms_check_v_ratio_in_prefetch_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + outputs->support.VRatioInPrefetchSupported = true; + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->VRatioPreY[k] > __DML2_CALCS_MAX_VRATIO_PRE__ + || inputs->VRatioPreC[k] > __DML2_CALCS_MAX_VRATIO_PRE__) { + outputs->support.VRatioInPrefetchSupported = false; + DML_LOG_VERBOSE("DML::%s: k=%d VRatioPreY = %f (should be <= %f)\n", __func__, k, inputs->VRatioPreY[k], __DML2_CALCS_MAX_VRATIO_PRE__); + DML_LOG_VERBOSE("DML::%s: k=%d VRatioPreC = %f (should be <= %f)\n", __func__, k, inputs->VRatioPreC[k], __DML2_CALCS_MAX_VRATIO_PRE__); + DML_LOG_VERBOSE("DML::%s: VRatioInPrefetchSupported = %u\n", __func__, outputs->support.VRatioInPrefetchSupported); + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.VRatioInPrefetchSupported); + DML_LOG_FUNC_EXIT(); + + return outputs->support.VRatioInPrefetchSupported; +} + +static void dcn6_ms_calculate_urgent_burst_factor_for_prefetch( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + double line_time_us; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + stream = &display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index]; + line_time_us = stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000); + DML_LOG_VERBOSE("DML::%s: k=%d, Calling CalculateUrgentBurstFactor (for prefetch)\n", __func__, k); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioPreY=%f\n", __func__, k, inputs->VRatioPreY[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, VRatioPreC=%f\n", __func__, k, inputs->VRatioPreC[k]); + dcn5_calculate_urgent_burst_factor( + &display_cfg->plane_descriptors[k], + inputs->swath_width_luma_ub[k], + inputs->swath_width_chroma_ub[k], + inputs->SwathHeightY[k], + inputs->SwathHeightC[k], + line_time_us, + inputs->UrgLatency, + inputs->VRatioPreY[k], + inputs->VRatioPreC[k], + inputs->BytePerPixelInDETY[k], + inputs->BytePerPixelInDETC[k], + inputs->DETBufferSizeY[k], + inputs->DETBufferSizeC[k], + /* Output */ + &outputs->UrgentBurstFactorLumaPre[k], + &outputs->UrgentBurstFactorChromaPre[k], + &outputs->NotEnoughUrgentLatencyHidingPre[k]); + } + + DML_LOG_DEBUG_ARRAY_BOOL(outputs->NotEnoughUrgentLatencyHidingPre, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->UrgentBurstFactorLumaPre, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->UrgentBurstFactorChromaPre, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_peak_bandwidth_required( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_calcs_calculate_peak_bandwidth_required_params *p = + &ctx->func_params->calculate_peak_bandwidth_params; + + DML_LOG_FUNC_ENTER(); + p->urg_vactive_bandwidth_required = outputs->support.urg_vactive_bandwidth_required; + p->urg_bandwidth_required = outputs->support.urg_bandwidth_required; + p->urg_bandwidth_required_qual = dummies->dummy_bw; + p->non_urg_bandwidth_required = outputs->support.non_urg_bandwidth_required; + p->surface_avg_vactive_required_bw = outputs->surface_avg_vactive_required_bw; + p->surface_peak_required_bw = dummies->surface_dummy_bw; + p->display_cfg = display_cfg; + p->inc_flip_bw = 0; + p->num_active_planes = display_cfg->num_planes; + p->num_of_dpp = inputs->NoOfDPP; + p->dcc_dram_bw_nom_overhead_factor_p0 = inputs->dcc_dram_bw_nom_overhead_factor_p0; + p->dcc_dram_bw_nom_overhead_factor_p1 = inputs->dcc_dram_bw_nom_overhead_factor_p1; + p->dcc_dram_bw_pref_overhead_factor_p0 = inputs->dcc_dram_bw_pref_overhead_factor_p0; + p->dcc_dram_bw_pref_overhead_factor_p1 = inputs->dcc_dram_bw_pref_overhead_factor_p1; + p->surface_read_bandwidth_l = inputs->vactive_sw_bw_l; + p->surface_read_bandwidth_c = inputs->vactive_sw_bw_c; + p->prefetch_bandwidth_l = inputs->RequiredPrefetchPixelDataBWLuma; + p->prefetch_bandwidth_c = inputs->RequiredPrefetchPixelDataBWChroma; + p->excess_vactive_fill_bw_l = inputs->excess_vactive_fill_bw_l; + p->excess_vactive_fill_bw_c = inputs->excess_vactive_fill_bw_c; + p->cursor_bw = inputs->cursor_bw; + p->dpte_row_bw = inputs->dpte_row_bw; + p->meta_row_bw = inputs->meta_row_bw; + p->prefetch_cursor_bw = inputs->prefetch_cursor_bw; + p->prefetch_vmrow_bw = inputs->prefetch_vmrow_bw; + p->flip_bw = inputs->final_flip_bw; + p->urgent_burst_factor_l = inputs->UrgentBurstFactorLuma; + p->urgent_burst_factor_c = inputs->UrgentBurstFactorChroma; + p->urgent_burst_factor_cursor = inputs->UrgentBurstFactorCursor; + p->urgent_burst_factor_prefetch_l = inputs->UrgentBurstFactorLumaPre; + p->urgent_burst_factor_prefetch_c = inputs->UrgentBurstFactorChromaPre; + p->urgent_burst_factor_prefetch_cursor = inputs->UrgentBurstFactorCursorPre; + dcn5_calculate_peak_bandwidth_required(ctx->func_params, p); + + p->urg_vactive_bandwidth_required = dummies->dummy_bw; + p->urg_bandwidth_required = outputs->support.urg_bandwidth_required_flip; + p->non_urg_bandwidth_required = outputs->support.non_urg_bandwidth_required_flip; + p->surface_avg_vactive_required_bw = dummies->surface_dummy_bw; + p->surface_peak_required_bw = outputs->surface_peak_required_bw; + p->inc_flip_bw = 1; + dcn5_calculate_peak_bandwidth_required(ctx->func_params, p); + + DML_LOG_DEBUG_2D_ARRAY_DOUBLE(outputs->support.urg_bandwidth_required, + dml2_core_internal_soc_state_max, dml2_core_internal_bw_max); + DML_LOG_DEBUG_3D_ARRAY_DOUBLE(outputs->surface_avg_vactive_required_bw, + dml2_core_internal_soc_state_max, dml2_core_internal_bw_max, display_cfg->num_planes); + DML_LOG_DEBUG_2D_ARRAY_DOUBLE(outputs->support.non_urg_bandwidth_required, + dml2_core_internal_soc_state_max, dml2_core_internal_bw_max); + DML_LOG_DEBUG_2D_ARRAY_DOUBLE(outputs->support.urg_vactive_bandwidth_required, + dml2_core_internal_soc_state_max, dml2_core_internal_bw_max); + DML_LOG_DEBUG_2D_ARRAY_DOUBLE(outputs->support.urg_bandwidth_required_qual, + dml2_core_internal_soc_state_max, dml2_core_internal_bw_max); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_final_prefetch_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int k; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.PrefetchSupported = inputs->support.PrefetchScheduleSupported; + + for (k = 0; k < display_cfg->num_planes; k++) { + if (inputs->NotEnoughUrgentLatencyHidingPre[k]) { + outputs->support.PrefetchSupported = false; + DML_LOG_VERBOSE("DML::%s: k=%d, NotEnoughUrgentLatencyHidingPre=%d\n", __func__, k, inputs->NotEnoughUrgentLatencyHidingPre[k]); + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.PrefetchSupported); + DML_LOG_FUNC_EXIT(); + + return outputs->support.PrefetchSupported; +} + +static void dcn6_ms_calculate_flip_schedule( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + + DML_LOG_FUNC_ENTER(); + outputs->TotImmediateFlipBytes = 0; + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + dcn6_calculate_flip_schedule( + ctx->func_params, + display_cfg->plane_descriptors[k].immediate_flip, + display_cfg->hostvm_enable, + display_cfg->ffbm_enable, + inputs->HostVMInefficiencyFactor, + inputs->Tvm_trips_flip[k], + inputs->Tr0_trips_flip[k], + inputs->Tvm_trips_flip_rounded[k], + inputs->Tr0_trips_flip_rounded[k], + display_cfg->gpuvm_enable, + inputs->vm_bytes[k], + inputs->DPTEBytesPerRow[k], + plane->pixel_format, + (stream->timing.h_total / ((double) stream->timing.pixel_clock_khz / 1000)), + plane->composition.scaler_info.plane0.v_ratio, + plane->composition.scaler_info.plane1.v_ratio, + inputs->Tno_bw_flip[k], + inputs->dpte_row_height[k], + inputs->dpte_row_height_chroma[k], + ip->max_flip_time_us, + ip->max_flip_time_lines, + inputs->meta_row_height_luma[k], + inputs->meta_row_height_chroma[k], + + /* Output */ + &outputs->dst_y_per_vm_flip[k], + &outputs->dst_y_per_row_flip[k], + &outputs->final_flip_bw[k], + &outputs->ImmediateFlipSupportedForPipe[k]); + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dst_y_per_vm_flip, display_cfg->num_planes); + DML_LOG_DEBUG_UINT(outputs->TotImmediateFlipBytes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->ImmediateFlipSupportedForPipe, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->final_flip_bw, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->dst_y_per_row_flip, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_immediate_flip_support(const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + outputs->support.ImmediateFlipSupport = true; + for (k = 0; k < display_cfg->num_planes; ++k) { + if ((display_cfg->plane_descriptors[k].immediate_flip || (display_cfg->gpuvm_enable && (display_cfg->hostvm_enable || display_cfg->ffbm_enable))) + && inputs->ImmediateFlipSupportedForPipe[k] == false) { + outputs->support.ImmediateFlipSupport = false; + DML_LOG_VERBOSE("DML::%s: Pipe %0u not supporting iflip!\n", __func__, k); + } + } + + DML_LOG_DEBUG_BOOL(outputs->support.ImmediateFlipSupport); + DML_LOG_FUNC_EXIT(); + return outputs->support.ImmediateFlipSupport; +} + +static void dcn6_ms_calculate_bandwidth_upper_bound(const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.bandwidth_upper_bound.dcn5.non_urgent_bandwidth_kbps = + (**inputs->support.non_urg_bandwidth_required_flip * 1000); + outputs->support.bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps = + math_max3(**inputs->support.urg_bandwidth_required_flip, + **inputs->support.non_urg_bandwidth_required / ctx->soc_bb->fraction_of_urgent_bandwidth_nominal_target, + **inputs->support.non_urg_bandwidth_required_flip / ctx->soc_bb->fraction_of_urgent_bandwidth_flip_target) * 1000; + DML_LOG_DEBUG_DOUBLE(outputs->support.bandwidth_upper_bound.dcn5.non_urgent_bandwidth_kbps); + DML_LOG_DEBUG_DOUBLE(outputs->support.bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_qos_bandwidth_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_sop_table *sop_table = &ctx->soc_bb->sop_table; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.qos_bandwidth_support = sop_table->is_bw_supported_at_index(sop_table, + &inputs->support.bandwidth_upper_bound, inputs->qos_param_index); + + DML_LOG_DEBUG_BOOL(outputs->support.qos_bandwidth_support); + DML_LOG_FUNC_EXIT(); + + return outputs->support.qos_bandwidth_support; +} + +static bool dcn6_ms_check_reordering_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_core_ip_params *ip = ctx->ip; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + outputs->support.ROBSupport = true; + + //Re-ordering Buffer Support Check + if (((ip->rob_buffer_size_kbytes - ip->pixel_chunk_size_kbytes) * 1024 + / **inputs->support.non_urg_bandwidth_required_flip) >= inputs->support.max_urgent_latency_us) { + outputs->support.ROBSupport = true; + } else { + outputs->support.ROBSupport = false; + } + + DML_LOG_VERBOSE("DML::%s: max_urgent_latency_us = %f\n", __func__, inputs->support.max_urgent_latency_us); + DML_LOG_VERBOSE("DML::%s: ROBSupport = %u\n", __func__, outputs->support.ROBSupport); + + DML_LOG_DEBUG_BOOL(outputs->support.ROBSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.ROBSupport; +} + +static void dcn6_ms_calculate_vactive_det_fill_latency( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + enum dml2_pstate_type pstate_type; + + DML_LOG_FUNC_ENTER(); + /* VActive fill time calculations (informative) */ + for (pstate_type = 0; pstate_type < dml2_pstate_type_count; pstate_type++) { + dcn5_calculate_vactive_det_fill_latency( + display_cfg, + display_cfg->num_planes, + inputs->pstate_bytes_required_l[pstate_type], + inputs->pstate_bytes_required_c[pstate_type], + inputs->dcc_dram_bw_nom_overhead_factor_p0, + inputs->dcc_dram_bw_nom_overhead_factor_p1, + inputs->vactive_sw_bw_l, + inputs->vactive_sw_bw_c, + **inputs->surface_avg_vactive_required_bw, + **inputs->surface_peak_required_bw, + /* outputs */ + outputs->pstate_vactive_det_fill_delay_us[pstate_type]); + } + + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_uclk], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_fclk], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_ppt], display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_temp_read], display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_mode_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states, + enum dml2_status status) +{ + (void)ctx; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + + DML_LOG_FUNC_ENTER(); + if (status != DML2_STATUS_OK) + goto fail; + /*Mode Support, Voltage State and SOC Configuration*/ + if (inputs->support.ScaleRatioAndTapsSupport == false) + goto fail; + if (inputs->support.SourceFormatPixelAndScanSupport == false) + goto fail; + if (inputs->support.ViewportSizeSupport == false) + goto fail; + if (inputs->support.LinkCapacitySupport == false) + goto fail; + if (inputs->support.LinkRateDoesNotMatchDPVersion) + goto fail; + if (inputs->support.MSOOrODMSplitWithNonDPLink) + goto fail; + if (inputs->support.NotEnoughLanesForMSO) + goto fail; + if (inputs->support.P2IWith420) + goto fail; + if (inputs->support.DSC422NativeNotSupported) + goto fail; + if (inputs->support.DSCSlicesODMModeSupported == false) + goto fail; + if (inputs->support.NotEnoughDSCUnits) + goto fail; + if (inputs->support.NotEnoughDSCSlices) + goto fail; + if (inputs->support.DSCCLKRequiredMoreThanSupported) + goto fail; + if (inputs->support.PixelsPerLinePerDSCUnitSupport == false) + goto fail; + if (inputs->support.DTBCLKRequiredMoreThanSupported) + goto fail; + if (inputs->support.ROBSupport == false) + goto fail; + if (inputs->support.OutstandingRequestsSupport == false) + goto fail; + if (inputs->support.OutstandingRequestsUrgencyAvoidance == false) + goto fail; + if (inputs->support.DISPCLK_DPPCLK_Support == false) + goto fail; + if (inputs->support.TotalAvailablePipesSupport == false) + goto fail; + if (inputs->support.NumberOfTDLUT33cubeSupport == false) + goto fail; + if (inputs->support.ODMSupport == false) + goto fail; + if (inputs->support.NumberOfOTGSupport == false) + goto fail; + if (inputs->support.NumberOfHDMIFRLSupport == false) + goto fail; + if (inputs->support.NumberOfDP2p0Support == false) + goto fail; + if (inputs->support.EnoughWritebackUnits == false) + goto fail; + if (inputs->support.WritebackLatencySupport == false) + goto fail; + if (inputs->support.WritebackScaleRatioAndTapsSupport == false) + goto fail; + if (inputs->support.CursorSupport == false) + goto fail; + if (inputs->support.PitchSupport == false) + goto fail; + if (inputs->support.ViewportExceedsSurface) + goto fail; + if (inputs->support.PrefetchSupported == false) + goto fail; + if (inputs->support.EnoughUrgentLatencyHidingSupport == false) + goto fail; + if (inputs->support.DynamicMetadataSupported == false) + goto fail; + if (inputs->support.VRatioInPrefetchSupported == false) + goto fail; + if (inputs->support.PTEBufferSizeNotExceeded == false) + goto fail; + if (inputs->support.DCCMetaBufferSizeNotExceeded == false) + goto fail; + if (inputs->support.global_temp_read_or_ppt_supported == false) + goto fail; + if (inputs->support.dcfclk_support == false) + goto fail; + if (inputs->support.qos_bandwidth_support == false) + goto fail; + if (inputs->support.global_dram_clock_change_supported == false) + if (inputs->support.global_dram_clock_change_support_required) + goto fail; + if (inputs->support.alternate_channel_size_support == false) + goto fail; + + DML_LOG_VERBOSE("DML::%s: mode is supported\n", __func__); + outputs->support.ModeSupport = true; + + DML_LOG_DEBUG_BOOL(outputs->support.ModeSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.ModeSupport; +fail: + dml2_core_utils_print_mode_support_info(&inputs->support, true); + DML_LOG_VERBOSE("DML::%s: mode is NOT supported\n", __func__); + outputs->support.ModeSupport = false; + + DML_LOG_DEBUG_BOOL(outputs->support.ModeSupport); + DML_LOG_FUNC_EXIT(); + + return outputs->support.ModeSupport; +} + +static void dcn6_ms_populate_informative( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int k; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + outputs->support.MPCCombineEnable[k] = inputs->MPCCombine[k]; + outputs->support.DPPPerSurface[k] = inputs->NoOfDPP[k]; + } + for (k = 0; k < display_cfg->num_planes; k++) { + outputs->support.ODMMode[k] = inputs->ODMMode[k]; + outputs->support.DSCEnabled[k] = inputs->RequiresDSC[k]; + outputs->support.FECEnabled[k] = inputs->RequiresFEC[k]; + outputs->support.OutputBpp[k] = inputs->OutputBpp[k]; + outputs->support.OutputType[k] = inputs->OutputType[k]; + outputs->support.OutputRate[k] = inputs->OutputRate[k]; + DML_LOG_VERBOSE("DML::%s: k=%d, ODMMode = %u\n", __func__, k, outputs->support.ODMMode[k]); + DML_LOG_VERBOSE("DML::%s: k=%d, DSCEnabled = %u\n", __func__, k, outputs->support.DSCEnabled[k]); + } + + DML_LOG_DEBUG_ARRAY_BOOL(outputs->support.FECEnabled, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_INT(outputs->support.ODMMode, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_INT(outputs->support.OutputType, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->support.DPPPerSurface, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->support.OutputBpp, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_INT(outputs->support.OutputRate, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->support.MPCCombineEnable, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_BOOL(outputs->support.DSCEnabled, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_get_plane_support_info( + const struct dml2_core_calculate_ms_context *ctx, + const struct dml2_core_internal_mode_support *states, + struct core_plane_support_info *plane_support, + unsigned int plane_idx) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_internal_mode_support *inputs = states; + const struct dml2_stream_parameters *stream = + &display_cfg->stream_descriptors[display_cfg->plane_descriptors[plane_idx].stream_index]; + + DML_LOG_FUNC_ENTER(); + plane_support->nominal_vblank_pstate_latency_hiding_us = (int)( + stream->timing.h_total + / ((double)stream->timing.pixel_clock_khz / 1000) + * inputs->TWait[plane_idx]); + + DML_LOG_VERBOSE("DML::%s: plane_idx=%d, VActiveLatencyHidingMargin = %f\n", __func__, plane_idx, inputs->VActiveLatencyHidingMargin[plane_idx]); + plane_support->dram_change_latency_hiding_margin_in_active = (int)inputs->VActiveLatencyHidingMargin[plane_idx]; + + plane_support->active_latency_hiding_us = (int)inputs->VActiveLatencyHidingUs[plane_idx]; + + plane_support->vactive_det_fill_delay_us[dml2_pstate_type_uclk] = (unsigned int)math_ceil( + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_uclk][plane_idx]); + plane_support->vactive_det_fill_delay_us[dml2_pstate_type_fclk] = (unsigned int)math_ceil( + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_fclk][plane_idx]); + plane_support->vactive_det_fill_delay_us[dml2_pstate_type_ppt] = (unsigned int)math_ceil( + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_ppt][plane_idx]); + plane_support->vactive_det_fill_delay_us[dml2_pstate_type_temp_read] = (unsigned int)math_ceil( + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_temp_read][plane_idx]); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_get_stream_support_info( + const struct dml2_core_calculate_ms_context *ctx, + const struct dml2_core_internal_mode_support *states, + struct core_stream_support_info *stream_support, + unsigned int plane_index) +{ + (void)states; + DML_LOG_FUNC_ENTER(); + stream_support->vblank_reserved_time_us = + ctx->display_cfg->plane_descriptors[plane_index].overrides.reserved_vblank_time_ns / 1000; + DML_LOG_VERBOSE("DML::%s: vblank_reserved_time_us = %u\n", __func__, stream_support->vblank_reserved_time_us); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_populate_mode_support_result( + const struct dml2_core_calculate_ms_context *ctx, + const struct dml2_core_internal_mode_support *states, + struct dml2_core_mode_support_result *result) +{ + unsigned int i, stream_index, stream_bitmask; + int unsigned odm_count, num_odm_output_segments, dpp_count; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_internal_mode_support *inputs = states; + double max_dst_y_pre = 0; + unsigned int max_dst_y_after_scaler = 0; + + DML_LOG_FUNC_ENTER(); + result->global.dispclk_khz = (unsigned int) math_ceil(inputs->RequiredDISPCLK * 1000); + result->global.dpprefclk_khz = (unsigned int)math_ceil(inputs->GlobalDPPCLK * 1000); + result->global.dtbrefclk_khz = (unsigned int)math_ceil(inputs->GlobalDTBCLK * 1000); + result->global.dcfclk_deepsleep_khz = (unsigned int)math_ceil(inputs->dcfclk_deepsleep * 1000); + result->global.active.dcfclk_khz = (unsigned int)math_ceil(inputs->DCFCLK * 1000); + result->global.fclk_pstate_supported = inputs->support.global_fclk_change_supported; + result->global.uclk_pstate_supported = inputs->support.global_dram_clock_change_supported; + + result->global.active.average_bw_sdp_kbps = (unsigned long)math_ceil2((**inputs->support.avg_bandwidth_required * 1000), 1.0); + result->global.active.urgent_bw_sdp_kbps = (unsigned long)math_ceil2((**inputs->support.urg_bandwidth_required_flip * 1000), 1.0); + + result->global.active.average_bw_dram_kbps = (unsigned long)math_ceil2((**inputs->support.avg_bandwidth_required * 1000), 1.0); + result->global.active.urgent_bw_dram_kbps = (unsigned long)math_ceil2((**inputs->support.urg_bandwidth_required_flip * 1000), 1.0); + result->global.alternate_total_bytes_copy_svp0 = inputs->svp0_max_bytes; + result->global.alternate_total_bytes_copy_svp1 = inputs->svp1_max_bytes; + result->global.lsdma_bw_req_for_alt_kbps = (unsigned int)inputs->lsdma_bw_req_for_alt_kbps; + DML_LOG_VERBOSE("DML::%s: result->global.active.urgent_bw_sdp_kbps = %ld\n", __func__, result->global.active.urgent_bw_sdp_kbps); + DML_LOG_VERBOSE("DML::%s: result->global.svp_prefetch.urgent_bw_sdp_kbps = %ld\n", __func__, result->global.svp_prefetch.urgent_bw_sdp_kbps); + DML_LOG_VERBOSE("DML::%s: result->global.active.urgent_bw_dram_kbps = %ld\n", __func__, result->global.active.urgent_bw_dram_kbps); + DML_LOG_VERBOSE("DML::%s: result->global.svp_prefetch.urgent_bw_dram_kbps = %ld\n", __func__, result->global.svp_prefetch.urgent_bw_dram_kbps); + + memcpy(&result->global.watermarks, &inputs->support.watermarks, sizeof(inputs->support.watermarks)); + + for (i = 0; i < display_cfg->num_planes; i++) { + result->per_plane[i].dppclk_khz = (unsigned int)(inputs->RequiredDPPCLK[i] * 1000); + } + + for (stream_index = 0; stream_index < display_cfg->num_streams; stream_index++) { + for (i = 0; i < display_cfg->num_planes; i++) { + if (inputs->DSTYAfterScaler[i] > max_dst_y_after_scaler) + max_dst_y_after_scaler = inputs->DSTYAfterScaler[i]; + if (inputs->dst_y_prefetch[i] > max_dst_y_pre) + max_dst_y_pre = inputs->dst_y_prefetch[i]; + } + result->cfg_support_info.stream_support_info[stream_index].max_dst_y_after_scaler = (unsigned int)max_dst_y_after_scaler + 1; + result->cfg_support_info.stream_support_info[stream_index].max_dst_y_prefetch = (unsigned int)max_dst_y_pre + 1; + } + + stream_bitmask = 0; + for (i = 0; i < display_cfg->num_planes; i++) { + odm_count = 1; + dpp_count = inputs->support.DPPPerSurface[i]; + num_odm_output_segments = 1; + + switch (inputs->support.ODMMode[i]) { + case dml2_odm_mode_bypass: + odm_count = 1; + dpp_count = inputs->support.DPPPerSurface[i]; + break; + case dml2_odm_mode_combine_2to1: + odm_count = 2; + dpp_count = 2; + break; + case dml2_odm_mode_combine_3to1: + odm_count = 3; + dpp_count = 3; + break; + case dml2_odm_mode_combine_4to1: + odm_count = 4; + dpp_count = 4; + break; + case dml2_odm_mode_split_1to2: + case dml2_odm_mode_mso_1to2: + num_odm_output_segments = 2; + break; + case dml2_odm_mode_mso_1to4: + num_odm_output_segments = 4; + break; + case dml2_odm_mode_auto: + default: + odm_count = 1; + dpp_count = inputs->support.DPPPerSurface[i]; + break; + } + + result->cfg_support_info.plane_support_info[i].dpps_used = dpp_count; + + dcn6_ms_get_plane_support_info(ctx, states, &result->cfg_support_info.plane_support_info[i], i); + + stream_index = display_cfg->plane_descriptors[i].stream_index; + + result->per_stream[stream_index].dscclk_khz = (unsigned int)inputs->required_dscclk_freq_mhz[i] * 1000; + DML_LOG_VERBOSE("CORE_DCN4::%s: i=%d stream_index=%d, result->per_stream[stream_index].dscclk_khz = %u\n", __func__, i, stream_index, result->per_stream[stream_index].dscclk_khz); + + if (!((stream_bitmask >> stream_index) & 0x1)) { + result->cfg_support_info.stream_support_info[stream_index].odms_used = odm_count; + result->cfg_support_info.stream_support_info[stream_index].num_odm_output_segments = num_odm_output_segments; + result->cfg_support_info.stream_support_info[stream_index].dsc_enable = inputs->support.DSCEnabled[i]; + result->cfg_support_info.stream_support_info[stream_index].num_dsc_slices = inputs->support.NumberOfDSCSlices[i]; + result->cfg_support_info.stream_support_info[stream_index].alternate_svp0_dst_lines = inputs->svp0_dst_lines[stream_index]; + result->cfg_support_info.stream_support_info[stream_index].alternate_svp1_dst_lines = inputs->svp1_dst_lines[stream_index]; + result->cfg_support_info.stream_support_info[stream_index].max_vstartup_lines = inputs->MaxVStartupLines[i]; + dcn6_ms_get_stream_support_info(ctx, states, &result->cfg_support_info.stream_support_info[stream_index], i); + result->per_stream[stream_index].dtbclk_khz = (unsigned int)(inputs->RequiredDTBCLK[i] * 1000); + stream_bitmask |= 0x1 << stream_index; + } + } + result->bandwidth_upper_bound = inputs->support.bandwidth_upper_bound; + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_dcfclk_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + double urg_bandwidth_required_MBps = inputs->support.bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps / 1000.0; + double non_urg_bandwidth_required_MBps = inputs->support.bandwidth_upper_bound.dcn5.non_urgent_bandwidth_kbps / 1000.0; + double min_urgent_dcfclk_mhz = urg_bandwidth_required_MBps + / (soc_bb->urgent_sdp_derate_percent / 100.0) + / soc_bb->return_bus_width_bytes; + double min_nominal_dcfclk_mhz = non_urg_bandwidth_required_MBps + / (soc_bb->nominal_sdp_derate_percent / 100.0) + / soc_bb->return_bus_width_bytes; + double min_required_dcfclk_mhz = math_max2(min_urgent_dcfclk_mhz, min_nominal_dcfclk_mhz); + + DML_LOG_FUNC_ENTER(); + outputs->support.dcfclk_support = inputs->DCFCLK >= min_required_dcfclk_mhz + && inputs->DCFCLK <= soc_bb->max_dcfclk_khz / 1000.0 + && inputs->DCFCLK >= soc_bb->min_dcfclk_khz / 1000.0; + + DML_LOG_VERBOSE("outputs->support.dcfclk_support = %s\n", outputs->support.dcfclk_support ? "true" : "false"); + + DML_LOG_DEBUG_BOOL(outputs->support.dcfclk_support); + DML_LOG_FUNC_EXIT(); + + return outputs->support.dcfclk_support; +} + +static void dcn6_ms_check_alternate_channel_size_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + unsigned int i; + bool alt_chan_in_use = false; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + unsigned int alternate_carveout_size_bytes = soc_bb->power_management_parameters.alternate_dram_carveout_size_mb < 0xFFF ? + soc_bb->power_management_parameters.alternate_dram_carveout_size_mb << 20 : 0xFFFFFFFF; + + DML_LOG_FUNC_ENTER(); + outputs->support.alternate_channel_size_support = true; + + //Alternate Channel Size Support Check - only fail if alternate channels are used AND exceed carveout limit + for (i = 0; i < ctx->display_cfg->num_planes; i++) { + if (ctx->display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy == dml2_uclk_pstate_change_strategy_force_alternate) { + alt_chan_in_use = true; + break; + } + } + + if (alt_chan_in_use && (inputs->svp0_max_bytes > alternate_carveout_size_bytes || + inputs->svp1_max_bytes > alternate_carveout_size_bytes)) { + outputs->support.alternate_channel_size_support = false; + } + + DML_LOG_VERBOSE("outputs->support.alternate_channel_size_support = %s\n", outputs->support.alternate_channel_size_support ? "true" : "false"); + + DML_LOG_DEBUG_BOOL(outputs->support.alternate_channel_size_support); + DML_LOG_FUNC_EXIT(); + + return; +} + +static void dcn6_ms_calculate_watermarks( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + const struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p = + &ctx->func_params->CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params; + + DML_LOG_FUNC_ENTER(); + dummies->mSOCParameters.UrgentLatency = inputs->UrgLatency; + dummies->mSOCParameters.ExtraLatency = inputs->ExtraLatency; + dummies->mSOCParameters.ExtraLatency_sr = inputs->ExtraLatency_sr; + dummies->mSOCParameters.WritebackLatency = soc_bb->writeback_base_latency_us; + dummies->mSOCParameters.DRAMClockChangeLatency = + soc_bb->power_management_parameters.dram_clk_change_blackout_us; + dummies->mSOCParameters.FCLKChangeLatency = soc_bb->power_management_parameters.fclk_change_blackout_us; + dummies->mSOCParameters.SRExitTime = soc_bb->power_management_parameters.stutter_exit_latency_us; + dummies->mSOCParameters.SREnterPlusExitTime = + soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us; + dummies->mSOCParameters.SRExitZ8Time = soc_bb->power_management_parameters.z8_stutter_exit_latency_us; + dummies->mSOCParameters.SREnterPlusExitZ8Time = + soc_bb->power_management_parameters.z8_stutter_enter_plus_exit_latency_us; + dummies->mSOCParameters.SRExitTimeLowPower = soc_bb->power_management_parameters.low_power_stutter_exit_latency_us; + dummies->mSOCParameters.SREnterPlusExitTimeLowPower = + soc_bb->power_management_parameters.low_power_stutter_enter_plus_exit_latency_us; + dummies->mSOCParameters.USRRetrainingLatency = 0; + dummies->mSOCParameters.SMNLatency = 0; + dummies->mSOCParameters.temp_read_or_ppt_blackout_us + = math_max2(soc_bb->power_management_parameters.g7_ppt_blackout_us, soc_bb->power_management_parameters.g7_temperature_read_blackout_us); + dummies->mSOCParameters.max_urgent_latency_us = inputs->support.max_urgent_latency_us; + dummies->mSOCParameters.df_response_time_us = inputs->support.df_response_time_us; + dummies->mSOCParameters.qos_type = dml2_qos_param_type_dcn4x; + + p->display_cfg = display_cfg; + p->USRRetrainingRequired = false; + p->NumberOfActiveSurfaces = display_cfg->num_planes; + p->MaxLineBufferLines = ip->max_line_buffer_lines; + p->LineBufferSize = ip->line_buffer_size_bits; + p->WritebackInterfaceBufferSize = ip->writeback_interface_buffer_size_kbytes; + p->DCFCLK = inputs->DCFCLK; + p->SynchronizeTimings = display_cfg->overrides.synchronize_timings; + p->SynchronizeDRRDisplaysForUCLKPStateChange = + display_cfg->overrides.synchronize_ddr_displays_for_uclk_pstate_change; + p->dpte_group_bytes = inputs->dpte_group_bytes; + p->mmSOCParameters = dummies->mSOCParameters; + p->WritebackChunkSize = ip->writeback_chunk_size_kbytes; + p->SOCCLK = 0.0; + p->DCFClkDeepSleep = inputs->dcfclk_deepsleep; + p->DETBufferSizeY = inputs->DETBufferSizeY; + p->DETBufferSizeC = inputs->DETBufferSizeC; + p->SwathHeightY = inputs->SwathHeightY; + p->SwathHeightC = inputs->SwathHeightC; + //CalculateWatermarks_params->LBBitPerPixel = 57; // FIXME_STAGE2, need a new ip param? + p->SwathWidthY = inputs->SwathWidthY; + p->SwathWidthC = inputs->SwathWidthC; + p->DPPPerSurface = inputs->NoOfDPP; + p->BytePerPixelDETY = inputs->BytePerPixelInDETY; + p->BytePerPixelDETC = inputs->BytePerPixelInDETC; + p->DSTXAfterScaler = inputs->DSTXAfterScaler; + p->DSTYAfterScaler = inputs->DSTYAfterScaler; + p->UnboundedRequestEnabled = inputs->UnboundedRequestEnabled; + p->CompressedBufferSizeInkByte = inputs->CompressedBufferSizeInkByte; + p->meta_row_height_l = inputs->meta_row_height_luma; + p->meta_row_height_c = inputs->meta_row_height_chroma; + p->uclk_pstate_switch_modes = inputs->uclk_pstate_switch_modes; + // Output + p->Watermark = &outputs->support.watermarks; // Watermarks *Watermark + p->DRAMClockChangeSupport = outputs->support.DRAMClockChangeSupport; + p->global_dram_clock_change_support_required = &outputs->support.global_dram_clock_change_support_required; + p->global_dram_clock_change_supported = &outputs->support.global_dram_clock_change_supported; + p->MaxActiveDRAMClockChangeLatencySupported = outputs->MaxActiveDRAMClockChangeLatencySupported; // double *MaxActiveDRAMClockChangeLatencySupported[] + p->FCLKChangeSupport = outputs->support.FCLKChangeSupport; + p->global_fclk_change_supported = &outputs->support.global_fclk_change_supported; + p->MaxActiveFCLKChangeLatencySupported = &outputs->MaxActiveFCLKChangeLatencySupported; // double *MaxActiveFCLKChangeLatencySupported + p->USRRetrainingSupport = &outputs->support.USRRetrainingSupport; + p->global_temp_read_or_ppt_supported = &outputs->support.global_temp_read_or_ppt_supported; + p->temp_read_or_ppt_support = outputs->support.temp_read_or_ppt_support; + p->VActiveLatencyHidingMargin = outputs->VActiveLatencyHidingMargin; + p->VActiveLatencyHidingUs = outputs->VActiveLatencyHidingUs; + dcn6_calculate_watermarks_and_dram_speed_change_support(ctx->func_params, p); + + DML_LOG_DEBUG_BOOL(outputs->support.global_dram_clock_change_supported); + DML_LOG_DEBUG_BOOL(outputs->support.global_fclk_change_supported); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.UrgentWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.WritebackUrgentWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.DRAMClockChangeWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.FCLKChangeWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.WritebackDRAMClockChangeWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.WritebackFCLKChangeWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.StutterExitWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.StutterEnterPlusExitWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.LowPowerStutterExitWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.LowPowerStutterEnterPlusExitWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.Z8StutterExitWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.Z8StutterEnterPlusExitWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.USRRetrainingWatermark); + DML_LOG_DEBUG_DOUBLE(outputs->support.watermarks.temp_read_or_ppt_watermark_us); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->VActiveLatencyHidingMargin, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_INT(outputs->support.DRAMClockChangeSupport, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_UINT(outputs->SubViewportLinesNeededInMALL, display_cfg->num_planes); + DML_LOG_DEBUG_BOOL(outputs->support.global_temp_read_or_ppt_supported); + DML_LOG_DEBUG_BOOL(outputs->support.USRRetrainingSupport); + DML_LOG_DEBUG_ARRAY_INT(outputs->support.FCLKChangeSupport, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->VActiveLatencyHidingUs, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->MaxActiveDRAMClockChangeLatencySupported, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static void dcn6_ms_calculate_pstate_schedule_windows( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb; + const struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + unsigned int *v_blank_start = dummies->dummy_integer_array[0]; + unsigned int *v_blank_end = dummies->dummy_integer_array[1]; + double *otg_vline_time_us = dummies->dummy_double_array[0]; + double *reserved_vblank_us = dummies->dummy_double_array[1]; + const struct dml2_plane_parameters *plane = NULL; + const struct dml2_stream_parameters *stream = NULL; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + v_blank_start[k] = stream->timing.v_blank_end + stream->timing.v_active; + v_blank_end[k] = stream->timing.v_blank_end; + otg_vline_time_us[k] = (double)stream->timing.h_total / stream->timing.pixel_clock_khz * 1000.0; + reserved_vblank_us[k] = plane->overrides.reserved_vblank_time_ns / 1000.0; + } + + /* fclk pstate */ + if (inputs->support.global_fclk_change_supported) + dcn6_calculate_pstate_schedule_windows( + display_cfg->num_planes, + v_blank_start, + v_blank_end, + otg_vline_time_us, + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_fclk], + reserved_vblank_us, + soc_bb->power_management_parameters.fclk_change_blackout_us, + // Outputs + outputs->fclk_pstate_allow_start_us, + outputs->fclk_pstate_allow_end_us + ); + + /* ppt pstate */ + if (inputs->support.global_temp_read_or_ppt_supported) + dcn6_calculate_pstate_schedule_windows( + display_cfg->num_planes, + v_blank_start, + v_blank_end, + otg_vline_time_us, + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_ppt], + reserved_vblank_us, + math_max2( + soc_bb->power_management_parameters.g7_ppt_blackout_us, + soc_bb->power_management_parameters.g7_temperature_read_blackout_us), + // Outputs + outputs->ppt_pstate_allow_start_us, + outputs->ppt_pstate_allow_end_us + ); + + /* temp read pstate */ + if (inputs->support.global_temp_read_or_ppt_supported) + dcn6_calculate_pstate_schedule_windows( + display_cfg->num_planes, + v_blank_start, + v_blank_end, + otg_vline_time_us, + inputs->pstate_vactive_det_fill_delay_us[dml2_pstate_type_temp_read], + reserved_vblank_us, + math_max2(soc_bb->power_management_parameters.g7_ppt_blackout_us, + soc_bb->power_management_parameters.g7_temperature_read_blackout_us), + // Outputs + outputs->temp_read_pstate_allow_start_us, + outputs->temp_read_pstate_allow_end_us + ); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->fclk_pstate_allow_start_us, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->fclk_pstate_allow_end_us, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->ppt_pstate_allow_start_us, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->ppt_pstate_allow_end_us, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->temp_read_pstate_allow_start_us, display_cfg->num_planes); + DML_LOG_DEBUG_ARRAY_DOUBLE(outputs->temp_read_pstate_allow_end_us, display_cfg->num_planes); + DML_LOG_FUNC_EXIT(); +} + +static bool dcn6_ms_check_pstate_schedule_admissibility( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + const struct dml2_core_ip_params *ip = ctx->ip; + const struct dml2_display_cfg *display_cfg = ctx->display_cfg; + const struct dml2_core_internal_mode_support *inputs = states; + struct dml2_core_internal_mode_support *outputs = states; + struct dml2_core_calcs_mode_support_locals *dummies = ctx->dummies; + // array of booleans indicating whether DRR is enabled for each plane + bool *is_drr = dummies->dummy_boolean_array[0]; + // array of frame times for each plane in microseconds + double *frame_time_us = dummies->dummy_double_array[0]; + enum dml2_pstate_method *pstate_method = dummies->dummy_pstate_method_array; + const struct dml2_stream_parameters *stream = NULL; + const struct dml2_plane_parameters *plane = NULL; + unsigned int k; + + DML_LOG_FUNC_ENTER(); + for (k = 0; k < display_cfg->num_planes; k++) { + plane = &display_cfg->plane_descriptors[k]; + stream = &display_cfg->stream_descriptors[plane->stream_index]; + frame_time_us[k] = dml2_core_utils_get_frame_time_us(stream); + is_drr[k] = stream->timing.drr_config.enabled + && !stream->timing.drr_config.disallowed + && (stream->timing.drr_config.drr_active_fixed + || stream->timing.drr_config.drr_active_variable); + pstate_method[k] = dml2_pstate_method_vactive; + } + + /* fclk pstate */ + outputs->support.fclk_pstate_schedule_admissible = false; + if (inputs->support.global_fclk_change_supported) + dcn6_calculate_pstate_schedule_admissibility( + display_cfg->num_planes, + ip->fams2_max_allow_delay_us, + ip->fams2_min_allow_width_us, + inputs->timing_group_id, + inputs->timing_group_count, + frame_time_us, + inputs->fclk_pstate_allow_start_us, + inputs->fclk_pstate_allow_end_us, + pstate_method, + is_drr, + // Outputs + dummies->dummy_double_array[1], + dummies->dummy_double_array[2], + &outputs->support.fclk_pstate_schedule_admissible + ); + + /* ppt pstate */ + outputs->support.ppt_pstate_schedule_admissible = false; + if (inputs->support.global_temp_read_or_ppt_supported) + dcn6_calculate_pstate_schedule_admissibility( + display_cfg->num_planes, + ip->ppt_max_allow_delay_us, + ip->fams2_min_allow_width_us, + inputs->timing_group_id, + inputs->timing_group_count, + frame_time_us, + inputs->ppt_pstate_allow_start_us, + inputs->ppt_pstate_allow_end_us, + pstate_method, + is_drr, + // Outputs + dummies->dummy_double_array[1], + dummies->dummy_double_array[2], + &outputs->support.ppt_pstate_schedule_admissible + ); + + /* temp read pstate */ + outputs->support.temp_read_pstate_schedule_admissible = false; + if (inputs->support.global_temp_read_or_ppt_supported) + dcn6_calculate_pstate_schedule_admissibility( + display_cfg->num_planes, + ip->temp_read_max_allow_delay_us, + ip->fams2_min_allow_width_us, + inputs->timing_group_id, + inputs->timing_group_count, + frame_time_us, + inputs->temp_read_pstate_allow_start_us, + inputs->temp_read_pstate_allow_end_us, + pstate_method, + is_drr, + // Outputs + dummies->dummy_double_array[1], + dummies->dummy_double_array[2], + &outputs->support.temp_read_pstate_schedule_admissible + ); + DML_LOG_DEBUG_BOOL(outputs->support.fclk_pstate_schedule_admissible); + DML_LOG_DEBUG_BOOL(outputs->support.ppt_pstate_schedule_admissible); + DML_LOG_DEBUG_BOOL(outputs->support.temp_read_pstate_schedule_admissible); + DML_LOG_FUNC_EXIT(); + return (!inputs->fclk_pstate_required || outputs->support.fclk_pstate_schedule_admissible) + && (!inputs->ppt_pstate_required || outputs->support.ppt_pstate_schedule_admissible) + && (!inputs->temp_read_pstate_required || outputs->support.temp_read_pstate_schedule_admissible); +} + +static void dcn6_ms_initialize_from_solution(struct dml2_core_internal_mode_support *outputs, + const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + DML_LOG_FUNC_ENTER(); + outputs->UrgLatency = solution->sop_constraint.dcn5.latency.dcn5.urgent_ramp; + outputs->TripToMemory = math_max2(solution->sop_constraint.dcn5.latency.dcn5.t_trip, + solution->sop_constraint.dcn5.latency.dcn5.urgent_ramp); + outputs->support.avg_urgent_latency_us = solution->sop_constraint.dcn5.latency.dcn5.avg_req_latency_urg; + outputs->support.avg_non_urgent_latency_us = solution->sop_constraint.dcn5.latency.dcn5.avg_req_latency_non_urg; + outputs->support.max_urgent_latency_us = solution->sop_constraint.dcn5.latency.dcn5.max_req_latency_urg; + outputs->support.max_non_urgent_latency_us = solution->sop_constraint.dcn5.latency.dcn5.max_req_latency_non_urg; + outputs->support.df_response_time_us = solution->sop_constraint.dcn5.latency.dcn5.df_response_time_us; + if (solution->dispcfg.overrides.hw.dcfclk_mhz > 0) + outputs->DCFCLK = solution->dispcfg.overrides.hw.dcfclk_mhz; + else + outputs->DCFCLK = solution->sop_constraint.dcn5.clocks.dcfclk_khz / 1000.0; + outputs->min_available_urgent_bandwidth_MBps = + math_min2(solution->sop_constraint.dcn5.min_available_urgent_bandwidth_KBps / 1000.0, + outputs->DCFCLK * utm_soc_bb->return_bus_width_bytes); + outputs->qos_param_index = solution->sop_constraint.dcn5.min_sop_index; + memcpy(outputs->uclk_pstate_switch_modes, solution->uclk_pstate_params.pstate_switch_modes, sizeof(solution->uclk_pstate_params.pstate_switch_modes)); + outputs->fclk_pstate_required = solution->fclk_pstate_support; + outputs->ppt_pstate_required = solution->ppt_temp_read_support; + outputs->temp_read_pstate_required = solution->ppt_temp_read_support; + outputs->timing_group_count = solution->timing_group_count; + memcpy(outputs->timing_group_id, solution->timing_group_ids, sizeof(solution->timing_group_ids)); + + DML_LOG_VERBOSE("DML::%s: urgent_ramp=%f\n", __func__, solution->sop_constraint.dcn5.latency.dcn5.urgent_ramp); + DML_LOG_VERBOSE("DML::%s: t_trip=%f\n", __func__, solution->sop_constraint.dcn5.latency.dcn5.t_trip); + DML_LOG_VERBOSE("DML::%s: avg_req_latency_urg=%f\n", __func__, + solution->sop_constraint.dcn5.latency.dcn5.avg_req_latency_urg); + DML_LOG_VERBOSE("DML::%s: avg_req_latency_non_urg=%f\n", __func__, + solution->sop_constraint.dcn5.latency.dcn5.avg_req_latency_non_urg); + DML_LOG_VERBOSE("DML::%s: max_req_latency_urg=%f\n", __func__, + solution->sop_constraint.dcn5.latency.dcn5.max_req_latency_urg); + DML_LOG_VERBOSE("DML::%s: max_req_latency_non_urg=%f\n", __func__, + solution->sop_constraint.dcn5.latency.dcn5.max_req_latency_non_urg); + DML_LOG_VERBOSE("DML::%s: DCFCLK=%f\n", __func__, outputs->DCFCLK); + DML_LOG_VERBOSE("DML::%s: min_available_urgent_bandwidth_MBps=%f\n", + __func__, outputs->min_available_urgent_bandwidth_MBps); + DML_LOG_FUNC_EXIT(); +} + +static enum dml2_status dcn6_ms_validate_prefetch( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + enum dml2_status status = DML2_STATUS_OK; + + DML_LOG_FUNC_ENTER(); + dcn6_ms_calculate_det_buffer_time_value_urgent_burst_factor_and_urgent_latency_hiding(ctx, states); + + dcn6_ms_calculate_min_dcfclk_deepsleep_clock(ctx, states); + + dcn6_ms_calculate_writeback_delay(ctx, states); + + dcn6_ms_calculate_alternate_svp_lines(ctx, states); + + dcn6_ms_calculate_max_vstartup(ctx, states); + + dcn6_ms_calculate_mcache_setting(ctx, states); + + dcn6_ms_calculate_avg_bandwidth_and_dcfclk_lb_required(ctx, states); + + dcn6_ms_check_average_latency_supports(ctx, states); + + dcn6_ms_calculate_t_calc(ctx, states); + + dcn6_ms_calculate_hostvm_inefficiency_factor(ctx, states); + + dcn6_ms_calculate_3dlut_settings(ctx, states); + + dcn6_ms_calculate_urgent_latency(ctx, states); + + if (status == DML2_STATUS_OK) { + + dcn6_ms_calculate_prefetch_schedule(ctx, states); + + dcn6_ms_check_urgent_latency_hiding_support(ctx, states); + + dcn6_ms_check_dynamic_metadata_support(ctx, states); + + if (!dcn6_ms_check_prefetch_support(ctx, states)) + status = DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_PREFETCH; + + if (!dcn6_ms_check_v_ratio_in_prefetch_support(ctx, states)) + status = DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_PREFETCH; + } + + if (status == DML2_STATUS_OK) { + dcn6_ms_calculate_urgent_burst_factor_for_prefetch(ctx, states); + + if (!dcn6_ms_check_final_prefetch_support(ctx, states)) + status = DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_PREFETCH_URGENT; + } + + if (status == DML2_STATUS_OK) { + dcn6_ms_calculate_flip_schedule(ctx, states); + + if (!dcn6_ms_check_immediate_flip_support(ctx, states)) + status = DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_QOS_BANDWIDTH; + + dcn6_ms_calculate_peak_bandwidth_required(ctx, states); + + dcn6_ms_calculate_bandwidth_upper_bound(ctx, states); + + dcn6_ms_check_qos_bandwidth_support(ctx, states); + + dcn6_ms_check_dcfclk_support(ctx, states); + } + + if (status == DML2_STATUS_OK) { + dcn6_ms_calculate_watermarks(ctx, states); + + dcn6_ms_check_reordering_support(ctx, states); + + dcn6_ms_calculate_vactive_det_fill_latency(ctx, states); + + dcn6_ms_calculate_alternate_params(ctx, states); + + dcn6_ms_check_alternate_channel_size_support(ctx, states); + + dcn6_ms_calculate_pstate_schedule_windows(ctx, states); + + if (!dcn6_ms_check_pstate_schedule_admissibility(ctx, states)) + status = DML2_STATUS_VALIDATE_FAIL_PSTATE_SCHEDULE; + } + + if (!dcn6_ms_check_mode_support(ctx, states, status) && status == DML2_STATUS_OK) + status = DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT; + + dcn6_ms_populate_informative(ctx, states); + + DML_LOG_VERBOSE("DML::%s: Done prefetch calculation\n", __func__); + DML_LOG_FUNC_EXIT(); + + return status; +} + +static enum dml2_status dcn6_mode_support( + const struct dml2_core_calculate_ms_context *ctx, + struct dml2_core_internal_mode_support *states) +{ + enum dml2_status status = DML2_STATUS_OK; + + DML_LOG_FUNC_ENTER(); + dcn6_ms_check_input_sanity(ctx, states); + + dcn6_ms_check_scaler_support(ctx, states); + + dcn6_ms_check_source_format_and_scan_direction(ctx, states); + + dcn6_ms_calculate_byte_per_pixel_and_block_sizes(ctx, states); + + dcn6_ms_calculate_read_bandwidth(ctx, states); + + dcn6_ms_calculate_writeback_bandwidth(ctx, states); + + dcn6_ms_check_writeback_bandwidth_latency_support(ctx, states); + + dcn6_ms_check_writeback_scale_ratio_and_taps_support(ctx, states); + + dcn6_ms_calculate_single_pipe_dppclk_and_pscl_factor(ctx, states); + + dcn6_ms_calculate_max_swath_widths(ctx, states); + + dcn6_ms_check_cursor_support(ctx, states); + + dcn6_ms_check_surface_alginment_requirements(ctx, states); + + dcn6_ms_calculate_effective_pixel_clock(ctx, states); + + dcn6_ms_calculate_estimated_num_of_dsc_slices(ctx, states); + + dcn6_ms_calculate_desired_output_bpp(ctx, states); + + dcn6_ms_calculate_output_link(ctx, states); + + dcn6_ms_calculate_odm_mode(ctx, states); + + dcn6_ms_calculate_num_of_dsc_slices(ctx, states); + + dcn6_ms_check_num_of_dsc_slices_support(ctx, states); + + dcn6_ms_calculate_max_det_and_min_compressed_buffer_size(ctx, states); + + dcn6_ms_calculate_swath_and_det_configuration_for_single_dpp(ctx, states); + + dcn6_ms_calculate_num_of_dpp_required(ctx, states); + + dcn6_ms_check_total_available_pipes_support(ctx, states); + + dcn6_ms_check_total_available_TDLUT_33cube_support(ctx, states); + + dcn6_ms_calculate_total_num_of_single_dpp_surfaces(ctx, states); + + dcn6_ms_calculate_dispclk_and_dppclk_required(ctx, states); + + dcn6_ms_check_dispclk_and_dppclk_support(ctx, states); + + dcn6_ms_calculate_dtbclk_required(ctx, states); + + dcn6_ms_check_dtbclk_support(ctx, states); + + dcn6_ms_check_otg_count_support(ctx, states); + + dcn6_ms_check_hpo_frl_encoder_count_support(ctx, states); + + dcn6_ms_check_hpo_dp_encoder_count_support(ctx, states); + + dcn6_ms_check_writeback_count_support(ctx, states); + + dcn6_ms_check_link_bandwidth_support(ctx, states); + + dcn6_ms_check_misc_link_supports(ctx, states); + + dcn6_ms_calculate_dscclk_required(ctx, states); + + dcn6_ms_check_dscclk_support(ctx, states); + + dcn6_ms_check_dsc_engine_supports(ctx, states); + + dcn6_ms_calculate_dsc_delay(ctx, states); + + dcn6_ms_calculate_swath_and_det_configuration(ctx, states); + + dcn6_ms_calculate_total_num_of_dcc_active_dpp(ctx, states); + + dcn6_ms_calculate_vm_row_and_swath_and_calculate_dcc_meta_cache_requirements(ctx, states); + + dcn6_ms_check_pte_buffer_size_support(ctx, states); + + dcn6_ms_check_dcc_meta_cache_support(ctx, states); + + dcn6_ms_calculate_vactive_pstate_requirements(ctx, states); + + status = dcn6_ms_validate_prefetch(ctx, states); + + DML_LOG_VERBOSE("DML::%s: done\n", __func__); + DML_LOG_FUNC_EXIT(); + return status; +} + +enum dml2_status dml2_core_dcn6_funcs_validate_solution(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_validation_result *result) +{ + enum dml2_status status = DML2_STATUS_OK; + struct dml2_core_calculate_ms_context *calc_ms_ctx = &core->scratch.mode_support_locals.calc_ms_ctx; + struct dml2_calculate_mcache_allocation_in_out *calc_mcache_allocation_params = + &core->scratch.mode_support_locals.calc_mcache_allocation_params;; + struct dml2_core_internal_display_mode_lib *mode_lib = &core->clean_me_up.mode_lib; + unsigned int i; + + DML_LOG_COMP_IF_ENTER(); + if (solution->unvalidated_change.bits.mpc_combine_overrides + || solution->unvalidated_change.bits.odm_combine_overrides + || solution->unvalidated_change.bits.reserved_vblank_time + || solution->unvalidated_change.bits.uclk_pstate_method + || solution->unvalidated_change.bits.fclk_pstate_support + || solution->unvalidated_change.bits.ppt_temp_read_pstate_support) + result->is_mode_support_valid = false; + if (solution->unvalidated_change.bits.sop_index + || solution->unvalidated_change.bits.dcfclk_override) + result->is_prefetch_valid = false; + + if (!result->is_mode_support_valid) { + calc_ms_ctx->display_cfg = &solution->dispcfg; + calc_ms_ctx->ip = &core->clean_me_up.mode_lib.ip; + calc_ms_ctx->soc_bb = core->utm_soc_bb; + calc_ms_ctx->clock_adjuster = core->clock_adjuster; + calc_ms_ctx->dummies = &mode_lib->scratch.dml_core_mode_support_locals; + calc_ms_ctx->func_params = &mode_lib->scratch; + memset(calc_ms_ctx->func_params, 0, sizeof(struct dml2_core_internal_scratch)); + memset(&mode_lib->ms, 0, sizeof(struct dml2_core_internal_mode_support)); + + dcn6_ms_initialize_from_solution(&mode_lib->ms, solution, core->utm_soc_bb); + status = dcn6_mode_support(calc_ms_ctx, &mode_lib->ms); + result->is_mode_support_valid = status == DML2_STATUS_OK; + result->is_prefetch_valid = result->is_mode_support_valid; + result->mode_support.cfg_support_info.is_supported = result->is_mode_support_valid; + if (result->is_mode_support_valid) + dcn6_ms_populate_mode_support_result(calc_ms_ctx, &mode_lib->ms, &result->mode_support); + } else if (!result->is_prefetch_valid) { + dcn6_ms_initialize_from_solution(&mode_lib->ms, solution, core->utm_soc_bb); + status = dcn6_ms_validate_prefetch(calc_ms_ctx, &mode_lib->ms); + if (status == DML2_STATUS_OK) { + dcn6_ms_populate_mode_support_result(calc_ms_ctx, &mode_lib->ms, &result->mode_support); + result->is_prefetch_valid = true; + } + } + + if (!result->is_mcache_allocation_valid) { + result->is_mcache_allocation_valid = true; + for (i = 0; i < solution->dispcfg.num_planes; i++) { + if (!solution->dispcfg.plane_descriptors[i].surface.dcc.enable) { + memset(&result->mcache_allocations[i], 0, sizeof(struct dml2_mcache_surface_allocation)); + continue; + } + + calc_mcache_allocation_params->instance = core; + calc_mcache_allocation_params->plane_descriptor = &solution->dispcfg.plane_descriptors[i]; + calc_mcache_allocation_params->mcache_allocation = &result->mcache_allocations[i]; + calc_mcache_allocation_params->plane_index = i; + if (!core->calculate_mcache_allocation(calc_mcache_allocation_params)) { + status = DML2_STATUS_VALIDATE_FAIL_MCACHE; + result->is_mcache_allocation_valid = false; + break; + } + } + } + + DML_LOG_VERBOSE("DML::%s: result->is_mode_support_valid = %d\n", __func__, result->is_mode_support_valid); + DML_LOG_VERBOSE("DML::%s: result->is_prefetch_valid = %d\n", __func__, result->is_prefetch_valid); + DML_LOG_VERBOSE("DML::%s: result->is_mcache_allocation_valid = %d\n", __func__, result->is_mcache_allocation_valid); + if (status == DML2_STATUS_OK) + DML_ASSERT_MSG(result->is_mode_support_valid + && result->is_prefetch_valid + && result->is_mcache_allocation_valid, + "mismatch between status and valid bits detected!\n"); + DML_LOG_DEBUG("%s exit with %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + return status; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.h new file mode 100644 index 000000000000..c035cd099cc6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_CORE_DCN6_FUNCS_MODE_SUPPORT_H__ +#define __DML2_CORE_DCN6_FUNCS_MODE_SUPPORT_H__ +#include "dml2_internal_shared_types.h" +enum dml2_status dml2_core_dcn6_funcs_validate_solution(struct dml2_core_instance *core, + const struct dml2_display_solution *solution, + struct dml2_validation_result *result); +#endif /* __DML2_CORE_DCN6_FUNCS_MODE_SUPPORT_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c index c60a1faf2d5d..22c757cdac07 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c @@ -763,7 +763,6 @@ bool dpmm_dcn4_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_i else in_out->programming->stutter.supported_in_blank = false; - // TODO: Fix me Sam if (in_out->soc_bb->power_management_parameters.z8_min_idle_time > 0 && in_out->programming->informative.power_management.z8.stutter_period >= in_out->soc_bb->power_management_parameters.z8_min_idle_time) in_out->programming->z8_stutter.meets_eco = true; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.c new file mode 100644 index 000000000000..f6230f8f9127 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_dpmm_dcn5.h" +#include "lib_float_math.h" + +static bool add_margin_and_round_to_dfs_grainularity(double clock_khz, double margin, unsigned long vco_freq_khz, unsigned long *rounded_khz, uint32_t *divider_id) +{ + enum dentist_divider_range { + DFS_DIVIDER_RANGE_1_START = 8, /* 2.00 */ + DFS_DIVIDER_RANGE_1_STEP = 1, /* 0.25 */ + DFS_DIVIDER_RANGE_2_START = 64, /* 16.00 */ + DFS_DIVIDER_RANGE_2_STEP = 2, /* 0.50 */ + DFS_DIVIDER_RANGE_3_START = 128, /* 32.00 */ + DFS_DIVIDER_RANGE_3_STEP = 4, /* 1.00 */ + DFS_DIVIDER_RANGE_4_START = 248, /* 62.00 */ + DFS_DIVIDER_RANGE_4_STEP = 264, /* 66.00 */ + DFS_DIVIDER_RANGE_SCALE_FACTOR = 4 + }; + + enum DFS_base_divider_id { + DFS_BASE_DID_1 = 0x08, + DFS_BASE_DID_2 = 0x40, + DFS_BASE_DID_3 = 0x60, + DFS_BASE_DID_4 = 0x7e, + DFS_MAX_DID = 0x7f + }; + + unsigned int divider; + + if (clock_khz < 1 || vco_freq_khz < 1 || clock_khz > vco_freq_khz) + return false; + + clock_khz *= 1.0 + margin; + + divider = (unsigned int)((int)DFS_DIVIDER_RANGE_SCALE_FACTOR * (vco_freq_khz / clock_khz)); + + /* we want to floor here to get higher clock than required rather than lower */ + if (divider < DFS_DIVIDER_RANGE_2_START) { + if (divider < DFS_DIVIDER_RANGE_1_START) + *divider_id = DFS_BASE_DID_1; + else + *divider_id = DFS_BASE_DID_1 + ((divider - DFS_DIVIDER_RANGE_1_START) / DFS_DIVIDER_RANGE_1_STEP); + } else if (divider < DFS_DIVIDER_RANGE_3_START) { + *divider_id = DFS_BASE_DID_2 + ((divider - DFS_DIVIDER_RANGE_2_START) / DFS_DIVIDER_RANGE_2_STEP); + } else if (divider < DFS_DIVIDER_RANGE_4_START) { + *divider_id = DFS_BASE_DID_3 + ((divider - DFS_DIVIDER_RANGE_3_START) / DFS_DIVIDER_RANGE_3_STEP); + } else { + *divider_id = DFS_BASE_DID_4 + ((divider - DFS_DIVIDER_RANGE_4_START) / DFS_DIVIDER_RANGE_4_STEP); + if (*divider_id > DFS_MAX_DID) + *divider_id = DFS_MAX_DID; + } + + *rounded_khz = vco_freq_khz * DFS_DIVIDER_RANGE_SCALE_FACTOR / divider; + + return true; +} + +static bool round_to_non_dfs_granularity(unsigned long dispclk_khz, unsigned long dpprefclk_khz, unsigned long dtbrefclk_khz, + unsigned long *rounded_dispclk_khz, unsigned long *rounded_dpprefclk_khz, unsigned long *rounded_dtbrefclk_khz) +{ + unsigned long pll_frequency_khz; + + pll_frequency_khz = (unsigned long) math_max2(600000, math_ceil2(math_max3(dispclk_khz, dpprefclk_khz, dtbrefclk_khz), 1000)); + + *rounded_dispclk_khz = pll_frequency_khz / (unsigned long) math_min2(pll_frequency_khz / dispclk_khz, 32); + + *rounded_dpprefclk_khz = pll_frequency_khz / (unsigned long) math_min2(pll_frequency_khz / dpprefclk_khz, 32); + + if (dtbrefclk_khz > 0) { + *rounded_dtbrefclk_khz = pll_frequency_khz / (unsigned long) math_min2(pll_frequency_khz / dtbrefclk_khz, 32); + } else { + *rounded_dtbrefclk_khz = 0; + } + + return true; +} + +static bool validate_min_clocks(const struct dml2_display_solution *solution, struct dml2_display_cfg_programming *programming, const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int i; + + if (!utm_soc_bb || !programming) + return false; + + if (programming->min_clocks.dcn4x.dispclk_khz > utm_soc_bb->max_dispclk_khz) + return false; + + if (programming->min_clocks.dcn4x.dpprefclk_khz > utm_soc_bb->max_dppclk_khz) + return false; + + if (programming->min_clocks.dcn4x.dtbrefclk_khz > utm_soc_bb->max_dtbclk_khz) + return false; + + for (i = 0; i < solution->dispcfg.num_planes; i++) + if (programming->plane_programming[i].min_clocks.dcn4x.dppclk_khz > utm_soc_bb->max_dppclk_khz) + return false; + + for (i = 0; i < solution->dispcfg.num_streams; i++) + if (programming->stream_programming[i].min_clocks.dcn4x.dscclk_khz > utm_soc_bb->max_dscclk_khz) + return false; + else if (programming->stream_programming[i].min_clocks.dcn4x.dtbclk_khz > utm_soc_bb->max_dtbclk_khz) + return false; + else if (programming->stream_programming[i].min_clocks.dcn4x.phyclk_khz > utm_soc_bb->max_phyclk_khz) + return false; + + return true; +} + +static bool are_timings_trivially_synchronizable(const struct dml2_display_solution *solution, int mask) +{ + unsigned int i; + bool identical = true; + bool contains_drr = false; + unsigned int remap_array[DML2_MAX_PLANES]; + unsigned int remap_array_size = 0; + + // Create a remap array to enable simple iteration through only masked stream indicies + for (i = 0; i < solution->dispcfg.num_streams; i++) { + if (mask & (0x1 << i)) { + remap_array[remap_array_size++] = i; + } + } + + // 0 or 1 display is always trivially synchronizable + if (remap_array_size <= 1) + return true; + + // Check that all displays timings are the same + for (i = 1; i < remap_array_size; i++) { + if (memcmp(&solution->dispcfg.stream_descriptors[remap_array[i - 1]].timing, &solution->dispcfg.stream_descriptors[remap_array[i]].timing, sizeof(struct dml2_timing_cfg))) { + identical = false; + break; + } + } + + // Check if any displays are drr + for (i = 0; i < remap_array_size; i++) { + if (solution->dispcfg.stream_descriptors[remap_array[i]].timing.drr_config.enabled) { + contains_drr = true; + break; + } + } + + // Trivial sync is possible if all displays are identical and none are DRR + return !contains_drr && identical; +} + +static int find_smallest_idle_time_in_vblank_us(const struct dml2_display_solution *solution, int mask) +{ + unsigned int i; + int min_idle_us = 0; + unsigned int remap_array[DML2_MAX_PLANES]; + unsigned int remap_array_size = 0; + const struct dml2_core_mode_support_result *mode_support_result = &solution->validation_result.mode_support; + + // Create a remap array to enable simple iteration through only masked stream indicies + for (i = 0; i < solution->dispcfg.num_streams; i++) { + if (mask & (0x1 << i)) { + remap_array[remap_array_size++] = i; + } + } + + if (remap_array_size == 0) + return 0; + + min_idle_us = mode_support_result->cfg_support_info.stream_support_info[remap_array[0]].vblank_reserved_time_us; + + for (i = 1; i < remap_array_size; i++) { + if (min_idle_us > mode_support_result->cfg_support_info.stream_support_info[remap_array[i]].vblank_reserved_time_us) + min_idle_us = mode_support_result->cfg_support_info.stream_support_info[remap_array[i]].vblank_reserved_time_us; + } + + return min_idle_us; +} + +static int get_displays_without_vactive_margin_mask(const struct dml2_display_solution *solution, const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int i; + int displays_without_vactive_margin_mask = 0x0; + const struct dml2_core_mode_support_result *mode_support_result = &solution->validation_result.mode_support; + + for (i = 0; i < solution->dispcfg.num_planes; i++) { + if (mode_support_result->cfg_support_info.plane_support_info[i].active_latency_hiding_us + < (int)utm_soc_bb->power_management_parameters.fclk_change_blackout_us) + displays_without_vactive_margin_mask |= (0x1 << i); + } + + return displays_without_vactive_margin_mask; +} + +static unsigned long calculate_dispclk_khz(const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb, const struct dml2_core_ip_params *ip_params) +{ + double dispclk_khz; + + // need some massaging for the dispclk ramping cases: + dispclk_khz = solution->validation_result.mode_support.global.dispclk_khz * (1 + utm_soc_bb->dcn_downspread_percent / 100.0) * (1.0 + ip_params->dispclk_ramp_margin_percent / 100.0); + // ramping margin should not make dispclk exceed the maximum dispclk speed: + dispclk_khz = math_min2(dispclk_khz, utm_soc_bb->max_dispclk_khz); + // but still the required dispclk can be more than the maximum dispclk speed: + dispclk_khz = math_max2(dispclk_khz, solution->validation_result.mode_support.global.dispclk_khz * (1 + utm_soc_bb->dcn_downspread_percent / 100.0)); + + return (unsigned long) dispclk_khz; +} + +static unsigned long calculate_dpprefclk_khz(const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned long dpprefclk_khz = 0; + unsigned int i; + + // DPP Ref is always set to max of all DPP clocks + for (i = 0; i < solution->dispcfg.num_planes; i++) + if (dpprefclk_khz < solution->validation_result.mode_support.per_plane[i].dppclk_khz) + dpprefclk_khz = solution->validation_result.mode_support.per_plane[i].dppclk_khz; + dpprefclk_khz = (unsigned long) (dpprefclk_khz * (1 + utm_soc_bb->dcn_downspread_percent / 100.0)); + + return dpprefclk_khz; +} + +static unsigned long calculate_dtbrefclk_khz(const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned long dtbrefclk_khz = 0; + unsigned int i; + + // DTB Ref is always set to max of all DTB clocks + for (i = 0; i < solution->dispcfg.num_streams; i++) + if (dtbrefclk_khz < solution->validation_result.mode_support.per_stream[i].dtbclk_khz) + dtbrefclk_khz = solution->validation_result.mode_support.per_stream[i].dtbclk_khz; + dtbrefclk_khz = (unsigned long)(dtbrefclk_khz * (1 + utm_soc_bb->dcn_downspread_percent / 100.0)); + + return dtbrefclk_khz; +} + +static unsigned long calculate_dppclk_khz_plane_index(unsigned int plane_index, + const struct dml2_display_solution *solution, + const struct dml2_utm_soc_bb *utm_soc_bb, + unsigned long dpprefclk_khz) +{ + return (unsigned long)(dpprefclk_khz / 255.0 + * math_ceil2(solution->validation_result.mode_support.per_plane[plane_index].dppclk_khz * (1.0 + utm_soc_bb->dcn_downspread_percent / 100.0) * 255.0 / dpprefclk_khz, 1.0)); +} + +static void round_min_clocks_to_granularity(struct dml2_display_cfg_programming *programming, const struct dml2_utm_soc_bb *utm_soc_bb) +{ + if (utm_soc_bb->no_dfs) { + round_to_non_dfs_granularity(programming->min_clocks.dcn4x.dispclk_khz, programming->min_clocks.dcn4x.dpprefclk_khz, programming->min_clocks.dcn4x.dtbrefclk_khz, + &programming->min_clocks.dcn4x.dispclk_khz, &programming->min_clocks.dcn4x.dpprefclk_khz, &programming->min_clocks.dcn4x.dtbrefclk_khz); + } else { + add_margin_and_round_to_dfs_grainularity(programming->min_clocks.dcn4x.dispclk_khz, 0.0, + (unsigned long)(utm_soc_bb->dispclk_dppclk_vco_speed_mhz * 1000), &programming->min_clocks.dcn4x.dispclk_khz, &programming->min_clocks.dcn4x.divider_ids.dispclk_did); + + add_margin_and_round_to_dfs_grainularity(programming->min_clocks.dcn4x.dpprefclk_khz, 0.0, + (unsigned long)(utm_soc_bb->dispclk_dppclk_vco_speed_mhz * 1000), &programming->min_clocks.dcn4x.dpprefclk_khz, &programming->min_clocks.dcn4x.divider_ids.dpprefclk_did); + + add_margin_and_round_to_dfs_grainularity(programming->min_clocks.dcn4x.dtbrefclk_khz, 0.0, + (unsigned long)(utm_soc_bb->dispclk_dppclk_vco_speed_mhz * 1000), &programming->min_clocks.dcn4x.dtbrefclk_khz, &programming->min_clocks.dcn4x.divider_ids.dtbrefclk_did); + } +} + +static bool dcn5_populate_min_clocks_in_programming(struct dml2_display_cfg_programming *programming, + const struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_core_ip_params *ip_params, + const struct dml2_display_solution *solution) +{ + unsigned int i; + + programming->min_clocks.dcn4x.dispclk_khz = + calculate_dispclk_khz(solution, utm_soc_bb, ip_params); + programming->min_clocks.dcn4x.dpprefclk_khz = + calculate_dpprefclk_khz(solution, utm_soc_bb); + programming->min_clocks.dcn4x.dtbrefclk_khz = + calculate_dtbrefclk_khz(solution, utm_soc_bb); + programming->min_clocks.dcn4x.deepsleep_dcfclk_khz = + (unsigned long) math_min2((double)solution->validation_result.mode_support.global.dcfclk_deepsleep_khz, (double)solution->sop_constraint.dcn5.clocks.dcfclk_khz); + programming->min_clocks.dcn4x.socclk_khz = solution->sop_constraint.dcn5.clocks.socclk_khz; + programming->min_clocks.dcn4x.active.dcfclk_khz = solution->sop_constraint.dcn5.clocks.dcfclk_khz; + programming->min_clocks.dcn4x.active.fclk_khz = solution->sop_constraint.dcn5.clocks.fclk_khz; + programming->min_clocks.dcn4x.active.uclk_khz = solution->sop_constraint.dcn5.clocks.uclk_khz; + round_min_clocks_to_granularity(programming, utm_soc_bb); + for (i = 0; i < solution->dispcfg.num_planes; i++) + programming->plane_programming[i].min_clocks.dcn4x.dppclk_khz = + calculate_dppclk_khz_plane_index(i, solution, utm_soc_bb, + programming->min_clocks.dcn4x.dpprefclk_khz); + for (i = 0; i < solution->dispcfg.num_streams; i++) { + programming->stream_programming[i].min_clocks.dcn4x.dscclk_khz = + solution->validation_result.mode_support.per_stream[i].dscclk_khz; + programming->stream_programming[i].min_clocks.dcn4x.dtbclk_khz = + solution->validation_result.mode_support.per_stream[i].dtbclk_khz; + programming->stream_programming[i].min_clocks.dcn4x.phyclk_khz = + solution->validation_result.mode_support.per_stream[i].phyclk_khz; + } + + return validate_min_clocks(solution, programming, utm_soc_bb); +} + +void dcn5_populate_pstate_support_in_programming(struct dml2_display_cfg_programming *programming, + const struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_display_solution *solution) +{ + unsigned int plane_index; + int displays_without_vactive_margin_mask = 0x0; + bool uclk_pstate_supported = true; + int min_idle_us = 0; + + for (plane_index = 0; plane_index < solution->dispcfg.num_planes; plane_index++) { + if (solution->uclk_pstate_params.pstate_switch_modes[plane_index] == dml2_pstate_method_na) { + /* UCLK P-State is supported if the pstate method is populated */ + uclk_pstate_supported = false; + break; + } + } + programming->uclk_pstate_supported = uclk_pstate_supported; + programming->fclk_pstate_supported = false; + displays_without_vactive_margin_mask = + get_displays_without_vactive_margin_mask(solution, utm_soc_bb); + if (displays_without_vactive_margin_mask == 0) { + programming->fclk_pstate_supported = true; + } else { + if (are_timings_trivially_synchronizable(solution, displays_without_vactive_margin_mask)) { + min_idle_us = find_smallest_idle_time_in_vblank_us(solution, displays_without_vactive_margin_mask); + + if (min_idle_us >= utm_soc_bb->power_management_parameters.fclk_change_blackout_us) + programming->fclk_pstate_supported = true; + } + } +} + +void dcn5_populate_stutter_support_in_programming(struct dml2_display_cfg_programming *programming, + const struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_display_solution *solution) +{ + int min_idle_us; + + min_idle_us = find_smallest_idle_time_in_vblank_us(solution, 0xFF); + + if (utm_soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us > 0 && + min_idle_us >= utm_soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us) + programming->stutter.supported_in_blank = true; + else + programming->stutter.supported_in_blank = false; + + if (utm_soc_bb->power_management_parameters.z8_min_idle_time > 0 && + programming->informative.power_management.z8.stutter_period >= utm_soc_bb->power_management_parameters.z8_min_idle_time) + programming->z8_stutter.meets_eco = true; + else + programming->z8_stutter.meets_eco = false; + + if (utm_soc_bb->power_management_parameters.z8_stutter_exit_latency_us > 0 && + min_idle_us >= utm_soc_bb->power_management_parameters.z8_stutter_exit_latency_us) + programming->z8_stutter.supported_in_blank = true; + else + programming->z8_stutter.supported_in_blank = false; +} + +static void dcn5_populate_qos_bound_in_programming(struct dml2_display_cfg_programming *programming, + const struct dml2_display_solution *solution) +{ + programming->qos_bound.latency_ub = solution->sop_constraint.dcn5.latency; + /* + * A true UTM design enables dynamic bandwidth percentage allocation. At DPM1 when the system is idle, we can + * allocate more bandwidth percentage share to DCN. The clocks can be designed to lower values to save power. + * When the system is at its peak load. The system can still increase to the max DPM level and reduce bandwidth + * percentage share to DCN as long as it still meets DCN's QoS requirements. So other clients such as GFX may be + * given even more bandwidth at its peak load. The UTM design increases DML complicity because higher DPM level + * no longer equates to more or equal bandwidth to DCN. It now depends on the bandwidth percentage share (aka. + * bandwidth derate). DCN needs to express its true QoS bandwidth requirements without knowing bandwidth + * availability at any DPM levels. + * + * We assume that there is a top down decision that this generation of hardware has a fixed bandwidth derate + * across all DPM levels and it doesn't require a true UTM design. As such the QoS bandwidth requirement can be + * based on the fixed bandwidth availability at lowest supported DPM. This simplifies mode programming + * calculation. We are making corresponding QoS Bound change below to be compatible with the simplified mode + * programming calculation. + */ + // programming->qos_bound.bandwidth_lb = solution->validation_result.mode_support.bandwidth_upper_bound; + programming->qos_bound.bandwidth_lb.dcn5.urgent_bandwidth_kbps = solution->sop_constraint.dcn5.min_available_urgent_bandwidth_KBps; +} + +bool dpmm_dcn5_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out) +{ + dcn5_populate_qos_bound_in_programming(in_out->programming, in_out->solution); + dcn5_populate_pstate_support_in_programming(in_out->programming, in_out->utm_soc_bb, in_out->solution); + dcn5_populate_stutter_support_in_programming(in_out->programming, in_out->utm_soc_bb, in_out->solution); + return dcn5_populate_min_clocks_in_programming( + in_out->programming, in_out->utm_soc_bb, in_out->ip, in_out->solution); +} + +bool dpmm_dcn5_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out) +{ + const struct dml2_display_cfg *display_cfg = &in_out->solution->dispcfg; + const struct dml2_core_internal_display_mode_lib *mode_lib = &in_out->core->clean_me_up.mode_lib; + struct dml2_dchub_global_register_set *dchubbub_regs = &in_out->programming->global_regs; + struct dml2_mcif_global_register_set *mcif_regs = &in_out->programming->mcif_global_regs; + + double refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : in_out->core->utm_soc_bb->dchub_refclk_mhz; + + /* set A */ + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_mall = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthMALL * 1000); + + /* set B */ + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000); + dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].frac_urg_bw_mall = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthMALL * 1000); + + dchubbub_regs->num_watermark_sets = 2; + + /* MCIF */ + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].urgent = (int unsigned)(mode_lib->mp.Watermark.WritebackUrgentWatermark * 1000.0); + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.WritebackDRAMClockChangeWatermark * 1000.0); + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.WritebackFCLKChangeWatermark * 1000.0); + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.writeback_temp_read_or_ppt_watermark_us * 1000.0); + + /* replicate sets A through D */ + memcpy(&mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B], &mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A], sizeof(mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A])); + memcpy(&mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_C], &mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A], sizeof(mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A])); + memcpy(&mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_D], &mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A], sizeof(mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A])); + + mcif_regs->num_watermark_sets = 4; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.h new file mode 100644 index 000000000000..24cd43c8f1ca --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn5.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_DPMM_DCN5_H__ +#define __DML2_DPMM_DCN5_H__ + +#include "dml2_internal_shared_types.h" + +bool dpmm_dcn5_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out); +bool dpmm_dcn5_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out); + +void dcn5_populate_pstate_support_in_programming(struct dml2_display_cfg_programming *programming, + const struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_display_solution *solution); +void dcn5_populate_stutter_support_in_programming(struct dml2_display_cfg_programming *programming, + const struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_display_solution *solution); +#endif /* #ifndef __DML2_DPMM_DCN5_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.c new file mode 100644 index 000000000000..3fd045e4a8b9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#include "dml2_pmo_dcn5.h" +#include "dml2_pmo_dcn4_fams2.h" +#include "dml2_pmo_dcn5_stage_optimizers.h" +#include "lib_float_math.h" +#include "dml2_debug.h" + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_1_display[] = { + // VActive Preferred + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Then VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // Then DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Finally VBlank, but allow base clocks for latency to increase + /* + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + */ +}; + +static const int base_pstate_strategy_list_1_display_size = sizeof(base_pstate_strategy_list_1_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_2_display[] = { + // VActive only is preferred + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Then VActive + VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // Then VBlank only + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // Then DRR + VActive + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_fw_drr, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Then DRR + DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Finally VBlank, but allow base clocks for latency to increase + /* + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + */ +}; + +static const int base_pstate_strategy_list_2_display_size = sizeof(base_pstate_strategy_list_2_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_3_display[] = { + // All VActive + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // VActive + 1 VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vblank, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // All VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // All DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // All VBlank, with state increase allowed + /* + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + */ +}; + +static const int base_pstate_strategy_list_3_display_size = sizeof(base_pstate_strategy_list_3_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_4_display[] = { + // All VActive + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive }, + .allow_state_increase = true, + }, + + // VActive + 1 VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vblank }, + .allow_state_increase = false, + }, + + // All Vblank + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank }, + .allow_state_increase = false, + }, + + // All DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr }, + .allow_state_increase = true, + }, + + // All VBlank, with state increase allowed + /* + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank }, + .allow_state_increase = true, + }, + */ +}; + +static const int base_pstate_strategy_list_4_display_size = sizeof(base_pstate_strategy_list_4_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static void dml2_pmo_dcn5_assign_pstate_strategies(struct dml2_pmo_instance *pmo) +{ + int i = 0; + + /* generate permutations of p-state configs from base strategy list */ + for (i = 1; i <= PMO_DCN4_MAX_DISPLAYS; i++) { + switch (i) { + case 1: + DML_ASSERT(base_pstate_strategy_list_1_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_1_display, + base_pstate_strategy_list_1_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + case 2: + DML_ASSERT(base_pstate_strategy_list_2_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_2_display, + base_pstate_strategy_list_2_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + case 3: + DML_ASSERT(base_pstate_strategy_list_3_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_3_display, + base_pstate_strategy_list_3_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + case 4: + DML_ASSERT(base_pstate_strategy_list_4_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_4_display, + base_pstate_strategy_list_4_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + } + } +} + +bool dml2_pmo_dcn5_initialize(struct dml2_pmo_initialize_in_out *in_out) +{ + struct dml2_pmo_instance *pmo = in_out->instance; + + pmo->ip_caps = in_out->ip_caps; + pmo->options = in_out->options; + pmo->utm_soc_bb = in_out->utm_soc_bb; + pmo->mpc_combine_limit = 2; + pmo->odm_combine_limit = 4; + pmo->fams_params.v2.drr.refresh_rate_limit_max = 1000; + pmo->fams_params.v2.drr.refresh_rate_limit_min = 119; + dml2_pmo_dcn5_assign_pstate_strategies(pmo); + + dml2_pmo_dcn5_stage_optimizer_mcache_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]); + dml2_pmo_dcn5_stage_optimizer_uclk_pstate_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]); + dml2_pmo_dcn5_stage_optimizer_qos_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_qos]); + dml2_pmo_dcn5_stage_optimizer_vmin_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_vmin]); + dml2_pmo_dcn5_stage_optimizer_stutter_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_stutter]); + + return true; +} + +int dml2_pmo_dcn5_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **stages) +{ + int count = 0; + + if (!pmo->options->force_optional_mcache_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]; + if (!pmo->options->force_optional_uclk_pstate_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]; + + return count; +} + +int dml2_pmo_dcn5_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **stages) +{ + int count = 0; + + if (pmo->options->force_optional_mcache_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]; + if (pmo->options->force_optional_uclk_pstate_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_qos]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_vmin]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_stutter]; + + return count; +} + +static void dml2_pmo_dcn5_assign_timing_groups(struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_stream_parameters *cur_stream, *other_stream; + const struct dml2_plane_parameters *plane; + unsigned int i, j; + + worksheet->timing_group_count = 0; + memset(worksheet->timing_group_ids, 0xFF, sizeof(worksheet->timing_group_ids)); + + /* assign timing group IDs per stream using the same synchronization logic as + * dcn5_build_synchronized_timing_groups: streams with identical timings are + * placed in the same group; DRR-enabled streams are never merged with others */ + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) { + if (worksheet->timing_group_ids[i] != 0xFFFFFFFF) + /* already assigned */ + continue; + worksheet->timing_group_ids[i] = worksheet->timing_group_count; + worksheet->timing_group_count++; + + plane = &worksheet->orig_dispcfg->plane_descriptors[i]; + cur_stream = &worksheet->orig_dispcfg->stream_descriptors[plane->stream_index]; + + for (j = i + 1; j < worksheet->orig_dispcfg->num_planes; j++) { + if (worksheet->timing_group_ids[j] != 0xFFFFFFFF) + /* already assigned */ + continue; + plane = &worksheet->orig_dispcfg->plane_descriptors[j]; + other_stream = &worksheet->orig_dispcfg->stream_descriptors[plane->stream_index]; + + if (cur_stream == other_stream) + /* same stream, must be in the same group */ + worksheet->timing_group_ids[j] = worksheet->timing_group_ids[i]; + + if (memcmp(&cur_stream->timing, &other_stream->timing, sizeof(struct dml2_timing_cfg)) == 0 + && !cur_stream->timing.drr_config.enabled) + /* identical timings, should be in the same group */ + worksheet->timing_group_ids[j] = worksheet->timing_group_ids[i]; + } + } +} + +void dml2_pmo_dcn5_initialize_worksheet(struct dml2_pmo_instance *pmo, + const struct dml2_display_cfg *dispcfg, + struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_sop_table *sop_table = &pmo->utm_soc_bb->sop_table; + + DML_LOG_COMP_IF_ENTER(); + memset(worksheet, 0, sizeof(struct dml2_optimization_worksheet)); + worksheet->orig_dispcfg = dispcfg; + worksheet->cur.config.min_sop_index = sop_table->get_highest_sop_index(sop_table); + worksheet->cur.unvalidated_change.raw = 0xFFFF; + dml2_pmo_dcn5_assign_timing_groups(worksheet); + DML_LOG_COMP_IF_EXIT(); +} + +static bool dml2_pmo_dcn5_check_total_pipe_usage(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_display_cfg *orig_dispcfg = worksheet->orig_dispcfg; + unsigned int total_pipe_usage = 0; + + for (i = 0; i < orig_dispcfg->num_planes; i++) + if (worksheet->cur.config.mpc_combine_overrides[i]) + total_pipe_usage += worksheet->cur.config.mpc_combine_overrides[i]; + else if (worksheet->cur.config.odm_combine_overrides[orig_dispcfg->plane_descriptors[i].stream_index]) + total_pipe_usage += worksheet->cur.config.odm_combine_overrides[orig_dispcfg->plane_descriptors[i].stream_index]; + else + total_pipe_usage += worksheet->validation_result.mode_support.cfg_support_info.plane_support_info[i].dpps_used; + + return total_pipe_usage <= pmo->ip_caps->pipe_count; +} + +static bool is_h_timing_divisible_by(const struct dml2_timing_cfg *timing, unsigned int denominator) +{ + /* + * Htotal, Hblank start/end, and Hsync start/end all must be divisible + * in order for the horizontal timing params to be considered divisible + * by 2. Hsync start is always 0. + */ + unsigned long h_blank_start = timing->h_total - timing->h_front_porch; + + return (timing->h_total % denominator == 0) && + (h_blank_start % denominator == 0) && + (timing->h_blank_end % denominator == 0) && + (timing->h_sync_width % denominator == 0); +} + +static bool dml2_pmo_dcn5_check_odm_divisibility(const struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_timing_cfg *timing; + + for (i = 0; i < worksheet->orig_dispcfg->num_streams; i++) { + if (worksheet->cur.config.odm_combine_overrides[i]) { + timing = &worksheet->orig_dispcfg->stream_descriptors[i].timing; + if (!is_h_timing_divisible_by(timing, worksheet->cur.config.odm_combine_overrides[i])) { + ((struct dml2_optimization_worksheet *)worksheet)->mcache.per_plane_status[i] = false; + return false; + } + + if (timing->dsc.overrides.num_slices && + timing->dsc.overrides.num_slices % worksheet->cur.config.odm_combine_overrides[i]) + return false; + } + } + return true; +} + +enum dml2_status dml2_pmo_dcn5_sanity_check(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet) +{ + enum dml2_status status = DML2_STATUS_OK; + + DML_LOG_COMP_IF_ENTER(); + if (!dml2_pmo_dcn5_check_total_pipe_usage(pmo, worksheet)) { + status = DML2_STATUS_VALIDATE_FAIL_PMO_SANITY_TOTAL_PIPE_USAGE; + goto exit; + } + + if (!dml2_pmo_dcn5_check_odm_divisibility(worksheet)) { + status = DML2_STATUS_VALIDATE_FAIL_PMO_SANITY_ODM_DIVISIBILITY; + goto exit; + } + +exit: + DML_LOG_DEBUG("%s exit with %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + + return status; +} + +static void dml2_pmo_dcn5_apply_optimization_to_solution(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_config *optimization, + struct dml2_display_solution *solution) +{ + unsigned int i; + const struct dml2_sop_table *sop_table = &pmo->utm_soc_bb->sop_table; + + solution->unvalidated_change.raw = optimization->unvalidated_change.raw; + DML_LOG_DEBUG("solution->unvalidated_change.raw = 0x%X\n", solution->unvalidated_change.raw); + + /* sop index */ + sop_table->get_sop_constraint_at_index(sop_table, + optimization->config.min_sop_index, &solution->sop_constraint); + DML_LOG_VERBOSE("min_sop_index = %d\n", optimization->config.min_sop_index); + + /* mpc overrides */ + for (i = 0; i < solution->dispcfg.num_planes; i++) + if (optimization->config.mpc_combine_overrides[i]) { + solution->dispcfg.plane_descriptors[i].overrides.mpcc_combine_factor = + optimization->config.mpc_combine_overrides[i]; + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.mpcc_combine_factor = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.mpcc_combine_factor); + } + + /* odm overrides */ + for (i = 0; i < solution->dispcfg.num_streams; i++) { + switch (optimization->config.odm_combine_overrides[i]) { + case 1: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_bypass; + break; + case 2: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_combine_2to1; + break; + case 3: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_combine_3to1; + break; + case 4: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_combine_4to1; + break; + default: + break; + } + DML_LOG_VERBOSE("solution->dispcfg.stream_descriptors[%d].overrides.odm_mode = %d\n", + i, solution->dispcfg.stream_descriptors[i].overrides.odm_mode); + } + + /* reserved vblank time */ + for (i = 0; i < solution->dispcfg.num_planes; i++) { + solution->dispcfg.plane_descriptors[i].overrides.reserved_vblank_time_ns = (long) math_max2( + solution->dispcfg.plane_descriptors[i].overrides.reserved_vblank_time_ns, + optimization->config.reserved_vblank_time_ns[i]); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.reserved_vblank_time_ns = %ld\n", + i, solution->dispcfg.plane_descriptors[i].overrides.reserved_vblank_time_ns); + } + + /* mcache allocations */ + for (i = 0; i < solution->dispcfg.num_planes; i++) + if (optimization->config.mcache_allocations[i].valid) + memcpy(&solution->mcache_allocations[i], &optimization->config.mcache_allocations[i], + sizeof(struct dml2_mcache_surface_allocation)); + + /* P-State switch method */ + solution->uclk_pstate_params.support = optimization->config.uclk_pstate_support; + for (i = 0; i < solution->dispcfg.num_planes; i++) { + solution->uclk_pstate_params.pstate_switch_modes[i] = optimization->config.uclk_pstate_switch_modes[i]; + DML_LOG_VERBOSE("solution->uclk_pstate_params.pstate_switch_modes[%d] = %d\n", + i, solution->uclk_pstate_params.pstate_switch_modes[i]); + } + + /* P-State latency hiding */ + for (i = 0; i < solution->dispcfg.num_planes; i++) { + memcpy(solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us, + optimization->config.max_vactive_det_fill_delay_us[i], + sizeof(optimization->config.max_vactive_det_fill_delay_us[i])); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.max_vactive_det_fill_delay_us[uclk] = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk]); + } + + /* FAMS2 related */ + solution->uclk_pstate_params.fams2_required = optimization->config.fams2_required; + DML_LOG_VERBOSE("solution->uclk_pstate_params.fams2_required = %s\n", solution->uclk_pstate_params.fams2_required ? + "true" : "false"); + memcpy(&solution->uclk_pstate_params.stream_pstate_meta, + &optimization->config.stream_pstate_meta, + sizeof(struct dml2_pstate_meta) * DML2_MAX_PLANES); + + /* fclk pstate */ + solution->fclk_pstate_support = optimization->config.fclk_pstate_support; + DML_LOG_VERBOSE("solution->fclk_pstate_support = %s\n", solution->fclk_pstate_support ? + "true" : "false"); + /* stutter */ + solution->stutter_support_in_vblank = optimization->config.stutter_support_in_vblank; + solution->z8_stutter_support_in_vblank = optimization->config.z8_stutter_support_in_vblank; + DML_LOG_VERBOSE("solution->stutter_support_in_vblank = %s\n", solution->stutter_support_in_vblank ? + "true" : "false"); + DML_LOG_VERBOSE("solution->z8_stutter_support_in_vblank = %s\n", solution->z8_stutter_support_in_vblank ? + "true" : "false"); + + /* dcfclk override */ + if (optimization->config.enable_vmin_dcfclk) { + solution->dispcfg.overrides.hw.dcfclk_mhz = pmo->utm_soc_bb->vmin_limit.dcfclk_khz / 1000.0; + } + DML_LOG_VERBOSE("solution->dispcfg.overrides.hw.dcfclk_mhz = %f\n", solution->dispcfg.overrides.hw.dcfclk_mhz); +} + +void dml2_pmo_dcn5_convert_worksheet_to_solution(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet, + struct dml2_display_solution *solution) +{ + DML_LOG_COMP_IF_ENTER(); + + memset(solution, 0, sizeof(struct dml2_display_solution)); + solution->orig_dispcfg = worksheet->orig_dispcfg; + memcpy(&solution->dispcfg, worksheet->orig_dispcfg, sizeof(solution->dispcfg)); + memcpy(&solution->validation_result, &worksheet->validation_result, sizeof(struct dml2_validation_result)); + dml2_pmo_dcn5_apply_optimization_to_solution(pmo, &worksheet->cur, solution); + DML_LOG_COMP_IF_EXIT(); +} + +void dml2_pmo_dcn5_clear_pre_validation_states(struct dml2_pmo_instance *pmo, + struct dml2_optimization_worksheet *worksheet) +{ + (void)pmo; + DML_LOG_COMP_IF_ENTER(); + worksheet->cur.unvalidated_change.raw = 0; + DML_LOG_DEBUG("worksheet->cur.unvalidated_change.raw = %d\n", worksheet->cur.unvalidated_change.raw); + DML_LOG_COMP_IF_EXIT(); +} + diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h new file mode 100644 index 000000000000..fdcc42b9bc32 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_PMO_DCN5_H__ +#define __DML2_PMO_DCN5_H__ +#include "dml2_internal_shared_types.h" +bool dml2_pmo_dcn5_initialize(struct dml2_pmo_initialize_in_out *in_out); +int dml2_pmo_dcn5_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, struct dml2_pmo_stage_optimizer **optimers); +int dml2_pmo_dcn5_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, struct dml2_pmo_stage_optimizer **optimers); +void dml2_pmo_dcn5_initialize_worksheet(struct dml2_pmo_instance *pmo, + const struct dml2_display_cfg *dispcfg, + struct dml2_optimization_worksheet *worksheet); +enum dml2_status dml2_pmo_dcn5_sanity_check(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet); +void dml2_pmo_dcn5_convert_worksheet_to_solution(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet, + struct dml2_display_solution *solution); +void dml2_pmo_dcn5_clear_pre_validation_states(struct dml2_pmo_instance *pmo, + struct dml2_optimization_worksheet *worksheet); +#endif /* __DML2_PMO_DCN5_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.c new file mode 100644 index 000000000000..1c74db7b19dc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.c @@ -0,0 +1,2228 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#include "dml2_pmo_dcn5_stage_optimizers.h" +#include "dml2_debug.h" +#include "lib_float_math.h" + +static bool dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage_with_odm(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_display_cfg *orig_dispcfg = worksheet->orig_dispcfg; + const struct core_stream_support_info *stream_support_info; + unsigned int stream_idx; + bool is_incremented = false; + + for (i = 0; i < orig_dispcfg->num_planes; i++) { + stream_idx = orig_dispcfg->plane_descriptors[i].stream_index; + stream_support_info = &worksheet->validation_result.mode_support.cfg_support_info.stream_support_info[stream_idx]; + if (!worksheet->mcache.per_plane_status[i]) { + if (worksheet->cur.unvalidated_change.bits.odm_combine_overrides) { + if (worksheet->cur.config.odm_combine_overrides[stream_idx] >= (unsigned int)stage->pmo->odm_combine_limit) + return false; + } else if (stream_support_info->odms_used >= (unsigned int)stage->pmo->odm_combine_limit) { + return false; + } + } + } + + for (i = 0; i < orig_dispcfg->num_planes; i++) { + if (worksheet->mcache.per_plane_status[i]) + continue; + + stream_idx = orig_dispcfg->plane_descriptors[i].stream_index; + if (worksheet->cur.unvalidated_change.bits.odm_combine_overrides) { + worksheet->cur.config.odm_combine_overrides[stream_idx]++; + } else { + worksheet->cur.config.odm_combine_overrides[stream_idx] = + worksheet->validation_result.mode_support.cfg_support_info.stream_support_info[stream_idx].odms_used + 1; + } + is_incremented = true; + } + + return is_incremented; +} + +static bool dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage_with_mpc(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_display_cfg *orig_dispcfg = worksheet->orig_dispcfg; + bool is_incremented = false; + + for (i = 0; i < orig_dispcfg->num_planes; i++) + if (worksheet->mcache.per_plane_status[i] == false + && worksheet->validation_result.mode_support.cfg_support_info.plane_support_info[i].dpps_used >= stage->pmo->mpc_combine_limit) + return false; + + for (i = 0; i < orig_dispcfg->num_planes; i++) { + if (worksheet->mcache.per_plane_status[i]) + continue; + + worksheet->cur.config.mpc_combine_overrides[i] = + worksheet->validation_result.mode_support.cfg_support_info.plane_support_info[i].dpps_used + 1; + is_incremented = true; + } + + return is_incremented; +} + +bool dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet) +{ + bool result; + + if (worksheet->mcache.is_single_stream_odm_case) { + result = dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage_with_odm(stage, worksheet); + if (result) + worksheet->cur.unvalidated_change.bits.odm_combine_overrides = true; + } else { + result = dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage_with_mpc(stage, worksheet); + if (result) + worksheet->cur.unvalidated_change.bits.mpc_combine_overrides = true; + } + + return result; +} + +bool dml2_pmo_dcn5_stage_optimizer_mcache_test_total_mcache_limit(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet) +{ + unsigned int total_mcaches_required = 0; + unsigned int i; + const struct dml2_mcache_surface_allocation *allocation = worksheet->validation_result.mcache_allocations; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (allocation[i].valid) { + total_mcaches_required += allocation[i].num_mcaches_plane0 + + allocation[i].num_mcaches_plane1; + if (allocation[i].last_slice_sharing.plane0_plane1) + total_mcaches_required--; + } + } + return total_mcaches_required <= stage->pmo->utm_soc_bb->num_dcc_mcaches; +} + +bool dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_status(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + unsigned int i; + + for (i = 0; i < DML2_MAX_PLANES; i++) + if (worksheet->orig_dispcfg->plane_descriptors[i].surface.dcc.enable && + !worksheet->mcache.per_plane_status[i]) + return false; + return true; +} + +static bool dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_pipe_limit(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + + unsigned int total_mcaches_required = 0; + unsigned int total_pipe_usage = 0; + unsigned int i; + const struct dml2_mcache_surface_allocation *allocation = worksheet->validation_result.mcache_allocations; + const struct dml2_display_cfg *orig_dispcfg = worksheet->orig_dispcfg; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (allocation[i].valid) { + total_mcaches_required += allocation[i].num_mcaches_plane0 + + allocation[i].num_mcaches_plane1; + if (allocation[i].last_slice_sharing.plane0_plane1) + total_mcaches_required--; + } + } + + for (i = 0; i < orig_dispcfg->num_planes; i++) + if (worksheet->cur.config.mpc_combine_overrides[i]) + total_pipe_usage += worksheet->cur.config.mpc_combine_overrides[i]; + else if (worksheet->cur.config.odm_combine_overrides[orig_dispcfg->plane_descriptors[i].stream_index]) + total_pipe_usage += worksheet->cur.config.odm_combine_overrides[orig_dispcfg->plane_descriptors[i].stream_index]; + else + total_pipe_usage += worksheet->validation_result.mode_support.cfg_support_info.plane_support_info[i].dpps_used; + + return total_pipe_usage >= total_mcaches_required; + +} + +void dml2_pmo_dcn5_stage_optimizer_mcache_apply_default_pipe_usage(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + unsigned int i; + + if (!worksheet->validation_result.is_mcache_allocation_valid) + return; + + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) + if (worksheet->validation_result.mcache_allocations[i].valid) + memcpy(&worksheet->cur.config.mcache_allocations[i], + &worksheet->validation_result.mcache_allocations[i], + sizeof(struct dml2_mcache_surface_allocation)); +} + +void dml2_pmo_dcn5_stage_optimizer_mcache_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + DML_LOG_COMP_IF_ENTER(); + if (worksheet->orig_dispcfg->num_streams == 1 + && worksheet->validation_result.mode_support.cfg_support_info.stream_support_info[0].odms_used > 1) + worksheet->mcache.is_single_stream_odm_case = true; + DML_LOG_COMP_IF_EXIT(); +} + +static void dml2_pmo_dcn5_stage_optimizer_mcache_decide_plane_status( + struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + const struct dml2_mcache_surface_allocation *allocation = worksheet->validation_result.mcache_allocations; + const struct dml2_display_cfg *orig_dispcfg = worksheet->orig_dispcfg; + + for (unsigned int plane_index = 0; plane_index < orig_dispcfg->num_planes; plane_index++) { + if (!orig_dispcfg->plane_descriptors[plane_index].surface.dcc.enable) + continue; + + unsigned int pipe_usage = 0; + unsigned int mcaches_required = 0; + int stream_index = orig_dispcfg->plane_descriptors[plane_index].stream_index; + + if (worksheet->cur.config.mpc_combine_overrides[plane_index]) { + pipe_usage = worksheet->cur.config.mpc_combine_overrides[plane_index]; + } else if (worksheet->cur.config.odm_combine_overrides[stream_index]) { + pipe_usage = worksheet->cur.config.odm_combine_overrides[stream_index]; + } else { + pipe_usage = worksheet->validation_result.mode_support.cfg_support_info + .plane_support_info[plane_index].dpps_used; + } + + if (allocation[plane_index].valid) { + mcaches_required = allocation[plane_index].num_mcaches_plane0 + + allocation[plane_index].num_mcaches_plane1; + + if (allocation[plane_index].last_slice_sharing.plane0_plane1) + mcaches_required--; + } + + worksheet->mcache.per_plane_status[plane_index] = (pipe_usage >= mcaches_required); + } +} + +static bool dml2_pmo_dcn5_stage_optimizer_mcache_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool should_continue = true; + + DML_LOG_COMP_IF_ENTER(); + if (!worksheet->validation_result.is_mcache_allocation_valid) { + /* validation has failed, stop optimizing further */ + should_continue = false; + goto exit; + } + + if (worksheet->validation_result.is_mode_support_valid && stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK) { + /* optimization is permissible, no need to optimize further */ + should_continue = false; + goto exit; + } + + if (worksheet->mcache.is_default_pipe_usage_attempted) { + if (!dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage(stage, worksheet)) { + should_continue = false; + goto exit; + } + } else { + dml2_pmo_dcn5_stage_optimizer_mcache_apply_default_pipe_usage(stage, worksheet); + worksheet->mcache.is_default_pipe_usage_attempted = true; + } + + dml2_pmo_dcn5_stage_optimizer_mcache_decide_plane_status(stage, worksheet); + worksheet->cur.unvalidated_change.bits.mcache_allocation = true; +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +static enum dml2_status dml2_pmo_dcn5_stage_optimizer_mcache_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + enum dml2_status status = DML2_STATUS_OK; + + DML_LOG_COMP_IF_ENTER(); + if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_total_mcache_limit(stage, worksheet)) { + status = DML2_STATUS_OPTIMIZE_FAIL_MCACHE; + goto exit; + } + + if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_status(stage, worksheet)) { + status = DML2_STATUS_OPTIMIZE_FAIL_MCACHE; + goto exit; + } + + if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_pipe_limit(stage, worksheet)) { + status = DML2_STATUS_OPTIMIZE_FAIL_MCACHE; + goto exit; + } +exit: + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + return status; +} + +void dml2_pmo_dcn5_stage_optimizer_mcache_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn5_stage_optimizer_mcache_init; + stage->optimize_next = dml2_pmo_dcn5_stage_optimizer_mcache_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn5_stage_optimizer_mcache_test_permissibility; +} + +void set_bit_in_bitfield(unsigned int *bit_field, unsigned int bit_offset) +{ + *bit_field = *bit_field | (0x1 << bit_offset); +} + +bool is_bit_set_in_bitfield(unsigned int bit_field, unsigned int bit_offset) +{ + if (bit_field & (0x1 << bit_offset)) + return true; + + return false; +} + +int dcn5_get_vactive_pstate_margin(const struct dml2_validation_result *validation_res, int plane_mask) +{ + unsigned char i; + int min_vactive_margin_us = 0xFFFFFFF; + + if (!validation_res->is_mode_support_valid) + return min_vactive_margin_us; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (validation_res->mode_support.cfg_support_info.plane_support_info[i].dram_change_latency_hiding_margin_in_active < min_vactive_margin_us) + min_vactive_margin_us = validation_res->mode_support.cfg_support_info.plane_support_info[i].dram_change_latency_hiding_margin_in_active; + } + } + + return min_vactive_margin_us; +} + +static enum dml2_pstate_method uclk_pstate_strategy_override_to_pstate_method(const enum dml2_uclk_pstate_change_strategy override_strategy) +{ + enum dml2_pstate_method method = dml2_pstate_method_na; + + switch (override_strategy) { + case dml2_uclk_pstate_change_strategy_force_vactive: + method = dml2_pstate_method_vactive; + break; + case dml2_uclk_pstate_change_strategy_force_vblank: + method = dml2_pstate_method_vblank; + break; + case dml2_uclk_pstate_change_strategy_force_drr: + method = dml2_pstate_method_fw_drr; + break; + case dml2_uclk_pstate_change_strategy_force_mall_svp: + case dml2_uclk_pstate_change_strategy_force_alternate: + case dml2_uclk_pstate_change_strategy_force_mall_full_frame: + case dml2_uclk_pstate_change_strategy_auto: + default: + method = dml2_pstate_method_na; + } + + return method; +} + +void dcn5_build_method_scheduling_params( + struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta, + const struct dml2_pstate_meta *stream_pstate_meta) +{ + if (stream_method_pstate_meta->allow_start_otg_vline < 0.0 || + stream_method_pstate_meta->allow_end_otg_vline < 0.0 || + stream_method_pstate_meta->allow_start_otg_vline > stream_method_pstate_meta->allow_end_otg_vline) { + /* method is not schedulable */ + stream_method_pstate_meta->allow_time_us = 0.0; + stream_method_pstate_meta->disallow_time_us = stream_method_pstate_meta->period_us; + return; + } + + stream_method_pstate_meta->allow_time_us = + (double)(stream_method_pstate_meta->allow_end_otg_vline - stream_method_pstate_meta->allow_start_otg_vline) * + stream_pstate_meta->otg_vline_time_us; + if (stream_method_pstate_meta->allow_time_us >= stream_method_pstate_meta->period_us) { + /* when allow wave overlaps an entire frame, it is always schedulable (DRR can do this)*/ + stream_method_pstate_meta->disallow_time_us = 0.0; + } else { + stream_method_pstate_meta->disallow_time_us = + stream_method_pstate_meta->period_us - stream_method_pstate_meta->allow_time_us; + } +} + +static void dcn5_build_pstate_meta_per_stream(const struct dml2_display_cfg *display_cfg, + const struct dml2_ip_capabilities *ip_caps, + double blackout_us, + double det_fill_delay_us, + double extra_vactive_allow_time_us, + int stream_index, + /* output */ + struct dml2_pstate_meta *stream_pstate_meta) +{ + const struct dml2_stream_parameters *stream_descriptor = &display_cfg->stream_descriptors[stream_index]; + const struct dml2_timing_cfg *timing = &stream_descriptor->timing; + + /* worst case all other streams require some programming at the same time, 0 if only 1 stream */ + unsigned int contention_delay_us = (ip_caps->fams2.vertical_interrupt_ack_delay_us + + (unsigned int)math_max2(ip_caps->fams2.drr_programming_delay_us, ip_caps->fams2.allow_programming_delay_us)) * + (display_cfg->num_streams - 1); + + /* common */ + stream_pstate_meta->valid = true; + stream_pstate_meta->nom_vtotal = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active; + stream_pstate_meta->otg_vline_time_us = (double)timing->h_total / timing->pixel_clock_khz * 1000.0; + stream_pstate_meta->vblank_start = timing->v_blank_end + timing->v_active; + stream_pstate_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 / + (stream_pstate_meta->nom_vtotal * timing->h_total); + stream_pstate_meta->nom_frame_time_us = + (double)stream_pstate_meta->nom_vtotal * stream_pstate_meta->otg_vline_time_us; + + if (stream_descriptor->timing.drr_config.enabled == true) { + if (stream_descriptor->timing.drr_config.min_refresh_uhz != 0.0) { + stream_pstate_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz / + ((double)stream_descriptor->timing.drr_config.min_refresh_uhz * stream_descriptor->timing.h_total) * 1e9); + } else { + /* assume min of 48Hz */ + stream_pstate_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz / + (48000000.0 * stream_descriptor->timing.h_total) * 1e9); + } + } else { + stream_pstate_meta->max_vtotal = stream_pstate_meta->nom_vtotal; + } + stream_pstate_meta->min_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 / + (stream_pstate_meta->max_vtotal * timing->h_total); + stream_pstate_meta->max_frame_time_us = + (double)stream_pstate_meta->max_vtotal * stream_pstate_meta->otg_vline_time_us; + + stream_pstate_meta->scheduling_delay_otg_vlines = + (unsigned int)math_ceil(ip_caps->fams2.scheduling_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines = + (unsigned int)math_ceil(ip_caps->fams2.vertical_interrupt_ack_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->contention_delay_otg_vlines = + (unsigned int)math_ceil(contention_delay_us / stream_pstate_meta->otg_vline_time_us); + /* worst case allow to target needs to account for all streams' allow events overlapping, and 1 line for error */ + stream_pstate_meta->allow_to_target_delay_otg_vlines = + (unsigned int)(math_ceil((ip_caps->fams2.vertical_interrupt_ack_delay_us + contention_delay_us + ip_caps->fams2.allow_programming_delay_us) / stream_pstate_meta->otg_vline_time_us)) + 1; + stream_pstate_meta->min_allow_width_otg_vlines = + (unsigned int)math_ceil(ip_caps->fams2.min_allow_width_us / stream_pstate_meta->otg_vline_time_us); + /* this value should account for urgent latency */ + stream_pstate_meta->blackout_otg_vlines = (unsigned int)math_ceil(blackout_us / stream_pstate_meta->otg_vline_time_us); + + /* scheduling params should be built based on the worst case for allow_time:disallow_time */ + + /* vactive */ + if (display_cfg->num_streams == 1) { + /* for single stream, guarantee at least an instant of allow */ + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (unsigned int)math_floor( + math_max2(0.0, + timing->v_active - math_max2(1.0, stream_pstate_meta->min_allow_width_otg_vlines) - stream_pstate_meta->blackout_otg_vlines)); + } else { + /* for multi stream, bound to a max fill time defined by IP caps */ + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = + (unsigned int)math_max2(1.0, math_floor(det_fill_delay_us / stream_pstate_meta->otg_vline_time_us)); + } + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us = stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines * stream_pstate_meta->otg_vline_time_us; + + if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us > 0.0) { + stream_pstate_meta->method_vactive.common.allow_start_otg_vline = + timing->v_blank_end + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines; + stream_pstate_meta->method_vactive.common.allow_end_otg_vline = + stream_pstate_meta->vblank_start - + stream_pstate_meta->blackout_otg_vlines + + (unsigned int)(extra_vactive_allow_time_us / stream_pstate_meta->otg_vline_time_us); + } else { + stream_pstate_meta->method_vactive.common.allow_start_otg_vline = 0; + stream_pstate_meta->method_vactive.common.allow_end_otg_vline = 0; + } + stream_pstate_meta->method_vactive.common.period_us = stream_pstate_meta->nom_frame_time_us; + + /* vblank */ + stream_pstate_meta->method_vblank.common.allow_start_otg_vline = stream_pstate_meta->vblank_start; + stream_pstate_meta->method_vblank.common.period_us = stream_pstate_meta->nom_frame_time_us; + stream_pstate_meta->method_vblank.common.allow_end_otg_vline = + stream_pstate_meta->method_vblank.common.allow_start_otg_vline + 1; + + /* drr */ + stream_pstate_meta->method_drr.common.period_us = stream_pstate_meta->nom_frame_time_us; + stream_pstate_meta->method_drr.programming_delay_otg_vlines = + (unsigned int)math_ceil(ip_caps->fams2.drr_programming_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->method_drr.common.allow_start_otg_vline = + stream_pstate_meta->vblank_start + + stream_pstate_meta->allow_to_target_delay_otg_vlines; + + if (display_cfg->num_streams <= 1) { + /* only need to stretch vblank for blackout time */ + stream_pstate_meta->method_drr.stretched_vtotal = + stream_pstate_meta->nom_vtotal + + stream_pstate_meta->allow_to_target_delay_otg_vlines + + stream_pstate_meta->min_allow_width_otg_vlines + + stream_pstate_meta->blackout_otg_vlines; + } else { + /* multi display needs to always be schedulable */ + stream_pstate_meta->method_drr.stretched_vtotal = + stream_pstate_meta->nom_vtotal * 2 + + stream_pstate_meta->allow_to_target_delay_otg_vlines + + stream_pstate_meta->min_allow_width_otg_vlines + + stream_pstate_meta->blackout_otg_vlines; + } + stream_pstate_meta->method_drr.common.allow_end_otg_vline = + stream_pstate_meta->method_drr.stretched_vtotal - + stream_pstate_meta->blackout_otg_vlines; + + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_vactive.common, stream_pstate_meta); + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_vblank.common, stream_pstate_meta); + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_drr.common, stream_pstate_meta); +} + +void dcn5_build_synchronized_timing_groups( + // Output + struct dml2_pmo_synchronized_timing_groups *s, + // Input + const struct dml2_display_cfg *display_config) +{ + unsigned int i, j; + const struct dml2_timing_cfg *master_timing; + + unsigned int stream_mapped_mask = 0; + unsigned int num_timing_groups = 0; + unsigned int timing_group_idx = 0; + + /* clear all group masks */ + memset(s->synchronized_timing_group_masks, 0, sizeof(s->synchronized_timing_group_masks)); + memset(s->group_is_drr_enabled, 0, sizeof(s->group_is_drr_enabled)); + memset(s->group_is_drr_active, 0, sizeof(s->group_is_drr_active)); + memset(s->group_line_time_us, 0, sizeof(s->group_line_time_us)); + s->num_timing_groups = 0; + + for (i = 0; i < display_config->num_streams; i++) { + master_timing = &display_config->stream_descriptors[i].timing; + + /* only need to build group of this stream is not in a group already */ + if (is_bit_set_in_bitfield(stream_mapped_mask, i)) { + continue; + } + + set_bit_in_bitfield(&stream_mapped_mask, i); + timing_group_idx = num_timing_groups; + num_timing_groups++; + + /* trivially set default timing group to itself */ + set_bit_in_bitfield(&s->synchronized_timing_group_masks[timing_group_idx], i); + s->group_line_time_us[timing_group_idx] = (double)master_timing->h_total / master_timing->pixel_clock_khz * 1000.0; + + /* if drr is in use, timing is not sychnronizable */ + if (master_timing->drr_config.enabled) { + s->group_is_drr_enabled[timing_group_idx] = true; + s->group_is_drr_active[timing_group_idx] = !master_timing->drr_config.disallowed && + (master_timing->drr_config.drr_active_fixed || master_timing->drr_config.drr_active_variable); + continue; + } + + /* find synchronizable timing groups */ + for (j = i + 1; j < display_config->num_streams; j++) { + if (memcmp(master_timing, + &display_config->stream_descriptors[j].timing, + sizeof(struct dml2_timing_cfg)) == 0) { + set_bit_in_bitfield(&s->synchronized_timing_group_masks[timing_group_idx], j); + set_bit_in_bitfield(&stream_mapped_mask, j); + } + } + } + + s->num_timing_groups = num_timing_groups; +} + +void dcn5_insert_strategy_into_expanded_list( + const struct dml2_pmo_pstate_strategy *per_stream_pstate_strategy, + const int stream_count, + struct dml2_pmo_pstate_strategy *expanded_strategy_list, + unsigned int *num_expanded_strategies) +{ + (void)stream_count; + if (expanded_strategy_list && num_expanded_strategies) { + memcpy(&expanded_strategy_list[*num_expanded_strategies], per_stream_pstate_strategy, sizeof(struct dml2_pmo_pstate_strategy)); + + (*num_expanded_strategies)++; + } +} + +static enum dml2_pstate_method convert_strategy_to_drr_variant(const enum dml2_pstate_method base_strategy) +{ + enum dml2_pstate_method variant_strategy = 0; + + switch (base_strategy) { + case dml2_pstate_method_vactive: + variant_strategy = dml2_pstate_method_fw_vactive_drr; + break; + case dml2_pstate_method_vblank: + variant_strategy = dml2_pstate_method_fw_vblank_drr; + break; + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_alternate: + case dml2_pstate_method_fw_vactive_drr: + case dml2_pstate_method_fw_vblank_drr: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_fw_drr: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_count: + case dml2_pstate_method_na: + default: + /* no variant for this mode */ + variant_strategy = base_strategy; + } + + return variant_strategy; +} + +bool dcn5_is_variant_method_valid(const struct dml2_pmo_pstate_strategy *base_strategy, + const struct dml2_pmo_pstate_strategy *variant_strategy, + const unsigned int num_streams_per_base_method[PMO_DCN4_MAX_DISPLAYS], + const unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS], + const unsigned int stream_count) +{ + (void)variant_strategy; + bool valid = true; + unsigned int i; + + /* check all restrictions are met */ + for (i = 0; i < stream_count; i++) { + /* vblank + vblank_drr variants are invalid */ + if (base_strategy->per_stream_pstate_method[i] == dml2_pstate_method_vblank && + ((num_streams_per_base_method[i] > 0 && num_streams_per_variant_method[i] > 0) || + num_streams_per_variant_method[i] > 1)) { + valid = false; + break; + } + } + + return valid; +} + +void dcn5_expand_base_strategy( + const struct dml2_pmo_pstate_strategy *base_strategy, + const unsigned int stream_count, + struct dml2_pmo_pstate_strategy *expanded_strategy_list, + unsigned int *num_expanded_strategies) +{ + bool skip_to_next_stream; + bool expanded_strategy_added; + bool skip_iteration; + unsigned int i, j; + unsigned int num_streams_per_method[PMO_DCN4_MAX_DISPLAYS] = { 0 }; + unsigned int stream_iteration_indices[PMO_DCN4_MAX_DISPLAYS] = { 0 }; + struct dml2_pmo_pstate_strategy cur_strategy_list = { 0 }; + + /* determine number of displays per method */ + for (i = 0; i < stream_count; i++) { + /* increment the count of the earliest index with the same method */ + for (j = 0; j < stream_count; j++) { + if (base_strategy->per_stream_pstate_method[i] == base_strategy->per_stream_pstate_method[j]) { + num_streams_per_method[j] = num_streams_per_method[j] + 1; + break; + } + } + } + + cur_strategy_list.allow_state_increase = base_strategy->allow_state_increase; + + i = 0; + /* uses a while loop instead of recursion to build permutations of base strategy */ + while (stream_iteration_indices[0] < stream_count) { + skip_to_next_stream = false; + expanded_strategy_added = false; + skip_iteration = false; + + /* determine what to do for this iteration */ + if (stream_iteration_indices[i] < stream_count && num_streams_per_method[stream_iteration_indices[i]] != 0) { + /* decrement count and assign method */ + cur_strategy_list.per_stream_pstate_method[i] = base_strategy->per_stream_pstate_method[stream_iteration_indices[i]]; + num_streams_per_method[stream_iteration_indices[i]] -= 1; + + if (i >= stream_count - 1) { + /* insert into strategy list */ + dcn5_insert_strategy_into_expanded_list(&cur_strategy_list, stream_count, expanded_strategy_list, num_expanded_strategies); + expanded_strategy_added = true; + } else { + /* skip to next stream */ + skip_to_next_stream = true; + } + } else { + skip_iteration = true; + } + + /* prepare for next iteration */ + if (skip_to_next_stream) { + i++; + } else { + /* restore count */ + if (!skip_iteration) { + num_streams_per_method[stream_iteration_indices[i]] += 1; + } + + /* increment iteration count */ + stream_iteration_indices[i]++; + + /* if iterations are complete, or last stream was reached */ + if ((stream_iteration_indices[i] >= stream_count || expanded_strategy_added) && i > 0) { + /* reset per stream index, decrement i */ + stream_iteration_indices[i] = 0; + i--; + + /* restore previous stream's count and increment index */ + num_streams_per_method[stream_iteration_indices[i]] += 1; + stream_iteration_indices[i]++; + } + } + } +} + +void dcn5_expand_variant_strategy( + const struct dml2_pmo_pstate_strategy *base_strategy, + const unsigned int stream_count, + const bool should_permute, + struct dml2_pmo_pstate_strategy *expanded_strategy_list, + unsigned int *num_expanded_strategies) +{ + bool variant_found; + unsigned int i, j; + unsigned int method_index; + unsigned int stream_index; + unsigned int num_streams_per_method[PMO_DCN4_MAX_DISPLAYS] = { 0 }; + unsigned int num_streams_per_base_method[PMO_DCN4_MAX_DISPLAYS] = { 0 }; + unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS] = { 0 }; + enum dml2_pstate_method per_stream_variant_method[DML2_MAX_PLANES]; + struct dml2_pmo_pstate_strategy variant_strategy = { 0 }; + + /* determine number of displays per method */ + for (i = 0; i < stream_count; i++) { + /* increment the count of the earliest index with the same method */ + for (j = 0; j < stream_count; j++) { + if (base_strategy->per_stream_pstate_method[i] == base_strategy->per_stream_pstate_method[j]) { + num_streams_per_method[j] = num_streams_per_method[j] + 1; + break; + } + } + + per_stream_variant_method[i] = convert_strategy_to_drr_variant(base_strategy->per_stream_pstate_method[i]); + } + memcpy(num_streams_per_base_method, num_streams_per_method, sizeof(unsigned int) * PMO_DCN4_MAX_DISPLAYS); + + memcpy(&variant_strategy, base_strategy, sizeof(struct dml2_pmo_pstate_strategy)); + + method_index = 0; + /* uses a while loop instead of recursion to build permutations of base strategy */ + while (num_streams_per_base_method[0] > 0 || method_index != 0) { + if (method_index == stream_count) { + /* construct variant strategy */ + variant_found = false; + stream_index = 0; + + for (i = 0; i < stream_count; i++) { + for (j = 0; j < num_streams_per_base_method[i]; j++) { + variant_strategy.per_stream_pstate_method[stream_index++] = base_strategy->per_stream_pstate_method[i]; + } + + for (j = 0; j < num_streams_per_variant_method[i]; j++) { + variant_strategy.per_stream_pstate_method[stream_index++] = per_stream_variant_method[i]; + if (base_strategy->per_stream_pstate_method[i] != per_stream_variant_method[i]) { + variant_found = true; + } + } + } + + if (variant_found && dcn5_is_variant_method_valid(base_strategy, &variant_strategy, num_streams_per_base_method, num_streams_per_variant_method, stream_count)) { + if (should_permute) { + /* permutations are permitted, proceed to expand */ + dcn5_expand_base_strategy(&variant_strategy, stream_count, expanded_strategy_list, num_expanded_strategies); + } else { + /* no permutations allowed, so add to list now */ + dcn5_insert_strategy_into_expanded_list(&variant_strategy, stream_count, expanded_strategy_list, num_expanded_strategies); + } + } + + /* rollback to earliest method with bases remaining */ + for (method_index = stream_count - 1; method_index > 0; method_index--) { + if (num_streams_per_base_method[method_index]) { + /* bases remaining */ + break; + } else { + /* reset counters */ + num_streams_per_base_method[method_index] = num_streams_per_method[method_index]; + num_streams_per_variant_method[method_index] = 0; + } + } + } + + if (num_streams_per_base_method[method_index]) { + num_streams_per_base_method[method_index]--; + num_streams_per_variant_method[method_index]++; + + method_index++; + } else if (method_index != 0) { + method_index++; + } + } +} + +const struct dml2_pmo_pstate_strategy *dcn5_get_expanded_strategy_list(struct dml2_pmo_stage_optimizer *stage, int stream_count) +{ + const struct dml2_pmo_pstate_strategy *expanded_strategy_list = NULL; + + switch (stream_count) { + case 1: + expanded_strategy_list = stage->pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display; + break; + case 2: + expanded_strategy_list = stage->pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display; + break; + case 3: + expanded_strategy_list = stage->pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display; + break; + case 4: + expanded_strategy_list = stage->pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display; + break; + default: + break; + } + + return expanded_strategy_list; +} + +unsigned int dcn5_get_num_expanded_strategies( + struct dml2_pmo_stage_optimizer *stage, + int stream_count) +{ + return stage->pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[stream_count - 1]; +} + +static enum dml2_uclk_pstate_change_strategy pstate_method_to_uclk_pstate_strategy_override(const enum dml2_pstate_method method) +{ + enum dml2_uclk_pstate_change_strategy override_strategy = dml2_uclk_pstate_change_strategy_auto; + + switch (method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_vactive; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_vblank; + break; + case dml2_pstate_method_fw_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_drr; + break; + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_alternate: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_count: + case dml2_pstate_method_na: + default: + override_strategy = dml2_uclk_pstate_change_strategy_auto; + } + + return override_strategy; +} + +static bool all_planes_match_method(const struct dml2_display_cfg *display_cfg, int plane_mask, enum dml2_pstate_method method) +{ + unsigned char i; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto && + display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy != pstate_method_to_uclk_pstate_strategy_override(method)) + return false; + } + } + + return true; +} + +bool dcn5_stream_matches_drr_policy(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_cfg, + const enum dml2_pstate_method stream_pstate_method, + unsigned int stream_index) +{ + const struct dml2_pmo_instance *pmo = stage->pmo; + const struct dml2_stream_parameters *stream_descriptor = &display_cfg->stream_descriptors[stream_index]; + bool strategy_matches_drr_requirements = true; + + /* check if strategy is compatible with stream drr capability and strategy */ + if (is_bit_set_in_bitfield(PMO_NO_DRR_STRATEGY_MASK, stream_pstate_method) && + display_cfg->num_streams > 1 && + stream_descriptor->timing.drr_config.enabled && + (stream_descriptor->timing.drr_config.drr_active_fixed || stream_descriptor->timing.drr_config.drr_active_variable)) { + /* DRR is active, so config may become unschedulable */ + strategy_matches_drr_requirements = false; + } else if (is_bit_set_in_bitfield(PMO_NO_DRR_STRATEGY_MASK, stream_pstate_method) && + is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, stream_pstate_method) && + stream_descriptor->timing.drr_config.enabled && + stream_descriptor->timing.drr_config.drr_active_variable) { + /* DRR is variable, fw exclusive methods require DRR to be clamped */ + strategy_matches_drr_requirements = false; + } else if (is_bit_set_in_bitfield(PMO_DRR_VAR_STRATEGY_MASK, stream_pstate_method) && + pmo->options->disable_drr_var_when_var_active && + stream_descriptor->timing.drr_config.enabled && + stream_descriptor->timing.drr_config.drr_active_variable) { + /* DRR variable is active, but policy blocks DRR for p-state when this happens */ + strategy_matches_drr_requirements = false; + } else if (is_bit_set_in_bitfield(PMO_DRR_VAR_STRATEGY_MASK, stream_pstate_method) && + (pmo->options->disable_drr_var || + !stream_descriptor->timing.drr_config.enabled || + stream_descriptor->timing.drr_config.disallowed)) { + /* DRR variable strategies are disallowed due to settings or policy */ + strategy_matches_drr_requirements = false; + } else if (is_bit_set_in_bitfield(PMO_DRR_CLAMPED_STRATEGY_MASK, stream_pstate_method) && + (pmo->options->disable_drr_clamped || + (!stream_descriptor->timing.drr_config.enabled || + (!stream_descriptor->timing.drr_config.drr_active_fixed && !stream_descriptor->timing.drr_config.drr_active_variable)) || + (pmo->options->disable_drr_clamped_when_var_active && + stream_descriptor->timing.drr_config.enabled && + stream_descriptor->timing.drr_config.drr_active_variable))) { + /* DRR fixed strategies are disallowed due to settings or policy */ + strategy_matches_drr_requirements = false; + } else if (is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, stream_pstate_method) && + pmo->options->disable_fams2) { + /* FW modes require FAMS2 */ + strategy_matches_drr_requirements = false; + } + + return strategy_matches_drr_requirements; +} + +bool dcn5_all_timings_support_vactive(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_config, + unsigned int mask) +{ + struct dml2_stage_optimizer_uclk_pstate_init_locals *s = &stage->func_locals->uclk_pstate_init; + unsigned int i; + bool valid = true; + + // Create a remap array to enable simple iteration through only masked stream indicies + for (i = 0; i < display_config->num_streams; i++) { + if (is_bit_set_in_bitfield(mask, i)) { + /* check if stream has enough vactive margin */ + valid &= is_bit_set_in_bitfield(s->stream_vactive_capability_mask, i); + } + } + + return valid; +} + +bool dcn5_all_timings_support_vblank(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_config, + unsigned int mask) +{ + struct dml2_pmo_synchronized_timing_groups *s = &stage->func_locals->uclk_pstate_init.synchronized_timing_groups; + unsigned int i; + bool synchronizable = true; + + /* find first vblank stream index and compare the timing group mask */ + for (i = 0; i < display_config->num_streams; i++) { + if (is_bit_set_in_bitfield(mask, i)) { + if (mask != s->synchronized_timing_group_masks[i]) { + /* vblank streams are not synchronizable */ + synchronizable = false; + } + break; + } + } + + return synchronizable; +} + +bool dcn5_all_timings_support_drr(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet, + const struct dml2_display_cfg *display_config, + unsigned int mask) +{ + const struct dml2_pmo_instance *pmo = stage->pmo; + unsigned char i; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + const struct dml2_stream_parameters *stream_descriptor; + const struct dml2_pstate_meta *stream_pstate_meta; + + if (is_bit_set_in_bitfield(mask, i)) { + stream_descriptor = &display_config->stream_descriptors[i]; + stream_pstate_meta = &worksheet->uclk_pstate.stream_pstate_meta[i]; + + if (!stream_descriptor->timing.drr_config.enabled) + return false; + + /* cannot support required vtotal */ + if (stream_pstate_meta->method_drr.stretched_vtotal > stream_pstate_meta->max_vtotal) { + return false; + } + + /* check rr is within bounds */ + if (stream_pstate_meta->nom_refresh_rate_hz < pmo->fams_params.v2.drr.refresh_rate_limit_min || + stream_pstate_meta->nom_refresh_rate_hz > pmo->fams_params.v2.drr.refresh_rate_limit_max) { + return false; + } + + /* check required stretch is allowed */ + if (stream_descriptor->timing.drr_config.max_instant_vtotal_delta > 0 && + stream_pstate_meta->method_drr.stretched_vtotal - stream_pstate_meta->nom_vtotal > (int)stream_descriptor->timing.drr_config.max_instant_vtotal_delta) { + return false; + } + } + } + + return true; +} + +static const struct dml2_pstate_per_method_common_meta *get_per_method_common_meta( + const struct dml2_pstate_meta *stream_pstate_meta, + enum dml2_pstate_method stream_pstate_method, + int stream_idx) +{ + const struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta = NULL; + + switch (stream_pstate_method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_vactive.common; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_vblank.common; + break; + case dml2_pstate_method_fw_drr: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_drr.common; + break; + case dml2_pstate_method_alternate: + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_count: + case dml2_pstate_method_na: + default: + stream_method_pstate_meta = NULL; + } + + return stream_method_pstate_meta; +} + +static bool dcn5_is_timing_group_schedulable( + const struct dml2_ip_capabilities *ip_caps, + const struct dml2_pstate_meta *stream_pstate_meta, + const struct dml2_display_cfg *display_cfg, + const enum dml2_pstate_method *per_stream_pstate_method, + const unsigned int timing_group_idx, + struct dml2_pstate_per_method_common_meta *group_pstate_meta, + struct dml2_pmo_synchronized_timing_groups *s) +{ + unsigned int i; + const struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta; + unsigned int base_stream_idx = 0; + + /* find base stream idx */ + for (base_stream_idx = 0; base_stream_idx < display_cfg->num_streams; base_stream_idx++) { + if (is_bit_set_in_bitfield(s->synchronized_timing_group_masks[timing_group_idx], base_stream_idx)) { + /* master stream found */ + break; + } + } + + /* init allow start and end lines for timing group */ + stream_method_pstate_meta = get_per_method_common_meta(stream_pstate_meta, per_stream_pstate_method[base_stream_idx], base_stream_idx); + if (!stream_method_pstate_meta) + return false; + + group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline; + group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline; + group_pstate_meta->period_us = stream_method_pstate_meta->period_us; + for (i = base_stream_idx + 1; i < display_cfg->num_streams; i++) { + if (is_bit_set_in_bitfield(s->synchronized_timing_group_masks[timing_group_idx], i)) { + stream_method_pstate_meta = get_per_method_common_meta(stream_pstate_meta, per_stream_pstate_method[i], i); + if (!stream_method_pstate_meta) + continue; + + if (group_pstate_meta->allow_start_otg_vline < stream_method_pstate_meta->allow_start_otg_vline) { + /* set group allow start to larger otg vline */ + group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline; + } + + if (group_pstate_meta->allow_end_otg_vline > stream_method_pstate_meta->allow_end_otg_vline) { + /* set group allow end to smaller otg vline */ + group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline; + } + + /* check waveform still has positive width */ + if (group_pstate_meta->allow_start_otg_vline >= group_pstate_meta->allow_end_otg_vline) { + /* timing group is not schedulable */ + return false; + } + } + } + + /* calculate the rest of the meta */ + dcn5_build_method_scheduling_params(group_pstate_meta, &stream_pstate_meta[base_stream_idx]); + + return group_pstate_meta->allow_time_us > 0.0 && + group_pstate_meta->disallow_time_us < ip_caps->fams2.max_allow_delay_us; +} + +static bool dcn5_is_pstate_schedulable( + const struct dml2_ip_capabilities *ip_caps, + struct dml2_pstate_meta *stream_pstate_meta, + const struct dml2_display_cfg *display_cfg, + const enum dml2_pstate_method *per_stream_pstate_method, + struct dml2_pmo_synchronized_timing_groups *synchronized_timing_groups, + struct dml2_scheduling_check_locals *s) +{ + double max_allow_delay_us = 0.0; + unsigned int i, j; + bool schedulable; + + memset(s->group_common_pstate_meta, 0, sizeof(s->group_common_pstate_meta)); + memset(s->sorted_group_gtl_disallow_index, 0, sizeof(unsigned int) * DML2_MAX_PLANES); + + /* search for a general solution to the schedule */ + + /* STAGE 0: Early return for special cases */ + if (display_cfg->num_streams == 0) { + return true; + } + + /* STAGE 1: confirm allow waves overlap for synchronizable streams */ + schedulable = true; + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + s->sorted_group_gtl_disallow_index[i] = i; + s->sorted_group_gtl_period_index[i] = i; + if (!dcn5_is_timing_group_schedulable(ip_caps, stream_pstate_meta, display_cfg, per_stream_pstate_method, i, &s->group_common_pstate_meta[i], synchronized_timing_groups)) { + /* synchronized timing group was not schedulable */ + schedulable = false; + break; + } + max_allow_delay_us += s->group_common_pstate_meta[i].disallow_time_us; + } + + if ((schedulable && synchronized_timing_groups->num_timing_groups <= 1) || !schedulable) { + /* 1. the only timing group was schedulable, so early pass + * 2. one of the timing groups was not schedulable, so early fail */ + return schedulable; + } + + /* STAGE 2: Check allow can't be masked entirely by other disallows */ + schedulable = true; + + /* sort disallow times from greatest to least */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + bool swapped = false; + + for (j = 0; j < synchronized_timing_groups->num_timing_groups - 1; j++) { + double j_disallow_us = s->group_common_pstate_meta[s->sorted_group_gtl_disallow_index[j]].disallow_time_us; + double jp1_disallow_us = s->group_common_pstate_meta[s->sorted_group_gtl_disallow_index[j + 1]].disallow_time_us; + if (j_disallow_us < jp1_disallow_us) { + /* swap as A < B */ + swap(s->sorted_group_gtl_disallow_index[j], + s->sorted_group_gtl_disallow_index[j+1]); + swapped = true; + } + } + + /* sorted, exit early */ + if (!swapped) + break; + } + + /* Check worst case disallow region occurs in the middle of allow for the + * other display, or when >2 streams continue to halve the remaining allow time. + */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + if (s->group_common_pstate_meta[i].disallow_time_us <= 0.0) { + /* this timing group always allows */ + continue; + } + + double max_allow_time_us = s->group_common_pstate_meta[i].allow_time_us; + for (j = 0; j < synchronized_timing_groups->num_timing_groups; j++) { + unsigned int sorted_j = s->sorted_group_gtl_disallow_index[j]; + /* stream can't overlap itself */ + if (i != sorted_j && s->group_common_pstate_meta[sorted_j].disallow_time_us > 0.0) { + double unmasked_allow_time_us = (max_allow_time_us - s->group_common_pstate_meta[sorted_j].disallow_time_us) / 2; + + max_allow_time_us = math_min2( + s->group_common_pstate_meta[sorted_j].allow_time_us, + unmasked_allow_time_us); + + if (max_allow_time_us < 0.0) { + /* failed exit early */ + break; + } + } + } + + if (max_allow_time_us <= 0.0) { + /* not enough time for microschedule in the worst case */ + schedulable = false; + break; + } + } + + if (schedulable && max_allow_delay_us < ip_caps->fams2.max_allow_delay_us) { + return true; + } + + /* STAGE 3: check larger allow can fit period of all other streams */ + schedulable = true; + + /* sort periods from greatest to least */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + bool swapped = false; + + for (j = 0; j < synchronized_timing_groups->num_timing_groups - 1; j++) { + double j_period_us = s->group_common_pstate_meta[s->sorted_group_gtl_period_index[j]].period_us; + double jp1_period_us = s->group_common_pstate_meta[s->sorted_group_gtl_period_index[j + 1]].period_us; + if (j_period_us < jp1_period_us) { + /* swap as A < B */ + swap(s->sorted_group_gtl_period_index[j], + s->sorted_group_gtl_period_index[j + 1]); + swapped = true; + } + } + + /* sorted, exit early */ + if (!swapped) + break; + } + + /* check larger allow can fit period of all other streams */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups - 1; i++) { + unsigned int sorted_i = s->sorted_group_gtl_period_index[i]; + unsigned int sorted_ip1 = s->sorted_group_gtl_period_index[i + 1]; + + if (s->group_common_pstate_meta[sorted_i].allow_time_us < s->group_common_pstate_meta[sorted_ip1].period_us || + (synchronized_timing_groups->group_is_drr_enabled[sorted_ip1] && synchronized_timing_groups->group_is_drr_active[sorted_ip1])) { + schedulable = false; + break; + } + } + + if (schedulable && max_allow_delay_us < ip_caps->fams2.max_allow_delay_us) { + return true; + } + + /* STAGE 4: When using HW exclusive modes, check disallow alignments are within allowed threshold */ + if (synchronized_timing_groups->num_timing_groups == 2 && + !is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, per_stream_pstate_method[0]) && + !is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, per_stream_pstate_method[1])) { + double sum_allow_time_us; + double shift_per_period; + double period_ratio; + double max_shift_us; + + /* default period_0 > period_1 */ + unsigned int lrg_idx = 0; + unsigned int sml_idx = 1; + if (s->group_common_pstate_meta[0].period_us < s->group_common_pstate_meta[1].period_us) { + /* period_0 < period_1 */ + lrg_idx = 1; + sml_idx = 0; + } + period_ratio = s->group_common_pstate_meta[lrg_idx].period_us / s->group_common_pstate_meta[sml_idx].period_us; + shift_per_period = s->group_common_pstate_meta[sml_idx].period_us * (period_ratio - math_floor(period_ratio)); + max_shift_us = s->group_common_pstate_meta[lrg_idx].disallow_time_us - s->group_common_pstate_meta[sml_idx].allow_time_us; + max_allow_delay_us = max_shift_us / shift_per_period * s->group_common_pstate_meta[lrg_idx].period_us; + sum_allow_time_us = s->group_common_pstate_meta[lrg_idx].allow_time_us + s->group_common_pstate_meta[sml_idx].allow_time_us; + + if (shift_per_period > 0.0 && + shift_per_period < sum_allow_time_us && + max_allow_delay_us < ip_caps->fams2.max_allow_delay_us) { + schedulable = true; + } + } + + return schedulable; +} + +static bool validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + const struct dml2_display_cfg *display_cfg, + const struct dml2_pmo_pstate_strategy *pstate_strategy) +{ + const struct dml2_pmo_instance *pmo = stage->pmo; + unsigned int vactive_stream_mask = 0; + unsigned int vblank_stream_mask = 0; + unsigned int drr_stream_mask = 0; + unsigned int vactive_count = 0; + unsigned int vblank_count = 0; + unsigned int drr_count = 0; + unsigned int stream_index = 0; + bool strategy_matches_forced_requirements = true; + bool strategy_matches_drr_requirements = true; + + // Tabulate everything + for (stream_index = 0; stream_index < display_cfg->num_streams; stream_index++) { + + enum dml2_pstate_method per_stream_pstate_method = pstate_strategy->per_stream_pstate_method[stream_index]; + unsigned int stream_plane_mask = worksheet->uclk_pstate.stream_plane_mask[stream_index]; + + if (!all_planes_match_method(display_cfg, stream_plane_mask, per_stream_pstate_method)) { + strategy_matches_forced_requirements = false; + break; + } + + strategy_matches_drr_requirements &= + dcn5_stream_matches_drr_policy(stage, display_cfg, per_stream_pstate_method, stream_index); + + bool is_fw_drr = per_stream_pstate_method == dml2_pstate_method_fw_drr; + bool is_vactive = per_stream_pstate_method == dml2_pstate_method_vactive; + bool is_vactive_drr = per_stream_pstate_method == dml2_pstate_method_fw_vactive_drr; + bool is_vblank = per_stream_pstate_method == dml2_pstate_method_vblank; + bool is_vblank_drr = per_stream_pstate_method == dml2_pstate_method_fw_vblank_drr; + + // Checks method and increases count + if (is_fw_drr) { + drr_count++; + set_bit_in_bitfield(&drr_stream_mask, stream_index); + } else if (is_vactive || is_vactive_drr) { + vactive_count++; + set_bit_in_bitfield(&vactive_stream_mask, stream_index); + } else if (is_vblank || is_vblank_drr) { + vblank_count++; + set_bit_in_bitfield(&vblank_stream_mask, stream_index); + } + } + + if (!strategy_matches_forced_requirements || !strategy_matches_drr_requirements) + return false; + + if (vactive_count > 0 && !dcn5_all_timings_support_vactive(stage, display_cfg, vactive_stream_mask)) + return false; + + if (vblank_count > 0 && (pmo->options->disable_vblank || !dcn5_all_timings_support_vblank(stage, display_cfg, vblank_stream_mask))) + return false; + + if (drr_count > 0 && (pmo->options->disable_drr_var || !dcn5_all_timings_support_drr(stage, worksheet, display_cfg, drr_stream_mask))) + return false; + + return dcn5_is_pstate_schedulable(pmo->ip_caps, + worksheet->uclk_pstate.stream_pstate_meta, + display_cfg, + pstate_strategy->per_stream_pstate_method, + &stage->func_locals->uclk_pstate_init.synchronized_timing_groups, + &stage->func_locals->uclk_pstate_init.scheduling_check_locals); +} + +void dcn5_insert_into_candidate_list(const struct dml2_pmo_pstate_strategy *pstate_strategy, int stream_count, struct dml2_optimization_worksheet *worksheet) +{ + (void)stream_count; + worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.num_pstate_candidates] = *pstate_strategy; + worksheet->uclk_pstate.num_pstate_candidates++; +} + + +static void dml2_pmo_dcn5_stage_optimizer_uclk_pstate_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + struct dml2_stage_optimizer_uclk_pstate_init_locals *s = &stage->func_locals->uclk_pstate_init; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_pmo_instance *pmo = stage->pmo; + struct dml2_pmo_pstate_strategy override_base_strategy = { 0 }; + const struct dml2_pmo_pstate_strategy *strategy_list = NULL; + unsigned int strategy_list_size = 0; + bool build_override_strategy = true; + unsigned int stream_index = 0; + unsigned int plane_index; + unsigned int i; + + DML_LOG_COMP_IF_ENTER(); + worksheet->uclk_pstate.num_pstate_candidates = 0; + + memset(s, 0, sizeof(struct dml2_stage_optimizer_uclk_pstate_init_locals)); + + if (display_config->overrides.all_streams_blanked) { + goto exit; + } + + // First build the stream plane mask (array of bitfields indexed by stream, indicating plane mapping) + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + const struct dml2_plane_parameters *plane_descriptor = &display_config->plane_descriptors[plane_index]; + enum dml2_uclk_pstate_change_strategy uclk_pstate_change_strategy = + plane_descriptor->overrides.uclk_pstate_change_strategy; + unsigned int *stream_plane_mask; + + stream_index = plane_descriptor->stream_index; + stream_plane_mask = &worksheet->uclk_pstate.stream_plane_mask[stream_index]; + + set_bit_in_bitfield(stream_plane_mask, plane_index); + + build_override_strategy &= uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto; + override_base_strategy.per_stream_pstate_method[stream_index] = + uclk_pstate_strategy_override_to_pstate_method(uclk_pstate_change_strategy); + + /* Save initial reserved vblank time as pstate optimize may overwrite this value. But + * if validation or permissibility fails then we must restore to the original value. + */ + worksheet->uclk_pstate.init_reserved_vblank_time_ns[plane_index] = + worksheet->cur.config.reserved_vblank_time_ns[plane_index]; + worksheet->uclk_pstate.init_max_vactive_det_fill_delay_us[plane_index] = + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk]; + } + + // Figure out which streams can do vactive, and also build up implicit FAMS2 meta + for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) { + unsigned int stream_plane_mask = worksheet->uclk_pstate.stream_plane_mask[stream_index]; + + if (dcn5_get_vactive_pstate_margin(&worksheet->validation_result, stream_plane_mask) > 0) + set_bit_in_bitfield(&s->stream_vactive_capability_mask, stream_index); + + /* FAMS2 meta */ + dcn5_build_pstate_meta_per_stream(worksheet->orig_dispcfg, + pmo->ip_caps, + pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us, + (double) pmo->ip_caps->max_vactive_det_fill_delay_us, + 0.0, //extra_vactive_allow_time_us + stream_index, + &worksheet->uclk_pstate.stream_pstate_meta[stream_index]); + } + + /* get synchronized timing groups */ + dcn5_build_synchronized_timing_groups(&stage->func_locals->uclk_pstate_init.synchronized_timing_groups, display_config); + + if (build_override_strategy) { + /* build expanded override strategy list (no permutations) */ + override_base_strategy.allow_state_increase = true; + s->num_expanded_override_strategies = 0; + + dcn5_insert_strategy_into_expanded_list(&override_base_strategy, + display_config->num_streams, + s->expanded_override_strategy_list, + &s->num_expanded_override_strategies); + + dcn5_expand_variant_strategy(&override_base_strategy, + display_config->num_streams, + false, + s->expanded_override_strategy_list, + &s->num_expanded_override_strategies); + + /* use override strategy list */ + strategy_list = s->expanded_override_strategy_list; + strategy_list_size = s->num_expanded_override_strategies; + } else { + /* use predefined strategy list */ + strategy_list = dcn5_get_expanded_strategy_list(stage, display_config->num_streams); + strategy_list_size = dcn5_get_num_expanded_strategies(stage, display_config->num_streams); + } + + if (!strategy_list || strategy_list_size == 0) + goto exit; + + for (i = 0; i < strategy_list_size && worksheet->uclk_pstate.num_pstate_candidates < DML2_PMO_PSTATE_CANDIDATE_LIST_SIZE; i++) { + const struct dml2_pmo_pstate_strategy *current_strategy = &strategy_list[i]; + + if (validate_pstate_support_strategy_cofunctionality(stage, worksheet, display_config, current_strategy)) + dcn5_insert_into_candidate_list(current_strategy, display_config->num_streams, worksheet); + } + + if (worksheet->uclk_pstate.num_pstate_candidates > 0) { + worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.num_pstate_candidates-1].allow_state_increase = true; + worksheet->uclk_pstate.cur_pstate_candidate = -1; + goto exit; + } else { + goto exit; + } +exit: + DML_LOG_COMP_IF_EXIT(); +} + +void dcn5_reset_worksheet_for_uclk_pstate(struct dml2_optimization_worksheet *worksheet) +{ + unsigned int plane_index; + + for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) { + + // Restore reserve time + worksheet->cur.config.reserved_vblank_time_ns[plane_index] = worksheet->uclk_pstate.init_reserved_vblank_time_ns[plane_index]; + + // Reset pstate switch mode + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_na; + + // Restore DET fill delay for ppt + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = worksheet->uclk_pstate.init_max_vactive_det_fill_delay_us[plane_index]; + } + worksheet->cur.config.fams2_required = false; +} + +void dcn5_setup_planes_for_vactive_by_mask(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet, int plane_mask) +{ + unsigned int plane_index; + unsigned int stream_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_pmo_instance *pmo = stage->pmo; + + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + stream_index = display_config->plane_descriptors[plane_index].stream_index; + + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_vactive; + + if (!pmo->options->disable_vactive_det_fill_bw_pad) { + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = (unsigned int)math_max2( + math_floor(worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us), + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk]); + } + } + } +} + +void dcn5_setup_planes_for_vblank_by_mask(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet, int plane_mask) +{ + unsigned int plane_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_pmo_instance *pmo = stage->pmo; + + worksheet->cur.unvalidated_change.bits.reserved_vblank_time = true; + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + worksheet->cur.config.reserved_vblank_time_ns[plane_index] = (long)math_max2(pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us * 1000.0, + worksheet->cur.config.reserved_vblank_time_ns[plane_index]); + + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_vblank; + } + } +} + +void dcn5_setup_planes_for_vactive_drr_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask) +{ + unsigned int plane_index; + unsigned int stream_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_pmo_instance *pmo = stage->pmo; + + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + stream_index = display_config->plane_descriptors[plane_index].stream_index; + + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_fw_vactive_drr; + + if (!pmo->options->disable_vactive_det_fill_bw_pad) { + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = (unsigned int) math_max2( + math_floor(worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us), + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk]); + } + } + } +} + +void dcn5_setup_planes_for_vblank_drr_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask) +{ + unsigned int plane_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_pmo_instance *pmo = stage->pmo; + + worksheet->cur.unvalidated_change.bits.reserved_vblank_time = true; + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + worksheet->cur.config.reserved_vblank_time_ns[plane_index] = (long)math_max2(pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us * 1000, + worksheet->cur.config.reserved_vblank_time_ns[plane_index]); + + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_fw_vblank_drr; + } + } +} + +void dcn5_setup_planes_for_drr_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask) +{ + (void)stage; + unsigned int plane_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_fw_drr; + } + } +} + +static bool setup_optimized_worksheet_for_uclk_pstate(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool fams2_required = false; + bool success = true; + unsigned int stream_index, plane_index; + int strategy_index = worksheet->uclk_pstate.cur_pstate_candidate; + const struct dml2_plane_parameters *plane_descriptor; + + for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) { + plane_descriptor = &worksheet->orig_dispcfg->plane_descriptors[plane_index]; + set_bit_in_bitfield(&worksheet->uclk_pstate.stream_plane_mask[plane_descriptor->stream_index], plane_index); + } + + for (stream_index = 0; stream_index < worksheet->orig_dispcfg->num_streams; stream_index++) { + enum dml2_pstate_method method = worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index]; + unsigned int stream_plane_mask = worksheet->uclk_pstate.stream_plane_mask[stream_index]; + + bool is_vactive_fw_drr = method == dml2_pstate_method_fw_vactive_drr; + bool is_vblank_fw_drr = method == dml2_pstate_method_fw_vblank_drr; + bool is_vactive = method == dml2_pstate_method_vactive; + bool is_vblank = method == dml2_pstate_method_vblank; + bool is_fw_drr = method == dml2_pstate_method_fw_drr; + bool is_na = method == dml2_pstate_method_na; + + if (is_na) { + success = false; + break; + } else if (is_vactive) { + dcn5_setup_planes_for_vactive_by_mask(stage, worksheet, stream_plane_mask); + } else if (is_vblank) { + dcn5_setup_planes_for_vblank_by_mask(stage, worksheet, stream_plane_mask); + } else if (is_vactive_fw_drr) { + fams2_required = true; + dcn5_setup_planes_for_vactive_drr_by_mask(stage, worksheet, stream_plane_mask); + } else if (is_vblank_fw_drr) { + fams2_required = true; + dcn5_setup_planes_for_vblank_drr_by_mask(stage, worksheet, stream_plane_mask); + } else if (is_fw_drr) { + fams2_required = true; + dcn5_setup_planes_for_drr_by_mask(stage, worksheet, stream_plane_mask); + } + } + + /* Indicate if FAMS2 required */ + if (success) { + worksheet->cur.config.fams2_required = fams2_required; + + // Copy FAMS2 meta unconditionally - we need for vactive as well + memcpy(&worksheet->cur.config.stream_pstate_meta, + &worksheet->uclk_pstate.stream_pstate_meta, + sizeof(struct dml2_pstate_meta) * DML2_MAX_PLANES); + worksheet->cur.config.uclk_pstate_support = true; + } + + return success; +} + +static bool dml2_pmo_dcn5_stage_optimizer_uclk_pstate_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool should_continue = true; + + DML_LOG_COMP_IF_ENTER(); + /* Nothing to optimize if there are no candidates, so return false */ + if (worksheet->uclk_pstate.num_pstate_candidates == 0) { + should_continue = false; + goto exit; + } + + /* Optimization is completed if we find a candidate that passed validation and also passes permissibility. + * There are scenarios where permissibility can pass even if validation fails, so we need to check the + * validation result here as well. + */ + if (worksheet->validation_result.is_mode_support_valid + && stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK) { + should_continue = false; + goto exit; + } + + /* If we've reached the end of the p-state candidate list, return false since + * there's no more potential optimization options */ + if (worksheet->uclk_pstate.cur_pstate_candidate == worksheet->uclk_pstate.num_pstate_candidates - 1) { + should_continue = false; + goto exit; + } + + /* Reset current settings since the previous optimization attempt did not pass */ + dcn5_reset_worksheet_for_uclk_pstate(worksheet); + + worksheet->uclk_pstate.cur_pstate_candidate++; + worksheet->cur.unvalidated_change.bits.uclk_pstate_method = + setup_optimized_worksheet_for_uclk_pstate(stage, worksheet); + DML_ASSERT_MSG(worksheet->cur.unvalidated_change.bits.uclk_pstate_method, "optimize_next must apply changes" + " when returning true!\n"); +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +int dcn5_get_vactive_det_fill_latency_delay_us(const struct dml2_validation_result *validation_res, int plane_mask) +{ + unsigned char i; + int max_vactive_fill_us = 0; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (validation_res->mode_support.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[dml2_pstate_type_uclk] > max_vactive_fill_us) + max_vactive_fill_us = validation_res->mode_support.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[dml2_pstate_type_uclk]; + } + } + + return max_vactive_fill_us; +} + +int dcn5_get_minimum_reserved_time_us_for_planes(const struct dml2_optimization_worksheet *worksheet, int plane_mask) +{ + int min_time_us = 0xFFFFFF; + unsigned int plane_index = 0; + + for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + if (min_time_us > (worksheet->cur.config.reserved_vblank_time_ns[plane_index] / 1000)) + min_time_us = worksheet->cur.config.reserved_vblank_time_ns[plane_index] / 1000; + } + } + return min_time_us; +} + +static enum dml2_status dml2_pmo_dcn5_stage_optimizer_uclk_pstate_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_validation_result *validation_result = &worksheet->validation_result; + const struct dml2_pmo_instance *pmo = stage->pmo; + enum dml2_status status = DML2_STATUS_OK; + int REQUIRED_RESERVED_TIME = 0; + unsigned int stream_index = 0; + + DML_LOG_COMP_IF_ENTER(); + /* Permissibility passes if all streams are blanked - p-state support is guaranteed for this case*/ + if (worksheet->orig_dispcfg->overrides.all_streams_blanked) { + status = DML2_STATUS_OK; + goto exit; + } + + /* If there are no pstate candidates then pstate support is false and permissibility fails */ + if (worksheet->uclk_pstate.num_pstate_candidates == 0) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + goto exit; + } + + if (worksheet->uclk_pstate.cur_pstate_candidate < 0) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + goto exit; + } + + REQUIRED_RESERVED_TIME = (int)pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + + for (stream_index = 0; stream_index < worksheet->orig_dispcfg->num_streams; stream_index++) { + struct dml2_pmo_pstate_strategy strategy = worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate]; + const struct dml2_pstate_meta *stream_pstate_meta = &worksheet->cur.config.stream_pstate_meta[stream_index]; + enum dml2_pstate_method perStreamMethod = strategy.per_stream_pstate_method[stream_index]; + unsigned int stream_plane_mask = worksheet->uclk_pstate.stream_plane_mask[stream_index]; + + int vactive_pstate_margin = dcn5_get_vactive_pstate_margin(validation_result, stream_plane_mask); + + bool is_vactive_fw_drr = perStreamMethod == dml2_pstate_method_fw_vactive_drr; + bool is_vblank_fw_drr = perStreamMethod == dml2_pstate_method_fw_vblank_drr; + bool is_vactive = perStreamMethod == dml2_pstate_method_vactive; + bool is_vblank = perStreamMethod == dml2_pstate_method_vblank; + bool is_fw_drr = perStreamMethod == dml2_pstate_method_fw_drr; + bool is_na = perStreamMethod == dml2_pstate_method_na; + + if (is_vactive || is_vactive_fw_drr) { + double max_vactive_det_fill_delay_us = math_ceil(stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us); + + unsigned int vactive_det_fill_latency_delay_us = dcn5_get_vactive_det_fill_latency_delay_us(validation_result, stream_plane_mask); + + if (vactive_pstate_margin < 0.0 || + vactive_det_fill_latency_delay_us > max_vactive_det_fill_delay_us) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (is_vblank || is_vblank_fw_drr) { + int minimum_reserved_time_us_for_planes = dcn5_get_minimum_reserved_time_us_for_planes(worksheet, stream_plane_mask); + + if (minimum_reserved_time_us_for_planes < REQUIRED_RESERVED_TIME) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (is_fw_drr) { + bool do_all_planes_match_method = all_planes_match_method(worksheet->orig_dispcfg, stream_plane_mask, dml2_pstate_method_fw_drr); + + if (!do_all_planes_match_method) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (is_na) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } +exit: + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + return status; +} + +void dml2_pmo_dcn5_stage_optimizer_uclk_pstate_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn5_stage_optimizer_uclk_pstate_init; + stage->optimize_next = dml2_pmo_dcn5_stage_optimizer_uclk_pstate_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn5_stage_optimizer_uclk_pstate_test_permissibility; +} + +static void dml2_pmo_dcn5_stage_optimizer_qos_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + DML_LOG_COMP_IF_ENTER(); + worksheet->qos.passing_index = worksheet->cur.config.min_sop_index; + DML_LOG_COMP_IF_EXIT(); +} + +static bool dml2_pmo_dcn5_stage_optimizer_qos_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + bool should_continue = true; + + DML_LOG_COMP_IF_ENTER(); + if (worksheet->cur.config.min_sop_index == 0) + worksheet->qos.is_index0_tested = true; + + if (worksheet->validation_result.is_prefetch_valid) + worksheet->qos.passing_index = worksheet->cur.config.min_sop_index; + else + worksheet->qos.failing_index = worksheet->cur.config.min_sop_index; + + if (!worksheet->qos.is_index0_tested) + /* first test index 0 to find a failing index */ + worksheet->cur.config.min_sop_index = 0; + else if (worksheet->qos.passing_index == 0) + /* stop when index 0 passes */ + should_continue = false; + else if (worksheet->qos.passing_index > worksheet->qos.failing_index + 1) + /* perform binary search to detect a failing to passing index edge */ + worksheet->cur.config.min_sop_index = (worksheet->qos.passing_index + worksheet->qos.failing_index) / 2; + else + /* no more index to try, optimization is complete */ + should_continue = false; + + if (should_continue) + worksheet->cur.unvalidated_change.bits.sop_index = true; + + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +static enum dml2_status dml2_pmo_dcn5_stage_optimizer_qos_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + (void)worksheet; + DML_LOG_COMP_IF_ENTER(); + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(DML2_STATUS_OK)); + DML_LOG_COMP_IF_EXIT(); + return DML2_STATUS_OK; +} + +void dml2_pmo_dcn5_stage_optimizer_qos_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn5_stage_optimizer_qos_init; + stage->optimize_next = dml2_pmo_dcn5_stage_optimizer_qos_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn5_stage_optimizer_qos_test_permissibility; +} + +static bool is_h_timing_divisible_by(const struct dml2_timing_cfg *timing, unsigned char denominator) +{ + /* + * Htotal, Hblank start/end, and Hsync start/end all must be divisible + * in order for the horizontal timing params to be considered divisible + * by 2. Hsync start is always 0. + */ + unsigned long h_blank_start = timing->h_total - timing->h_front_porch; + + return (timing->h_total % denominator == 0) && + (h_blank_start % denominator == 0) && + (timing->h_blank_end % denominator == 0) && + (timing->h_sync_width % denominator == 0); +} + +static bool is_dp_encoder(enum dml2_output_encoder_class encoder_type) +{ + switch (encoder_type) { + case dml2_dp: + case dml2_edp: + case dml2_dp2p0: + case dml2_none: + return true; + case dml2_hdmi: + case dml2_hdmifrl: + default: + return false; + } +} + +static void dml2_pmo_dcn5_stage_optimizer_vmin_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_core_mode_support_result *mode_support_result = + &worksheet->validation_result.mode_support; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + struct dml2_pmo_options *options = stage->pmo->options; + const struct dml2_utm_soc_bb *utm_soc_bb = stage->pmo->utm_soc_bb; + + DML_LOG_COMP_IF_ENTER(); + if (options->disable_dyn_odm + || (options->disable_dyn_odm_for_multi_stream && display_config->num_streams > 1) + || utm_soc_bb->vmin_limit.dispclk_khz == 0) + goto exit; + + for (i = 0; i < display_config->num_planes; i++) + /* + * vmin optimization is required to be seamlessly switched off + * at any time when the new configuration is no longer + * supported. However switching from ODM combine to MPC combine + * is not always seamless. When there not enough free pipes, we + * will have to use the same secondary OPP heads as secondary + * DPP pipes in MPC combine in new state. This transition is + * expected to cause glitches. To avoid the transition, we only + * allow vmin optimization if the stream's base configuration + * doesn't require MPC combine. This condition checks if MPC + * combine is enabled. If so do not optimize the stream. + */ + if (mode_support_result->cfg_support_info.plane_support_info[i].dpps_used > 1 && + mode_support_result->cfg_support_info.stream_support_info[display_config->plane_descriptors[i].stream_index].odms_used == 1) + worksheet->vmin.unoptimizable_streams[display_config->plane_descriptors[i].stream_index] = true; + + for (i = 0; i < display_config->num_streams; i++) { + if (display_config->stream_descriptors[i].overrides.disable_dynamic_odm) + worksheet->vmin.unoptimizable_streams[i] = true; + /* + * ODM Combine requires horizontal timing divisible by 2 so each + * ODM segment has the same size. + */ + else if (!is_h_timing_divisible_by(&display_config->stream_descriptors[i].timing, 2)) + worksheet->vmin.unoptimizable_streams[i] = true; + /* + * Our hardware support seamless ODM transitions for DP encoders + * only. + */ + else if (!is_dp_encoder(display_config->stream_descriptors[i].output.output_encoder)) + worksheet->vmin.unoptimizable_streams[i] = true; + } + + for (i = 0; i < display_config->num_streams; i++) + worksheet->vmin.init_odms_used[i] = mode_support_result->cfg_support_info.stream_support_info[i].odms_used; + + worksheet->vmin.is_initialized = true; +exit: + DML_LOG_COMP_IF_EXIT(); +} + +static int find_highest_odm_load_stream_index(struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + int odm_load, highest_odm_load = -1, highest_odm_load_index = -1; + unsigned int odms_used; + + for (i = 0; i < worksheet->orig_dispcfg->num_streams; i++) { + odms_used = worksheet->cur.config.odm_combine_overrides[i] > 0 ? + worksheet->cur.config.odm_combine_overrides[i] : + worksheet->vmin.init_odms_used[i]; + + if (odms_used > 0) + odm_load = worksheet->orig_dispcfg->stream_descriptors[i].timing.pixel_clock_khz / odms_used; + else + odm_load = 0; + + if (odm_load > highest_odm_load) { + highest_odm_load_index = i; + highest_odm_load = odm_load; + } + } + + return highest_odm_load_index; +} + +static bool dml2_pmo_dcn5_stage_optimizer_vmin_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool should_continue = true; + int stream_index; + + DML_LOG_COMP_IF_ENTER(); + if (!worksheet->vmin.is_initialized) { + should_continue = false; + goto exit; + } + + if (worksheet->validation_result.is_mode_support_valid + && (worksheet->validation_result.mode_support.global.dispclk_khz + <= stage->pmo->utm_soc_bb->vmin_limit.dispclk_khz)) { + should_continue = false; + goto exit; + } + /* + * highest odm load stream must be optimizable to continue as dispclk is + * bounded by it. + */ + stream_index = find_highest_odm_load_stream_index(worksheet); + if (stream_index < 0 || + worksheet->vmin.unoptimizable_streams[stream_index]) { + should_continue = false; + goto exit; + } + + if ((int) worksheet->cur.config.odm_combine_overrides[stream_index] >= stage->pmo->odm_combine_limit) { + should_continue = false; + goto exit; + } + + // Do not optimize forced ODM Combine number + if (worksheet->orig_dispcfg->stream_descriptors[stream_index].overrides.odm_mode != dml2_odm_mode_auto) { + should_continue = false; + goto exit; + } + + if (worksheet->cur.config.odm_combine_overrides[stream_index] > 0) + worksheet->cur.config.odm_combine_overrides[stream_index] = worksheet->cur.config.odm_combine_overrides[stream_index] + 1; + else + worksheet->cur.config.odm_combine_overrides[stream_index] = worksheet->vmin.init_odms_used[stream_index] + 1; + + worksheet->cur.unvalidated_change.bits.odm_combine_overrides = true; +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +static enum dml2_status dml2_pmo_dcn5_stage_optimizer_vmin_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + (void)worksheet; + DML_LOG_COMP_IF_ENTER(); + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(DML2_STATUS_OK)); + DML_LOG_COMP_IF_EXIT(); + return DML2_STATUS_OK; +} + +void dml2_pmo_dcn5_stage_optimizer_vmin_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn5_stage_optimizer_vmin_init; + stage->optimize_next = dml2_pmo_dcn5_stage_optimizer_vmin_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn5_stage_optimizer_vmin_test_permissibility; +} + + +static bool dml2_pmo_dcn5_stage_optimizer_should_optimize_z8_stutter( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_soc_power_management_parameters *power_params = + &stage->pmo->utm_soc_bb->power_management_parameters; + unsigned int i; + const struct core_plane_support_info *plane_support_info = + worksheet->validation_result.mode_support.cfg_support_info.plane_support_info; + double line_time_us; + double vblank_nom_time_us; + double pixel_clock_hz; + + if (power_params->z8_stutter_exit_latency_us <= 0) + /* z8 stutter not supported */ + return false; + + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) + if (plane_support_info[i].active_latency_hiding_us < + power_params->z8_stutter_exit_latency_us + power_params->z8_min_idle_time) + /* stutter period doesn't meet z8 eco */ + return false; + + for (i = 0; i < worksheet->orig_dispcfg->num_streams; i++) { + pixel_clock_hz = worksheet->orig_dispcfg->stream_descriptors[i].timing.pixel_clock_khz * 1000.0; + line_time_us = worksheet->orig_dispcfg->stream_descriptors[i].timing.h_total / pixel_clock_hz * 1000000; + vblank_nom_time_us = line_time_us * worksheet->orig_dispcfg->stream_descriptors[i].timing.vblank_nom; + + if (vblank_nom_time_us < power_params->z8_stutter_exit_latency_us) + /* z8 stutter optimization is too expensive to accept */ + return false; + } + + return true; +} + +static void dml2_pmo_dcn5_stage_optimizer_stutter_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_soc_power_management_parameters *power_params = + &stage->pmo->utm_soc_bb->power_management_parameters; + + DML_LOG_COMP_IF_ENTER(); + if (power_params->z8_stutter_exit_latency_us > 0 && + power_params->stutter_enter_plus_exit_latency_us > 0) + DML_ASSERT_MSG(power_params->z8_stutter_exit_latency_us > power_params->stutter_enter_plus_exit_latency_us, + "z8 stutter is expected to have more strict latency requirement!\n"); + + /* keep a copy of current reserved vblank time as the base */ + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) + worksheet->stutter.init_reserved_vblank_time_ns[i] = worksheet->cur.config.reserved_vblank_time_ns[i]; + + /* calculate preconditions */ + worksheet->stutter.should_optimize_z8_stutter = + dml2_pmo_dcn5_stage_optimizer_should_optimize_z8_stutter(stage, worksheet); + worksheet->stutter.should_optimize_stutter = (power_params->stutter_enter_plus_exit_latency_us > 0); + DML_LOG_COMP_IF_EXIT(); +} + +static void dml2_pmo_dcn5_stage_optimizer_reset_stutter( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + unsigned int i; + + /* reset the reserved vblank time */ + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) + worksheet->cur.config.reserved_vblank_time_ns[i] = worksheet->stutter.init_reserved_vblank_time_ns[i]; +} + +static void dml2_pmo_dcn5_stage_optimizer_optimize_z8_stutter( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_soc_power_management_parameters *power_params = + &stage->pmo->utm_soc_bb->power_management_parameters; + const long z8_stutter_exit_latency_ns = (const long) power_params->z8_stutter_exit_latency_us * 1000; + bool reserved_vblank_time_changed = false; + + /* we assume z8 stutter is always the first optimization attempt so there is no need to reset */ + // dml2_pmo_dcn5_stage_optimizer_reset_stutter(stage, worksheet); + + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) { + if (worksheet->cur.config.reserved_vblank_time_ns[i] < z8_stutter_exit_latency_ns) { + worksheet->cur.config.reserved_vblank_time_ns[i] = z8_stutter_exit_latency_ns; + reserved_vblank_time_changed = true; + } + } + + worksheet->cur.config.stutter_support_in_vblank = worksheet->stutter.should_optimize_stutter; + worksheet->cur.config.z8_stutter_support_in_vblank = true; + worksheet->cur.unvalidated_change.bits.reserved_vblank_time = reserved_vblank_time_changed; +} + +static void dml2_pmo_dcn5_stage_optimizer_optimize_stutter( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + unsigned int i; + const struct dml2_soc_power_management_parameters *power_params = + &stage->pmo->utm_soc_bb->power_management_parameters; + const long stutter_enter_plus_exit_latency_ns = (const long) power_params->stutter_enter_plus_exit_latency_us * 1000; + bool reserved_vblank_time_changed = false; + + dml2_pmo_dcn5_stage_optimizer_reset_stutter(stage, worksheet); + + for (i = 0; i < worksheet->orig_dispcfg->num_planes; i++) { + if (worksheet->cur.config.reserved_vblank_time_ns[i] < stutter_enter_plus_exit_latency_ns) { + worksheet->cur.config.reserved_vblank_time_ns[i] = stutter_enter_plus_exit_latency_ns; + reserved_vblank_time_changed = true; + } + } + + worksheet->cur.config.stutter_support_in_vblank = true; + worksheet->cur.config.z8_stutter_support_in_vblank = false; + worksheet->cur.unvalidated_change.bits.reserved_vblank_time = reserved_vblank_time_changed; +} + +static bool dml2_pmo_dcn5_stage_optimizer_stutter_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool should_continue = true; + + DML_LOG_COMP_IF_ENTER(); + if (worksheet->validation_result.is_mode_support_valid + && stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK) { + /* optimization has passed no need to optimize further, stop */ + should_continue = false; + goto exit; + } + + if (!worksheet->stutter.should_optimize_z8_stutter + && !worksheet->stutter.should_optimize_stutter) { + /* stutter optimization isn't supported */ + should_continue = false; + goto exit; + } + + if (worksheet->stutter.should_optimize_z8_stutter && !worksheet->stutter.is_z8_stutter_attempted) { + dml2_pmo_dcn5_stage_optimizer_optimize_z8_stutter(stage, worksheet); + worksheet->stutter.is_z8_stutter_attempted = true; + } else if (worksheet->stutter.should_optimize_stutter && !worksheet->stutter.is_stutter_attempted) { + dml2_pmo_dcn5_stage_optimizer_optimize_stutter(stage, worksheet); + worksheet->stutter.is_stutter_attempted = true; + } else { + worksheet->cur.config.stutter_support_in_vblank = false; + worksheet->cur.config.z8_stutter_support_in_vblank = false; + } + + worksheet->cur.unvalidated_change.bits.stutter_support = true; +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +static enum dml2_status dml2_pmo_dcn5_stage_optimizer_stutter_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + enum dml2_status status = worksheet->cur.config.stutter_support_in_vblank + || worksheet->cur.config.z8_stutter_support_in_vblank ? + DML2_STATUS_OK : DML2_STATUS_OPTIMIZE_FAIL_STUTTER; + + DML_LOG_COMP_IF_ENTER(); + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + + return status; +} + +void dml2_pmo_dcn5_stage_optimizer_stutter_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn5_stage_optimizer_stutter_init; + stage->optimize_next = dml2_pmo_dcn5_stage_optimizer_stutter_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn5_stage_optimizer_stutter_test_permissibility; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.h new file mode 100644 index 000000000000..0eb6ad9ca5a7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024-2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_PMO_DCN5_STAGE_OPTIMIZERS_H__ +#define __DML2_PMO_DCN5_STAGE_OPTIMIZERS_H__ +#include "dml2_internal_shared_types.h" + +void set_bit_in_bitfield(unsigned int *bit_field, unsigned int bit_offset); +bool is_bit_set_in_bitfield(unsigned int bit_field, unsigned int bit_offset); +int dcn5_get_vactive_pstate_margin(const struct dml2_validation_result *validation_res, int plane_mask); +void dcn5_build_method_scheduling_params( + struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta, + const struct dml2_pstate_meta *stream_pstate_meta); +void dcn5_build_synchronized_timing_groups( + // Output + struct dml2_pmo_synchronized_timing_groups *s, + // Input + const struct dml2_display_cfg *display_config); +void dcn5_insert_strategy_into_expanded_list( + const struct dml2_pmo_pstate_strategy *per_stream_pstate_strategy, + const int stream_count, + struct dml2_pmo_pstate_strategy *expanded_strategy_list, + unsigned int *num_expanded_strategies); +bool dcn5_is_variant_method_valid(const struct dml2_pmo_pstate_strategy *base_strategy, + const struct dml2_pmo_pstate_strategy *variant_strategy, + const unsigned int num_streams_per_base_method[PMO_DCN4_MAX_DISPLAYS], + const unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS], + const unsigned int stream_count); +void dcn5_expand_base_strategy( + const struct dml2_pmo_pstate_strategy *base_strategy, + const unsigned int stream_count, + struct dml2_pmo_pstate_strategy *expanded_strategy_list, + unsigned int *num_expanded_strategies); +void dcn5_expand_variant_strategy( + const struct dml2_pmo_pstate_strategy *base_strategy, + const unsigned int stream_count, + const bool should_permute, + struct dml2_pmo_pstate_strategy *expanded_strategy_list, + unsigned int *num_expanded_strategies); +const struct dml2_pmo_pstate_strategy *dcn5_get_expanded_strategy_list(struct dml2_pmo_stage_optimizer *stage, int stream_count); +unsigned int dcn5_get_num_expanded_strategies( + struct dml2_pmo_stage_optimizer *stage, + int stream_count); +bool dcn5_stream_matches_drr_policy(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_cfg, + const enum dml2_pstate_method stream_pstate_method, + unsigned int stream_index); +bool dcn5_all_timings_support_vactive(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_config, + unsigned int mask); +bool dcn5_all_timings_support_vblank(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_config, + unsigned int mask); +bool dcn5_all_timings_support_drr(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet, + const struct dml2_display_cfg *display_config, + unsigned int mask); +void dcn5_insert_into_candidate_list(const struct dml2_pmo_pstate_strategy *pstate_strategy, int stream_count, struct dml2_optimization_worksheet *worksheet); +void dcn5_reset_worksheet_for_uclk_pstate(struct dml2_optimization_worksheet *worksheet); +void dcn5_setup_planes_for_vactive_by_mask(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet, int plane_mask); +void dcn5_setup_planes_for_vblank_by_mask(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet, int plane_mask); +void dcn5_setup_planes_for_vactive_drr_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask); +void dcn5_setup_planes_for_vblank_drr_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask); +void dcn5_setup_planes_for_drr_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask); +int dcn5_get_vactive_det_fill_latency_delay_us(const struct dml2_validation_result *validation_res, int plane_mask); +int dcn5_get_minimum_reserved_time_us_for_planes(const struct dml2_optimization_worksheet *worksheet, int plane_mask); + +/* Public DCN5 PMO optimizers */ +void dml2_pmo_dcn5_stage_optimizer_qos_create(struct dml2_pmo_instance *pmo_inst, + struct dml2_pmo_stage_optimizer *optimizer); +void dml2_pmo_dcn5_stage_optimizer_mcache_create(struct dml2_pmo_instance *pmo_inst, + struct dml2_pmo_stage_optimizer *optimizer); +void dml2_pmo_dcn5_stage_optimizer_uclk_pstate_create(struct dml2_pmo_instance *pmo_inst, + struct dml2_pmo_stage_optimizer *optimizer); +void dml2_pmo_dcn5_stage_optimizer_vmin_create(struct dml2_pmo_instance *pmo_inst, + struct dml2_pmo_stage_optimizer *optimizer); +void dml2_pmo_dcn5_stage_optimizer_stutter_create(struct dml2_pmo_instance *pmo_inst, + struct dml2_pmo_stage_optimizer *optimizer); +void dml2_pmo_dcn5_stage_optimizer_mcache_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet); +bool dml2_pmo_dcn5_stage_optimizer_mcache_test_total_mcache_limit(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet); +bool dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_status(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_optimization_worksheet *worksheet); +bool dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet); +void dml2_pmo_dcn5_stage_optimizer_mcache_apply_default_pipe_usage(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet); + +#endif /* __DML2_PMO_DCN5_STAGE_OPTIMIZERS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.c new file mode 100644 index 000000000000..0bdf0d902aa0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dml2_pmo_dcn6.h" +#include "dml2_pmo_dcn4_fams2.h" +#include "dml2_pmo_dcn5_stage_optimizers.h" +#include "dml2_pmo_dcn6_stage_optimizers.h" +#include "dml2_debug.h" +#include "lib_float_math.h" + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_1_display[] = { + // VActive Preferred + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Then VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // Then DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Even-Odd + { + .per_stream_pstate_method = { dml2_pstate_method_alternate, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, +}; + +static const int base_pstate_strategy_list_1_display_size = sizeof(base_pstate_strategy_list_1_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_2_display[] = { + // VActive only is preferred + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Then VActive + VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // Then VBlank only + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // Then DRR + VActive + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_fw_drr, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Then DRR + DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Even-Odd + { + .per_stream_pstate_method = { dml2_pstate_method_alternate, dml2_pstate_method_alternate, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, +}; + +static const int base_pstate_strategy_list_2_display_size = sizeof(base_pstate_strategy_list_2_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_3_display[] = { + // All VActive + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // VActive + 1 VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vblank, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // All VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na }, + .allow_state_increase = false, + }, + + // All DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_na }, + .allow_state_increase = true, + }, + + // Even-Odd + { + .per_stream_pstate_method = { dml2_pstate_method_alternate, dml2_pstate_method_alternate, dml2_pstate_method_alternate, dml2_pstate_method_na }, + .allow_state_increase = true, + }, +}; + +static const int base_pstate_strategy_list_3_display_size = sizeof(base_pstate_strategy_list_3_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy base_pstate_strategy_list_4_display[] = { + // All VActive + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive }, + .allow_state_increase = true, + }, + + // VActive + 1 VBlank + { + .per_stream_pstate_method = { dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vactive, dml2_pstate_method_vblank }, + .allow_state_increase = false, + }, + + // All Vblank + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank }, + .allow_state_increase = false, + }, + + // All DRR + { + .per_stream_pstate_method = { dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr, dml2_pstate_method_fw_drr }, + .allow_state_increase = true, + }, + + // Even-Odd + { + .per_stream_pstate_method = { dml2_pstate_method_alternate, dml2_pstate_method_alternate, dml2_pstate_method_alternate, dml2_pstate_method_alternate }, + .allow_state_increase = true, + }, +}; + +static const int base_pstate_strategy_list_4_display_size = sizeof(base_pstate_strategy_list_4_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static void dml2_pmo_dcn6_assign_pstate_strategies(struct dml2_pmo_instance *pmo) +{ + int i = 0; + + /* generate permutations of p-state configs from base strategy list */ + for (i = 1; i <= PMO_DCN4_MAX_DISPLAYS; i++) { + switch (i) { + case 1: + DML_ASSERT(base_pstate_strategy_list_1_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_1_display, + base_pstate_strategy_list_1_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + case 2: + DML_ASSERT(base_pstate_strategy_list_2_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_2_display, + base_pstate_strategy_list_2_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + case 3: + DML_ASSERT(base_pstate_strategy_list_3_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_3_display, + base_pstate_strategy_list_3_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + case 4: + DML_ASSERT(base_pstate_strategy_list_4_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* populate list */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_pstate_strategy_list_4_display, + base_pstate_strategy_list_4_display_size, + i, + pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display, + &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]); + break; + } + } +} + +int dml2_pmo_dcn6a_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **stages) +{ + int count = 0; + + DML_LOG_COMP_IF_ENTER(); + if (!pmo->options->force_optional_ppt_temp_read_admissibility) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_fclk_ppt_temp_read_pstate]; + if (!pmo->options->force_optional_mcache_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]; + if (!pmo->options->force_optional_uclk_pstate_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]; + DML_LOG_DEBUG("%s exit with %d\n", __func__, count); + DML_LOG_COMP_IF_EXIT(); + + return count; +} + +int dml2_pmo_dcn6a_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **stages) +{ + int count = 0; + + DML_LOG_COMP_IF_ENTER(); + if (pmo->options->force_optional_ppt_temp_read_admissibility) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_fclk_ppt_temp_read_pstate]; + if (pmo->options->force_optional_mcache_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]; + if (pmo->options->force_optional_uclk_pstate_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_qos]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_vmin]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_stutter]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_vmin_dcfclk]; + DML_LOG_DEBUG("%s exit with %d\n", __func__, count); + DML_LOG_COMP_IF_EXIT(); + + return count; +} + +int dml2_pmo_dcn6b_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **stages) +{ + int count = 0; + + DML_LOG_COMP_IF_ENTER(); + if (!pmo->options->force_optional_ppt_temp_read_admissibility) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_fclk_ppt_temp_read_pstate]; + if (!pmo->options->force_optional_mcache_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]; + DML_LOG_DEBUG("%s exit with %d\n", __func__, count); + DML_LOG_COMP_IF_EXIT(); + + return count; +} + +int dml2_pmo_dcn6b_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **stages) +{ + int count = 0; + + DML_LOG_COMP_IF_ENTER(); + if (pmo->options->force_optional_ppt_temp_read_admissibility) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_fclk_ppt_temp_read_pstate]; + if (pmo->options->force_optional_mcache_support) + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_qos]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_vmin]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_stutter]; + stages[count++] = &pmo->stage_optimizers[dml2_pmo_stage_index_vmin_dcfclk]; + DML_LOG_DEBUG("%s exit with %d\n", __func__, count); + DML_LOG_COMP_IF_EXIT(); + + return count; +} + + +static enum dml2_uclk_pstate_change_strategy pstate_method_to_uclk_pstate_strategy_override(const enum dml2_pstate_method method) +{ + enum dml2_uclk_pstate_change_strategy override_strategy = dml2_uclk_pstate_change_strategy_auto; + + switch (method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_vactive; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_vblank; + break; + case dml2_pstate_method_fw_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_drr; + break; + case dml2_pstate_method_alternate: + override_strategy = dml2_uclk_pstate_change_strategy_force_alternate; + break; + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_count: + case dml2_pstate_method_na: + default: + override_strategy = dml2_uclk_pstate_change_strategy_auto; + } + + return override_strategy; +} + +static void dml2_pmo_dcn6_apply_optimization_to_solution(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_config *optimization, + struct dml2_display_solution *solution) +{ + unsigned int i; + const struct dml2_sop_table *sop_table = &pmo->utm_soc_bb->sop_table; + + solution->unvalidated_change.raw = optimization->unvalidated_change.raw; + DML_LOG_DEBUG("solution->unvalidated_change.raw = 0x%X\n", solution->unvalidated_change.raw); + + /* sop index */ + sop_table->get_sop_constraint_at_index(sop_table, + optimization->config.min_sop_index, &solution->sop_constraint); + DML_LOG_VERBOSE("min_sop_index = %d\n", optimization->config.min_sop_index); + + /* mpc overrides */ + for (i = 0; i < solution->dispcfg.num_planes; i++) + if (optimization->config.mpc_combine_overrides[i]) { + solution->dispcfg.plane_descriptors[i].overrides.mpcc_combine_factor = + optimization->config.mpc_combine_overrides[i]; + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.mpcc_combine_factor = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.mpcc_combine_factor); + } + + /* odm overrides */ + for (i = 0; i < solution->dispcfg.num_streams; i++) { + switch (optimization->config.odm_combine_overrides[i]) { + case 1: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_bypass; + break; + case 2: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_combine_2to1; + break; + case 3: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_combine_3to1; + break; + case 4: + solution->dispcfg.stream_descriptors[i].overrides.odm_mode = dml2_odm_mode_combine_4to1; + break; + default: + break; + } + DML_LOG_VERBOSE("solution->dispcfg.stream_descriptors[%d].overrides.odm_mode = %d\n", + i, solution->dispcfg.stream_descriptors[i].overrides.odm_mode); + } + + /* reserved vblank time */ + for (i = 0; i < solution->dispcfg.num_planes; i++) { + solution->dispcfg.plane_descriptors[i].overrides.reserved_vblank_time_ns = (long) math_max2( + solution->dispcfg.plane_descriptors[i].overrides.reserved_vblank_time_ns, + optimization->config.reserved_vblank_time_ns[i]); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.reserved_vblank_time_ns = %ld\n", + i, solution->dispcfg.plane_descriptors[i].overrides.reserved_vblank_time_ns); + } + + /* mcache allocations */ + for (i = 0; i < solution->dispcfg.num_planes; i++) + if (optimization->config.mcache_allocations[i].valid) + memcpy(&solution->mcache_allocations[i], &optimization->config.mcache_allocations[i], + sizeof(struct dml2_mcache_surface_allocation)); + + /* P-State switch method */ + solution->uclk_pstate_params.support = optimization->config.uclk_pstate_support; + for (i = 0; i < solution->dispcfg.num_planes; i++) { + solution->uclk_pstate_params.pstate_switch_modes[i] = optimization->config.uclk_pstate_switch_modes[i]; + solution->dispcfg.plane_descriptors[i].overrides.uclk_pstate_change_strategy + = pstate_method_to_uclk_pstate_strategy_override(optimization->config.uclk_pstate_switch_modes[i]); + DML_LOG_VERBOSE("solution->uclk_pstate_params.pstate_switch_modes[%d] = %d\n", + i, solution->uclk_pstate_params.pstate_switch_modes[i]); + } + + /* VActive P-State DET fill time */ + for (i = 0; i < solution->dispcfg.num_planes; i++) { + memcpy(solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us, + optimization->config.max_vactive_det_fill_delay_us[i], + sizeof(optimization->config.max_vactive_det_fill_delay_us[i])); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.max_vactive_det_fill_delay_us[uclk] = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk]); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.max_vactive_det_fill_delay_us[fclk] = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_fclk]); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.max_vactive_det_fill_delay_us[ppt] = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_ppt]); + DML_LOG_VERBOSE("solution->dispcfg.plane_descriptors[%d].overrides.max_vactive_det_fill_delay_us[temp] = %d\n", + i, solution->dispcfg.plane_descriptors[i].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_temp_read]); + } + + /* FAMS2 related */ + solution->uclk_pstate_params.fams2_required = optimization->config.fams2_required; + solution->uclk_pstate_params.legacy_pstate_info_for_dmu = optimization->config.legacy_pstate_info_for_dmu; + DML_LOG_VERBOSE("solution->uclk_pstate_params.fams2_required = %s\n", solution->uclk_pstate_params.fams2_required ? + "true" : "false"); + memcpy(&solution->uclk_pstate_params.stream_pstate_meta, + &optimization->config.stream_pstate_meta, + sizeof(struct dml2_pstate_meta) * DML2_MAX_PLANES); + + /* fclk pstate */ + solution->fclk_pstate_support = optimization->config.fclk_pstate_support; + DML_LOG_VERBOSE("solution->fclk_pstate_support = %s\n", solution->fclk_pstate_support ? + "true" : "false"); + + /* ppt and temp read pstate */ + solution->ppt_temp_read_support = optimization->config.ppt_temp_read_support; + DML_LOG_VERBOSE("solution->ppt_temp_read_support = %s\n", solution->ppt_temp_read_support ? + "true" : "false"); + + /* stutter */ + solution->stutter_support_in_vblank = optimization->config.stutter_support_in_vblank; + solution->z8_stutter_support_in_vblank = optimization->config.z8_stutter_support_in_vblank; + DML_LOG_VERBOSE("solution->stutter_support_in_vblank = %s\n", solution->stutter_support_in_vblank ? + "true" : "false"); + DML_LOG_VERBOSE("solution->z8_stutter_support_in_vblank = %s\n", solution->z8_stutter_support_in_vblank ? + "true" : "false"); + + /* dcfclk override */ + if (optimization->config.enable_vmin_dcfclk) { + solution->dispcfg.overrides.hw.dcfclk_mhz = pmo->utm_soc_bb->vmin_limit.dcfclk_khz / 1000.0; + } + DML_LOG_VERBOSE("solution->dispcfg.overrides.hw.dcfclk_mhz = %f\n", solution->dispcfg.overrides.hw.dcfclk_mhz); +} + +void dml2_pmo_dcn6_convert_worksheet_to_solution(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet, + struct dml2_display_solution *solution) +{ + DML_LOG_COMP_IF_ENTER(); + + memset(solution, 0, sizeof(struct dml2_display_solution)); + solution->orig_dispcfg = worksheet->orig_dispcfg; + memcpy(&solution->dispcfg, worksheet->orig_dispcfg, sizeof(solution->dispcfg)); + memcpy(solution->timing_group_ids, worksheet->timing_group_ids, sizeof(solution->timing_group_ids)); + solution->timing_group_count = worksheet->timing_group_count; + memcpy(&solution->validation_result, &worksheet->validation_result, sizeof(struct dml2_validation_result)); + dml2_pmo_dcn6_apply_optimization_to_solution(pmo, &worksheet->cur, solution); + DML_LOG_COMP_IF_EXIT(); +} + +bool dml2_pmo_dcn6a_initialize(struct dml2_pmo_initialize_in_out *in_out) +{ + struct dml2_pmo_instance *pmo = in_out->instance; + + DML_LOG_COMP_IF_ENTER(); + pmo->ip_caps = in_out->ip_caps; + pmo->options = in_out->options; + pmo->utm_soc_bb = in_out->utm_soc_bb; + pmo->mpc_combine_limit = 2; + pmo->odm_combine_limit = 4; + pmo->fams_params.v2.drr.refresh_rate_limit_max = 1000; + pmo->fams_params.v2.drr.refresh_rate_limit_min = 119; + + dml2_pmo_dcn6_assign_pstate_strategies(pmo); + + dml2_pmo_dcn6_stage_optimizer_mcache_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]); + dml2_pmo_dcn6_stage_optimizer_uclk_pstate_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]); + dml2_pmo_dcn5_stage_optimizer_qos_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_qos]); + dml2_pmo_dcn5_stage_optimizer_vmin_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_vmin]); + dml2_pmo_dcn5_stage_optimizer_stutter_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_stutter]); + dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_vmin_dcfclk]); + dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_fclk_ppt_temp_read_pstate]); + DML_LOG_DEBUG("%s exit with true\n", __func__); + DML_LOG_COMP_IF_EXIT(); + + return true; +} + +bool dml2_pmo_dcn6b_initialize(struct dml2_pmo_initialize_in_out *in_out) +{ + struct dml2_pmo_instance *pmo = in_out->instance; + + DML_LOG_COMP_IF_ENTER(); + pmo->ip_caps = in_out->ip_caps; + pmo->options = in_out->options; + pmo->options->disable_alternate_memory_training = true; + pmo->utm_soc_bb = in_out->utm_soc_bb; + pmo->mpc_combine_limit = 2; + pmo->odm_combine_limit = 4; + pmo->fams_params.v2.drr.refresh_rate_limit_max = 1000; + pmo->fams_params.v2.drr.refresh_rate_limit_min = 119; + + dml2_pmo_dcn6_assign_pstate_strategies(pmo); + + dml2_pmo_dcn6_stage_optimizer_mcache_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_mcache]); + dml2_pmo_dcn6_stage_optimizer_uclk_pstate_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_uclk_pstate]); + dml2_pmo_dcn5_stage_optimizer_qos_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_qos]); + dml2_pmo_dcn5_stage_optimizer_vmin_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_vmin]); + dml2_pmo_dcn5_stage_optimizer_stutter_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_stutter]); + dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_vmin_dcfclk]); + dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_create(pmo, &pmo->stage_optimizers[dml2_pmo_stage_index_fclk_ppt_temp_read_pstate]); + DML_LOG_DEBUG("%s exit with true\n", __func__); + DML_LOG_COMP_IF_EXIT(); + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h new file mode 100644 index 000000000000..dd7d9f855988 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_PMO_DCN6_H__ +#define __DML2_PMO_DCN6_H__ +#include "dml2_internal_shared_types.h" + +bool dml2_pmo_dcn6a_initialize(struct dml2_pmo_initialize_in_out *in_out); +int dml2_pmo_dcn6a_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **optimers); +int dml2_pmo_dcn6a_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **optimers); +bool dml2_pmo_dcn6b_initialize(struct dml2_pmo_initialize_in_out *in_out); +int dml2_pmo_dcn6b_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **optimers); +int dml2_pmo_dcn6b_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer **optimers); +void dml2_pmo_dcn6_convert_worksheet_to_solution(struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet, + struct dml2_display_solution *solution); +#endif /* __DML2_PMO_DCN6_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c new file mode 100644 index 000000000000..0b884a8661c8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c @@ -0,0 +1,1789 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dml2_pmo_dcn5_stage_optimizers.h" +#include "dml2_pmo_dcn6_stage_optimizers.h" +#include "dml2_debug.h" +#include "lib_float_math.h" + +static enum dml2_pstate_method uclk_pstate_strategy_override_to_pstate_method(const enum dml2_uclk_pstate_change_strategy override_strategy) +{ + enum dml2_pstate_method method = dml2_pstate_method_na; + + switch (override_strategy) { + case dml2_uclk_pstate_change_strategy_force_vactive: + method = dml2_pstate_method_vactive; + break; + case dml2_uclk_pstate_change_strategy_force_vblank: + method = dml2_pstate_method_vblank; + break; + case dml2_uclk_pstate_change_strategy_force_drr: + method = dml2_pstate_method_fw_drr; + break; + case dml2_uclk_pstate_change_strategy_force_alternate: + method = dml2_pstate_method_alternate; + break; + case dml2_uclk_pstate_change_strategy_force_mall_svp: + case dml2_uclk_pstate_change_strategy_force_mall_full_frame: + case dml2_uclk_pstate_change_strategy_auto: + default: + method = dml2_pstate_method_na; + } + + return method; +} + +static enum dml2_uclk_pstate_change_strategy pstate_method_to_uclk_pstate_strategy_override(const enum dml2_pstate_method method) +{ + enum dml2_uclk_pstate_change_strategy override_strategy = dml2_uclk_pstate_change_strategy_auto; + + switch (method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_vactive; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_vblank; + break; + case dml2_pstate_method_fw_drr: + override_strategy = dml2_uclk_pstate_change_strategy_force_drr; + break; + case dml2_pstate_method_alternate: + override_strategy = dml2_uclk_pstate_change_strategy_force_alternate; + break; + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_count: + case dml2_pstate_method_na: + default: + override_strategy = dml2_uclk_pstate_change_strategy_auto; + } + + return override_strategy; +} + +static bool all_planes_match_method(const struct dml2_display_cfg *display_cfg, int plane_mask, enum dml2_pstate_method method) +{ + unsigned char i; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto && + display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy != pstate_method_to_uclk_pstate_strategy_override(method)) + return false; + } + } + + return true; +} + +static const struct dml2_pstate_per_method_common_meta *get_per_method_common_meta( + const struct dml2_pstate_meta *stream_pstate_meta, + enum dml2_pstate_method stream_pstate_method, + int stream_idx) +{ + const struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta = NULL; + + switch (stream_pstate_method) { + case dml2_pstate_method_vactive: + case dml2_pstate_method_fw_vactive_drr: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_vactive.common; + break; + case dml2_pstate_method_vblank: + case dml2_pstate_method_fw_vblank_drr: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_vblank.common; + break; + case dml2_pstate_method_fw_drr: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_drr.common; + break; + case dml2_pstate_method_alternate: + stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_alternate.common; + break; + case dml2_pstate_method_fw_svp: + case dml2_pstate_method_fw_svp_drr: + case dml2_pstate_method_reserved_hw: + case dml2_pstate_method_reserved_fw: + case dml2_pstate_method_reserved_fw_drr_clamped: + case dml2_pstate_method_reserved_fw_drr_var: + case dml2_pstate_method_count: + case dml2_pstate_method_na: + default: + stream_method_pstate_meta = NULL; + } + + return stream_method_pstate_meta; +} + +static void dcn6_get_params_for_pstate_type(const struct dml2_pmo_instance *pmo, + const struct dml2_optimization_worksheet *worksheet, + enum dml2_pstate_type pstate_type, + double *allow_delay_us, + double *blackout_us, + double *watermark_us) +{ + switch (pstate_type) { + case dml2_pstate_type_uclk: + *allow_delay_us = (double)pmo->ip_caps->fams2.max_allow_delay_us; + *blackout_us = pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + *watermark_us = worksheet->validation_result.mode_support.global.watermarks.DRAMClockChangeWatermark; + break; + case dml2_pstate_type_fclk: + *allow_delay_us = (double)pmo->ip_caps->fams2.max_allow_delay_us; /* TODO placeholder */ + *blackout_us = pmo->utm_soc_bb->power_management_parameters.fclk_change_blackout_us; + *watermark_us = worksheet->validation_result.mode_support.global.watermarks.FCLKChangeWatermark; + break; + case dml2_pstate_type_ppt: + *allow_delay_us = (double)pmo->ip_caps->ppt_max_allow_delay_us; + *blackout_us = math_max2( + pmo->utm_soc_bb->power_management_parameters.g7_ppt_blackout_us, + pmo->utm_soc_bb->power_management_parameters.g7_temperature_read_blackout_us); + *watermark_us = worksheet->validation_result.mode_support.global.watermarks.temp_read_or_ppt_watermark_us; + break; + case dml2_pstate_type_temp_read: + case dml2_pstate_type_dummy_pstate: + *allow_delay_us = (double)pmo->ip_caps->temp_read_max_allow_delay_us; + *blackout_us = math_max2( + pmo->utm_soc_bb->power_management_parameters.g7_ppt_blackout_us, + pmo->utm_soc_bb->power_management_parameters.g7_temperature_read_blackout_us); + *watermark_us = worksheet->validation_result.mode_support.global.watermarks.temp_read_or_ppt_watermark_us; + break; + case dml2_pstate_type_count: + default: + *allow_delay_us = 0.0; + *blackout_us = 0.0; + *watermark_us = 0.0; + break; + } +} + +static bool dcn6_is_timing_group_schedulable( + const struct dml2_pstate_meta *stream_pstate_meta, + const struct dml2_display_cfg *display_cfg, + const enum dml2_pstate_method *per_stream_pstate_method, + const unsigned int timing_group_idx, + const double max_allow_delay_us, + struct dml2_pstate_per_method_common_meta *group_pstate_meta, + struct dml2_pmo_synchronized_timing_groups *s) +{ + unsigned int i; + const struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta; + unsigned int base_stream_idx = 0; + + /* find base stream idx */ + for (base_stream_idx = 0; base_stream_idx < display_cfg->num_streams; base_stream_idx++) { + if (is_bit_set_in_bitfield(s->synchronized_timing_group_masks[timing_group_idx], base_stream_idx)) { + /* master stream found */ + break; + } + } + + /* init allow start and end lines for timing group */ + stream_method_pstate_meta = get_per_method_common_meta(stream_pstate_meta, per_stream_pstate_method[base_stream_idx], base_stream_idx); + if (!stream_method_pstate_meta) + return false; + + group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline; + group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline; + group_pstate_meta->period_us = stream_method_pstate_meta->period_us; + for (i = base_stream_idx + 1; i < display_cfg->num_streams; i++) { + if (is_bit_set_in_bitfield(s->synchronized_timing_group_masks[timing_group_idx], i)) { + stream_method_pstate_meta = get_per_method_common_meta(stream_pstate_meta, per_stream_pstate_method[i], i); + if (!stream_method_pstate_meta) + continue; + + if (group_pstate_meta->allow_start_otg_vline < stream_method_pstate_meta->allow_start_otg_vline) { + /* set group allow start to larger otg vline */ + group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline; + } + + if (group_pstate_meta->allow_end_otg_vline > stream_method_pstate_meta->allow_end_otg_vline) { + /* set group allow end to smaller otg vline */ + group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline; + } + + /* check waveform still has positive width */ + if (group_pstate_meta->allow_start_otg_vline >= group_pstate_meta->allow_end_otg_vline) { + /* timing group is not schedulable */ + return false; + } + } + } + + /* calculate the rest of the meta */ + dcn5_build_method_scheduling_params(group_pstate_meta, &stream_pstate_meta[base_stream_idx]); + + return group_pstate_meta->allow_time_us > 0.0 && + group_pstate_meta->disallow_time_us <= max_allow_delay_us; +} + +static bool dcn6_is_pstate_schedulable( + struct dml2_pstate_meta *stream_pstate_meta, + const struct dml2_display_cfg *display_cfg, + const enum dml2_pstate_method *per_stream_pstate_method, + const double max_allow_delay_us, + struct dml2_pmo_synchronized_timing_groups *synchronized_timing_groups, + struct dml2_scheduling_check_locals *s) +{ + double max_disallow_time_us = 0.0; + unsigned int i, j; + bool schedulable; + + memset(s->group_common_pstate_meta, 0, sizeof(s->group_common_pstate_meta)); + memset(s->sorted_group_gtl_disallow_index, 0, sizeof(unsigned int) * DML2_MAX_PLANES); + + /* search for a general solution to the schedule */ + + /* STAGE 0: Early return for special cases */ + if (display_cfg->num_streams == 0) { + return true; + } + + /* STAGE 1: confirm allow waves overlap for synchronizable streams */ + schedulable = true; + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + s->sorted_group_gtl_disallow_index[i] = i; + s->sorted_group_gtl_period_index[i] = i; + if (!dcn6_is_timing_group_schedulable(stream_pstate_meta, + display_cfg, + per_stream_pstate_method, + i, + max_allow_delay_us, + &s->group_common_pstate_meta[i], + synchronized_timing_groups)) { + /* synchronized timing group was not schedulable */ + schedulable = false; + break; + } + max_disallow_time_us += s->group_common_pstate_meta[i].disallow_time_us; + } + + if ((schedulable && synchronized_timing_groups->num_timing_groups <= 1) || !schedulable) { + /* 1. the only timing group was schedulable, so early pass + * 2. one of the timing groups was not schedulable, so early fail */ + return schedulable; + } + + /* STAGE 2: Check allow can't be masked entirely by other disallows */ + schedulable = true; + + /* sort disallow times from greatest to least */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + bool swapped = false; + + for (j = 0; j < synchronized_timing_groups->num_timing_groups - 1; j++) { + double j_disallow_us = s->group_common_pstate_meta[s->sorted_group_gtl_disallow_index[j]].disallow_time_us; + double jp1_disallow_us = s->group_common_pstate_meta[s->sorted_group_gtl_disallow_index[j + 1]].disallow_time_us; + if (j_disallow_us < jp1_disallow_us) { + /* swap as A < B */ + swap(s->sorted_group_gtl_disallow_index[j], + s->sorted_group_gtl_disallow_index[j+1]); + swapped = true; + } + } + + /* sorted, exit early */ + if (!swapped) + break; + } + + /* Check worst case disallow region occurs in the middle of allow for the + * other display, or when >2 streams continue to halve the remaining allow time. + */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + if (s->group_common_pstate_meta[i].disallow_time_us <= 0.0) { + /* this timing group always allows */ + continue; + } + + double max_allow_time_us = s->group_common_pstate_meta[i].allow_time_us; + for (j = 0; j < synchronized_timing_groups->num_timing_groups; j++) { + unsigned int sorted_j = s->sorted_group_gtl_disallow_index[j]; + /* stream can't overlap itself */ + if (i != sorted_j && s->group_common_pstate_meta[sorted_j].disallow_time_us > 0.0) { + double unmasked_allow_time_us = (max_allow_time_us - s->group_common_pstate_meta[sorted_j].disallow_time_us) / 2; + + max_allow_time_us = math_min2( + s->group_common_pstate_meta[sorted_j].allow_time_us, + unmasked_allow_time_us); + + if (max_allow_time_us < 0.0) { + /* failed exit early */ + break; + } + } + } + + if (max_allow_time_us <= 0.0) { + /* not enough time for microschedule in the worst case */ + schedulable = false; + break; + } + } + + if (schedulable && max_disallow_time_us < max_allow_delay_us) { + return true; + } + + /* STAGE 3: check larger allow can fit period of all other streams */ + schedulable = true; + + /* sort periods from greatest to least */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) { + bool swapped = false; + + for (j = 0; j < synchronized_timing_groups->num_timing_groups - 1; j++) { + double j_period_us = s->group_common_pstate_meta[s->sorted_group_gtl_period_index[j]].period_us; + double jp1_period_us = s->group_common_pstate_meta[s->sorted_group_gtl_period_index[j + 1]].period_us; + if (j_period_us < jp1_period_us) { + /* swap as A < B */ + swap(s->sorted_group_gtl_period_index[j], + s->sorted_group_gtl_period_index[j + 1]); + swapped = true; + } + } + + /* sorted, exit early */ + if (!swapped) + break; + } + + /* check larger allow can fit period of all other streams */ + for (i = 0; i < synchronized_timing_groups->num_timing_groups - 1; i++) { + unsigned int sorted_i = s->sorted_group_gtl_period_index[i]; + unsigned int sorted_ip1 = s->sorted_group_gtl_period_index[i + 1]; + + if (s->group_common_pstate_meta[sorted_i].allow_time_us < s->group_common_pstate_meta[sorted_ip1].period_us || + (synchronized_timing_groups->group_is_drr_enabled[sorted_ip1] && synchronized_timing_groups->group_is_drr_active[sorted_ip1])) { + schedulable = false; + break; + } + } + + if (schedulable && max_disallow_time_us < max_allow_delay_us) { + return true; + } + + /* STAGE 4: When using HW exclusive modes, check disallow alignments are within allowed threshold */ + if (synchronized_timing_groups->num_timing_groups == 2 && + !is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, per_stream_pstate_method[0]) && + !is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, per_stream_pstate_method[1])) { + double sum_allow_time_us; + double shift_per_period; + double period_ratio; + double max_shift_us; + + /* default period_0 > period_1 */ + unsigned int lrg_idx = 0; + unsigned int sml_idx = 1; + if (s->group_common_pstate_meta[0].period_us < s->group_common_pstate_meta[1].period_us) { + /* period_0 < period_1 */ + lrg_idx = 1; + sml_idx = 0; + } + period_ratio = s->group_common_pstate_meta[lrg_idx].period_us / s->group_common_pstate_meta[sml_idx].period_us; + shift_per_period = s->group_common_pstate_meta[sml_idx].period_us * (period_ratio - math_floor(period_ratio)); + max_shift_us = s->group_common_pstate_meta[lrg_idx].disallow_time_us - s->group_common_pstate_meta[sml_idx].allow_time_us; + max_disallow_time_us = max_shift_us / shift_per_period * s->group_common_pstate_meta[lrg_idx].period_us; + sum_allow_time_us = s->group_common_pstate_meta[lrg_idx].allow_time_us + s->group_common_pstate_meta[sml_idx].allow_time_us; + + if (shift_per_period > 0.0 && + shift_per_period < sum_allow_time_us && + max_disallow_time_us < max_allow_delay_us) { + schedulable = true; + } + } + + return schedulable; +} + +static bool dcn6_update_worksheet_for_pstate_admissibility(struct dml2_optimization_worksheet *worksheet, + struct dml2_pstate_meta *per_stream_pstate_meta, + enum dml2_pstate_type pstate_type) +{ + const double vblank_ratio = 0.8; + unsigned int plane_index, stream_index; + double ideal_relative_disallow, ideal_disallow_time_us, disallow_time_us; + double extra_time_required_us; + double vblank_time_us; + double min_unreserved_vblank_time_us; + double min_det_fill_delay_us; + double delta_max_det_fill_delay_us; + double delta_reserved_vblank_time_us; + + int max_vactive_det_fill_delay_us; + long reserved_vblank_time_ns; + bool unvalidated_changes = false; + + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + /* Two possible locations to obtain the extra time required to pass admissibility + * 1. Allocate some time from the reserved vblank + * 2. Use some of the time reserved for the vactive det fill delay + * For now statically favour getting most of the required time from the reserved vblank since + * configs failing pstate admissibility tend to have a large vblank to vactive ratio. + */ + + ideal_relative_disallow = 1.0; + if (display_config->num_streams > 1) { + /* The disallow region should be less than (0.5) ^ (number of displays - 1) * total frame time + * Calculation assumes identical displays / timings when performing the scheduling check + * Make the relative disallow 5% smaller than the ideal case to add extra margin + */ + ideal_relative_disallow = (double)math_pow(0.5, (float)(display_config->num_streams - 1)) * 0.95; + } + + for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) { + disallow_time_us = per_stream_pstate_meta[stream_index].method_vactive.common.disallow_time_us; + ideal_disallow_time_us = per_stream_pstate_meta[stream_index].method_vactive.common.period_us * ideal_relative_disallow; + + extra_time_required_us = 0.0; + delta_max_det_fill_delay_us = 0.0; + delta_reserved_vblank_time_us = 0.0; + if (disallow_time_us > ideal_disallow_time_us) { + /* minimum unreserved 15% of blank, or 50us left over for prefetch */ + vblank_time_us = display_config->stream_descriptors[stream_index].timing.vblank_nom * + per_stream_pstate_meta[stream_index].otg_vline_time_us; + min_unreserved_vblank_time_us = math_min2(vblank_time_us * 0.15, 50); + + /* minmum of blackout time (~2x VActive bandwidth) */ + min_det_fill_delay_us = per_stream_pstate_meta[stream_index].blackout_otg_vlines * + per_stream_pstate_meta[stream_index].otg_vline_time_us; + + extra_time_required_us = disallow_time_us - ideal_disallow_time_us; + + /* try compressing VActive fill first */ + delta_max_det_fill_delay_us = math_min2(extra_time_required_us * (1.0 - vblank_ratio), + math_max2(0.0, + per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us - min_det_fill_delay_us)); + delta_max_det_fill_delay_us = math_floor2(delta_max_det_fill_delay_us, per_stream_pstate_meta[stream_index].otg_vline_time_us); + extra_time_required_us -= delta_max_det_fill_delay_us; + + /* try reserving VBlank */ + delta_reserved_vblank_time_us = math_min2(extra_time_required_us, + math_max2(0.0, + vblank_time_us - per_stream_pstate_meta[stream_index].method_vactive.reserved_vblank_required_us - min_unreserved_vblank_time_us)); + delta_reserved_vblank_time_us = math_floor2(delta_reserved_vblank_time_us, per_stream_pstate_meta[stream_index].otg_vline_time_us); + extra_time_required_us -= delta_reserved_vblank_time_us; + + if (extra_time_required_us > 0.0 && + per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us > math_ceil2(delta_max_det_fill_delay_us + extra_time_required_us, per_stream_pstate_meta[stream_index].otg_vline_time_us)) { + /* final attempt to compress fill time */ + delta_max_det_fill_delay_us = delta_max_det_fill_delay_us + extra_time_required_us; + delta_max_det_fill_delay_us = math_ceil2(delta_max_det_fill_delay_us, per_stream_pstate_meta[stream_index].otg_vline_time_us); + extra_time_required_us = 0.0; + } + } + + /* Update the worksheet */ + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (display_config->plane_descriptors[plane_index].stream_index != stream_index) { + continue; + } + + max_vactive_det_fill_delay_us = 0; + if (worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] > 0.0) { + max_vactive_det_fill_delay_us = (int)math_floor(math_min2( + (double)worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type], + per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us - delta_max_det_fill_delay_us)); + } else { + max_vactive_det_fill_delay_us = (int)math_floor( + per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us - delta_max_det_fill_delay_us); + } + + reserved_vblank_time_ns = (long)math_max2( + (double)worksheet->cur.config.reserved_vblank_time_ns[plane_index], + (per_stream_pstate_meta[stream_index].method_vactive.reserved_vblank_required_us + delta_reserved_vblank_time_us) * 1000.0); + + if ((max_vactive_det_fill_delay_us > 0 && worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] == 0) || + max_vactive_det_fill_delay_us < worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] || + (reserved_vblank_time_ns > 0 && worksheet->cur.config.reserved_vblank_time_ns[plane_index] == 0) || + reserved_vblank_time_ns > worksheet->cur.config.reserved_vblank_time_ns[plane_index]) { + /* only modify the worksheet if required */ + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] = max_vactive_det_fill_delay_us; + worksheet->cur.config.reserved_vblank_time_ns[plane_index] = reserved_vblank_time_ns; + + worksheet->cur.unvalidated_change.bits.reserved_vblank_time = true; + unvalidated_changes = true; + } + DML_LOG_DEBUG("worksheet->cur.config.reserved_vblank_time_ns[%d] = %lu\n", plane_index, worksheet->cur.config.reserved_vblank_time_ns[plane_index]); + DML_LOG_DEBUG("worksheet->cur.config.max_vactive_det_fill_delay_us[%d] = %u\n", plane_index, worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type]); + } + } + + if (!worksheet->cur.config.fclk_pstate_support + || !worksheet->cur.config.ppt_temp_read_support) { + worksheet->cur.config.fclk_pstate_support = true; + worksheet->cur.config.ppt_temp_read_support = true; + worksheet->cur.unvalidated_change.bits.uclk_pstate_method = true; + unvalidated_changes = true; + } + + return unvalidated_changes; +} + +static int dcn6_get_vactive_latency_hiding(const struct dml2_validation_result *validation_res, int plane_mask) +{ + unsigned char i; + int min_vactive_latency_hiding_us = 0xFFFFFFF; + + if (!validation_res->is_mode_support_valid) + return min_vactive_latency_hiding_us; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (validation_res->mode_support.cfg_support_info.plane_support_info[i].active_latency_hiding_us < min_vactive_latency_hiding_us) + min_vactive_latency_hiding_us = validation_res->mode_support.cfg_support_info.plane_support_info[i].active_latency_hiding_us; + } + } + + return min_vactive_latency_hiding_us; +} + +static int dcn6_get_vactive_det_fill_delay_us( + const struct dml2_validation_result *validation_res, + enum dml2_pstate_type pstate_type, + int plane_mask) +{ + unsigned int i; + int max_vactive_det_fill_delay_us = 0; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (validation_res->mode_support.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[pstate_type] > max_vactive_det_fill_delay_us) + max_vactive_det_fill_delay_us = validation_res->mode_support.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[pstate_type]; + } + } + + return max_vactive_det_fill_delay_us; +} + +static int dcn6_get_required_vactive_det_fill_delay_us( + const struct dml2_optimization_worksheet *worksheet, + enum dml2_pstate_type pstate_type, + int plane_mask) +{ + unsigned int i; + int max_vactive_det_fill_delay_us = 0; + + for (i = 0; i < DML2_MAX_PLANES; i++) { + if (is_bit_set_in_bitfield(plane_mask, i)) { + if (worksheet->cur.config.max_vactive_det_fill_delay_us[i][pstate_type] > max_vactive_det_fill_delay_us) + max_vactive_det_fill_delay_us = worksheet->cur.config.max_vactive_det_fill_delay_us[i][pstate_type]; + } + } + + return max_vactive_det_fill_delay_us; +} + +static bool dcn6_all_timings_support_vactive(struct dml2_pmo_stage_optimizer *stage, + const struct dml2_display_cfg *display_config, + unsigned int mask) +{ + struct dml2_stage_optimizer_uclk_pstate_init_locals *s = &stage->func_locals->uclk_pstate_init; + unsigned int i; + bool valid = true; + + // Create a remap array to enable simple iteration through only masked stream indicies + for (i = 0; i < display_config->num_streams; i++) { + if (is_bit_set_in_bitfield(mask, i)) { + /* check if stream has enough vactive margin, or single display in case blank can also be used */ + valid &= is_bit_set_in_bitfield(s->stream_vactive_capability_mask, i) || + display_config->num_streams == 1; + } + } + + return valid; +} + +static bool validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + const struct dml2_display_cfg *display_cfg, + const struct dml2_pmo_pstate_strategy *pstate_strategy) +{ + const struct dml2_pmo_instance *pmo = stage->pmo; + + unsigned int stream_index = 0; + + unsigned int drr_count = 0; + unsigned int drr_stream_mask = 0; + unsigned int vactive_count = 0; + unsigned int vactive_stream_mask = 0; + unsigned int vblank_count = 0; + unsigned int vblank_stream_mask = 0; + unsigned int alternate_count = 0; + unsigned int alternate_stream_mask = 0; + + bool strategy_matches_forced_requirements = true; + bool strategy_matches_drr_requirements = true; + + // Tabulate everything + for (stream_index = 0; stream_index < display_cfg->num_streams; stream_index++) { + + if (!all_planes_match_method(display_cfg, worksheet->uclk_pstate.stream_plane_mask[stream_index], + pstate_strategy->per_stream_pstate_method[stream_index])) { + strategy_matches_forced_requirements = false; + break; + } + + strategy_matches_drr_requirements &= + dcn5_stream_matches_drr_policy(stage, display_cfg, pstate_strategy->per_stream_pstate_method[stream_index], stream_index); + + if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) { + drr_count++; + set_bit_in_bitfield(&drr_stream_mask, stream_index); + } else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive || + pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) { + vactive_count++; + set_bit_in_bitfield(&vactive_stream_mask, stream_index); + } else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank || + pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) { + vblank_count++; + set_bit_in_bitfield(&vblank_stream_mask, stream_index); + } else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_alternate) { + alternate_count++; + set_bit_in_bitfield(&alternate_stream_mask, stream_index); + } + } + + if (!strategy_matches_forced_requirements || !strategy_matches_drr_requirements) + return false; + + if (vactive_count > 0 && !dcn6_all_timings_support_vactive(stage, display_cfg, vactive_stream_mask)) + return false; + + if (vblank_count > 0 && (pmo->options->disable_vblank || !dcn5_all_timings_support_vblank(stage, display_cfg, vblank_stream_mask))) + return false; + + if (drr_count > 0 && (pmo->options->disable_drr_var || !dcn5_all_timings_support_drr(stage, worksheet, display_cfg, drr_stream_mask))) + return false; + + if (alternate_count > 0 && pmo->options->disable_alternate_memory_training) + return false; + + return dcn6_is_pstate_schedulable( + worksheet->uclk_pstate.stream_pstate_meta, + display_cfg, + pstate_strategy->per_stream_pstate_method, + stage->func_locals->uclk_pstate_init.allow_delay_us, + &stage->func_locals->uclk_pstate_init.synchronized_timing_groups, + &stage->func_locals->uclk_pstate_init.scheduling_check_locals); +} + +static void dcn6_build_pstate_meta_per_stream(const struct dml2_display_cfg *display_cfg, + const struct dml2_ip_capabilities *ip_caps, + const struct dml2_optimization_worksheet *worksheet, + enum dml2_pstate_type pstate_type, + double watermark_us, + double blackout_us, + double max_allow_delay_us, + int stream_index, + unsigned int stream_plane_mask, + /* output */ + struct dml2_pstate_meta *stream_pstate_meta) +{ + const struct dml2_stream_parameters *stream_descriptor = &display_cfg->stream_descriptors[stream_index]; + const struct dml2_timing_cfg *timing = &stream_descriptor->timing; + + int max_det_fill_delay_otg_vlines; + int min_reserved_blank_otg_vlines; + + /* worst case all other streams require some programming at the same time, 0 if only 1 stream */ + double contention_delay_us = ((double)ip_caps->fams2.vertical_interrupt_ack_delay_us + + math_max2(ip_caps->fams2.drr_programming_delay_us, ip_caps->fams2.allow_programming_delay_us)) * + (display_cfg->num_streams - 1); + + /* common */ + stream_pstate_meta->valid = true; + stream_pstate_meta->nom_vtotal = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active; + stream_pstate_meta->otg_vline_time_us = (double)timing->h_total / timing->pixel_clock_khz * 1000.0; + stream_pstate_meta->vblank_start = timing->v_blank_end + timing->v_active; + stream_pstate_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 / + (stream_pstate_meta->nom_vtotal * timing->h_total); + stream_pstate_meta->nom_frame_time_us = + (double)stream_pstate_meta->nom_vtotal * stream_pstate_meta->otg_vline_time_us; + + if (stream_descriptor->timing.drr_config.enabled == true) { + if (stream_descriptor->timing.drr_config.min_refresh_uhz != 0.0) { + stream_pstate_meta->max_vtotal = (int)math_floor((double)stream_descriptor->timing.pixel_clock_khz / + ((double)stream_descriptor->timing.drr_config.min_refresh_uhz * stream_descriptor->timing.h_total) * 1e9); + } else { + /* assume min of 48Hz */ + stream_pstate_meta->max_vtotal = (int)math_floor((double)stream_descriptor->timing.pixel_clock_khz / + (48000000.0 * stream_descriptor->timing.h_total) * 1e9); + } + } else { + stream_pstate_meta->max_vtotal = stream_pstate_meta->nom_vtotal; + } + stream_pstate_meta->min_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 / + (stream_pstate_meta->max_vtotal * timing->h_total); + stream_pstate_meta->max_frame_time_us = + (double)stream_pstate_meta->max_vtotal * stream_pstate_meta->otg_vline_time_us; + + stream_pstate_meta->scheduling_delay_otg_vlines = + (int)math_ceil(ip_caps->fams2.scheduling_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines = + (int)math_ceil(ip_caps->fams2.vertical_interrupt_ack_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->contention_delay_otg_vlines = + (int)math_ceil(contention_delay_us / stream_pstate_meta->otg_vline_time_us); + /* worst case allow to target needs to account for all streams' allow events overlapping, and 1 line for error */ + stream_pstate_meta->allow_to_target_delay_otg_vlines = + (int)(math_ceil((ip_caps->fams2.vertical_interrupt_ack_delay_us + contention_delay_us + ip_caps->fams2.allow_programming_delay_us) / stream_pstate_meta->otg_vline_time_us)) + 1; + stream_pstate_meta->min_allow_width_otg_vlines = + (int)math_ceil(ip_caps->fams2.min_allow_width_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->blackout_otg_vlines = (int)math_ceil(blackout_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->max_allow_delay_otg_vlines = (int)math_floor((double)max_allow_delay_us / stream_pstate_meta->otg_vline_time_us); + if (stream_pstate_meta->max_allow_delay_otg_vlines < 0) + stream_pstate_meta->max_allow_delay_otg_vlines = 0; + max_det_fill_delay_otg_vlines = (int)math_floor( + (double)dcn6_get_required_vactive_det_fill_delay_us(worksheet, pstate_type, stream_plane_mask) / + stream_pstate_meta->otg_vline_time_us); + min_reserved_blank_otg_vlines = (int)math_ceil( + (double)dcn5_get_minimum_reserved_time_us_for_planes(worksheet, stream_plane_mask) / + stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->nom_vblank_time_us = stream_descriptor->timing.vblank_nom * stream_pstate_meta->otg_vline_time_us; + + /* scheduling params should be built based on the worst case for allow_time:disallow_time */ + + /* vactive */ + stream_pstate_meta->method_vactive.vactive_latency_hiding_us = + (double)dcn6_get_vactive_latency_hiding(&worksheet->validation_result, stream_plane_mask); + if (stream_pstate_meta->method_vactive.vactive_latency_hiding_us < watermark_us) { + /* achieve single pulse of allow by utilizing blank */ + stream_pstate_meta->method_vactive.reserved_vblank_required_us = + blackout_us - + stream_pstate_meta->method_vactive.vactive_latency_hiding_us; + stream_pstate_meta->method_vactive.reserved_blank_required_vlines = (int)math_max3( + 0.0, + math_ceil(stream_pstate_meta->method_vactive.reserved_vblank_required_us / + stream_pstate_meta->otg_vline_time_us), + (double)min_reserved_blank_otg_vlines); + } else { + /* account for already reserved vblank */ + stream_pstate_meta->method_vactive.reserved_vblank_required_us = + min_reserved_blank_otg_vlines * + stream_pstate_meta->otg_vline_time_us; + stream_pstate_meta->method_vactive.reserved_blank_required_vlines = min_reserved_blank_otg_vlines; + } + + if (display_cfg->num_streams == 1) { + /* for single stream, guarantee at least an instant of allow */ + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_floor( + math_max2(0.0, + timing->v_active - math_max2(1.0, stream_pstate_meta->min_allow_width_otg_vlines) - + (stream_pstate_meta->blackout_otg_vlines - + stream_pstate_meta->method_vactive.reserved_blank_required_vlines))); + } else { + /* for multi stream, bound to a max fill time defined by the parameter */ + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = + (int)math_floor((double)ip_caps->max_vactive_det_fill_delay_us / stream_pstate_meta->otg_vline_time_us); + } + + if (max_det_fill_delay_otg_vlines > 0) { + /* consider existing DET fill time enforcement */ + if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines > 0) { + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_min2( + (double)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines, + (double)max_det_fill_delay_otg_vlines); + } else { + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = max_det_fill_delay_otg_vlines; + } + } + + /* consider max allow delay enforcement */ + if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines > 0) { + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_min2( + (double)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines, + math_max2(0.0, + stream_pstate_meta->max_allow_delay_otg_vlines + + stream_pstate_meta->method_vactive.reserved_blank_required_vlines - + (int)stream_descriptor->timing.vblank_nom - + stream_pstate_meta->blackout_otg_vlines)); + } else { + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_max2(0.0, + stream_pstate_meta->max_allow_delay_otg_vlines + + stream_pstate_meta->method_vactive.reserved_blank_required_vlines - + (int)stream_descriptor->timing.vblank_nom - + stream_pstate_meta->blackout_otg_vlines); + } + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us = + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines * + stream_pstate_meta->otg_vline_time_us; + + if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us > 0.0) { + stream_pstate_meta->method_vactive.common.allow_start_otg_vline = + timing->v_blank_end + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines; + stream_pstate_meta->method_vactive.common.allow_end_otg_vline = + stream_pstate_meta->vblank_start - + stream_pstate_meta->blackout_otg_vlines + + stream_pstate_meta->method_vactive.reserved_blank_required_vlines; + } else { + stream_pstate_meta->method_vactive.common.allow_start_otg_vline = 0; + stream_pstate_meta->method_vactive.common.allow_end_otg_vline = 0; + } + stream_pstate_meta->method_vactive.common.period_us = stream_pstate_meta->nom_frame_time_us; + + /* vblank */ + stream_pstate_meta->method_vblank.common.allow_start_otg_vline = stream_pstate_meta->vblank_start; + stream_pstate_meta->method_vblank.common.period_us = stream_pstate_meta->nom_frame_time_us; + stream_pstate_meta->method_vblank.common.allow_end_otg_vline = + stream_pstate_meta->method_vblank.common.allow_start_otg_vline + 1; + + if (pstate_type == dml2_pstate_type_uclk) { + /* alternate */ + stream_pstate_meta->method_alternate.programming_delay_otg_vlines = + (int)math_ceil(ip_caps->fams2.subvp_programming_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->method_alternate.pmfw_throttle_delay_otg_vlines = + (int)math_ceil(ip_caps->fams2.subvp_df_throttle_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->method_alternate.common.period_us = stream_pstate_meta->nom_frame_time_us; + stream_pstate_meta->method_alternate.common.allow_start_otg_vline = 0; + stream_pstate_meta->method_alternate.common.allow_end_otg_vline = stream_pstate_meta->nom_vtotal; + + /* drr */ + stream_pstate_meta->method_drr.common.period_us = stream_pstate_meta->nom_frame_time_us; + stream_pstate_meta->method_drr.programming_delay_otg_vlines = + (int)math_ceil(ip_caps->fams2.drr_programming_delay_us / stream_pstate_meta->otg_vline_time_us); + stream_pstate_meta->method_drr.common.allow_start_otg_vline = + stream_pstate_meta->vblank_start + + stream_pstate_meta->allow_to_target_delay_otg_vlines; + + if (display_cfg->num_streams <= 1) { + /* only need to stretch vblank for blackout time */ + stream_pstate_meta->method_drr.stretched_vtotal = + stream_pstate_meta->nom_vtotal + + stream_pstate_meta->allow_to_target_delay_otg_vlines + + stream_pstate_meta->min_allow_width_otg_vlines + + stream_pstate_meta->blackout_otg_vlines; + } else { + /* multi display needs to always be schedulable */ + stream_pstate_meta->method_drr.stretched_vtotal = + stream_pstate_meta->nom_vtotal * 2 + + stream_pstate_meta->allow_to_target_delay_otg_vlines + + stream_pstate_meta->min_allow_width_otg_vlines + + stream_pstate_meta->blackout_otg_vlines; + } + stream_pstate_meta->method_drr.common.allow_end_otg_vline = + stream_pstate_meta->method_drr.stretched_vtotal - + stream_pstate_meta->blackout_otg_vlines; + + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_drr.common, stream_pstate_meta); + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_alternate.common, stream_pstate_meta); + } + + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_vactive.common, stream_pstate_meta); + dcn5_build_method_scheduling_params(&stream_pstate_meta->method_vblank.common, stream_pstate_meta); +} + + +static void dml2_pmo_dcn6_stage_optimizer_uclk_pstate_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_pmo_instance *pmo = stage->pmo; + struct dml2_stage_optimizer_uclk_pstate_init_locals *s = &stage->func_locals->uclk_pstate_init; + + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_plane_parameters *plane_descriptor; + const struct dml2_pmo_pstate_strategy *strategy_list = NULL; + struct dml2_pmo_pstate_strategy override_base_strategy = { 0 }; + unsigned int strategy_list_size = 0; + unsigned int plane_index, i; + unsigned int stream_index; + bool build_override_strategy = true; + + DML_LOG_COMP_IF_ENTER(); + memset(s, 0, sizeof(struct dml2_stage_optimizer_uclk_pstate_init_locals)); + + if (display_config->overrides.all_streams_blanked) { + goto exit; + } + + // First build the stream plane mask (array of bitfields indexed by stream, indicating plane mapping) + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + plane_descriptor = &display_config->plane_descriptors[plane_index]; + + set_bit_in_bitfield(&worksheet->uclk_pstate.stream_plane_mask[plane_descriptor->stream_index], plane_index); + + build_override_strategy &= plane_descriptor->overrides.uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto; + override_base_strategy.per_stream_pstate_method[plane_descriptor->stream_index] = + uclk_pstate_strategy_override_to_pstate_method(plane_descriptor->overrides.uclk_pstate_change_strategy); + + /* Save initial reserved vblank time as pstate optimize may overwrite this value. But + * if validation or permissibility fails then we must restore to the original value. + */ + worksheet->uclk_pstate.init_reserved_vblank_time_ns[plane_index] = worksheet->cur.config.reserved_vblank_time_ns[plane_index]; + } + + dcn6_get_params_for_pstate_type(pmo, worksheet, dml2_pstate_type_uclk, &s->allow_delay_us, &s->blackout_us, &s->watermark_us); + + // Figure out which streams can do vactive, and also build up implicit FAMS2 meta + for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) { + unsigned int stream_plane_mask = worksheet->uclk_pstate.stream_plane_mask[stream_index]; + struct dml2_validation_result *validation_result = &worksheet->validation_result; + + if (dcn5_get_vactive_pstate_margin(validation_result, stream_plane_mask) > 0) + set_bit_in_bitfield(&s->stream_vactive_capability_mask, stream_index); + + /* pstate meta */ + dcn6_build_pstate_meta_per_stream(worksheet->orig_dispcfg, + pmo->ip_caps, + worksheet, + dml2_pstate_type_uclk, + s->watermark_us, + s->blackout_us, + s->allow_delay_us, + stream_index, + stream_plane_mask, + &worksheet->uclk_pstate.stream_pstate_meta[stream_index]); + } + + /* get synchronized timing groups */ + dcn5_build_synchronized_timing_groups(&stage->func_locals->uclk_pstate_init.synchronized_timing_groups, display_config); + + if (build_override_strategy) { + /* build expanded override strategy list (no permutations) */ + override_base_strategy.allow_state_increase = true; + s->num_expanded_override_strategies = 0; + dcn5_insert_strategy_into_expanded_list(&override_base_strategy, + display_config->num_streams, + s->expanded_override_strategy_list, + &s->num_expanded_override_strategies); + dcn5_expand_variant_strategy(&override_base_strategy, + display_config->num_streams, + false, + s->expanded_override_strategy_list, + &s->num_expanded_override_strategies); + + /* use override strategy list */ + strategy_list = s->expanded_override_strategy_list; + strategy_list_size = s->num_expanded_override_strategies; + } else { + /* use predefined strategy list */ + strategy_list = dcn5_get_expanded_strategy_list(stage, display_config->num_streams); + strategy_list_size = dcn5_get_num_expanded_strategies(stage, display_config->num_streams); + } + + worksheet->uclk_pstate.num_pstate_candidates = 0; + + if (!strategy_list || strategy_list_size == 0) + goto exit; + + for (i = 0; i < strategy_list_size && worksheet->uclk_pstate.num_pstate_candidates < DML2_PMO_PSTATE_CANDIDATE_LIST_SIZE; i++) { + if (validate_pstate_support_strategy_cofunctionality(stage, worksheet, display_config, &strategy_list[i])) { + dcn5_insert_into_candidate_list(&strategy_list[i], display_config->num_streams, worksheet); + } + } + + if (worksheet->uclk_pstate.num_pstate_candidates > 0) { + worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.num_pstate_candidates-1].allow_state_increase = true; + worksheet->uclk_pstate.cur_pstate_candidate = -1; + goto exit; + } else { + goto exit; + } +exit: + DML_LOG_COMP_IF_EXIT(); +} + +static void setup_planes_for_alternate_by_mask(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet, + int plane_mask) +{ + (void)stage; + unsigned int plane_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) + if (is_bit_set_in_bitfield(plane_mask, plane_index)) + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_alternate; +} + +static void dcn6_setup_planes_for_vactive_by_mask(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet, int plane_mask) +{ + unsigned int plane_index; + unsigned int stream_index; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + const struct dml2_pmo_instance *pmo = stage->pmo; + + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (is_bit_set_in_bitfield(plane_mask, plane_index)) { + stream_index = display_config->plane_descriptors[plane_index].stream_index; + + worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_vactive; + + if (!pmo->options->disable_vactive_det_fill_bw_pad) { + if (worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] > 0) { + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = (int)math_min2( + math_floor(worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us), + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk]); + } else { + worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = (int)math_floor( + worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us); + } + } + + worksheet->cur.config.reserved_vblank_time_ns[plane_index] = (long)math_max2( + worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.reserved_vblank_required_us * 1000, + worksheet->cur.config.reserved_vblank_time_ns[plane_index]); + } + } +} + +static bool setup_optimized_worksheet_for_uclk_pstate(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool fams2_required = false; + bool legacy_pstate_info_for_dmu = false; + bool success = true; + unsigned int stream_index, plane_index; + int strategy_index = worksheet->uclk_pstate.cur_pstate_candidate; + const struct dml2_plane_parameters *plane_descriptor; + + for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) { + plane_descriptor = &worksheet->orig_dispcfg->plane_descriptors[plane_index]; + set_bit_in_bitfield(&worksheet->uclk_pstate.stream_plane_mask[plane_descriptor->stream_index], plane_index); + } + + for (stream_index = 0; stream_index < worksheet->orig_dispcfg->num_streams; stream_index++) { + + if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_na) { + success = false; + break; + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive) { + legacy_pstate_info_for_dmu = true; + dcn6_setup_planes_for_vactive_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]); + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank) { + legacy_pstate_info_for_dmu = true; + dcn5_setup_planes_for_vblank_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]); + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) { + fams2_required = true; + dcn5_setup_planes_for_vactive_drr_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]); + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) { + fams2_required = true; + dcn5_setup_planes_for_vblank_drr_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]); + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) { + fams2_required = true; + dcn5_setup_planes_for_drr_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]); + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_alternate) { + fams2_required = true; + setup_planes_for_alternate_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]); + } + } + + /* Indicate if FAMS2 required */ + if (success) { + worksheet->cur.config.fams2_required = fams2_required; + worksheet->cur.config.legacy_pstate_info_for_dmu = legacy_pstate_info_for_dmu; + // Copy FAMS2 meta unconditionally - we need for vactive as well + memcpy(&worksheet->cur.config.stream_pstate_meta, + &worksheet->uclk_pstate.stream_pstate_meta, + sizeof(struct dml2_pstate_meta) * DML2_MAX_PLANES); + worksheet->cur.config.uclk_pstate_support = true; + } + + return success; +} + +static bool dml2_pmo_dcn6_stage_optimizer_uclk_pstate_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool should_continue = true; + + DML_LOG_COMP_IF_ENTER(); + /* Nothing to optimize if there are no candidates, so return false */ + if (worksheet->uclk_pstate.num_pstate_candidates == 0) { + should_continue = false; + goto exit; + } + + /* Optimization is completed if we find a candidate that passed validation and also passes permissibility. + * There are scenarios where permissibility can pass even if validation fails, so we need to check the + * validation result here as well. + */ + if (worksheet->validation_result.is_mode_support_valid + && stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK) { + should_continue = false; + goto exit; + } + + /* If we've reached the end of the p-state candidate list, return false since + * there's no more potential optimization options */ + if (worksheet->uclk_pstate.cur_pstate_candidate == worksheet->uclk_pstate.num_pstate_candidates - 1) { + should_continue = false; + goto exit; + } + + /* Reset current settings since the previous optimization attempt did not pass */ + dcn5_reset_worksheet_for_uclk_pstate(worksheet); + worksheet->uclk_pstate.cur_pstate_candidate++; + worksheet->cur.unvalidated_change.bits.uclk_pstate_method = + setup_optimized_worksheet_for_uclk_pstate(stage, worksheet); + DML_ASSERT_MSG(worksheet->cur.unvalidated_change.bits.uclk_pstate_method, "optimize_next must apply changes" + " when returning true!\n"); + +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +static bool dcn6_alternate_permissible( + const struct dml2_validation_result *validation_res, + const struct dml2_pmo_instance *pmo, + const struct dml2_display_cfg *display_cfg, + int stream_idx) +{ + (void)pmo; + unsigned int svp0_dst_lines = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].alternate_svp0_dst_lines; + unsigned int svp1_dst_lines = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].alternate_svp1_dst_lines; + unsigned int max_dst_y_pre = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].max_dst_y_prefetch; + unsigned int max_dst_y_after_scaler = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].max_dst_y_after_scaler; + const unsigned int max_hw_cursor_size = 135; // Actual is 128, but set to 135 for margin + + // svp0 + svp1 < vtotal - vstartup is required to support alt-chan + // TBD if we need to increase constraint to vtotal - vstartup - cursor_height -> required if last cursor deadline is beyond vblank end + if (svp0_dst_lines + svp1_dst_lines >= display_cfg->stream_descriptors[stream_idx].timing.v_total - max_dst_y_pre - max_dst_y_after_scaler - max_hw_cursor_size) + return false; + + return true; +} + +static enum dml2_status dml2_pmo_dcn6_stage_optimizer_uclk_pstate_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + enum dml2_status status = DML2_STATUS_OK; + unsigned int stream_index; + const struct dml2_pmo_instance *pmo = stage->pmo; + + int REQUIRED_RESERVED_TIME = 0; + + DML_LOG_COMP_IF_ENTER(); + + /* Permissibility passes if all streams are blanked - p-state support is guaranteed for this case*/ + if (worksheet->orig_dispcfg->overrides.all_streams_blanked) { + status = DML2_STATUS_OK; + goto exit; + } + + /* If there are no pstate candidates then pstate support is false and permissibility fails */ + if (worksheet->uclk_pstate.num_pstate_candidates == 0) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + goto exit; + } + + if (worksheet->uclk_pstate.cur_pstate_candidate < 0) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + goto exit; + } + + if (!worksheet->validation_result.mode_support.global.uclk_pstate_supported) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + goto exit; + } + + REQUIRED_RESERVED_TIME = (int)pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us; + + for (stream_index = 0; stream_index < worksheet->orig_dispcfg->num_streams; stream_index++) { + const struct dml2_pstate_meta *stream_pstate_meta = &worksheet->cur.config.stream_pstate_meta[stream_index]; + + if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive || + worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) { + if (worksheet->orig_dispcfg->num_streams == 1) { + /* Peak VActive + VBlank (single stream only) */ + if (dcn6_get_vactive_latency_hiding(&worksheet->validation_result, worksheet->uclk_pstate.stream_plane_mask[stream_index]) + + dcn5_get_minimum_reserved_time_us_for_planes(worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (dcn5_get_vactive_pstate_margin(&worksheet->validation_result, worksheet->uclk_pstate.stream_plane_mask[stream_index]) < 0.0 || + dcn6_get_vactive_det_fill_delay_us(&worksheet->validation_result, dml2_pstate_type_uclk, worksheet->uclk_pstate.stream_plane_mask[stream_index]) > math_ceil(stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us)) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank || + worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) { + if (dcn5_get_minimum_reserved_time_us_for_planes(worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) { + if (!all_planes_match_method(worksheet->orig_dispcfg, worksheet->uclk_pstate.stream_plane_mask[stream_index], dml2_pstate_method_fw_drr)) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_alternate) { + if (!all_planes_match_method(worksheet->orig_dispcfg, worksheet->uclk_pstate.stream_plane_mask[stream_index], dml2_pstate_method_alternate) || + !dcn6_alternate_permissible(&worksheet->validation_result, pmo, worksheet->orig_dispcfg, stream_index)) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_na) { + status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE; + break; + } + } +exit: + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + return status; +} + +void dml2_pmo_dcn6_stage_optimizer_uclk_pstate_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn6_stage_optimizer_uclk_pstate_init; + stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_uclk_pstate_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn6_stage_optimizer_uclk_pstate_test_permissibility; +} + +/* +* Counts the number of elements inside input array within the given span length. +* Formally, what is the size of the largest subset of the array where the largest and smallest element +* differ no more than the span. +*/ +static unsigned int count_elements_in_span(const int *array, unsigned int array_size, unsigned int span) +{ + unsigned int i; + unsigned int span_start_value; + unsigned int span_start_index; + unsigned int greatest_element_count; + + if (array_size == 0) + return 1; + + if (span == 0) + return array_size > 0 ? 1 : 0; + + span_start_value = 0; + span_start_index = 0; + greatest_element_count = 0; + + while (span_start_index < array_size) { + for (i = span_start_index; i < array_size; i++) { + if (array[i] - span_start_value <= span) { + if (i - span_start_index + 1 > greatest_element_count) { + greatest_element_count = i - span_start_index + 1; + } + } else + break; + } + + span_start_index++; + + if (span_start_index < array_size) { + span_start_value = array[span_start_index - 1] + 1; + } + } + + return greatest_element_count; +} + +static bool calculate_h_split_for_scaling_transform(int full_vp_width, int h_active, int num_pipes, + enum dml2_scaling_transform scaling_transform, int *pipe_vp_x_start, int *pipe_vp_x_end) +{ + (void)h_active; + int i, slice_width; + const char MAX_SCL_VP_OVERLAP = 3; + bool success = false; + + switch (scaling_transform) { + case dml2_scaling_transform_centered: + case dml2_scaling_transform_aspect_ratio: + case dml2_scaling_transform_fullscreen: + slice_width = full_vp_width / num_pipes; + for (i = 0; i < num_pipes; i++) { + pipe_vp_x_start[i] = i * slice_width; + pipe_vp_x_end[i] = (i + 1) * slice_width - 1; + + if (pipe_vp_x_start[i] < MAX_SCL_VP_OVERLAP) + pipe_vp_x_start[i] = 0; + else + pipe_vp_x_start[i] -= MAX_SCL_VP_OVERLAP; + + if (pipe_vp_x_end[i] > full_vp_width - MAX_SCL_VP_OVERLAP - 1) + pipe_vp_x_end[i] = full_vp_width - 1; + else + pipe_vp_x_end[i] += MAX_SCL_VP_OVERLAP; + } + break; + case dml2_scaling_transform_explicit: + default: + success = false; + break; + } + + return success; +} + +/* +* Takes an input set of mcache boundaries and finds the appropriate setting of cache programming. +* Returns true if a valid set of programming can be made, and false otherwise. "Valid" means +* that the horizontal viewport does not span more than 2 cache slices. +* +* It optionally also can apply a constant shift to all the cache boundaries. +*/ +static bool calculate_first_second_splitting(const int *mcache_boundaries, int num_boundaries, int shift, + int pipe_h_vp_start, int pipe_h_vp_end, int *first_offset, int *second_offset) +{ + const int MAX_VP = 0xFFFFFF; + int left_cache_id; + int right_cache_id; + int range_start; + int range_end; + bool success = false; + + if (num_boundaries <= 1) { + if (first_offset && second_offset) { + *first_offset = 0; + *second_offset = -1; + } + success = true; + return success; + } else { + range_start = 0; + for (left_cache_id = 0; left_cache_id < num_boundaries; left_cache_id++) { + range_end = mcache_boundaries[left_cache_id] - shift - 1; + + if (range_start <= pipe_h_vp_start && pipe_h_vp_start <= range_end) + break; + + range_start = range_end + 1; + } + + range_end = MAX_VP; + for (right_cache_id = num_boundaries - 1; right_cache_id >= -1; right_cache_id--) { + if (right_cache_id >= 0) + range_start = mcache_boundaries[right_cache_id] - shift; + else + range_start = 0; + + if (range_start <= pipe_h_vp_end && pipe_h_vp_end <= range_end) { + break; + } + range_end = range_start - 1; + } + right_cache_id = (right_cache_id + 1) % num_boundaries; + + if (right_cache_id == left_cache_id) { + if (first_offset && second_offset) { + *first_offset = left_cache_id; + *second_offset = -1; + } + success = true; + } else if (right_cache_id == (left_cache_id + 1) % num_boundaries) { + if (first_offset && second_offset) { + *first_offset = left_cache_id; + *second_offset = right_cache_id; + } + success = true; + } + } + + return success; +} + +/* +* For a given set of pipe start/end x positions, checks to see it can support the input mcache splitting. +* It also attempts to "optimize" by finding a shift if the default 0 shift does not work. +*/ +static bool find_shift_for_valid_cache_id_assignment(const int *mcache_boundaries, unsigned int num_boundaries, + int *pipe_vp_startx, int *pipe_vp_endx, unsigned int pipe_count, int shift_granularity, int *shift) +{ + int max_shift = 0xFFFF; + unsigned int pipe_index; + unsigned int i, slice_width; + bool success = false; + + for (i = 0; i < num_boundaries; i++) { + if (i == 0) + slice_width = mcache_boundaries[i]; + else + slice_width = mcache_boundaries[i] - mcache_boundaries[i - 1]; + + if (max_shift > (int)slice_width) { + max_shift = slice_width; + } + } + + for (*shift = 0; *shift <= max_shift; *shift += shift_granularity) { + success = true; + for (pipe_index = 0; pipe_index < pipe_count; pipe_index++) { + if (!calculate_first_second_splitting(mcache_boundaries, num_boundaries, *shift, + pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], 0, 0)) { + success = false; + break; + } + } + if (success) + break; + } + + return success; +} + + + +static void dml2_pmo_dcn6_stage_optimizer_mcache_decide_shifts(struct dml2_pmo_stage_optimizer *stage, + struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + const int MAX_PIXEL_OVERLAP = 6; + int max_per_pipe_vp_p0 = 0; + int max_per_pipe_vp_p1 = 0; + int temp, p0shift, p1shift; + unsigned int plane_index = 0; + unsigned int i; + unsigned int odm_combine_factor; + unsigned int mpc_combine_factor; + unsigned int num_dpps; + unsigned int num_boundaries; + enum dml2_scaling_transform scaling_transform; + const struct dml2_plane_parameters *plane; + const struct dml2_stream_parameters *stream; + const struct dml2_mcache_surface_allocation *base_allocations = worksheet->validation_result.mcache_allocations; + struct dml2_mcache_surface_allocation *new_allocations = worksheet->cur.config.mcache_allocations; + bool p0pass = false; + bool p1pass = false; + + for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) { + if (!worksheet->orig_dispcfg->plane_descriptors[plane_index].surface.dcc.enable) + continue; + + plane = &worksheet->orig_dispcfg->plane_descriptors[plane_index]; + stream = &worksheet->orig_dispcfg->stream_descriptors[plane->stream_index]; + + odm_combine_factor = worksheet->cur.config.odm_combine_overrides[plane->stream_index] > 0 ? + worksheet->cur.config.odm_combine_overrides[plane->stream_index] : + worksheet->validation_result.mode_support.cfg_support_info.stream_support_info[plane->stream_index].odms_used; + if (odm_combine_factor == 1) { + mpc_combine_factor = worksheet->cur.config.mpc_combine_overrides[plane_index] > 0 ? + worksheet->cur.config.mpc_combine_overrides[plane_index] : + (unsigned int)worksheet->validation_result.mode_support.cfg_support_info.plane_support_info[plane_index].dpps_used; + num_dpps = mpc_combine_factor; + } else { + mpc_combine_factor = 1; + num_dpps = odm_combine_factor; + } + + if (odm_combine_factor > 1) { + max_per_pipe_vp_p0 = plane->surface.plane0.width; + temp = (unsigned int)math_ceil( + plane->composition.scaler_info.plane0.h_ratio * stream->timing.h_active + / odm_combine_factor); + if (temp < max_per_pipe_vp_p0) + max_per_pipe_vp_p0 = temp; + + max_per_pipe_vp_p1 = plane->surface.plane1.width; + temp = (unsigned int)math_ceil( + plane->composition.scaler_info.plane1.h_ratio * stream->timing.h_active + / odm_combine_factor); + if (temp < max_per_pipe_vp_p1) + max_per_pipe_vp_p1 = temp; + } else { + max_per_pipe_vp_p0 = plane->surface.plane0.width / mpc_combine_factor; + max_per_pipe_vp_p1 = plane->surface.plane1.width / mpc_combine_factor; + } + max_per_pipe_vp_p0 += 2 * MAX_PIXEL_OVERLAP; + max_per_pipe_vp_p1 += MAX_PIXEL_OVERLAP; + p0shift = 0; + p1shift = 0; + // The last element in the unshifted boundary array will always be the first pixel outside the + // plane, which means theres no mcache associated with it, so -1 + num_boundaries = + base_allocations[plane_index].num_mcaches_plane0 == 0 ? + 0 : base_allocations[plane_index].num_mcaches_plane0 - 1; + if ((count_elements_in_span(base_allocations[plane_index].mcache_x_offsets_plane0, num_boundaries, + max_per_pipe_vp_p0) <= 1) && (num_boundaries <= num_dpps)) { + p0pass = true; + } + num_boundaries = + base_allocations[plane_index].num_mcaches_plane1 == 0 ? + 0 : base_allocations[plane_index].num_mcaches_plane1 - 1; + if ((count_elements_in_span(base_allocations[plane_index].mcache_x_offsets_plane1, num_boundaries, + max_per_pipe_vp_p1) <= 1) && (num_boundaries <= num_dpps)) { + p1pass = true; + } + if (!p0pass || !p1pass) { + if (odm_combine_factor > 1) { + num_dpps = odm_combine_factor; + scaling_transform = plane->composition.scaling_transform; + } else { + num_dpps = mpc_combine_factor; + scaling_transform = dml2_scaling_transform_fullscreen; + } + if (!p0pass) { + if (plane->composition.viewport.stationary) { + calculate_h_split_for_scaling_transform(plane->surface.plane0.width, + stream->timing.h_active, num_dpps, scaling_transform, + &worksheet->mcache.plane0.pipe_vp_startx[plane_index], + &worksheet->mcache.plane0.pipe_vp_endx[plane_index]); + p0pass = find_shift_for_valid_cache_id_assignment( + base_allocations[plane_index].mcache_x_offsets_plane0, + base_allocations[plane_index].num_mcaches_plane0, + &worksheet->mcache.plane0.pipe_vp_startx[plane_index], + &worksheet->mcache.plane0.pipe_vp_endx[plane_index], num_dpps, + base_allocations[plane_index].shift_granularity.p0, &p0shift); + } + } + if (!p1pass) { + if (plane->composition.viewport.stationary) { + calculate_h_split_for_scaling_transform(plane->surface.plane1.width, + stream->timing.h_active, num_dpps, scaling_transform, + &worksheet->mcache.plane0.pipe_vp_startx[plane_index], + &worksheet->mcache.plane0.pipe_vp_endx[plane_index]); + p1pass = find_shift_for_valid_cache_id_assignment( + base_allocations[plane_index].mcache_x_offsets_plane1, + base_allocations[plane_index].num_mcaches_plane1, + &worksheet->mcache.plane1.pipe_vp_startx[plane_index], + &worksheet->mcache.plane1.pipe_vp_endx[plane_index], num_dpps, + base_allocations[plane_index].shift_granularity.p1, &p1shift); + } + } + } + if (p0pass && p1pass) { + for (i = 0; i < base_allocations[plane_index].num_mcaches_plane0; i++) + new_allocations[plane_index].mcache_x_offsets_plane0[i] = + base_allocations[plane_index].mcache_x_offsets_plane0[i] - p0shift; + for (i = 0; i < base_allocations[plane_index].num_mcaches_plane1; i++) + new_allocations[plane_index].mcache_x_offsets_plane1[i] = + base_allocations[plane_index].mcache_x_offsets_plane1[i] - p1shift; + } + worksheet->mcache.per_plane_status[plane_index] = p0pass && p1pass; + } +} + +static enum dml2_status dml2_pmo_dcn6_stage_optimizer_mcache_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_total_mcache_limit(stage, worksheet)) + return DML2_STATUS_OPTIMIZE_FAIL_MCACHE; + + if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_status(stage, worksheet)) + return DML2_STATUS_OPTIMIZE_FAIL_MCACHE; + + return DML2_STATUS_OK; +} + +static bool dml2_pmo_dcn6_stage_optimizer_mcache_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + if (!worksheet->validation_result.is_mode_support_valid + || !worksheet->validation_result.is_mcache_allocation_valid) + /* validation has failed, stop optimizing further */ + return false; + + if (stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK) + /* optimization is permissible, no need to optimize further */ + return false; + + if (worksheet->mcache.is_default_pipe_usage_attempted) { + if (!dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage(stage, worksheet)) + return false; + } else { + dml2_pmo_dcn5_stage_optimizer_mcache_apply_default_pipe_usage(stage, worksheet); + worksheet->mcache.is_default_pipe_usage_attempted = true; + } + + dml2_pmo_dcn6_stage_optimizer_mcache_decide_shifts(stage, worksheet); + worksheet->cur.unvalidated_change.bits.mcache_allocation = true; + + return true; +} + +void dml2_pmo_dcn6_stage_optimizer_mcache_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn5_stage_optimizer_mcache_init; + stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_mcache_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn6_stage_optimizer_mcache_test_permissibility; +} + +static void dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_utm_soc_bb *utm_soc_bb = stage->pmo->utm_soc_bb; + + DML_LOG_COMP_IF_ENTER(); + worksheet->dcfclk_vmin.max_available_bandwidth_kbps = + utm_soc_bb->vmin_limit.dcfclk_khz * utm_soc_bb->return_bus_width_bytes; + DML_LOG_COMP_IF_EXIT(); +} + +static bool dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + bool should_continue = true; + const struct dml2_utm_soc_bb *utm_soc_bb = stage->pmo->utm_soc_bb; + const struct dml2_sop_table *sop_table = &utm_soc_bb->sop_table; + + DML_LOG_COMP_IF_ENTER(); + if (utm_soc_bb->vmin_limit.dcfclk_khz == 0) { + /* vmin limit for dcfclk is not configured in soc bb */ + should_continue = false; + goto exit; + } + + if (worksheet->cur.config.enable_vmin_dcfclk) { + /* vmin dcfclk is already enabled, nothing else to try */ + should_continue = false; + goto exit; + } + + if (sop_table->sop_optimal_dcfclks_khz[worksheet->cur.config.min_sop_index] <= utm_soc_bb->vmin_limit.dcfclk_khz) { + /* current sop optimal dcfclk is already less than vmin dcfclk */ + should_continue = false; + goto exit; + } + if (worksheet->validation_result.mode_support.bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps + > worksheet->dcfclk_vmin.max_available_bandwidth_kbps) { + /* + * required urgent bandwidth exceeds max bandwidth available, reducing dcfclk will only increase + * bandwidth requirements even more. It is guaranteed to fail bandwidth validation. No need to attempt. + */ + should_continue = false; + goto exit; + } + + worksheet->cur.unvalidated_change.bits.dcfclk_override = true; + worksheet->cur.config.enable_vmin_dcfclk = true; +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return should_continue; +} + +static enum dml2_status dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + enum dml2_status status = worksheet->cur.config.enable_vmin_dcfclk ? + DML2_STATUS_OK : DML2_STATUS_OPTIMIZE_FAIL_VMIN_DCFCLK; + + DML_LOG_COMP_IF_ENTER(); + DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status)); + DML_LOG_COMP_IF_EXIT(); + return status; +} + +void dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_init; + stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_test_permissibility; +} + +static void dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_init( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + DML_LOG_COMP_IF_ENTER(); + worksheet->fclk_ppt_temp_read_pstate.is_attempted = false; + memset(&stage->func_locals->fclk_ppt_temp_read_pstate_optimize, 0, + sizeof(struct dml2_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_locals)); + DML_LOG_COMP_IF_EXIT(); +} + +static bool dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_next( + struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet) +{ + const struct dml2_pmo_instance *pmo = stage->pmo; + const enum dml2_pstate_type pstate_type_list[3] = { + dml2_pstate_type_fclk, + dml2_pstate_type_ppt, + dml2_pstate_type_temp_read, + }; + struct dml2_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_locals *l + = &stage->func_locals->fclk_ppt_temp_read_pstate_optimize; + const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg; + unsigned int stream_index, plane_index, plane_mask; + unsigned int i; + bool modified = false; + + DML_LOG_COMP_IF_ENTER(); + + if (worksheet->fclk_ppt_temp_read_pstate.is_attempted) { + DML_LOG_COMP_IF_EXIT(); + return false; + } + + worksheet->fclk_ppt_temp_read_pstate.is_attempted = true; + + if (display_config->overrides.all_streams_blanked) + goto exit; + + for (i = 0; i < 3; i++) { + dcn6_get_params_for_pstate_type(pmo, worksheet, + pstate_type_list[i], + &l->pstate_allow_delay_us, + &l->pstate_blackout_us, + &l->pstate_watermark_us); + + if (l->pstate_blackout_us <= 0.0) + continue; + + for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) { + plane_mask = 0; + for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) { + if (display_config->plane_descriptors[plane_index].stream_index == stream_index) + set_bit_in_bitfield(&plane_mask, plane_index); + } + + l->per_stream_pstate_method[stream_index] = dml2_pstate_method_vactive; + + dcn6_build_pstate_meta_per_stream(display_config, + pmo->ip_caps, + worksheet, + pstate_type_list[i], + l->pstate_watermark_us, + l->pstate_blackout_us, + l->pstate_allow_delay_us, + stream_index, + plane_mask, + &l->per_stream_pstate_meta[stream_index]); + } + + /* always update the worksheet with latest requirements */ + if (dcn6_update_worksheet_for_pstate_admissibility(worksheet, + l->per_stream_pstate_meta, pstate_type_list[i])) { + modified = true; + DML_LOG_VERBOSE("fclk_ppt_temp_read pstate not admissible with current worksheet, adjusting the reserved vblank time\n"); + } + } + +exit: + DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, modified ? "true" : "false"); + DML_LOG_COMP_IF_EXIT(); + return modified; +} + +static enum dml2_status dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_test_permissibility( + struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet) +{ + (void)stage; + (void)worksheet; + DML_LOG_COMP_IF_ENTER(); + DML_LOG_COMP_IF_EXIT(); + return DML2_STATUS_OK; +} + +void dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage) +{ + stage->pmo = pmo; + stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals; + stage->init = dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_init; + stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_next; + stage->test_permissibility = + dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_test_permissibility; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.h new file mode 100644 index 000000000000..f97a091fec78 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_PMO_DCN6_STAGE_OPTIMIZERS_H__ +#define __DML2_PMO_DCN6_STAGE_OPTIMIZERS_H__ +#include "dml2_internal_shared_types.h" + +void dml2_pmo_dcn6_stage_optimizer_uclk_pstate_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage); +void dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage); +void dml2_pmo_dcn6_stage_optimizer_mcache_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage); +void dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_create(struct dml2_pmo_instance *pmo, + struct dml2_pmo_stage_optimizer *stage); +#endif /* __DML2_PMO_DCN6_STAGE_OPTIMIZERS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.c new file mode 100644 index 000000000000..53ead9f687ac --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + +#include "alternate_pstate_shared_lib.h" +#define CEILING(x, y) (((x + y) - 1)) / (y) +#define FLOOR(x, y) (((x) / (y)) * (y)) +#define ROUND_UP(num, N) (((num) + (N) - 1) / (N)) * (N) // rounds up to nearest multiple of N +#define ROUND_DOWN(num, N) ((num) / (N)) * (N) +#ifndef MAX +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +#endif +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +/** + * *********************************************************************************************************** + * in_circular_range: Helper function to check if a value is within the range of two numbers (circular range) + * + * @return: True if value is within the range of [start, end], where start and end can be a circular range + * (i.e., start > end) + * *********************************************************************************************************** + */ +bool in_circular_range(uint32_t start, uint32_t end, uint32_t value) +{ + if (start <= end) { + return value >= start && value <= end; + } else { + // The range wraps around the buffer. + return value >= start || value <= end; + } +} + +static bool ranges_overlap(uint32_t start1, uint32_t end1, uint32_t start2, uint32_t end2) +{ + return in_circular_range(start1, end1, start2) || in_circular_range(start1, end1, end2) || + in_circular_range(start2, end2, start1) || in_circular_range(start2, end2, end1); +} + +static bool in_circular_range_excl_end(uint32_t start, uint32_t end, uint32_t value) +{ + if (start <= end) { + return value >= start && value < end; + } else { + // The range wraps around the buffer. + return value >= start || value < end; + } +} + +static uint32_t convert_swaths_to_lines( + uint16_t swath_height, + uint32_t num_swaths) +{ + return swath_height * num_swaths; +} + +static uint32_t convert_swath_idx_to_line( + uint16_t swath_height, + uint32_t swath_index, + uint16_t viewport_start, + uint16_t viewport_size, + uint8_t access_direction) +{ + if (access_direction == 0) // access_direction == 0 means top down or bottom up + return FLOOR(swath_height * swath_index + viewport_start, swath_height); + else + return FLOOR(viewport_start + viewport_size - swath_height * swath_index - 1, swath_height); +} + +static uint32_t count_vstartups(uint32_t vstartup, uint32_t current_frame_count, uint32_t current_line_count, + uint32_t target_frame_count, uint32_t target_line_count) +{ + uint32_t vstartup_count = 0; + + if (current_frame_count == target_frame_count) { + if (current_line_count < vstartup && target_line_count > vstartup) + vstartup_count++; + } else { + vstartup_count = target_frame_count > current_frame_count ? + target_frame_count - current_frame_count - 1 : MAX_FRAME_COUNT + target_frame_count - current_frame_count; + if (current_line_count < vstartup) + vstartup_count++; + if (target_line_count >= vstartup) + vstartup_count++; + } + return vstartup_count; +} + +/** + * ******************************************************************************************************************* + * apply_svp1_workaround: Apply workaround required for scenario where svp0_height_next != 0 && svp1_height_next == 0 + * + * The workaround requires: + * - svp1_start_line = svp0_start_line_next + svp0_height_next for access_direction == 0 + * - svp1_start_line = svp0_start_line_next - svp0_height_next for access_direction == 1 + * - From DCN IP team: Do not clamp any value from this calculation, just used the calculated value in svp1_start_line as is + * ****************************************************************************************************************** + */ +static inline void apply_svp1_workaround( + uint32_t svp0_start_sw_idx_next, + uint32_t svp0_num_swaths_next, + uint32_t svp1_num_swaths_next, + uint16_t viewport_start, + uint16_t viewport_size, + uint16_t swath_height, + uint8_t access_direction, + /* output */ + uint16_t *svp1_start_line) +{ + if (svp0_num_swaths_next != 0 && svp1_num_swaths_next == 0) + *svp1_start_line = (uint16_t)convert_swath_idx_to_line(swath_height, svp0_start_sw_idx_next + svp0_num_swaths_next, viewport_start, viewport_size, access_direction); +} + +/** + * *********************************************************************************************************** + * calculate_start_line_and_height_from_swath: Takes output from calculate_hubp_start_end_lines and calculates + * hardware programming values (SVP0/1_START_LINE, SVP0/1_HEIGHT) + * + * This function takes the output from calculate_hubp_start_end_lines and calculates the values for START_LINE + * and HEIGHT that will be used to program directly into hardware. + * *********************************************************************************************************** + */ +static void calculate_start_line_and_height_from_swath( + uint32_t svp0_start_sw_idx_curr, + uint32_t svp0_num_swaths_curr, + uint32_t svp0_start_sw_idx_next, + uint32_t svp0_num_swaths_next, + uint32_t svp1_start_sw_idx_curr, + uint32_t svp1_num_swaths_curr, + uint32_t svp1_start_sw_idx_next, + uint32_t svp1_num_swaths_next, + uint16_t rec_start_dst, + uint16_t svp0_start_dst, + uint32_t svp0_frame_count, + uint32_t program_go_line, + uint32_t program_go_frame_count, + uint16_t viewport_start, + uint16_t viewport_size, + uint8_t access_direction, + uint16_t vstartup, + uint16_t swath_height, + /* output */ + uint16_t *svp0_start_line, + uint16_t *svp0_height, + uint16_t *svp0_height_next, + uint16_t *svp1_start_line, + uint16_t *svp1_height, + uint16_t *svp1_height_next) +{ + *svp0_height = (uint16_t)convert_swaths_to_lines(swath_height, svp0_num_swaths_curr); + *svp0_height_next = (uint16_t)convert_swaths_to_lines(swath_height, svp0_num_swaths_next); + if (*svp0_height != 0) + *svp0_start_line = (uint16_t)convert_swath_idx_to_line(swath_height, svp0_start_sw_idx_curr, viewport_start, viewport_size, access_direction); + else if (*svp0_height_next != 0) + *svp0_start_line = (uint16_t)convert_swath_idx_to_line(swath_height, svp0_start_sw_idx_next, viewport_start, viewport_size, access_direction); + else + *svp0_start_line = 0; + + *svp1_height = (uint16_t)convert_swaths_to_lines(swath_height, svp1_num_swaths_curr); + *svp1_height_next = (uint16_t)convert_swaths_to_lines(swath_height, svp1_num_swaths_next); + if (*svp1_height != 0) + *svp1_start_line = (uint16_t)convert_swath_idx_to_line(swath_height, svp1_start_sw_idx_curr, viewport_start, viewport_size, access_direction); + else if (*svp1_height_next != 0) + *svp1_start_line = (uint16_t)convert_swath_idx_to_line(swath_height, svp1_start_sw_idx_next, viewport_start, viewport_size, access_direction); + else + *svp1_start_line = 0; + + if (*svp0_height == 0 && *svp0_height_next == 0 && *svp1_height == 0 && *svp1_height_next == 0) { + if (count_vstartups(vstartup, program_go_frame_count, program_go_line, svp0_frame_count, svp0_start_dst) > 0) + *svp0_height_next = MAX_SUBVP_HEIGHT; // Special case only needs to assign SVP0 + if (svp0_start_dst > rec_start_dst) + *svp0_start_line = MAX_SUBVP_START_LINE; // Special case only needs to assign SVP0 + } + apply_svp1_workaround( + svp0_start_sw_idx_next, + svp0_num_swaths_next, + svp1_num_swaths_next, + viewport_start, + viewport_size, + swath_height, + access_direction, + svp1_start_line); +} + +/** + * *********************************************************************************************************** + * get_swath_deadlines: Function that produces an array of dst lines associated with each swath deadline + * + * This function returns an array of dst lines associated with each swath deadline, where the index of the array + * represents the swath index. The array is populated based on display config parameters. + * + * Currently this function calculates the deadlines internally. Once the deadline schedule is calculated by DML + * then this function will calculate the deadlines based on the input deadline schedule. + * + * @output: + * swath_array - array of dst lines associated with each swath deadline + * array_size - size of the swath_array, which is equal to the total number of swaths + * *********************************************************************************************************** + */ +void get_swath_deadlines(struct get_swath_deadlines_params *p) +{ + uint32_t i; + uint16_t swath_height = p->chroma_plane ? p->alternate_static_state->swath_height_c[p->plane_index] : p->alternate_static_state->swath_height[p->plane_index]; + uint16_t viewport_start = p->chroma_plane ? p->alternate_static_state->viewport_start_c[p->plane_index] : p->alternate_static_state->viewport_start[p->plane_index]; + uint16_t viewport_size = p->chroma_plane ? p->alternate_static_state->viewport_size_c[p->plane_index] : p->alternate_static_state->viewport_size[p->plane_index]; + uint16_t vblank_end = p->base->vblank_end; + uint16_t prefetch_swaths = p->chroma_plane ? p->alternate_static_state->prefetch_swaths_c[p->plane_index] : p->alternate_static_state->prefetch_swaths[p->plane_index]; + uint16_t total_swaths = p->chroma_plane ? p->alternate_static_state->total_swaths_c[p->plane_index] : p->alternate_static_state->total_swaths[p->plane_index]; + uint32_t prefetch_start_x1000 = (get_prefetch_start_line_x1000(p->vtotal, vblank_end, p->rec_y_start, p->alternate_static_state->dst_y_prefetch_x1000[p->plane_index], p->alternate_static_state->config[p->plane_index].bits.prefetch_relative_vblank, p->alternate_static_state->dst_y_after_scaler[p->plane_index])); + uint16_t pre_hdl_delta_x1000 = p->chroma_plane ? p->alternate_static_state->pre_hdl_delta_c_x1000[p->plane_index] : p->alternate_static_state->pre_hdl_delta_x1000[p->plane_index]; + uint16_t rec_hdl_delta_x1000 = p->chroma_plane ? p->alternate_static_state->rec_hdl_delta_c_x1000[p->plane_index] : p->alternate_static_state->rec_hdl_delta_x1000[p->plane_index]; + uint32_t pre_first_hdl_x1000 = (prefetch_start_x1000 + p->alternate_static_state->dst_y_per_vm_vblank_x1000[p->plane_index] + + 2 * p->alternate_static_state->dst_y_per_row_vblank_x1000[p->plane_index] + + pre_hdl_delta_x1000) % (p->vtotal * 1000); + uint16_t vinit = p->chroma_plane ? p->alternate_static_state->vinit_prefill_c[p->plane_index] : p->alternate_static_state->vinit_prefill[p->plane_index]; + uint16_t vratio_x1000 = p->chroma_plane ? p->alternate_static_state->vratio_c_x1000[p->plane_index] : p->alternate_static_state->vratio_x1000[p->plane_index]; + uint32_t rec_first_hdl_x1000 = p->alternate_static_state->config[p->plane_index].bits.access_direction ? + (viewport_start + viewport_size - vinit - swath_height - FLOOR(viewport_start + viewport_size - vinit, swath_height)) * 1000 * 1000 / vratio_x1000 + (p->base->vblank_end - p->alternate_static_state->dst_y_after_scaler[p->plane_index] + p->rec_y_start) * 1000 + rec_hdl_delta_x1000 : + (FLOOR((viewport_start + vinit - 1), swath_height) - viewport_start - vinit) * 1000 * 1000 / vratio_x1000 + (p->base->vblank_end - p->alternate_static_state->dst_y_after_scaler[p->plane_index] + p->rec_y_start) * 1000 + rec_hdl_delta_x1000; // Should this be CEILING? + + for (i = 0; i < total_swaths; i++) { + if (i < prefetch_swaths) + p->swath_array[i] = (uint16_t)((pre_first_hdl_x1000 + pre_hdl_delta_x1000 * i) / 1000); + else + p->swath_array[i] = (uint16_t)((rec_first_hdl_x1000 + rec_hdl_delta_x1000 * (i - prefetch_swaths)) / 1000); + } + *p->array_size = total_swaths; +} + +/** + * *********************************************************************************************************** + * calculate_swath_deadline_dst: Helper function that calculates hdl dst position given a swath index + * + * @return: dst line associated with the swath deadline + * *********************************************************************************************************** + */ +static uint32_t calculate_swath_deadline_dst( + uint32_t swath_index, + uint32_t prefetch_swaths, + uint32_t pre_first_hdl_x1000, + uint32_t rec_first_hdl_x1000, + uint32_t pre_hdl_delta_x1000, + uint32_t rec_hdl_delta_x1000) +{ + uint32_t dst_line; + + if (swath_index < prefetch_swaths) + dst_line = (swath_index * pre_hdl_delta_x1000 + pre_first_hdl_x1000) / 1000; + else + dst_line = ((swath_index - prefetch_swaths) * rec_hdl_delta_x1000 + rec_first_hdl_x1000) / 1000; + + return dst_line; +} + +/** + * *********************************************************************************************************** + * is_dst_in_next_frame: Helper function which determines if a given dst line is in the "next" frame + * + * This function determines if a given dst line is in the "next" frame. If the target frame count is not + * equal to the current frame count, the dst line is considered in the next frame no matter which line it + * is on. If frame counts are equal, dst line is considered next frame if the current line < frame_boundary, + * and dst_line >= frame_boundary. + * + * @input: + * - curr_otg_line: current otg line + * - curr_frame_count: current otg frame count + * - dst_line: otg line that want to determine is part of "next" frame + * - target_frame_count: target frame count that is used to determine of dst_line is in "next" frame + * - frame_boundary: line boundary which determines current or next frame + * + * @return: True if dst line is considered part of "next" frame, false otherwise + * *********************************************************************************************************** + */ +static bool is_dst_in_next_frame( + uint32_t curr_otg_line, + uint32_t curr_frame_count, + uint32_t dst_line, + uint32_t target_frame_count, + uint16_t frame_boundary) +{ + if (count_vstartups(frame_boundary, curr_frame_count, curr_otg_line, target_frame_count, dst_line) > 0) + return true; + + return false; +} + +/** + * *********************************************************************************************************** + * is_end_swath_in_next_frame: Helper function which determines if dst_line associated with an END swath + * is in the "next" frame + * + * This function determines if a dst line associated with an END swath is in the "next" frame. END swaths + * have special handling for determining current vs. next compared to regular swaths. + * + * If the dst line is beyond the boundary (prefetch start) of the next frame, it is considered part of "next". + * + * @input: + * - curr_frame_count: current otg frame count + * - target_frame_count: target frame count that is used to determine of dst_line is in "next" frame + * - dst_line: otg line that want to determine is part of "next" frame + * - frame_boundary: line boundary which determines current or next frame + * + * @return: True if dst line is considered part of "next" frame, false otherwise + * *********************************************************************************************************** + */ +static bool is_end_swath_in_next_frame( + uint32_t target_frame_count, + uint32_t curr_frame_count, + uint32_t curr_line, + uint32_t dst_line, + uint16_t frame_boundary, + uint16_t vstartup) +{ + uint32_t vstartup_count = count_vstartups(vstartup, curr_frame_count, curr_line, target_frame_count, dst_line); + + if (vstartup_count == 1 && !in_circular_range(vstartup, frame_boundary, dst_line)) + return true; + + if (vstartup_count == 2) + return true; + + return false; +} + +/** + * *********************************************************************************************************** + * is_svp_dst_in_next_frame: Helper function that determines if a swath index (and dst line associated with it) + * is in the current or next frame. The swath index could be a regular or END swath. + * + * @return: True if swath and associated dst line is considered part of "next" frame, false otherwise + * *********************************************************************************************************** + */ +static bool is_svp_dst_in_next_frame( + uint32_t swath_index, + uint16_t pre_swath_incl_boundary, + uint32_t prefetch_swaths, + uint32_t pre_first_hdl_x1000, + uint32_t rec_first_hdl_x1000, + uint32_t pre_hdl_delta_x1000, + uint32_t rec_hdl_delta_x1000, + uint32_t curr_otg_line, + uint32_t curr_frame_count, + uint32_t dst_line, // not necessarily the target otg p-state position (coulud be vp0 end for example) + uint32_t target_frame_count, // target frame for p-state + uint16_t vstartup) +{ + uint32_t dst_deadline; + + dst_deadline = calculate_swath_deadline_dst( + swath_index, + prefetch_swaths, + pre_first_hdl_x1000, + rec_first_hdl_x1000, + pre_hdl_delta_x1000, + rec_hdl_delta_x1000); + + if ((swath_index < END_SWATH_REC && is_dst_in_next_frame(curr_otg_line, curr_frame_count, dst_deadline, target_frame_count, vstartup)) || + (swath_index >= END_SWATH_REC && is_end_swath_in_next_frame(target_frame_count, curr_frame_count, curr_otg_line, dst_line, pre_swath_incl_boundary, vstartup))) { + return true; + } + return false; +} + +/** + * *********************************************************************************************************** + * calculate_swath_idx_from_dst: Helper function that returns a swath index based on a dst line + * + * This function takes in a dst line, and calculates the next or previous swath deadline based on the input + * dst line. The swath index returned is also marked with "current" or "next" (bit 31). + * + * @return: Swath index associated with the input dst line + * *********************************************************************************************************** + */ +static uint32_t calculate_swath_idx_from_dst( + uint32_t curr_otg_line, + uint32_t curr_frame_count, + uint32_t target_frame_count, + uint16_t vstartup, + uint32_t dst_line, + uint16_t pre_swath_incl_boundary, // first pre hdl for svp0 end, prefetch_start for svp1 end + uint32_t rec_swath_incl_boundary, // first rec hdl for svp0 end, earliest_rec_req for svp1 end + uint16_t prefetch_swaths, + uint16_t total_swaths, + uint32_t pre_first_hdl_dst_x1000, + uint16_t pre_last_hdl_dst, + uint32_t earliest_rec_req, + uint32_t rec_first_hdl_dst_x1000, + uint16_t rec_last_hdl_dst, + uint16_t pre_hdl_delta_x1000, + uint16_t rec_hdl_delta_x1000, + bool round_up, + bool start) +{ + (void)earliest_rec_req; + uint32_t swath_index, new_dst_line; + uint32_t dst_line_x1000 = dst_line * 1000; + + if (in_circular_range(pre_swath_incl_boundary, pre_last_hdl_dst, dst_line)) { + if (pre_hdl_delta_x1000 < 1000) + round_up = false; + swath_index = round_up ? CEILING((dst_line_x1000 < pre_first_hdl_dst_x1000 || dst_line > pre_last_hdl_dst ? 0 : dst_line_x1000 - pre_first_hdl_dst_x1000), pre_hdl_delta_x1000) : + (dst_line_x1000 < pre_first_hdl_dst_x1000 || dst_line > pre_last_hdl_dst ? 0 : dst_line_x1000 - pre_first_hdl_dst_x1000) / pre_hdl_delta_x1000; + if (!start && pre_hdl_delta_x1000 < 1000) { + new_dst_line = dst_line_x1000 < pre_first_hdl_dst_x1000 || dst_line > pre_last_hdl_dst ? pre_first_hdl_dst_x1000 + 1000 : dst_line_x1000 + 1000; + swath_index = (new_dst_line - pre_first_hdl_dst_x1000) / pre_hdl_delta_x1000 - 1; + if (swath_index > (uint16_t)(prefetch_swaths - 1)) + swath_index = prefetch_swaths - 1; + } + } else if (in_circular_range(rec_swath_incl_boundary, rec_last_hdl_dst, dst_line) && total_swaths > prefetch_swaths) { + if (rec_hdl_delta_x1000 < 1000) + round_up = false; + swath_index = round_up ? prefetch_swaths + CEILING((dst_line_x1000 < rec_first_hdl_dst_x1000 || dst_line > rec_last_hdl_dst ? 0 : dst_line_x1000 - rec_first_hdl_dst_x1000), rec_hdl_delta_x1000) : + prefetch_swaths + (dst_line_x1000 < rec_first_hdl_dst_x1000 || dst_line > rec_last_hdl_dst ? 0 : dst_line_x1000 - rec_first_hdl_dst_x1000) / rec_hdl_delta_x1000; + if (!start && rec_hdl_delta_x1000 < 1000 && swath_index < (uint16_t)(total_swaths - 1)) { + new_dst_line = dst_line_x1000 < rec_first_hdl_dst_x1000 || dst_line > rec_last_hdl_dst ? rec_first_hdl_dst_x1000 + 1000 : dst_line_x1000 + 1000; + swath_index = prefetch_swaths + (new_dst_line - rec_first_hdl_dst_x1000) / rec_hdl_delta_x1000 - 1; + if (swath_index > (uint16_t)(total_swaths - 1)) + swath_index = total_swaths - 1; + } + } else { + if (in_circular_range(rec_last_hdl_dst, pre_swath_incl_boundary, dst_line) && total_swaths > prefetch_swaths) + swath_index = END_SWATH_REC; + else + swath_index = END_SWATH_PRE; + } + if (is_svp_dst_in_next_frame(swath_index, pre_swath_incl_boundary, prefetch_swaths, pre_first_hdl_dst_x1000, rec_first_hdl_dst_x1000, + pre_hdl_delta_x1000, rec_hdl_delta_x1000, curr_otg_line, curr_frame_count, dst_line, target_frame_count, vstartup)) + swath_index |= NEXT_FRAME_MASK; + + return swath_index; +} + +/** + * *********************************************************************************************************** + * populate_svp_params: Helper function that calculates start_idx and number of swaths for a single svp + * + * This function only calculates parameters for ONE of curr or next for a single svp. This means the caller + * of this function MUST separate the swath indices to be within the span of a single frame (the swath indices + * cannot span across frame boundaries). + * + * @output: svp_start_sw_idx - svp start swath index + * svp_num_swaths - number of swaths spanned by the svp + * *********************************************************************************************************** + */ +static void populate_svp_params( + uint16_t prefetch_swaths, + uint16_t total_swaths, + uint32_t svp_start_swath_idx, + uint32_t svp_end_swath_idx, + /* output */ + uint32_t *svp_start_sw_idx, + uint32_t *svp_num_swaths) +{ + uint32_t start_idx = svp_start_swath_idx & SWATH_MASK; + uint32_t end_idx = svp_end_swath_idx & SWATH_MASK; + + if (start_idx == END_SWATH_REC && end_idx == END_SWATH_PRE) { + *svp_start_sw_idx = 0; + *svp_num_swaths = prefetch_swaths; + } else if (start_idx == END_SWATH_PRE && end_idx == END_SWATH_REC) { + *svp_start_sw_idx = prefetch_swaths; + *svp_num_swaths = total_swaths - prefetch_swaths; + } else if (start_idx == END_SWATH_PRE) { + *svp_start_sw_idx = prefetch_swaths; + *svp_num_swaths = end_idx < END_SWATH_REC ? end_idx - prefetch_swaths + 1 : 0; + } else if (start_idx == END_SWATH_REC) { + *svp_start_sw_idx = 0; + *svp_num_swaths = end_idx < END_SWATH_REC ? end_idx + 1 : 0; + } else if (end_idx == END_SWATH_PRE) { + *svp_start_sw_idx = start_idx; + *svp_num_swaths = start_idx < prefetch_swaths ? prefetch_swaths - start_idx : 0; + } else if (end_idx == END_SWATH_REC) { + *svp_start_sw_idx = start_idx; + *svp_num_swaths = start_idx < total_swaths ? total_swaths - start_idx : 0; + } else { + *svp_start_sw_idx = start_idx; + *svp_num_swaths = end_idx - start_idx + 1; + } +} + +/** + * *********************************************************************************************************** + * populate_start_index_num_swaths_helper: Helper function that calculates svp params for current and next + * + * This function calculates the parameters used for a single svp (which includes both curr and next params). + * This function is responsible for splitting up the start and end swath indices between frames when the + * svp spans a frame boundary, and calling the helper which calculates parameters per curr / next frame. + * + * @output: svp_start_sw_idx_curr - svp start swath index for curr + * svp_num_swaths_curr - number of swaths spanned by the svp curr + * svp_start_sw_idx_next - svp start swath index for next + * svp_num_swaths_next - number of swaths spanned by the svp next + * *********************************************************************************************************** + */ +static void populate_start_index_num_swaths_helper( + uint32_t svp_start_swath_idx, + uint32_t svp_end_swath_idx, + uint16_t prefetch_swaths, + uint16_t total_swaths, + /* output */ + uint32_t *svp_start_sw_idx_curr, + uint32_t *svp_num_swaths_curr, + uint32_t *svp_start_sw_idx_next, + uint32_t *svp_num_swaths_next) +{ + uint32_t svp_start_curr_idx = 0, svp_end_curr_idx = 0, svp_start_next_idx = 0, svp_end_next_idx = 0; + bool start_in_curr = (svp_start_swath_idx & NEXT_FRAME_MASK) == 0; + bool end_in_curr = (svp_end_swath_idx & NEXT_FRAME_MASK) == 0; + + if (!start_in_curr && end_in_curr) { + svp_start_curr_idx = END_SWATH_REC; + svp_end_curr_idx = END_SWATH_REC; + svp_start_next_idx = END_SWATH_REC; + svp_end_next_idx = END_SWATH_REC; + } else if (start_in_curr && !end_in_curr) { + svp_start_curr_idx = svp_start_swath_idx; + svp_end_curr_idx = END_SWATH_REC; + svp_start_next_idx = NEXT_FRAME_MASK; + svp_end_next_idx = svp_end_swath_idx; + } else if (start_in_curr && end_in_curr) { + svp_start_curr_idx = svp_start_swath_idx; + svp_end_curr_idx = svp_end_swath_idx; + } else { + svp_start_next_idx = svp_start_swath_idx; + svp_end_next_idx = svp_end_swath_idx; + } + if (start_in_curr) + populate_svp_params(prefetch_swaths, total_swaths, svp_start_curr_idx, svp_end_curr_idx, svp_start_sw_idx_curr, svp_num_swaths_curr); + if (!end_in_curr) { + populate_svp_params(prefetch_swaths, total_swaths, svp_start_next_idx, svp_end_next_idx, svp_start_sw_idx_next, svp_num_swaths_next); + } +} + +/** + * *********************************************************************************************************** + * calculate_start_index_num_swaths: Helper function that calculates svp params for both vp0 and vp1 + * + * Calculates svp parameters in terms of start swath idx and number of swaths for both vp0 and vp1. + * *********************************************************************************************************** + */ +static void calculate_start_index_num_swaths( + uint32_t svp0_start_swath_idx, + uint32_t svp0_end_swath_idx, + uint32_t svp1_start_swath_idx, + uint32_t svp1_end_swath_idx, + uint16_t prefetch_swaths, + uint16_t total_swaths, + /* output */ + uint32_t *svp0_start_sw_idx_curr, + uint32_t *svp0_num_swaths_curr, + uint32_t *svp0_start_sw_idx_next, + uint32_t *svp0_num_swaths_next, + uint32_t *svp1_start_sw_idx_curr, + uint32_t *svp1_num_swaths_curr, + uint32_t *svp1_start_sw_idx_next, + uint32_t *svp1_num_swaths_next) +{ + *svp0_start_sw_idx_curr = 0; + *svp0_num_swaths_curr = 0; + *svp0_start_sw_idx_next = 0; + *svp0_num_swaths_next = 0; + *svp1_start_sw_idx_curr = 0; + *svp1_num_swaths_curr = 0; + *svp1_start_sw_idx_next = 0; + *svp1_num_swaths_next = 0; + + populate_start_index_num_swaths_helper(svp0_start_swath_idx, svp0_end_swath_idx, prefetch_swaths, total_swaths, + svp0_start_sw_idx_curr, svp0_num_swaths_curr, svp0_start_sw_idx_next, svp0_num_swaths_next); + populate_start_index_num_swaths_helper(svp1_start_swath_idx, svp1_end_swath_idx, prefetch_swaths, total_swaths, + svp1_start_sw_idx_curr, svp1_num_swaths_curr, svp1_start_sw_idx_next, svp1_num_swaths_next); + + if (*svp0_num_swaths_curr == 0 && *svp0_num_swaths_next != 0) + *svp0_start_sw_idx_curr = *svp0_start_sw_idx_next; + if (*svp1_num_swaths_curr == 0 && *svp1_num_swaths_next != 0) + *svp1_start_sw_idx_curr = *svp1_start_sw_idx_next; +} + +/** + * *********************************************************************************************************** + * calculate_hubp_start_end_lines: Function that calculates svp parameters given a target p-state line + * + * This function calculates the the svp parameters (vp0 and vp1 programming) in terms of swath index and number + * of swaths given an otg p-state target. This function should be called per plane. There are 5 steps: + * + * 1. Calculate svp0_start_dst_line and svp0_end_dst_line. + * 2. Calculate svp1_start_dst_line and svp1_end_dst_line + * 3. Calculate svp0_swath_start_idx and svp0_swath_end_idx + * 4. Calculate svp1_swath_start_idx and svp1_swath_end_idx + * 5. Convert to start swath index and number of swaths (for height) + * 6. Convert swath indices to START_LINE and HEIGHT + * + * *********************************************************************************************************** + */ +void calculate_hubp_start_end_lines(struct calculate_hubp_start_end_lines_params *p) +{ + uint16_t svp0_start_dst_line, svp0_end_dst_line, svp1_start_dst_line, svp1_end_dst_line; + uint16_t pre_last_hdl_dst, rec_last_hdl_dst; + uint32_t earliest_rec_req; + uint32_t svp0_start_swath_idx, svp0_end_swath_idx, svp1_start_swath_idx, svp1_end_swath_idx; + uint32_t svp0_end_frame_count, svp1_end_frame_count; + uint32_t program_vp_otg_line; + uint32_t program_vp_frame_count; + uint32_t svp0_start_sw_idx_curr, svp0_num_swaths_curr, svp0_start_sw_idx_next, svp0_num_swaths_next, + svp1_start_sw_idx_curr, svp1_num_swaths_curr, svp1_start_sw_idx_next, svp1_num_swaths_next; + + uint32_t svp0_dst_lines = p->alternate_static_state->svp0_dst_lines; + uint32_t svp1_dst_lines = p->alternate_static_state->svp1_dst_lines; + uint16_t vblank_end = p->base->vblank_end; + uint16_t vstartup = p->alternate_static_state->vstartup_start > vblank_end ? vblank_end - p->alternate_static_state->vstartup_start + p->vtotal : vblank_end - p->alternate_static_state->vstartup_start; + uint16_t vready = (vstartup + p->alternate_static_state->vready_offset_lines) % p->vtotal; + uint16_t req_limit = p->alternate_static_state->svp_req_limit; + uint16_t swath_height = p->chroma_plane ? p->alternate_static_state->swath_height_c[p->plane_index] : p->alternate_static_state->swath_height[p->plane_index]; + uint16_t viewport_start = p->chroma_plane ? p->alternate_static_state->viewport_start_c[p->plane_index] : p->alternate_static_state->viewport_start[p->plane_index]; + uint16_t viewport_size = p->chroma_plane ? p->alternate_static_state->viewport_size_c[p->plane_index] : p->alternate_static_state->viewport_size[p->plane_index]; + uint16_t prefetch_swaths = p->chroma_plane ? p->alternate_static_state->prefetch_swaths_c[p->plane_index] : p->alternate_static_state->prefetch_swaths[p->plane_index]; + uint16_t total_swaths = p->chroma_plane ? p->alternate_static_state->total_swaths_c[p->plane_index] : p->alternate_static_state->total_swaths[p->plane_index]; + uint32_t prefetch_start_x1000 = (get_prefetch_start_line_x1000(p->vtotal, vblank_end, p->rec_y_start, p->alternate_static_state->dst_y_prefetch_x1000[p->plane_index], p->alternate_static_state->config[p->plane_index].bits.prefetch_relative_vblank, p->alternate_static_state->dst_y_after_scaler[p->plane_index])); + int32_t prefetch_end_line = get_prefetch_end_line(p->vtotal, vblank_end, p->rec_y_start, p->alternate_static_state->config[p->plane_index].bits.prefetch_relative_vblank, p->alternate_static_state->dst_y_after_scaler[p->plane_index]); + uint16_t pre_hdl_delta_x1000 = p->chroma_plane ? p->alternate_static_state->pre_hdl_delta_c_x1000[p->plane_index] : p->alternate_static_state->pre_hdl_delta_x1000[p->plane_index]; + uint16_t rec_hdl_delta_x1000 = p->chroma_plane ? p->alternate_static_state->rec_hdl_delta_c_x1000[p->plane_index] : p->alternate_static_state->rec_hdl_delta_x1000[p->plane_index]; + uint32_t pre_first_hdl_x1000 = (prefetch_start_x1000 + p->alternate_static_state->dst_y_per_vm_vblank_x1000[p->plane_index] + + 2 * p->alternate_static_state->dst_y_per_row_vblank_x1000[p->plane_index] + + pre_hdl_delta_x1000) % (p->vtotal * 1000); + uint16_t vinit = p->chroma_plane ? p->alternate_static_state->vinit_prefill_c[p->plane_index] : p->alternate_static_state->vinit_prefill[p->plane_index]; + uint16_t vratio_x1000 = p->chroma_plane ? p->alternate_static_state->vratio_c_x1000[p->plane_index] : p->alternate_static_state->vratio_x1000[p->plane_index]; + uint32_t rec_first_hdl_x1000 = p->alternate_static_state->config[p->plane_index].bits.access_direction ? + (viewport_start + viewport_size - vinit - swath_height - FLOOR(viewport_start + viewport_size - vinit, swath_height)) * 1000 * 1000 / vratio_x1000 + (p->base->vblank_end - p->alternate_static_state->dst_y_after_scaler[p->plane_index] + p->rec_y_start) * 1000 + rec_hdl_delta_x1000 : + (FLOOR((viewport_start + vinit - 1), swath_height) - viewport_start - vinit) * 1000 * 1000 / vratio_x1000 + (p->base->vblank_end - p->alternate_static_state->dst_y_after_scaler[p->plane_index] + p->rec_y_start) * 1000 + rec_hdl_delta_x1000; // Should this be CEILING? + uint32_t vstartup_count; + bool in_vstartup_to_prefetch; + + pre_last_hdl_dst = (uint16_t)((pre_first_hdl_x1000 + pre_hdl_delta_x1000 * (prefetch_swaths - 1)) / 1000); + rec_last_hdl_dst = (uint16_t)((rec_first_hdl_x1000 + rec_hdl_delta_x1000 * (total_swaths - prefetch_swaths - 1)) / 1000); + earliest_rec_req = (uint16_t)((int32_t)(rec_first_hdl_x1000 / 1000 - req_limit) < 0 ? rec_first_hdl_x1000 / 1000 - req_limit + p->vtotal : rec_first_hdl_x1000 / 1000 - req_limit); + + if ((earliest_rec_req < prefetch_start_x1000 / 1000) || (req_limit > rec_first_hdl_x1000 / 1000 && prefetch_start_x1000 / 1000 < vblank_end)) + earliest_rec_req = prefetch_start_x1000 / 1000; + + /* + * ************************************************************************ + * STEP 1 - Calculate svp0_start_dst_line and svp0_end_dst_line + * ************************************************************************ + */ + svp0_start_dst_line = (uint16_t)p->otg_pstate_target; + svp0_end_dst_line = (svp0_start_dst_line + svp0_dst_lines) % p->vtotal; + svp0_end_frame_count = p->target_frame_count + ((svp0_start_dst_line + svp0_dst_lines) >= p->vtotal); + + /* + * ************************************************************************ + * STEP 2 - Calculate svp1_start_dst_line and svp1_end_dst_line + * ************************************************************************ + */ + svp1_start_dst_line = svp0_end_dst_line; + svp1_end_dst_line = (svp1_start_dst_line + svp1_dst_lines) % p->vtotal; + svp1_end_frame_count = svp0_end_frame_count + ((svp1_start_dst_line + svp1_dst_lines) >= p->vtotal); + + /* If the current otg position causes us to need to look 3+ frames ahead, then defer the programming + * to the next VREADY that allows for proper scheduling (this could result in using NEXT as opposed to + * CURR). This is scenario is possible if the current otg position is just before vstartup, and the + * [lead time + svp0 + svp1] time exceeds a frames length. + */ + vstartup_count = count_vstartups(vstartup, p->current_frame_count, p->current_otg_line, svp1_end_frame_count, svp1_end_dst_line); + in_vstartup_to_prefetch = in_circular_range_excl_end(vstartup, prefetch_start_x1000 / 1000, svp1_end_dst_line); + if ((vstartup_count > 2) || (vstartup_count == 2 && !in_vstartup_to_prefetch)) { + program_vp_otg_line = vready; + program_vp_frame_count = svp1_end_frame_count ? svp1_end_frame_count - 1 : MAX_FRAME_COUNT; + if (vstartup > p->base->vblank_start && svp1_end_dst_line < p->base->vblank_start) + program_vp_frame_count = program_vp_frame_count ? program_vp_frame_count - 1 : MAX_FRAME_COUNT; + if (vready < vstartup) + program_vp_frame_count = program_vp_frame_count == MAX_FRAME_COUNT ? 0 : program_vp_frame_count + 1; + if (vstartup_count > 2 && in_vstartup_to_prefetch) + program_vp_frame_count = program_vp_frame_count ? program_vp_frame_count - 1 : MAX_FRAME_COUNT; + } else { + /* There is a slim possibility that by the time the snapshot is taken in macroscheduling, we are within + * the "keepout" region of programming GO (being too close to vstartup). Special handling will not be + * added for this corner case here, since it should be handled at the state machine layer. Enough margin + * should be added such that if the current OTG position is very close to the keepout region at the state + * machine layer (but not in the keepout region), the programming will still be valid if the OTG gets into + * the keepout region by the time the programming is executed. + */ + program_vp_otg_line = p->current_otg_line; + program_vp_frame_count = p->current_frame_count; + } + + /* + * ************************************************************************ + * STEP 3 - Calculate svp0_swath_start_idx and svp0_swath_end_idx + * ************************************************************************ + */ + svp0_start_swath_idx = calculate_swath_idx_from_dst( + program_vp_otg_line, + program_vp_frame_count, + p->target_frame_count, + vstartup, + svp0_start_dst_line, + (uint16_t)(pre_first_hdl_x1000 / 1000), // must match with input param for svp0_end_swath_idx call to ensure end swath curr vs. next flag is calculated correctly + earliest_rec_req, + prefetch_swaths, + total_swaths, + pre_first_hdl_x1000, + pre_last_hdl_dst, + earliest_rec_req, + rec_first_hdl_x1000, + rec_last_hdl_dst, + pre_hdl_delta_x1000, + rec_hdl_delta_x1000, + true, + true); + + svp0_end_swath_idx = calculate_swath_idx_from_dst( + program_vp_otg_line, + program_vp_frame_count, + svp0_end_frame_count, + vstartup, + svp0_end_dst_line, + (uint16_t)(pre_first_hdl_x1000 / 1000), + rec_first_hdl_x1000 / 1000, + prefetch_swaths, + total_swaths, + pre_first_hdl_x1000, + pre_last_hdl_dst, + earliest_rec_req, + rec_first_hdl_x1000, + rec_last_hdl_dst, + pre_hdl_delta_x1000, + rec_hdl_delta_x1000, + false, + false); + /* + * ************************************************************************ + * STEP 4 - Calculate svp1_swath_start_idx and svp1_swath_end_idx + * ************************************************************************ + */ + if ((svp0_end_swath_idx & SWATH_MASK) == END_SWATH_REC) { + /* Special case where svp0_end_swath_idx is END_SWATH_REC but also in next frame. + * In this case svp1_start_swath_idx absolutely cannot be 0 because that would imply + * it is swath index 0 of the NEXT NEXT frame. */ + if ((svp0_end_swath_idx & NEXT_FRAME_MASK) != 0) + svp1_start_swath_idx = svp0_end_swath_idx; + else + svp1_start_swath_idx = NEXT_FRAME_MASK; + } else if ((svp0_end_swath_idx & SWATH_MASK) == END_SWATH_PRE) { + svp1_start_swath_idx = prefetch_swaths | (NEXT_FRAME_MASK & svp0_end_swath_idx); + } else { + svp1_start_swath_idx = (svp0_end_swath_idx & SWATH_MASK) + 1; + if (svp1_start_swath_idx >= total_swaths) + svp1_start_swath_idx = END_SWATH_REC; + svp1_start_swath_idx |= (NEXT_FRAME_MASK & svp0_end_swath_idx); + } + + svp1_end_swath_idx = calculate_swath_idx_from_dst( + program_vp_otg_line, + program_vp_frame_count, + svp1_end_frame_count, + vstartup, + svp1_end_dst_line, + (uint16_t)(prefetch_start_x1000 / 1000), + earliest_rec_req, + prefetch_swaths, + total_swaths, + pre_first_hdl_x1000, + pre_last_hdl_dst, + earliest_rec_req, + rec_first_hdl_x1000, + rec_last_hdl_dst, + pre_hdl_delta_x1000, + rec_hdl_delta_x1000, + true, + false); + + /* + * ************************************************************************ + * STEP 5 - Convert to start swath index and number of swaths (for height) + * ************************************************************************ + */ + calculate_start_index_num_swaths(svp0_start_swath_idx, + svp0_end_swath_idx, svp1_start_swath_idx, svp1_end_swath_idx, prefetch_swaths, total_swaths, + &svp0_start_sw_idx_curr, &svp0_num_swaths_curr, &svp0_start_sw_idx_next, &svp0_num_swaths_next, + &svp1_start_sw_idx_curr, &svp1_num_swaths_curr, &svp1_start_sw_idx_next, &svp1_num_swaths_next); + + /* + * ************************************************************************ + * STEP 6 - Convert swath indices to START_LINE and HEIGHT + * ************************************************************************ + */ + calculate_start_line_and_height_from_swath( + svp0_start_sw_idx_curr, + svp0_num_swaths_curr, + svp0_start_sw_idx_next, + svp0_num_swaths_next, + svp1_start_sw_idx_curr, + svp1_num_swaths_curr, + svp1_start_sw_idx_next, + svp1_num_swaths_next, + p->base->vblank_end + p->rec_y_start, + svp0_start_dst_line, + p->target_frame_count, + program_vp_otg_line, + program_vp_frame_count, + viewport_start, + viewport_size, + p->alternate_static_state->config[p->plane_index].bits.access_direction, + vstartup, + swath_height, + &p->svp0_start_line, + &p->svp0_height, + &p->svp0_height_next, + &p->svp1_start_line, + &p->svp1_height, + &p->svp1_height_next); + + p->svp0_start_dst_line = svp0_start_dst_line; + p->svp0_end_dst_line = svp0_end_dst_line; + p->svp1_start_dst_line = svp1_start_dst_line; + p->svp1_end_dst_line = svp1_end_dst_line; + p->svp_position = ranges_overlap(prefetch_start_x1000 / 1000, prefetch_end_line + p->cursor_size, svp0_start_dst_line, svp0_end_dst_line) || + ranges_overlap(prefetch_start_x1000 / 1000, prefetch_end_line + p->cursor_size, svp1_start_dst_line, svp1_end_dst_line); + if (program_vp_otg_line == p->current_otg_line && program_vp_frame_count == p->current_frame_count) { + p->program_go_line = PROGRAM_GO_IMMEDIATE; + p->program_go_frame_count = PROGRAM_GO_IMMEDIATE; + } else { + p->program_go_line = program_vp_otg_line; + p->program_go_frame_count = program_vp_frame_count; + } +} + +void calculate_copy_from_primary(struct calculate_copy_from_primary_params *p) +{ + p->copy_from_primary = false; + if (!p->flip_pending) + p->copy_from_primary = false; + else { + if (((p->target_frame - p->flip_pending_clear_frame) & MAX_FRAME_COUNT) < MAX_FRAME_COUNT / 2) + p->copy_from_primary = true; + } +} + +static void populate_lsdma_start_end_lines( + const struct dmub_fams2_cmd_alternate_stream_static_state *state, + const struct svp_params *svp, + uint8_t plane_index, + uint8_t dir, + bool chroma, + uint16_t *start_line, + uint16_t *end_line, + uint16_t *start_line_next, + uint16_t *end_line_next) +{ + bool curr = !(svp->height == 0 || svp->height == MAX_SUBVP_HEIGHT); + bool next = !(svp->height_next == 0 || svp->height_next == MAX_SUBVP_HEIGHT); + uint8_t swath_height = chroma ? state->swath_height_c[plane_index] : state->swath_height[plane_index]; + uint16_t viewport_start = chroma ? state->viewport_start_c[plane_index] : state->viewport_start[plane_index]; + uint16_t viewport_size = chroma ? state->viewport_size_c[plane_index] : state->viewport_size[plane_index]; + + if (dir) { + if (curr && !next) { + *start_line = svp->start_line + swath_height - svp->height; + *end_line = svp->start_line + swath_height; + } else if (!curr && next) { + *start_line_next = svp->start_line + swath_height - svp->height_next; + *end_line_next = svp->start_line + swath_height; + } else if (curr && next) { + *end_line = ROUND_UP(viewport_start + viewport_size, swath_height); + *start_line = *end_line - svp->height_next; + *start_line_next = svp->start_line + swath_height - svp->height; + *end_line_next = *start_line_next + svp->height; + } + } else { + if (curr && !next) { + *start_line = svp->start_line; + *end_line = *start_line + svp->height; + } else if (!curr && next) { + *start_line_next = svp->start_line; + *end_line_next = *start_line_next + svp->height_next; + } else if (curr && next) { + *start_line = svp->start_line; + *end_line = *start_line + svp->height; + *start_line_next = FLOOR(viewport_start, swath_height); + *end_line_next = *start_line_next + svp->height_next; + } + } +} + +static uint8_t element_size_to_bytes_per_pixel(uint8_t element_size) +{ + uint8_t bytes_per_pixel = 0; + + switch (element_size) { // 0: 8bpp, 1: 16bpp, 2: 32bpp, 3: 64bpp, 4: 128bpp - Values supported by LSDMA Controller + case 0: + bytes_per_pixel = 1; + break; + case 1: + bytes_per_pixel = 2; + break; + case 2: + bytes_per_pixel = 4; + break; + case 3: + bytes_per_pixel = 8; + break; + case 4: + bytes_per_pixel = 16; + break; + } + + return bytes_per_pixel; +} + +static void populate_lsdma( + const struct dmub_fams2_cmd_alternate_stream_static_state *state, + const struct dmub_rect16 *pipe_vp, + const struct svp_params *svp, + uint8_t plane_index, + uint8_t pipe_idx, + bool chroma, + struct lsdma_outputs *out) +{ + // Common parameters + uint16_t start_line = 0; + uint16_t end_line = 0; + uint16_t start_line_next = 0; + uint16_t end_line_next = 0; + uint8_t dir = state->config[plane_index].bits.access_direction; + + // Tiled specific parameters + uint16_t block_256b_width; + uint16_t macro_tile_width; + uint16_t block_256b_height; + uint8_t vert; + + // Linear specific parameters + uint32_t pitch_byte_size; + uint16_t surface_pitch; + uint16_t round_bytes; + uint16_t req_width; + uint8_t bytes_per_element; + uint8_t element_size; + bool odd_req_width; + + populate_lsdma_start_end_lines(state, svp, plane_index, dir, chroma, &start_line, &end_line, &start_line_next, &end_line_next); + + if (state->swizzle_mode[plane_index] != 0) { + // Tiled mode + vert = state->config[plane_index].bits.vertical_access; + block_256b_width = chroma ? state->block_256b_width_c[plane_index] : state->block_256b_width[plane_index]; + macro_tile_width = chroma ? state->macro_tile_width_c[plane_index] : state->macro_tile_width[plane_index]; + block_256b_height = chroma ? state->block_256b_height_c[plane_index] : state->block_256b_height[plane_index]; + + out->src_x[0][pipe_idx] = FLOOR(vert ? start_line : pipe_vp->x, block_256b_width); + out->src_y[0][pipe_idx] = FLOOR(vert ? pipe_vp->y : start_line, block_256b_height); + out->dst_x[0][pipe_idx] = 0; + out->dst_y[0][pipe_idx] = 0; + out->width[0][pipe_idx] = ROUND_UP(vert ? end_line : pipe_vp->x + pipe_vp->width, block_256b_width) - out->src_x[0][pipe_idx]; + out->height[0][pipe_idx] = ROUND_UP(vert ? pipe_vp->y + pipe_vp->height : end_line, block_256b_height) - out->src_y[0][pipe_idx]; + out->src_x[1][pipe_idx] = vert ? FLOOR(start_line_next, block_256b_width) : out->src_x[0][pipe_idx]; + out->src_y[1][pipe_idx] = vert ? out->src_y[0][pipe_idx] : FLOOR(start_line_next, block_256b_height); + out->dst_x[1][pipe_idx] = vert ? out->width[0][pipe_idx] : 0; + out->dst_y[1][pipe_idx] = vert ? 0 : out->height[0][pipe_idx]; + out->width[1][pipe_idx] = vert ? ROUND_UP(end_line_next, block_256b_width) - out->src_x[1][pipe_idx] : out->width[0][pipe_idx]; + out->height[1][pipe_idx] = vert ? out->height[0][pipe_idx] : ROUND_UP(end_line_next, block_256b_height) - out->src_y[1][pipe_idx]; + out->dst_pitch[pipe_idx] = ROUND_UP(out->width[0][pipe_idx] + (vert ? out->width[1][pipe_idx] : 0), macro_tile_width); + } else { + // Linear mode + element_size = chroma ? state->element_size_c[plane_index] : state->element_size[plane_index]; + surface_pitch = chroma ? state->surface_pitch_c[plane_index] : state->surface_pitch[plane_index]; + bytes_per_element = element_size_to_bytes_per_pixel(element_size); + odd_req_width = ((surface_pitch * bytes_per_element) % 256) != 0 ? true : false; + req_width = (uint16_t)((odd_req_width ? 128 : 256) / bytes_per_element); + + out->src_x[0][pipe_idx] = FLOOR(pipe_vp->x, req_width); + out->src_y[0][pipe_idx] = start_line; // no rounding required + out->dst_x[0][pipe_idx] = 0; + out->dst_y[0][pipe_idx] = 0; + out->width[0][pipe_idx] = ROUND_UP(pipe_vp->x + pipe_vp->width, req_width) - out->src_x[0][pipe_idx]; + out->height[0][pipe_idx] = end_line - out->src_y[0][pipe_idx]; // no rounding required + out->src_x[1][pipe_idx] = out->src_x[0][pipe_idx]; + out->src_y[1][pipe_idx] = start_line_next; // no rounding required + out->dst_x[1][pipe_idx] = 0; + out->dst_y[1][pipe_idx] = out->height[0][pipe_idx]; + out->width[1][pipe_idx] = out->width[0][pipe_idx]; + out->height[1][pipe_idx] = end_line_next - out->src_y[1][pipe_idx]; + + /* Note: If odd_req_width == true (not divisable by 256), then dst_pitch must be rounded up such that + the total bytes (pitch * bytes_per_element) is divisible by 128 but not 256. + Otherwise it must be rounded up such that it is divisible by 256. + If out.dst_pitch already meets this requirement on assigning out.dst_pitch = out.width[0], + */ + pitch_byte_size = out->width[0][pipe_idx] * bytes_per_element; + round_bytes = odd_req_width ? 128 : 256; + pitch_byte_size = ROUND_UP(pitch_byte_size, round_bytes); + + if (odd_req_width && (pitch_byte_size % 256) == 0) + pitch_byte_size += 128; + + out->dst_pitch[pipe_idx] = (uint16_t)(pitch_byte_size / bytes_per_element); + } +} + +static void populate_copy_params(struct calculate_lsdma_copy_params *p, uint8_t svp_idx, uint8_t pipe_idx) +{ + struct dmub_fams2_cmd_alternate_stream_static_state *state = p->alternate_static_state; + struct plane_pipe_rect *pipe_vp = &p->alternate_static_state->pipe_viewports[pipe_idx]; + + populate_lsdma(state, &pipe_vp->luma, &p->svp[svp_idx], p->plane_index, pipe_idx, false, &p->out[svp_idx]); + if (state->config[p->plane_index].bits.is_multi_planar) { + populate_lsdma(state, &pipe_vp->chroma, &p->svp_c[svp_idx], p->plane_index, pipe_idx, true, &p->out_c[svp_idx]); + } +} + +void calculate_lsdma_copy(struct calculate_lsdma_copy_params *p) +{ + uint8_t pipe_mask = p->base->plane_pipe_masks[p->plane_index]; + uint8_t i, j; + + for (i = 0; i < 4; i++) { + if ((pipe_mask & (1 << i))) { + for (j = 0; j < 2; j++) { + populate_copy_params(p, j, i); + } + } + } +} + +/** + * *********************************************************************************************************** + * get_prefetch_start_line: Helper to calculate prefetch start line + * + * To be used by FW to calculate and store the prefetch_start_line so it doesn't need to be computed + * multiple times. + * + * @return: Calculates prefetch start given the input timing params + * *********************************************************************************************************** + */ +int32_t get_prefetch_start_line_x1000(uint32_t vtotal, uint16_t vblank_end, uint16_t recout_y, uint16_t dst_y_prefetch_x1000, uint8_t prefetch_relative_vblank, uint16_t dst_y_after_scaler) +{ + int32_t prefetch_end_x1000 = get_prefetch_end_line(vtotal, vblank_end, recout_y, prefetch_relative_vblank, dst_y_after_scaler) * 1000; + int32_t prefetch_start_line = prefetch_end_x1000 - dst_y_prefetch_x1000; + + /* Case where dst_y_prefetch starts in FP1: prefetch start line could change due to DRR */ + if (prefetch_start_line < 0) + prefetch_start_line = vtotal * 1000 + prefetch_start_line; + + return prefetch_start_line; +} + +/** + * *********************************************************************************************************** + * get_prefetch_end_line: Helper to calculate prefetch end line + * + * To be used by FW to calculate and store the prefetch_end_line so it doesn't need to be computed + * multiple times. + * + * @return: Calculates prefetch end given the input timing params + * *********************************************************************************************************** + */ +int32_t get_prefetch_end_line(uint32_t vtotal, uint16_t vblank_end, uint16_t recout_y, uint8_t prefetch_relative_vblank, uint16_t dst_y_after_scaler) +{ + if (prefetch_relative_vblank) + return (vblank_end - dst_y_after_scaler) % vtotal; + + return (vblank_end + recout_y - dst_y_after_scaler) % vtotal; +} + +/** + * *********************************************************************************************************** + * get_effective_vblank_start: Helper to calculate the effective vblank start + * + * To be used by FW to calculate and store the effective vblank start so it doesn't need to be computed + * multiple times. The effective vblank start is the end of the recout in OTG timing space. + * + * @return: Calculates effective vblank start given the input timing params + * *********************************************************************************************************** + */ +uint16_t get_effective_vblank_start(uint16_t vblank_start, uint16_t vblank_end, uint16_t recout_y, uint16_t recout_height) +{ + return vblank_start - ((vblank_start - vblank_end) - recout_y - recout_height); +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.h new file mode 100644 index 000000000000..8983ea6937a6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/alternate_pstate_shared_lib.h @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + */ + +#ifndef ALT_PSTATE_SHARED_H +#define ALT_PSTATE_SHARED_H + +#include "dmub_cmd.h" + +#define END_SWATH_REC 0xFFFF +#define END_SWATH_PRE 0xFFFFF +#define NEXT_FRAME_MASK 0x80000000 +#define SWATH_MASK 0x7FFFFFFF +#define MAX_FRAME_COUNT 0xFFFFFF +#define PROGRAM_GO_IMMEDIATE 0xFFFFFFFF +#define MAX_SUBVP_HEIGHT 0xFFF +#define MAX_SUBVP_START_LINE 0xFFFF + +struct get_swath_deadlines_params { + /* inputs */ + struct dmub_fams2_cmd_stream_static_base_state *base; + struct dmub_fams2_cmd_alternate_stream_static_state *alternate_static_state; + uint8_t plane_index; + uint16_t vtotal; + uint16_t rec_y_start; + bool chroma_plane; + /* outputs */ + uint16_t *swath_array; // caller must allocate it's own memory for the output + uint16_t *array_size; +}; + +struct calculate_hubp_start_end_lines_params { + /* inputs */ + struct dmub_fams2_cmd_stream_static_base_state *base; + struct dmub_fams2_cmd_alternate_stream_static_state *alternate_static_state; + uint32_t current_otg_line; // [dst line] + uint32_t current_frame_count; // reference frame count + uint32_t otg_pstate_target; // [dst line] + uint32_t target_frame_count; // target frame count that we expect to assert P-State allow + uint16_t vtotal; + uint16_t rec_y_start; + uint8_t plane_index; + uint8_t cursor_size; + bool chroma_plane; + + /* outputs */ + uint16_t svp0_start_line; + uint16_t svp0_height; + uint16_t svp0_height_next; + uint16_t svp1_start_line; + uint16_t svp1_height; + uint16_t svp1_height_next; + uint8_t svp_position; + uint32_t program_go_line; + uint32_t program_go_frame_count; + /* for debug */ + uint16_t svp0_start_dst_line; + uint16_t svp0_end_dst_line; + uint16_t svp1_start_dst_line; + uint16_t svp1_end_dst_line; +}; + +struct calculate_copy_from_primary_params { + /* inputs */ + uint32_t target_frame; + uint32_t flip_pending; + uint32_t flip_pending_clear_frame; + /* outputs */ + bool copy_from_primary; +}; + +struct svp_params { + uint16_t start_line; + uint16_t height; + uint16_t height_next; +}; + +struct calculate_lsdma_copy_params { + /* inputs */ + struct dmub_fams2_cmd_stream_static_base_state *base; + struct dmub_fams2_cmd_alternate_stream_static_state *alternate_static_state; + uint8_t plane_index; + struct svp_params svp[2]; // array of 2 for svp0 and svp1 + struct svp_params svp_c[2]; // array of 2 for svp0 and svp1 + + /* outputs */ + struct lsdma_outputs out[2]; // array of 2 for svp0 and svp1 + struct lsdma_outputs out_c[2]; // array of 2 for svp0 and svp1 +}; + +void calculate_lsdma_copy(struct calculate_lsdma_copy_params *p); + +void calculate_copy_from_primary(struct calculate_copy_from_primary_params *p); + +void get_swath_deadlines(struct get_swath_deadlines_params *p); + +void calculate_hubp_start_end_lines(struct calculate_hubp_start_end_lines_params *p); + +int32_t get_prefetch_start_line_x1000(uint32_t vtotal, uint16_t vblank_end, uint16_t recout_y, uint16_t dst_y_prefetch_x1000, uint8_t prefetch_relative_vblank, uint16_t dst_y_after_scaler); + +int32_t get_prefetch_end_line(uint32_t vtotal, uint16_t vblank_end, uint16_t recout_y, uint8_t prefetch_relative_vblank, uint16_t dst_y_after_scaler); + +uint16_t get_effective_vblank_start(uint16_t vblank_start, uint16_t vblank_end, uint16_t recout_y, uint16_t recout_height); + +bool in_circular_range(uint32_t start, uint32_t end, uint32_t value); + +#endif /* ALT_PSTATE_SHARED_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c new file mode 100644 index 000000000000..ab9cadaf5a60 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_top_utm.h" +#include "dml2_top_soc15.h" +#include "dml2_mcg_factory.h" +#include "dml2_dpmm_factory.h" +#include "dml2_core_factory.h" +#include "dml2_pmo_factory.h" +#include "dml2_utm_soc_bb_factory.h" +#include "dml2_cga_factory.h" +#include "dml2_debug.h" + +static void dml2_top_backup_worksheet(struct dml2_instance *dml, const struct dml2_optimization_worksheet *worksheet) +{ + memcpy(&dml->scratch.worksheet_backup, worksheet, sizeof(struct dml2_optimization_worksheet)); +} + +static void dml2_top_restore_worksheet(struct dml2_instance *dml, struct dml2_optimization_worksheet *worksheet) +{ + memcpy(worksheet, &dml->scratch.worksheet_backup, sizeof(struct dml2_optimization_worksheet)); +} + +static enum dml2_status dml2_top_validate_worksheet(struct dml2_instance *dml, + struct dml2_optimization_worksheet *worksheet) +{ + struct dml2_pmo_instance *pmo = &dml->pmo_instance; + struct dml2_core_instance *core = &dml->core_instance; + struct dml2_display_solution *solution = &dml->scratch.solution; + enum dml2_status status; + + /* + * validate_solution is time consuming. So this sanity check is a performance optimization so we can rule out + * some common configuration problems during PMO optimization without running the whole core sequence. It is a + * subset of validation_solution interface. For example, it may check if pipe usage exceeds total pipe + * availability. Ideally validate_solution should have done a better job of optimizing its performance so this + * PMO interface is not necessary. + */ + status = pmo->optional_sanity_check(pmo, worksheet); + + if (status == DML2_STATUS_OK) { + pmo->convert_worksheet_to_solution(pmo, worksheet, solution); + status = core->validate_solution(core, solution, &worksheet->validation_result); + pmo->clear_pre_validation_states(pmo, worksheet); + } else + worksheet->validation_result.is_mode_support_valid = false; + + return status; +} + +static enum dml2_status dml2_top_perform_stage_optimization(struct dml2_instance *dml, + struct dml2_pmo_stage_optimizer *optimizer, + struct dml2_optimization_worksheet *worksheet) +{ + static const unsigned int MAX_OPTIMIZATION_ITERATIONS = 20; + unsigned int iteration = 0; + enum dml2_status cur_validate_status = DML2_STATUS_OK; + enum dml2_status cur_optimize_status = DML2_STATUS_UNKNOWN; + + /* + * true if the function finds at least one validated worksheet passing permissibility test from current stage + * optimizer. + */ + bool is_permissible_found = false; + + dml2_top_backup_worksheet(dml, worksheet); + /* + * init interface builds the initial states associated with the current stage optimizer into the worksheet. It + * is for state initialization only. It should not apply new optimization or cause changes to current validation + * result. It is safe to assume that the worksheet passed in or exited from this interface is always validated. + */ + optimizer->init(optimizer, worksheet); + /* + * optimize_next interface controls current optimization's stop conditions. When the interface returns false, it + * means the stage optimizer no longer needs to attempt further optimization. The current worksheet should be + * left unmodified. When the interface returns true, it means the stage optimizer applied new optimization to + * the worksheet. DML top will need to validate and test permissibility again. The worksheet passed in is based + * off the optimization decision from last attempt. It may or may not be validated or permissible. It is upto + * DML top to keep track of the last valid permissible worksheet. This interface is also responsible to clear + * corresponding valid bits in worksheet's validation result based on what optimization it gets applied. When + * the valid bits are cleared, it will be revalidated by top. Otherwise, DML top will assume it is safe to skip + * certain re-validations based on the remaining valid bits. Stage optimizers should clear only the necessary + * valid bits based on the optimization applied to speed up the process. + */ + while (optimizer->optimize_next(optimizer, worksheet)) { + if (++iteration >= MAX_OPTIMIZATION_ITERATIONS) { + cur_optimize_status = DML2_STATUS_OPTIMIZE_FAIL_EXCEED_MAX_ITERATION; + break; + } + cur_optimize_status = DML2_STATUS_UNKNOWN; + cur_validate_status = dml2_top_validate_worksheet(dml, worksheet); + if (cur_validate_status == DML2_STATUS_OK) + /* + * test_permissibility interface should only check against current optimizer policy specific + * minimum requirements. Test permissibility result is orthogonal to validation result. It is + * safe to assume the worksheet constant passed in is always validated. The interface checks if + * the validated result fulfills the minimum requirements additionally imposed by current stage + * optimizer in order to consider the current optimization as a potential candidate. Stage + * optimizer may still attempt further optimization even if the current one is permissible. + */ + cur_optimize_status = optimizer->test_permissibility(optimizer, worksheet); + + if (cur_validate_status == DML2_STATUS_OK && cur_optimize_status == DML2_STATUS_OK) { + is_permissible_found = true; + dml2_top_backup_worksheet(dml, worksheet); + } + } + + /* + * When optimize next returns false in the first iteration, test permissibility interface is never called. In + * this case, we need to call it and check if current worksheet is already permissible. + */ + if (cur_validate_status == DML2_STATUS_OK && cur_optimize_status == DML2_STATUS_UNKNOWN) { + cur_optimize_status = optimizer->test_permissibility(optimizer, worksheet); + if (cur_optimize_status == DML2_STATUS_OK) + is_permissible_found = true; + } + + if (cur_validate_status != DML2_STATUS_OK || cur_optimize_status != DML2_STATUS_OK) + dml2_top_restore_worksheet(dml, worksheet); + + DML_ASSERT_MSG(worksheet->validation_result.is_mode_support_valid + && worksheet->validation_result.is_mcache_allocation_valid + && worksheet->validation_result.is_prefetch_valid, + "worksheet must be valid on exit independent from optmization resul!\n"); +// DML_ASSERT_MSG(iteration <= MAX_OPTIMIZATION_ITERATIONS, +// "exceeds max optimization iterations!\n" +// "\t is_permissible_found = %s\n" +// "\t cur_validate_status = %s\n" +// "\t cur_optimize_status = %s\n", +// is_permissible_found ? "true" : "false", +// dml2_status_str(cur_validate_status), +// dml2_status_str(cur_optimize_status)); + + return is_permissible_found ? DML2_STATUS_OK : + (cur_validate_status != DML2_STATUS_OK) ? cur_validate_status : cur_optimize_status; +} + +static enum dml2_status dml2_top_build_and_validate_unoptimized_worksheet( + struct dml2_instance *dml, + const struct dml2_display_cfg *orig_dispcfg, + struct dml2_optimization_worksheet *worksheet) +{ + enum dml2_status status = DML2_STATUS_OK; + struct dml2_pmo_instance *pmo = &dml->pmo_instance; + struct dml2_pmo_stage_optimizer *optimizers[dml2_pmo_stage_index_max]; + int count; + int i; + + if (status == DML2_STATUS_OK) { + pmo->initialize_worksheet(pmo, orig_dispcfg, worksheet); + status = dml2_top_validate_worksheet(dml, worksheet); + } + + if (status == DML2_STATUS_OK) { + count = pmo->get_ordered_mandatory_stage_optimizers(pmo, optimizers); + for (i = 0; i < count; i++) { + status = dml2_top_perform_stage_optimization(dml, optimizers[i], worksheet); + if (status != DML2_STATUS_OK) + break; + } + } + + return status; +} + +static void dml2_top_optimize_worksheet(struct dml2_instance *dml, + struct dml2_optimization_worksheet *worksheet) +{ + struct dml2_pmo_instance *pmo = &dml->pmo_instance; + struct dml2_pmo_stage_optimizer *ordered_optional_stage_optimizers[dml2_pmo_stage_index_max]; + int count; + int i; + + DML_ASSERT(worksheet->validation_result.is_mode_support_valid); + count = pmo->get_ordered_optional_stage_optimizers(pmo, ordered_optional_stage_optimizers); + for (i = 0; i < count; i++) + dml2_top_perform_stage_optimization(dml, ordered_optional_stage_optimizers[i], worksheet); +} + +static enum dml2_status dml2_top_map_minimum_clock_state(struct dml2_instance *dml, + struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming) +{ + bool result; + struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *params = + &dml->scratch.build_mode_programming_locals.dppm_map_mode_params; + struct dml2_dpmm_instance *dpmm = &dml->dpmm_instance; + + if (!dpmm->map_mode_to_soc_dpm) + return DML2_STATUS_OK; + + params->utm_soc_bb = &dml->utm_soc_bb; + params->ip = &dml->core_instance.clean_me_up.mode_lib.ip; + params->solution = solution; + params->programming = programming; + + result = dpmm->map_mode_to_soc_dpm(params); + + return result ? DML2_STATUS_OK : DML2_STATUS_POPULATE_FAIL_MIN_CLOCK_STATE; +} + +static enum dml2_status dml2_top_populate_mode_programming(struct dml2_instance *dml, + const struct dml2_display_solution *solution, + struct dml2_display_cfg_programming *programming) +{ + enum dml2_status status; + struct dml2_core_instance *core = &dml->core_instance; + + status = core->populate_programming(core, solution, programming); + + return status; +} + +static void dml2_top_populate_informative(struct dml2_instance *dml, + enum dml2_status status, + struct dml2_display_cfg_programming *programming) +{ + struct dml2_core_populate_informative_in_out *params = &dml->scratch.build_mode_programming_locals.informative_params; + struct dml2_core_instance *core = &dml->core_instance; + + params->instance = core; + params->programming = programming; + params->mode_is_supported = (status == DML2_STATUS_OK); + params->instance->scratch.mode_programming_locals.mode_programming_ex_params.min_clk_index = + dml->scratch.solution.sop_constraint.dcn5.min_sop_index; + dml->core_instance.populate_informative(params); + + if (status == DML2_STATUS_POPULATE_FAIL_PROGRAMMING || + status == DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_PREFETCH || + status == DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_PREFETCH_URGENT) + programming->informative.failed_mode_programming_prefetch = true; + else if (status == DML2_STATUS_POPULATE_FAIL_PROGRAMMING_DCFCLK) + programming->informative.failed_mode_programming_dcfclk = true; + else if (status == DML2_STATUS_POPULATE_FAIL_PROGRAMMING_FLIP_BANDWIDTH || + status == DML2_STATUS_VALIDATE_FAIL_MODE_SUPPORT_QOS_BANDWIDTH) + programming->informative.failed_mode_programming_flip = true; + else if (status == DML2_STATUS_POPULATE_FAIL_MIN_CLOCK_STATE) + programming->informative.failed_dpmm = true; + else if (status == DML2_STATUS_VALIDATE_FAIL_MCACHE || + status == DML2_STATUS_OPTIMIZE_FAIL_MCACHE || + status == DML2_STATUS_VALIDATE_FAIL_PMO_SANITY_TOTAL_PIPE_USAGE) + programming->informative.failed_mcache_validation = true; + else if (status == DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE) + programming->informative.failed_uclk_pstate = true; + else if (status == DML2_STATUS_VALIDATE_FAIL_PREFETCH) + programming->informative.failed_prefetch = true; +} + +static enum dml2_status dml2_top_build_programming_for_worksheet( + struct dml2_instance *dml, const struct dml2_optimization_worksheet *worksheet, + struct dml2_display_cfg_programming *programming) +{ + enum dml2_status status = DML2_STATUS_OK; + struct dml2_display_solution *solution = &dml->scratch.solution; + struct dml2_pmo_instance *pmo = &dml->pmo_instance; + + memset(programming, 0, sizeof(struct dml2_display_cfg_programming)); + pmo->convert_worksheet_to_solution(pmo, worksheet, solution); + + if (status == DML2_STATUS_OK) + status = dml2_top_map_minimum_clock_state(dml, solution, programming); + + if (status == DML2_STATUS_OK) + status = dml2_top_populate_mode_programming(dml, solution, programming); + + return status; +} + +static bool dml2_top_utm_check_mode_supported(struct dml2_check_mode_supported_in_out *in_out) +{ + enum dml2_status status = DML2_STATUS_OK; + struct dml2_instance *dml = in_out->dml2_instance; + + DML_LOG_TOP_IF_ENTER(); + /* + * Design Policy Note: + * To keep the consistency of check mode support and build mode programming interfaces, the returned status + * should be both based on the unified function below. Check mode support should not make coding assumptions in + * an effort to optimize check mode support performance. If A comes out as the unoptimized worksheet in check + * mode support interface, build mode programming must regenerate A with the same logic and then execute extra + * (i.e A->B->C->D). If check mode support does A->C', while build mode programming does A->B->C->D, then the + * design is considered as compromised. We are making the assumption that C' is always equal to C. This + * assumption can not be universally guaranteed for all DCNs by current design. + */ + status = dml2_top_build_and_validate_unoptimized_worksheet( + dml, in_out->display_config, &dml->scratch.worksheet); + in_out->is_supported = (status == DML2_STATUS_OK); + DML_LOG_INFO("%s exit with %s\n", __func__, dml2_status_str(status)); + DML_LOG_TOP_IF_EXIT(); + + return true; +} + +static bool dml2_top_utm_build_mode_programming(struct dml2_build_mode_programming_in_out *in_out) +{ + struct dml2_instance *dml = in_out->dml2_instance; + struct dml2_optimization_worksheet *worksheet = &dml->scratch.worksheet; + enum dml2_status status = DML2_STATUS_OK; + + DML_LOG_TOP_IF_ENTER(); + if (status == DML2_STATUS_OK) + status = dml2_top_build_and_validate_unoptimized_worksheet(dml, in_out->display_config, worksheet); + + if (status == DML2_STATUS_OK) { + dml2_top_optimize_worksheet(dml, worksheet); + status = dml2_top_build_programming_for_worksheet(dml, worksheet, in_out->programming); + if (status != DML2_STATUS_OK) + DML_LOG_ERROR("build mode programming fails for a supported display config! (%s)\n", + dml2_status_str(status)); + } + + dml2_top_populate_informative(dml, status, in_out->programming); + if (status == DML2_STATUS_OK) + DML_LOG_INFO("%s exit with %s\n", __func__, dml2_status_str(status)); + else + DML_LOG_WARN("%s exit with %s\n", __func__, dml2_status_str(status)); + DML_LOG_TOP_IF_EXIT(); + + return status == DML2_STATUS_OK; +} + +static const struct dml2_top_funcs utm_funcs = { + .check_mode_supported = dml2_top_utm_check_mode_supported, + .build_mode_programming = dml2_top_utm_build_mode_programming, + .build_mcache_programming = dml2_top_soc15_build_mcache_programming, +}; + + +bool dml2_top_utm_initialize_instance(struct dml2_initialize_instance_in_out *in_out) +{ + struct dml2_instance *dml = in_out->dml2_instance; + struct dml2_core_initialize_in_out core_init_params = { 0 }; + struct dml2_pmo_initialize_in_out pmo_init_params = { 0 }; + struct dml2_cga_initialize_in_out cga_init_params = { 0 }; + bool result = true; + + DML_LOG_TOP_IF_ENTER(); + memset(dml, 0, sizeof(struct dml2_instance)); + + if (result) { + memcpy(&dml->ip_caps, &in_out->ip_caps, sizeof(struct dml2_ip_capabilities)); + dml->project_id = in_out->options.project_id; + dml->pmo_options = in_out->options.pmo_options; + dml->funcs = utm_funcs; + } + + if (result) + result = dml2_dpmm_create(in_out->options.project_id, &dml->dpmm_instance); + + if (result) + result = dml2_core_create(in_out->options.project_id, &dml->core_instance); + + if (result) + result = dml2_pmo_create(in_out->options.project_id, &dml->pmo_instance); + + if (result) + result = dml2_utm_soc_bb_create(in_out->options.project_id, &dml->utm_soc_bb, + &in_out->soc_bb, in_out->overrides.explicit_qos_model); + + if (result) + result = dml2_cga_create(in_out->options.project_id, &dml->clock_adjuster); + + if (result) { + core_init_params.project_id = in_out->options.project_id; + core_init_params.instance = &dml->core_instance; + core_init_params.explicit_ip_bb = in_out->overrides.explicit_ip_bb; + core_init_params.explicit_ip_bb_size = in_out->overrides.explicit_ip_bb_size; + core_init_params.ip_caps = &dml->ip_caps; + core_init_params.utm_soc_bb = &dml->utm_soc_bb; + core_init_params.clock_adjuster = &dml->clock_adjuster; + result = dml->core_instance.initialize(&core_init_params); + } + + if (result) { + pmo_init_params.instance = &dml->pmo_instance; + pmo_init_params.ip_caps = &dml->ip_caps; + pmo_init_params.utm_soc_bb = &dml->utm_soc_bb; + pmo_init_params.options = &dml->pmo_options; + dml->pmo_instance.initialize(&pmo_init_params); + } + + if (result && dml->clock_adjuster.initialize) { + cga_init_params.adjuster = &dml->clock_adjuster; + cga_init_params.soc_bb = &in_out->soc_bb; + cga_init_params.ip = &dml->core_instance.clean_me_up.mode_lib.ip; + dml->clock_adjuster.initialize(&cga_init_params); + } + DML_LOG_DEBUG("%s exit with %s\n", __func__, result ? "true":"false"); + DML_LOG_TOP_IF_EXIT(); + + return result; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.h new file mode 100644 index 000000000000..376c4dc25b2d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_TOP_UTM_H__ +#define __DML2_TOP_UTM_H__ +#include "dml2_internal_shared_types.h" +bool dml2_top_utm_initialize_instance(struct dml2_initialize_instance_in_out *in_out); +#endif /* __DML2_TOP_UTM_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.c new file mode 100644 index 000000000000..a58c3944addb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_utm_soc_bb_dcn5.h" +#include "bounding_boxes/dcn5_soc_bb.h" +#include "dml2_debug.h" + +static unsigned int dcn5_sop_table_get_highest_index(const struct dml2_sop_table *table) +{ + return table->model->sop_count - 1; +} + +static void dcn5_sop_table_get_sop_constraint_at_index(const struct dml2_sop_table *table, + unsigned int index, struct dml2_sop_constraint *constraint) +{ + const struct utm_qos_model *model = table->model; + const struct utm_qos_model_dchub_v1 *dchub = model->dchub_v1; + + DML_ASSERT_MSG(index < model->sop_count, "unsupported sop index\n"); + constraint->dcn5.clocks.fclk_khz = model->sops[index].fclk_khz; + constraint->dcn5.clocks.uclk_khz = model->sops[index].uclk_khz; + constraint->dcn5.clocks.dcfclk_khz = dchub->dcfclks_khz[index]; + constraint->dcn5.clocks.socclk_khz = dchub->socclks_khz[index]; + constraint->dcn5.latency.dcn5.urgent_ramp = dchub->latencies[index].urgent_ramp_ps / 1000000.0; + constraint->dcn5.latency.dcn5.t_trip = dchub->latencies[index].t_trip_ps / 1000000.0; + constraint->dcn5.latency.dcn5.meta_trip_to_mem = dchub->latencies[index].meta_trip_to_mem_ps / 1000000.0; + constraint->dcn5.latency.dcn5.max_req_latency_urg = dchub->latencies[index].max_req_latency_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.avg_req_latency_urg = dchub->latencies[index].avg_req_latency_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.max_req_latency_non_urg = dchub->latencies[index].max_req_latency_non_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.avg_req_latency_non_urg = dchub->latencies[index].avg_req_latency_non_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.df_response_time_us = dchub->latencies[index].df_response_time_ps / 1000000.0; + constraint->dcn5.min_available_urgent_bandwidth_KBps = dchub->bandwidths[index].urgent_bandwidth_KBps; + constraint->dcn5.min_sop_index = index; +} + +static bool dcn5_sop_table_is_bandwidth_supported_at_index( + const struct dml2_sop_table *table, const struct dml2_memory_path_bandwidth *bw, unsigned int index) +{ + struct utm_qos_model_dchub_memory_path_bandwidth_v1 qos_bandwidth; + bool result = true; + + qos_bandwidth.nominal_bandwidth_KBps = (uint32_t) bw->dcn5.non_urgent_bandwidth_kbps; + qos_bandwidth.urgent_bandwidth_KBps = (uint32_t) bw->dcn5.urgent_bandwidth_kbps; + + if (!dchub_v1_is_qos_bandwidth_supported_by_sop(table->model, &qos_bandwidth, (uint8_t) index)) + result = false; + + return result; +} + +static void dcn5_sop_table_get_max_sop(const struct dml2_sop_table *table, struct dml2_soc_operating_point *sop) +{ + const struct utm_qos_model *model = table->model; + const struct utm_qos_model_dchub_v1 *dchub = model->dchub_v1; + + DML_ASSERT_MSG(model->sop_count > 0, "utm_qos_model must contain at least 1 sop\n"); + if (model->sop_count > 0) { + sop->fclk_khz = model->sops[model->sop_count-1].fclk_khz; + sop->uclk_khz = model->sops[model->sop_count-1].uclk_khz; + sop->dcfclk_khz = dchub->dcfclks_khz[model->sop_count-1]; + sop->socclk_khz = dchub->socclks_khz[model->sop_count-1]; + } +} + +static void dcn5_sop_table_get_min_sop(const struct dml2_sop_table *table, struct dml2_soc_operating_point *sop) +{ + const struct utm_qos_model *model = table->model; + const struct utm_qos_model_dchub_v1 *dchub = model->dchub_v1; + + sop->fclk_khz = model->sops[0].fclk_khz; + sop->uclk_khz = model->sops[0].uclk_khz; + sop->dcfclk_khz = dchub->dcfclks_khz[0]; + sop->socclk_khz = dchub->socclks_khz[0]; +} + +void dml2_utm_soc_bb_dcn5_build_sop_table(struct dml2_sop_table *table, const struct dml2_utm_soc_bb *utm_soc_bb) +{ + table->get_highest_sop_index = dcn5_sop_table_get_highest_index; + table->get_sop_constraint_at_index = dcn5_sop_table_get_sop_constraint_at_index; + table->is_bw_supported_at_index = dcn5_sop_table_is_bandwidth_supported_at_index; + table->get_max_sop = dcn5_sop_table_get_max_sop; + table->get_min_sop = dcn5_sop_table_get_min_sop; + table->model = &utm_soc_bb->qos_model; + + DML_ASSERT_MSG(table->model->sop_count > 0, "qos_model must contain at least 1 sop\n"); +} + +static void dcn5_copy_utm_qos_model(struct utm_qos_model *dest, struct utm_qos_model_dchub_v1 *dest_dchub, const struct utm_qos_model *src) +{ + *dest = *src; + *dest_dchub = *src->dchub_v1; + dest->dchub_v1 = dest_dchub; +} + +bool dml2_utm_soc_bb_dcn5_create(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model) +{ + const struct utm_qos_model *qos_model = &utm_soc_bb->qos_model; + + if (explicit_qos_model) + dcn5_copy_utm_qos_model(&utm_soc_bb->qos_model, &utm_soc_bb->qos_model_dchub_v1, explicit_qos_model); + else + dcn5_initialize_utm_qos_model(&utm_soc_bb->qos_model, &utm_soc_bb->qos_model_dchub_v1); + + + /* initialize based on soc bb */ + utm_soc_bb->max_dispclk_khz = soc_bb->clk_table.dispclk.clk_values_khz[soc_bb->clk_table.dispclk.num_clk_values - 1]; + utm_soc_bb->max_dppclk_khz = soc_bb->clk_table.dppclk.clk_values_khz[soc_bb->clk_table.dppclk.num_clk_values - 1]; + utm_soc_bb->max_dtbclk_khz = (soc_bb->clk_table.dtbclk.num_clk_values > 0) ? + soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1] : 0; + utm_soc_bb->max_phyclk_khz = (soc_bb->clk_table.phyclk.num_clk_values > 0) ? + soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1] : 0; + utm_soc_bb->max_dscclk_khz = (soc_bb->clk_table.dscclk.num_clk_values > 0) ? + soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1] : 0; + utm_soc_bb->max_phyclk_d18_khz = (soc_bb->clk_table.phyclk_d18.num_clk_values > 0) ? + soc_bb->clk_table.phyclk_d18.clk_values_khz[soc_bb->clk_table.phyclk_d18.num_clk_values - 1] : 0; + utm_soc_bb->max_phyclk_d32_khz = (soc_bb->clk_table.phyclk_d32.num_clk_values > 0) ? + soc_bb->clk_table.phyclk_d32.clk_values_khz[soc_bb->clk_table.phyclk_d32.num_clk_values - 1] : 0; + utm_soc_bb->power_management_parameters = soc_bb->power_management_parameters; + utm_soc_bb->writeback_base_latency_us = soc_bb->qos_parameters.writeback.base_latency_us; + utm_soc_bb->vmin_limit = soc_bb->vmin_limit; + utm_soc_bb->dchub_refclk_mhz = soc_bb->dchub_refclk_mhz; + utm_soc_bb->max_outstanding_reqs = soc_bb->max_outstanding_reqs; + utm_soc_bb->return_bus_width_bytes = soc_bb->return_bus_width_bytes; + utm_soc_bb->phy_downspread_percent = soc_bb->phy_downspread_percent; + utm_soc_bb->dcn_downspread_percent = soc_bb->dcn_downspread_percent; + utm_soc_bb->dispclk_dppclk_vco_speed_mhz = soc_bb->dispclk_dppclk_vco_speed_mhz; + utm_soc_bb->no_dfs = soc_bb->no_dfs; + utm_soc_bb->mem_word_bytes = soc_bb->mem_word_bytes; + utm_soc_bb->num_dcc_mcaches = soc_bb->num_dcc_mcaches; + utm_soc_bb->mcache_size_bytes = soc_bb->mcache_size_bytes; + utm_soc_bb->mcache_line_size_bytes = soc_bb->mcache_line_size_bytes; + utm_soc_bb->lower_bound_bandwidth_dchub = soc_bb->lower_bound_bandwidth_dchub; + + /* initialize based on qos model */ + utm_soc_bb->dram_config.channel_width_bytes = qos_model->socbb.dram_channel_width_bytes; + utm_soc_bb->dram_config.channel_count = qos_model->socbb.dram_channel_count; + utm_soc_bb->dram_config.transactions_per_clock = qos_model->socbb.dram_transactions_per_clock; + utm_soc_bb->max_dtbclk_khz = qos_model->dchub_v1->dcfclks_khz[qos_model->sop_count-1]; + dml2_utm_soc_bb_dcn5_build_sop_table(&utm_soc_bb->sop_table, utm_soc_bb); + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.h new file mode 100644 index 000000000000..4a9deefc2aae --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_UTM_SOC_BB_DCN5_H__ +#define __DML2_UTM_SOC_BB_DCN5_H__ +#include "dml2_internal_shared_types.h" +bool dml2_utm_soc_bb_dcn5_create(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model); +void dml2_utm_soc_bb_dcn5_build_sop_table(struct dml2_sop_table *table, const struct dml2_utm_soc_bb *utm_soc_bb); +#endif /* __DML2_UTM_SOC_BB_DCN5_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c new file mode 100644 index 000000000000..9c6892edca8a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dml2_utm_soc_bb_dcn6.h" +#include "bounding_boxes/dcn6_soc_bb.h" +#include "lib_float_math.h" +#include "dml2_debug.h" + +static unsigned int dcn6_sop_table_get_highest_index(const struct dml2_sop_table *table) +{ + return table->model->sop_count - 1; +} + +static void dcn6_sop_table_get_sop_constraint_at_index(const struct dml2_sop_table *table, + unsigned int index, struct dml2_sop_constraint *constraint) +{ + const struct utm_qos_model *model = table->model; + const struct utm_qos_model_dchub_v2 *dchub = model->dchub_v2; + + DML_ASSERT_MSG(index < model->sop_count, "unsupported sop index\n"); + constraint->dcn5.clocks.fclk_khz = model->sops[index].fclk_khz; + constraint->dcn5.clocks.uclk_khz = model->sops[index].uclk_khz; + constraint->dcn5.clocks.dcfclk_khz = table->sop_optimal_dcfclks_khz[index]; + + constraint->dcn5.latency.dcn5.urgent_ramp = dchub->latencies[index].urgent_ramp_ps / 1000000.0; + constraint->dcn5.latency.dcn5.t_trip = dchub->latencies[index].t_trip_ps / 1000000.0; + constraint->dcn5.latency.dcn5.meta_trip_to_mem = dchub->latencies[index].meta_trip_to_mem_ps / 1000000.0; + constraint->dcn5.latency.dcn5.max_req_latency_urg = dchub->latencies[index].max_req_latency_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.avg_req_latency_urg = dchub->latencies[index].avg_req_latency_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.max_req_latency_non_urg = dchub->latencies[index].max_req_latency_non_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.avg_req_latency_non_urg = dchub->latencies[index].avg_req_latency_non_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.df_response_time_us = dchub->latencies[index].df_response_time_ps / 1000000.0; + constraint->dcn5.min_available_urgent_bandwidth_KBps = table->sop_min_available_urgent_bandwidths_KBps[index]; + constraint->dcn5.min_sop_index = index; +} + +static bool dcn6_sop_table_is_bandwidth_supported_at_index( + const struct dml2_sop_table *table, const struct dml2_memory_path_bandwidth *bw, unsigned int index) +{ + const struct utm_qos_model *model = table->model; + struct utm_qos_model_dchub_memory_path_bandwidth_v2 qos_bandwidth; + bool result = true; + unsigned int highest_sop_index = table->model->sop_count - 1; + + qos_bandwidth.nominal_bandwidth_KBps = (uint32_t) bw->dcn5.non_urgent_bandwidth_kbps; + qos_bandwidth.urgent_bandwidth_KBps = (uint32_t) bw->dcn5.urgent_bandwidth_kbps; + + /* check if the bandwidth is supported by current sop at idle */ + if (!dchub_v2_is_qos_bandwidth_supported_by_sop(table->model, &qos_bandwidth, (uint8_t) index, + model->dchub_v2->max_nominal_utm_budget_percent, model->dchub_v2->max_urgent_utm_budget_percent)) + result = false; + + /* check if the bandwidth is supported by the highest sop at active */ + if (!dchub_v2_is_qos_bandwidth_supported_by_sop(table->model, &qos_bandwidth, (uint8_t) highest_sop_index, + model->dchub_v2->min_nominal_utm_budget_percent, model->dchub_v2->min_urgent_utm_budget_percent)) + result = false; + + return result; +} + +static void dcn6_sop_table_get_max_sop(const struct dml2_sop_table *table, struct dml2_soc_operating_point *sop) +{ + const struct utm_qos_model *model = table->model; + + DML_ASSERT_MSG(model->sop_count > 0, "utm_qos_model must contain at least 1 sop\n"); + if (model->sop_count > 0) { + sop->fclk_khz = model->sops[model->sop_count-1].fclk_khz; + sop->uclk_khz = model->sops[model->sop_count-1].uclk_khz; + sop->dcfclk_khz = table->sop_optimal_dcfclks_khz[model->sop_count - 1]; + } + +} + +static void dcn6_sop_table_get_min_sop(const struct dml2_sop_table *table, struct dml2_soc_operating_point *sop) +{ + const struct utm_qos_model *model = table->model; + + sop->fclk_khz = model->sops[0].fclk_khz; + sop->uclk_khz = model->sops[0].uclk_khz; + sop->dcfclk_khz = table->sop_optimal_dcfclks_khz[0]; +} + +#define SDP_DERATE_PERCENT_NOMINAL 76.0 +#define SDP_DERATE_PERCENT_URGENT 100.0 + +static uint32_t dcn6_sop_table_calculate_optimal_dcfclk_khz( + const struct dml2_sop_table *table, + const struct dml2_utm_soc_bb *utm_soc_bb, + struct utm_qos_model_dchub_memory_path_bandwidth_v2 *total_available_bandwidth) +{ + const struct utm_qos_model *model = table->model; + double nominal_dcfclk_khz; + double urgent_dcfclk_khz; + uint32_t sop_optimal_dcfclk_khz; + + /* calculate the optimal dcfclk based on the available bandwidth */ + nominal_dcfclk_khz = total_available_bandwidth->nominal_bandwidth_KBps + * (model->dchub_v2->max_nominal_utm_budget_percent / 100.0) + / (SDP_DERATE_PERCENT_NOMINAL / 100.0) + / utm_soc_bb->return_bus_width_bytes; + urgent_dcfclk_khz = total_available_bandwidth->urgent_bandwidth_KBps + * (model->dchub_v2->max_urgent_utm_budget_percent / 100.0) + / (SDP_DERATE_PERCENT_URGENT / 100.0) + / utm_soc_bb->return_bus_width_bytes; + sop_optimal_dcfclk_khz = (uint32_t)math_ceil(math_max2(urgent_dcfclk_khz, nominal_dcfclk_khz)); + + /* ensure the calculated dcfclk is within dcfclk limits */ + if (sop_optimal_dcfclk_khz > utm_soc_bb->max_dcfclk_khz) + sop_optimal_dcfclk_khz = utm_soc_bb->max_dcfclk_khz; + else if (sop_optimal_dcfclk_khz < utm_soc_bb->min_dcfclk_khz) + sop_optimal_dcfclk_khz = utm_soc_bb->min_dcfclk_khz; + + return sop_optimal_dcfclk_khz; +} + +static void dml2_utm_soc_bb_dcn6_build_sop_table(struct dml2_sop_table *table, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + unsigned int i; + struct utm_qos_model_dchub_memory_path_bandwidth_v2 total_available_bandwidth; + + table->get_highest_sop_index = dcn6_sop_table_get_highest_index; + table->get_sop_constraint_at_index = dcn6_sop_table_get_sop_constraint_at_index; + table->is_bw_supported_at_index = dcn6_sop_table_is_bandwidth_supported_at_index; + table->get_max_sop = dcn6_sop_table_get_max_sop; + table->get_min_sop = dcn6_sop_table_get_min_sop; + table->model = &utm_soc_bb->qos_model; + + for (i = 0; i < table->model->sop_count; i++) { + dchub_v2_get_sop_total_available_bandwidth_KBps(table->model, &total_available_bandwidth, (uint8_t) i); + table->sop_optimal_dcfclks_khz[i] = + dcn6_sop_table_calculate_optimal_dcfclk_khz(table, utm_soc_bb, &total_available_bandwidth); + table->sop_min_available_urgent_bandwidths_KBps[i] = (uint32_t) math_floor( + total_available_bandwidth.urgent_bandwidth_KBps + * (utm_soc_bb->qos_model.dchub_v2->min_urgent_utm_budget_percent / 100.0)); + } + + DML_ASSERT_MSG(table->model->sop_count > 0, "qos_model must contain at least 1 sop\n"); +} + +/* + * v3 SOP table functions — flat pre-computed bandwidth/latency model + */ + +static unsigned int dcn6_v3_sop_table_get_highest_index(const struct dml2_sop_table *table) +{ + return table->model->dchub_v3->sop_count - 1; +} + +static void dcn6_v3_sop_table_get_sop_constraint_at_index(const struct dml2_sop_table *table, + unsigned int index, struct dml2_sop_constraint *constraint) +{ + const struct utm_qos_model_dchub_v3 *dchub = table->model->dchub_v3; + const struct utm_qos_model_dchub_v3_sop_entry *entry = + &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE][index]; + + DML_ASSERT_MSG(index < dchub->sop_count, "unsupported sop index\n"); + constraint->dcn5.clocks.fclk_khz = 0; + constraint->dcn5.clocks.uclk_khz = 0; + constraint->dcn5.clocks.dcfclk_khz = table->sop_optimal_dcfclks_khz[index]; + + constraint->dcn5.latency.dcn5.urgent_ramp = entry->urgent_ramp_ps / 1000000.0; + constraint->dcn5.latency.dcn5.t_trip = entry->t_trip_ps / 1000000.0; + constraint->dcn5.latency.dcn5.meta_trip_to_mem = entry->meta_trip_to_mem_ps / 1000000.0; + constraint->dcn5.latency.dcn5.max_req_latency_urg = entry->max_req_latency_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.avg_req_latency_urg = entry->avg_req_latency_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.max_req_latency_non_urg = entry->max_req_latency_non_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.avg_req_latency_non_urg = entry->avg_req_latency_non_urg_ps / 1000000.0; + constraint->dcn5.latency.dcn5.df_response_time_us = entry->df_response_time_ps / 1000000.0; + constraint->dcn5.min_available_urgent_bandwidth_KBps = table->sop_min_available_urgent_bandwidths_KBps[index]; + constraint->dcn5.min_sop_index = index; +} + +static bool dcn6_v3_sop_table_is_bandwidth_supported_at_index( + const struct dml2_sop_table *table, const struct dml2_memory_path_bandwidth *bw, unsigned int index) +{ + const struct utm_qos_model_dchub_v3 *dchub = table->model->dchub_v3; + unsigned int highest_sop_index = dchub->sop_count - 1; + const struct utm_qos_model_dchub_v3_sop_entry *idle_entry = + &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE][index]; + const struct utm_qos_model_dchub_v3_sop_entry *active_entry = + &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE][highest_sop_index]; + + if (bw->dcn5.non_urgent_bandwidth_kbps > idle_entry->nominal_bandwidth_KBps + || bw->dcn5.urgent_bandwidth_kbps > idle_entry->urgent_bandwidth_KBps) + return false; + + if (bw->dcn5.non_urgent_bandwidth_kbps > active_entry->nominal_bandwidth_KBps + || bw->dcn5.urgent_bandwidth_kbps > active_entry->urgent_bandwidth_KBps) + return false; + + return true; +} + +static void dcn6_v3_sop_table_get_max_sop(const struct dml2_sop_table *table, struct dml2_soc_operating_point *sop) +{ + const struct utm_qos_model_dchub_v3 *dchub = table->model->dchub_v3; + const struct dml2_utm_soc_bb *utm_soc_bb = + (const struct dml2_utm_soc_bb *)((const char *)table - offsetof(struct dml2_utm_soc_bb, sop_table)); + + DML_ASSERT_MSG(dchub->sop_count > 0, "utm_qos_model must contain at least 1 sop\n"); + sop->fclk_khz = utm_soc_bb->max_fclk_khz; + sop->uclk_khz = utm_soc_bb->max_uclk_khz; + sop->dcfclk_khz = table->sop_optimal_dcfclks_khz[dchub->sop_count - 1]; +} + +static void dcn6_v3_sop_table_get_min_sop(const struct dml2_sop_table *table, struct dml2_soc_operating_point *sop) +{ + const struct dml2_utm_soc_bb *utm_soc_bb = + (const struct dml2_utm_soc_bb *)((const char *)table - offsetof(struct dml2_utm_soc_bb, sop_table)); + + sop->fclk_khz = utm_soc_bb->max_fclk_khz; + sop->uclk_khz = utm_soc_bb->max_uclk_khz; + sop->dcfclk_khz = table->sop_optimal_dcfclks_khz[0]; +} + +static uint32_t dcn6_v3_sop_table_calculate_optimal_dcfclk_khz( + const struct dml2_utm_soc_bb *utm_soc_bb, + const struct utm_qos_model_dchub_v3_sop_entry *idle_entry) +{ + double nominal_dcfclk_khz; + double urgent_dcfclk_khz; + uint32_t optimal; + + nominal_dcfclk_khz = (double)idle_entry->nominal_bandwidth_KBps + / (utm_soc_bb->nominal_sdp_derate_percent / 100.0) + / utm_soc_bb->return_bus_width_bytes; + urgent_dcfclk_khz = (double)idle_entry->urgent_bandwidth_KBps + / (utm_soc_bb->urgent_sdp_derate_percent / 100.0) + / utm_soc_bb->return_bus_width_bytes; + optimal = (uint32_t)math_ceil(math_max2(urgent_dcfclk_khz, nominal_dcfclk_khz)); + + if (optimal > utm_soc_bb->max_dcfclk_khz) + optimal = utm_soc_bb->max_dcfclk_khz; + else if (optimal < utm_soc_bb->min_dcfclk_khz) + optimal = utm_soc_bb->min_dcfclk_khz; + + return optimal; +} + +static void dml2_utm_soc_bb_dcn6_v3_build_sop_table(struct dml2_sop_table *table, + const struct dml2_utm_soc_bb *utm_soc_bb) +{ + const struct utm_qos_model_dchub_v3 *dchub = utm_soc_bb->qos_model.dchub_v3; + unsigned int i; + + table->get_highest_sop_index = dcn6_v3_sop_table_get_highest_index; + table->get_sop_constraint_at_index = dcn6_v3_sop_table_get_sop_constraint_at_index; + table->is_bw_supported_at_index = dcn6_v3_sop_table_is_bandwidth_supported_at_index; + table->get_max_sop = dcn6_v3_sop_table_get_max_sop; + table->get_min_sop = dcn6_v3_sop_table_get_min_sop; + table->model = &utm_soc_bb->qos_model; + + DML_ASSERT_MSG(dchub->sop_count > 0, "qos_model must contain at least 1 sop\n"); + + for (i = 0; i < dchub->sop_count; i++) { + const struct utm_qos_model_dchub_v3_sop_entry *idle_entry = + &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE][i]; + const struct utm_qos_model_dchub_v3_sop_entry *active_entry = + &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE][i]; + + table->sop_optimal_dcfclks_khz[i] = + dcn6_v3_sop_table_calculate_optimal_dcfclk_khz(utm_soc_bb, idle_entry); + table->sop_min_available_urgent_bandwidths_KBps[i] = + active_entry->urgent_bandwidth_KBps; + } +} + +static void dcn6_copy_utm_qos_model(struct utm_qos_model *dest, struct utm_qos_model_dchub_v2 *dest_dchub, const struct utm_qos_model *src) +{ + *dest = *src; + *dest_dchub = *src->dchub_v2; + dest->dchub_v2 = dest_dchub; +} + +static void dcn6_initialize_from_soc_bb(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb) +{ + DML_ASSERT_MSG(soc_bb->clk_table.dcfclk.num_clk_values == 2, "soc_bb must provide min and max dcfclk values!\n"); + + /* initialize based on soc bb */ + utm_soc_bb->max_dispclk_khz = soc_bb->clk_table.dispclk.clk_values_khz[soc_bb->clk_table.dispclk.num_clk_values - 1]; + utm_soc_bb->max_dppclk_khz = soc_bb->clk_table.dppclk.clk_values_khz[soc_bb->clk_table.dppclk.num_clk_values - 1]; + utm_soc_bb->max_dtbclk_khz = (soc_bb->clk_table.dtbclk.num_clk_values > 0) ? + soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1] : 0; + utm_soc_bb->max_phyclk_khz = (soc_bb->clk_table.phyclk.num_clk_values > 0) ? + soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1] : 0; + utm_soc_bb->max_dscclk_khz = (soc_bb->clk_table.dscclk.num_clk_values > 0) ? + soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1] : 0; + utm_soc_bb->max_phyclk_d18_khz = (soc_bb->clk_table.phyclk_d18.num_clk_values > 0) ? + soc_bb->clk_table.phyclk_d18.clk_values_khz[soc_bb->clk_table.phyclk_d18.num_clk_values - 1] : 0; + utm_soc_bb->max_phyclk_d32_khz = (soc_bb->clk_table.phyclk_d32.num_clk_values > 0) ? + soc_bb->clk_table.phyclk_d32.clk_values_khz[soc_bb->clk_table.phyclk_d32.num_clk_values - 1] : 0; + utm_soc_bb->min_socclk_khz = soc_bb->clk_table.socclk.clk_values_khz[0]; + utm_soc_bb->max_dcfclk_khz = soc_bb->clk_table.dcfclk.clk_values_khz[soc_bb->clk_table.dcfclk.num_clk_values - 1]; + utm_soc_bb->min_dcfclk_khz = soc_bb->clk_table.dcfclk.clk_values_khz[0]; + utm_soc_bb->max_uclk_khz = (soc_bb->clk_table.uclk.num_clk_values > 0) ? + soc_bb->clk_table.uclk.clk_values_khz[soc_bb->clk_table.uclk.num_clk_values - 1] : 0; + utm_soc_bb->max_fclk_khz = (soc_bb->clk_table.fclk.num_clk_values > 0) ? + soc_bb->clk_table.fclk.clk_values_khz[soc_bb->clk_table.fclk.num_clk_values - 1] : 0; + utm_soc_bb->dram_config.channel_width_bytes = soc_bb->clk_table.dram_config.channel_width_bytes; + utm_soc_bb->dram_config.channel_count = soc_bb->clk_table.dram_config.channel_count; + utm_soc_bb->dram_config.transactions_per_clock = soc_bb->clk_table.dram_config.transactions_per_clock; + utm_soc_bb->power_management_parameters = soc_bb->power_management_parameters; + utm_soc_bb->writeback_base_latency_us = soc_bb->qos_parameters.writeback.base_latency_us; + utm_soc_bb->vmin_limit = soc_bb->vmin_limit; + utm_soc_bb->dchub_refclk_mhz = soc_bb->dchub_refclk_mhz; + utm_soc_bb->max_outstanding_reqs = soc_bb->max_outstanding_reqs; + utm_soc_bb->return_bus_width_bytes = soc_bb->return_bus_width_bytes; + utm_soc_bb->phy_downspread_percent = soc_bb->phy_downspread_percent; + utm_soc_bb->dcn_downspread_percent = soc_bb->dcn_downspread_percent; + utm_soc_bb->nominal_sdp_derate_percent = SDP_DERATE_PERCENT_NOMINAL; + utm_soc_bb->urgent_sdp_derate_percent = SDP_DERATE_PERCENT_URGENT; + utm_soc_bb->dispclk_dppclk_vco_speed_mhz = soc_bb->dispclk_dppclk_vco_speed_mhz; + utm_soc_bb->no_dfs = soc_bb->no_dfs; + utm_soc_bb->mem_word_bytes = soc_bb->mem_word_bytes; + utm_soc_bb->num_dcc_mcaches = soc_bb->num_dcc_mcaches; + utm_soc_bb->mcache_size_bytes = soc_bb->mcache_size_bytes; + utm_soc_bb->mcache_line_size_bytes = soc_bb->mcache_line_size_bytes; + utm_soc_bb->lower_bound_bandwidth_dchub = soc_bb->lower_bound_bandwidth_dchub; + utm_soc_bb->fraction_of_urgent_bandwidth_nominal_target = soc_bb->fraction_of_urgent_bandwidth_nominal_target; + utm_soc_bb->fraction_of_urgent_bandwidth_flip_target = soc_bb->fraction_of_urgent_bandwidth_flip_target; +} + +static void dcn6_initialize_from_qos_model(struct dml2_utm_soc_bb *utm_soc_bb, + const struct utm_qos_model *qos_model) +{ + utm_soc_bb->dram_config.channel_width_bytes = qos_model->socbb.dram_channel_width_bytes; + utm_soc_bb->dram_config.channel_count = qos_model->socbb.dram_channel_count; + utm_soc_bb->dram_config.transactions_per_clock = qos_model->socbb.dram_transactions_per_clock; +} + +static void dcn6a_initialize_qos_model(struct dml2_utm_soc_bb *utm_soc_bb, + const struct utm_qos_model *explicit_qos_model) +{ + if (explicit_qos_model) + dcn6_copy_utm_qos_model(&utm_soc_bb->qos_model, &utm_soc_bb->qos_model_dchub_v2, explicit_qos_model); + else + dcn6_test_initialize_utm_qos_model(&utm_soc_bb->qos_model, &utm_soc_bb->qos_model_dchub_v2); + +} + +static void dcn6b_initialize_qos_model(struct dml2_utm_soc_bb *utm_soc_bb, + const struct utm_qos_model *explicit_qos_model) +{ + if (explicit_qos_model) + dcn6_copy_utm_qos_model(&utm_soc_bb->qos_model, &utm_soc_bb->qos_model_dchub_v2, explicit_qos_model); + else + dcn6b_test_initialize_utm_qos_model(&utm_soc_bb->qos_model, &utm_soc_bb->qos_model_dchub_v2); +} + +static bool dml2_utm_soc_bb_dcn6a_create_legacy(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model) +{ + dcn6_initialize_from_soc_bb(utm_soc_bb, soc_bb); + dcn6a_initialize_qos_model(utm_soc_bb, explicit_qos_model); + dcn6_initialize_from_qos_model(utm_soc_bb, &utm_soc_bb->qos_model); + dml2_utm_soc_bb_dcn6_build_sop_table(&utm_soc_bb->sop_table, utm_soc_bb); + + return true; +} + +static bool dml2_utm_soc_bb_dcn6b_create_legacy(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model) +{ + dcn6_initialize_from_soc_bb(utm_soc_bb, soc_bb); + dcn6b_initialize_qos_model(utm_soc_bb, explicit_qos_model); + dcn6_initialize_from_qos_model(utm_soc_bb, &utm_soc_bb->qos_model); + dml2_utm_soc_bb_dcn6_build_sop_table(&utm_soc_bb->sop_table, utm_soc_bb); + + return true; +} + +bool dml2_utm_soc_bb_dcn6a_create(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model) +{ + if (explicit_qos_model && explicit_qos_model->version == utm_qos_model_version_v3) { + dcn6_initialize_from_soc_bb(utm_soc_bb, soc_bb); + utm_soc_bb->qos_model_dchub_v3 = *explicit_qos_model->dchub_v3; + utm_soc_bb->qos_model.version = utm_qos_model_version_v3; + utm_soc_bb->qos_model.dchub_v3 = &utm_soc_bb->qos_model_dchub_v3; + dml2_utm_soc_bb_dcn6_v3_build_sop_table(&utm_soc_bb->sop_table, utm_soc_bb); + } else { + return dml2_utm_soc_bb_dcn6a_create_legacy(utm_soc_bb, soc_bb, explicit_qos_model); + } + + return true; +} + +bool dml2_utm_soc_bb_dcn6b_create(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model) +{ + if (explicit_qos_model && explicit_qos_model->version == utm_qos_model_version_v3) { + dcn6_initialize_from_soc_bb(utm_soc_bb, soc_bb); + utm_soc_bb->qos_model_dchub_v3 = *explicit_qos_model->dchub_v3; + utm_soc_bb->qos_model.version = utm_qos_model_version_v3; + utm_soc_bb->qos_model.dchub_v3 = &utm_soc_bb->qos_model_dchub_v3; + dml2_utm_soc_bb_dcn6_v3_build_sop_table(&utm_soc_bb->sop_table, utm_soc_bb); + } else { + return dml2_utm_soc_bb_dcn6b_create_legacy(utm_soc_bb, soc_bb, explicit_qos_model); + } + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.h new file mode 100644 index 000000000000..086b7fffa078 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DML2_UTM_SOC_BB_DCN6_H__ +#define __DML2_UTM_SOC_BB_DCN6_H__ +#include "dml2_internal_shared_types.h" +bool dml2_utm_soc_bb_dcn6a_create(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model); +bool dml2_utm_soc_bb_dcn6b_create(struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *explicit_qos_model); +#endif /* __DML2_UTM_SOC_BB_DCN6_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.c new file mode 100644 index 000000000000..2faf28f753fe --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_utm_soc_bb_factory.h" +#include "dml2_utm_soc_bb_dcn5.h" +#include "dml2_utm_soc_bb_dcn6.h" + +bool dml2_utm_soc_bb_create(enum dml2_project_id project_id, struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *qos_model) +{ + switch (project_id) { + case dml2_project_dcn5x_utm: + return dml2_utm_soc_bb_dcn5_create(utm_soc_bb, soc_bb, qos_model); + case dml2_project_dcn6x_soc_var_a: + return dml2_utm_soc_bb_dcn6a_create(utm_soc_bb, soc_bb, qos_model); + case dml2_project_dcn6x_soc_var_b: + return dml2_utm_soc_bb_dcn6b_create(utm_soc_bb, soc_bb, qos_model); + case dml2_project_dcn4x_utm: + case dml2_project_dcn5x: + case dml2_project_dcn4x_stage1: + case dml2_project_dcn42: + case dml2_project_dcn4x_stage2: + case dml2_project_dcn4x_stage2_auto_drr_svp: + case dml2_project_invalid: + default: + return false; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.h new file mode 100644 index 000000000000..16486a059df6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DML2_UTM_SOC_BB_FACTORY_H__ +#define __DML2_UTM_SOC_BB_FACTORY_H__ + +#include "dml2_internal_shared_types.h" +bool dml2_utm_soc_bb_create(enum dml2_project_id project_id, struct dml2_utm_soc_bb *utm_soc_bb, + const struct dml2_soc_bb *soc_bb, const struct utm_qos_model *qos_model); +#endif /* __DML2_UTM_SOC_BB_FACTORY_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c new file mode 100644 index 000000000000..04cab7d3a5c2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + +#include "dm_services.h" +#include "core_types.h" +#include "reg_helper.h" +#include "dcn401/dcn401_dpp.h" +#include "dcn50_dpp.h" +#include "basics/conversion.h" +#include "dcn30/dcn30_cm_common.h" +#include "dcn32/dcn32_dpp.h" +#include "dcn35/dcn35_dpp.h" + +#define REG(reg)\ + dpp->tf_regs->reg + +#define CTX \ + dpp->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + dpp->tf_shift->field_name, dpp->tf_mask->field_name + +void dpp50_set_pregam_state( + struct dpp *dpp_base, + enum dc_transfer_func_predefined tr, + enum dc_scaling_linearity scaling) +{ + struct dcn50_dpp *dpp = TO_DCN50_DPP(dpp_base); + enum pregam_mode pre_degam_en = PREGAM_DEGAM; + enum degam_lut degamma_lut_selection = 0; + + if (scaling == DC_SCALING_LINEARITY_SOURCE) { + //If scaling in non-linear, apply regamma + REG_SET_2(PRE_GAM, 0, + PRE_GAM_MODE, PREGAM_REGAM, + PRE_REGAM_SELECT, REGAM_20); + } else { + //If scaling in linear, apply degamma based on TF + switch (tr) { + case TRANSFER_FUNCTION_SRGB: + degamma_lut_selection = DEGAM_SRGB; + break; + case TRANSFER_FUNCTION_GAMMA22: + degamma_lut_selection = DEGAM_GAMMA_22; + break; + case TRANSFER_FUNCTION_GAMMA24: + degamma_lut_selection = DEGAM_GAMMA_24; + break; + case TRANSFER_FUNCTION_GAMMA26: + degamma_lut_selection = DEGAM_GAMMA_26; + break; + case TRANSFER_FUNCTION_BT709: + degamma_lut_selection = DEGAM_BT2020; + break; + case TRANSFER_FUNCTION_PQ: + degamma_lut_selection = DEGAM_BT2100PQ; + break; + case TRANSFER_FUNCTION_HLG: + degamma_lut_selection = DEGAM_BT2100HLG; + break; + case TRANSFER_FUNCTION_LINEAR: + case TRANSFER_FUNCTION_UNITY: + case TRANSFER_FUNCTION_HLG12: + default: + pre_degam_en = PREGAM_BYPASS; + break; + } + + REG_SET_2(PRE_GAM, 0, + PRE_GAM_MODE, pre_degam_en, + PRE_DEGAM_SELECT, degamma_lut_selection); + } +} + +void dpp50_dpp_setup( + struct dpp *dpp_base, + enum surface_pixel_format format, + enum expansion_mode mode, + struct dc_csc_transform input_csc_color_matrix, + enum dc_color_space input_color_space, + struct cnv_alpha_2bit_lut *alpha_2bit_lut) +{ + struct dcn50_dpp *dpp = TO_DCN50_DPP(dpp_base); + uint32_t pixel_format = 0; + uint32_t alpha_en = 1; + enum dc_color_space color_space = COLOR_SPACE_SRGB; + enum dcn10_input_csc_select select = INPUT_CSC_SELECT_BYPASS; + uint32_t is_2bit = 0; + uint32_t alpha_plane_enable = 0; + uint32_t dealpha_en = 0, dealpha_ablnd_en = 0; + uint32_t realpha_en = 0, realpha_ablnd_en = 0; + struct out_csc_color_matrix tbl_entry; + int i; + + REG_SET_2(FORMAT_CONTROL, 0, + CNVC_BYPASS, 0, + FORMAT_EXPANSION_MODE, mode); + + REG_UPDATE(FORMAT_CONTROL, FORMAT_CNV16, 0); + REG_UPDATE(FORMAT_CONTROL, CNVC_BYPASS_MSB_ALIGN, 0); + REG_UPDATE(FORMAT_CONTROL, CLAMP_POSITIVE, 0); + REG_UPDATE(FORMAT_CONTROL, CLAMP_POSITIVE_C, 0); + + REG_UPDATE(FORMAT_CONTROL, FORMAT_CROSSBAR_R, 0); + REG_UPDATE(FORMAT_CONTROL, FORMAT_CROSSBAR_G, 1); + REG_UPDATE(FORMAT_CONTROL, FORMAT_CROSSBAR_B, 2); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + pixel_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + pixel_format = 3; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + pixel_format = 8; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + pixel_format = 10; + is_2bit = 1; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P208: + pixel_format = 64; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P208: + pixel_format = 65; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P210: + pixel_format = 66; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P210: + pixel_format = 67; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P212: + pixel_format = 68; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P212: + pixel_format = 69; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + /* Packed 422 formats*/ + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCrYCb: + pixel_format = 72; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCbYCr: + pixel_format = 73; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrYCbY: + pixel_format = 74; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbYCrY: + pixel_format = 75; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCrYCb: + pixel_format = 76; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCbYCr: + pixel_format = 77; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CrYCbY: + pixel_format = 78; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CbYCrY: + pixel_format = 79; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCrYCb: + pixel_format = 80; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCbYCr: + pixel_format = 81; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CrYCbY: + pixel_format = 82; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CbYCrY: + pixel_format = 83; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: + pixel_format = 26; /* ARGB16161616_UNORM */ + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + pixel_format = 24; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + pixel_format = 25; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_AYCrCb8888: + pixel_format = 12; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB111110_FIX: + pixel_format = 112; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_BGR101111_FIX: + pixel_format = 113; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_ACrYCb2101010: + pixel_format = 114; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + is_2bit = 1; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_CrYCbA1010102: + pixel_format = 115; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + is_2bit = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGBE: + pixel_format = 116; + alpha_plane_enable = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA: + pixel_format = 116; + alpha_plane_enable = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB111110_FLOAT: + pixel_format = 118; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_BGR101111_FLOAT: + pixel_format = 119; + alpha_en = 0; + break; + default: + break; + } + + /* Set default color space based on format if none is given. */ + color_space = input_color_space ? input_color_space : color_space; + + if (is_2bit == 1 && alpha_2bit_lut != NULL) { + REG_UPDATE(ALPHA_2BIT_LUT, ALPHA_2BIT_LUT0, alpha_2bit_lut->lut0); + REG_UPDATE(ALPHA_2BIT_LUT, ALPHA_2BIT_LUT1, alpha_2bit_lut->lut1); + REG_UPDATE(ALPHA_2BIT_LUT, ALPHA_2BIT_LUT2, alpha_2bit_lut->lut2); + REG_UPDATE(ALPHA_2BIT_LUT, ALPHA_2BIT_LUT3, alpha_2bit_lut->lut3); + } + + REG_SET_2(CNVC_SURFACE_PIXEL_FORMAT, 0, + CNVC_SURFACE_PIXEL_FORMAT, pixel_format, + CNVC_ALPHA_PLANE_ENABLE, alpha_plane_enable); + REG_UPDATE(FORMAT_CONTROL, FORMAT_CONTROL__ALPHA_EN, alpha_en); + + REG_SET_2(PRE_DEALPHA, 0, + PRE_DEALPHA_EN, dealpha_en, + PRE_DEALPHA_ABLND_EN, dealpha_ablnd_en); + REG_SET_2(PRE_REALPHA, 0, + PRE_REALPHA_EN, realpha_en, + PRE_REALPHA_ABLND_EN, realpha_ablnd_en); + + /* If input adjustment exists, program the ICSC with those values. */ + if (input_csc_color_matrix.enable_adjustment == true) { + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = input_csc_color_matrix.matrix[i]; + + tbl_entry.color_space = input_color_space; + + if (dpp3_should_bypass_post_csc_for_colorspace(color_space)) + select = INPUT_CSC_SELECT_BYPASS; + else + select = INPUT_CSC_SELECT_ICSC; + + dpp3_program_post_csc(dpp_base, color_space, select, + &tbl_entry); + } else { + dpp3_program_post_csc(dpp_base, color_space, select, NULL); + } +} + +static struct dpp_funcs dcn50_dpp_funcs = { + .dpp_program_gamcor_lut = dpp3_program_gamcor_lut, + .dpp_read_state = dpp401_read_state, + .dpp_reset = dpp_reset, + .dpp_set_scaler = dpp401_dscl_set_scaler_manual_scale, + .dpp_get_optimal_number_of_taps = dpp3_get_optimal_number_of_taps, + .dpp_set_gamut_remap = NULL, + .dpp_set_csc_adjustment = NULL, + .dpp_set_csc_default = NULL, + .dpp_program_regamma_pwl = NULL, + .dpp_set_pre_degam = NULL, + .dpp_program_input_lut = NULL, + .dpp_full_bypass = NULL, + .dpp_setup = dpp50_dpp_setup, + .dpp_program_degamma_pwl = NULL, + .dpp_program_cm_dealpha = dpp3_program_cm_dealpha, + .dpp_program_cm_bias = dpp3_program_cm_bias, + + .dpp_program_blnd_lut = NULL, // BLNDGAM is removed completely in DCN3.2 DPP + .dpp_program_shaper_lut = NULL, // CM SHAPER block is removed in DCN3.2 DPP, + //(it is in MPCC, programmable before or after BLND) + .dpp_program_3dlut = NULL, // CM 3DLUT block is removed in DCN3.2 DPP, + //(it is in MPCC, programmable before or after BLND) + + .dpp_program_bias_and_scale = dpp35_program_bias_and_scale_fcnv, + .dpp_cnv_set_alpha_keyer = dpp2_cnv_set_alpha_keyer, + .set_cursor_attributes = dpp401_set_cursor_attributes, + .set_cursor_position = dpp401_set_cursor_position, + .set_optional_cursor_attributes = dpp401_set_optional_cursor_attributes, + .dpp_dppclk_control = dpp1_dppclk_control, + .dpp_set_hdr_multiplier = dpp3_set_hdr_multiplier, + .dpp_read_reg_state = dpp30_read_reg_state, + .set_cursor_matrix = dpp401_set_cursor_matrix, + .dpp_set_pregam_state = dpp50_set_pregam_state, +}; + +static struct dpp_caps dcn50_dpp_cap = { + .dscl_data_proc_format = DSCL_DATA_PRCESSING_FLOAT_FORMAT, + .max_lb_partitions = 63, + .dscl_calc_lb_num_partitions = dscl401_calc_lb_num_partitions, +}; + +bool dpp50_construct( + struct dcn50_dpp *dpp, + struct dc_context *ctx, + uint32_t inst, + const struct dcn50_dpp_registers *tf_regs, + const struct dcn50_dpp_shift *tf_shift, + const struct dcn50_dpp_mask *tf_mask) +{ + dpp->base.ctx = ctx; + + dpp->base.inst = inst; + dpp->base.funcs = &dcn50_dpp_funcs; + dpp->base.caps = &dcn50_dpp_cap; + + dpp->tf_regs = tf_regs; + dpp->tf_shift = tf_shift; + dpp->tf_mask = tf_mask; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h new file mode 100644 index 000000000000..efa76579dc9b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + */ + +#ifndef __DCN50_DPP_H__ +#define __DCN50_DPP_H__ + +#include "dcn20/dcn20_dpp.h" +#include "dcn30/dcn30_dpp.h" +#include "dcn32/dcn32_dpp.h" +#include "dcn401/dcn401_dpp.h" + + +#define TO_DCN50_DPP(dpp)\ + container_of(dpp, struct dcn50_dpp, base) + +#define DPP_REG_LIST_SH_MASK_DCN50_COMMON(mask_sh)\ + DPP_REG_LIST_SH_MASK_DCN401_COMMON(mask_sh), \ + TF_SF(CNVC_CFG0_PRE_GAM, PRE_GAM_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_GAM, PRE_DEGAM_SELECT, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_GAM, PRE_REGAM_SELECT, mask_sh) + +#define DPP_REG_FIELD_LIST_DCN50(type) \ + DPP_REG_FIELD_LIST_DCN401(type); \ + type PRE_GAM_MODE; \ + type PRE_REGAM_SELECT + +#define DPP_REG_VARIABLE_LIST_DCN50 \ + DPP_REG_VARIABLE_LIST_DCN401; \ + uint32_t PRE_GAM; + +struct dcn50_dpp_registers { + DPP_REG_VARIABLE_LIST_DCN50 +}; + +struct dcn50_dpp_shift { + DPP_REG_FIELD_LIST_DCN50(uint8_t); +}; + +struct dcn50_dpp_mask { + DPP_REG_FIELD_LIST_DCN50(uint32_t); +}; + +struct dcn50_dpp { + struct dpp base; + + const struct dcn50_dpp_registers *tf_regs; + const struct dcn50_dpp_shift *tf_shift; + const struct dcn50_dpp_mask *tf_mask; + + const uint16_t *filter_v; + const uint16_t *filter_h; + const uint16_t *filter_v_c; + const uint16_t *filter_h_c; + int lb_pixel_depth_supported; + int lb_memory_size; + int lb_bits_per_entry; + bool is_write_to_ram_a_safe; + struct scaler_data scl_data; + struct pwl_params pwl_data; +}; + +bool dpp50_construct( + struct dcn50_dpp *dpp50, + struct dc_context *ctx, + uint32_t inst, + const struct dcn50_dpp_registers *tf_regs, + const struct dcn50_dpp_shift *tf_shift, + const struct dcn50_dpp_mask *tf_mask); + +void dpp50_dpp_setup( + struct dpp *dpp_base, + enum surface_pixel_format format, + enum expansion_mode mode, + struct dc_csc_transform input_csc_color_matrix, + enum dc_color_space input_color_space, + struct cnv_alpha_2bit_lut *alpha_2bit_lut); + +void dpp50_set_pregam_state( + struct dpp *dpp_base, + enum dc_transfer_func_predefined tr, + enum dc_scaling_linearity scaling); + +#endif /* __DCN50_DPP_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c new file mode 100644 index 000000000000..24a686e94171 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "core_types.h" +#include "reg_helper.h" +#include "basics/conversion.h" +#include "dcn60/dcn60_dpp.h" + +#define REG(reg)\ + dpp->tf_regs->reg + +#define CTX \ + dpp->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + dpp->tf_shift->field_name, dpp->tf_mask->field_name + +void dpp60_dpp_setup( + struct dpp *dpp_base, + enum surface_pixel_format format, + enum expansion_mode mode, + struct dc_csc_transform input_csc_color_matrix, + enum dc_color_space input_color_space, + struct cnv_alpha_2bit_lut *alpha_2bit_lut) +{ + struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base); + uint32_t pixel_format = 8; + uint32_t alpha_en = 1; + enum dc_color_space color_space = COLOR_SPACE_SRGB; + enum dcn10_input_csc_select select = INPUT_CSC_SELECT_BYPASS; + uint32_t is_2bit = 0; + uint32_t dealpha_en = 0, dealpha_ablnd_en = 0; + uint32_t realpha_en = 0, realpha_ablnd_en = 0; + struct out_csc_color_matrix tbl_entry; + int i; + + REG_SET_2(FORMAT_CONTROL, 0, + CNVC_BYPASS, 0, + FORMAT_EXPANSION_MODE, mode); + + REG_UPDATE(FORMAT_CONTROL, FORMAT_CNV16, 0); + REG_UPDATE(FORMAT_CONTROL, CNVC_BYPASS_MSB_ALIGN, 0); + REG_UPDATE(FORMAT_CONTROL, CLAMP_POSITIVE, 0); + REG_UPDATE(FORMAT_CONTROL, CLAMP_POSITIVE_C, 0); + + REG_UPDATE(FORMAT_CONTROL, FORMAT_CROSSBAR_R, 0); + REG_UPDATE(FORMAT_CONTROL, FORMAT_CROSSBAR_G, 1); + REG_UPDATE(FORMAT_CONTROL, FORMAT_CROSSBAR_B, 2); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + pixel_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + pixel_format = 3; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + pixel_format = 8; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + pixel_format = 10; + is_2bit = 1; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P208: + pixel_format = 64; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P208: + pixel_format = 65; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P210: + pixel_format = 66; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P210: + pixel_format = 67; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P212: + pixel_format = 68; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P212: + pixel_format = 69; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + /* Packed 422 formats*/ + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCrYCb: + pixel_format = 72; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCbYCr: + pixel_format = 73; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrYCbY: + pixel_format = 74; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbYCrY: + pixel_format = 75; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCrYCb: + pixel_format = 76; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCbYCr: + pixel_format = 77; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CrYCbY: + pixel_format = 78; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CbYCrY: + pixel_format = 79; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCrYCb: + pixel_format = 80; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCbYCr: + pixel_format = 81; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CrYCbY: + pixel_format = 82; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CbYCrY: + pixel_format = 83; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + // color space and select subjected to change + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: + pixel_format = 26; /* ARGB16161616_UNORM */ + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + pixel_format = 24; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + pixel_format = 25; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_AYCrCb8888: + pixel_format = 12; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB111110_FIX: + pixel_format = 112; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_BGR101111_FIX: + pixel_format = 113; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_ACrYCb2101010: + pixel_format = 114; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + is_2bit = 1; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_CrYCbA1010102: + pixel_format = 115; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + is_2bit = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGBE: + pixel_format = 116; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA: + pixel_format = 116; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB111110_FLOAT: + pixel_format = 118; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_BGR101111_FLOAT: + pixel_format = 119; + alpha_en = 0; + break; + default: + break; + } + + /* Set default color space based on format if none is given. */ + color_space = input_color_space ? input_color_space : color_space; + + if (is_2bit == 1 && alpha_2bit_lut != NULL) { + REG_UPDATE(ALPHA_2BIT_LUT01, ALPHA_2BIT_LUT0, alpha_2bit_lut->lut0); + REG_UPDATE(ALPHA_2BIT_LUT01, ALPHA_2BIT_LUT1, alpha_2bit_lut->lut1); + REG_UPDATE(ALPHA_2BIT_LUT23, ALPHA_2BIT_LUT2, alpha_2bit_lut->lut2); + REG_UPDATE(ALPHA_2BIT_LUT23, ALPHA_2BIT_LUT3, alpha_2bit_lut->lut3); + } + + REG_SET(CNVC_SURFACE_PIXEL_FORMAT, 0, + CNVC_SURFACE_PIXEL_FORMAT, pixel_format); + + REG_UPDATE(FORMAT_CONTROL, FORMAT_CONTROL__ALPHA_EN, alpha_en); + + REG_SET_2(PRE_DEALPHA, 0, + PRE_DEALPHA_EN, dealpha_en, + PRE_DEALPHA_ABLND_EN, dealpha_ablnd_en); + REG_SET_2(PRE_REALPHA, 0, + PRE_REALPHA_EN, realpha_en, + PRE_REALPHA_ABLND_EN, realpha_ablnd_en); + + if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN || + format >= SURFACE_PIXEL_FORMAT_SUBSAMPLE_END || + color_space != COLOR_SPACE_SRGB) { + /* If input adjustment exists, program the ICSC with those values. */ + if (input_csc_color_matrix.enable_adjustment == true) { + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = input_csc_color_matrix.matrix[i]; + + tbl_entry.color_space = input_color_space; + + if (dpp3_should_bypass_post_csc_for_colorspace(color_space)) + select = INPUT_CSC_SELECT_BYPASS; + else + select = INPUT_CSC_SELECT_ICSC; + + dpp3_program_post_csc(dpp_base, color_space, select, + &tbl_entry); + } else { + dpp3_program_post_csc(dpp_base, color_space, select, NULL); + } + } +} + +void dpp60_full_bypass(struct dpp *dpp_base) +{ + struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base); + + /* Input pixel format: ARGB8888 */ + REG_SET(CNVC_SURFACE_PIXEL_FORMAT, 0, + CNVC_SURFACE_PIXEL_FORMAT, 0x8); + + /* Zero expansion */ + REG_SET_3(FORMAT_CONTROL, 0, + CNVC_BYPASS, 0, + FORMAT_CONTROL__ALPHA_EN, 0, + FORMAT_EXPANSION_MODE, 0); + + /* COLOR_KEYER_CONTROL.COLOR_KEYER_EN = 0 this should be default */ + if (dpp->tf_mask->CM_BYPASS_EN) + REG_SET(CM_CONTROL, 0, CM_BYPASS_EN, 1); + else + REG_SET(CM_CONTROL, 0, CM_BYPASS, 1); +} + +static struct dpp_funcs dcn60_dpp_funcs = { + .dpp_program_gamcor_lut = dpp3_program_gamcor_lut, + .dpp_read_state = dpp401_read_state, + .dpp_reset = dpp_reset, + .dpp_set_scaler = dpp60_dscl_set_scaler_manual_scale, + .dpp_get_optimal_number_of_taps = dpp3_get_optimal_number_of_taps, + .dpp_set_pre_degam = NULL, + .dpp_full_bypass = dpp60_full_bypass, + .dpp_setup = dpp60_dpp_setup, + .dpp_program_cm_dealpha = dpp3_program_cm_dealpha, + .dpp_program_cm_bias = dpp3_program_cm_bias, + .dpp_program_bias_and_scale = dpp35_program_bias_and_scale_fcnv, + .dpp_cnv_set_alpha_keyer = dpp2_cnv_set_alpha_keyer, + .set_cursor_attributes = dpp401_set_cursor_attributes, + .set_cursor_position = dpp401_set_cursor_position, + .set_optional_cursor_attributes = dpp401_set_optional_cursor_attributes, + .dpp_dppclk_control = dpp1_dppclk_control, + .dpp_set_hdr_multiplier = dpp3_set_hdr_multiplier, + .set_cursor_matrix = dpp401_set_cursor_matrix, + .dpp_cm_hist_control = dpp42_dpp_cm_hist_control, + .dpp_cm_hist_read = dpp42_dpp_cm_hist_read, + .dpp_read_reg_state = dpp30_read_reg_state, + .dpp_set_pregam_state = dpp50_set_pregam_state, +}; + +static struct dpp_caps dcn60_dpp_cap = { + .dscl_data_proc_format = DSCL_DATA_PRCESSING_FLOAT_FORMAT, + .max_lb_partitions = 63, + .dscl_calc_lb_num_partitions = dscl401_calc_lb_num_partitions, +}; + +bool dpp60_construct( + struct dcn60_dpp *dpp, + struct dc_context *ctx, + uint32_t inst, + const struct dcn60_dpp_registers *tf_regs, + const struct dcn60_dpp_shift *tf_shift, + const struct dcn60_dpp_mask *tf_mask) +{ + dpp->base.ctx = ctx; + + dpp->base.inst = inst; + dpp->base.funcs = &dcn60_dpp_funcs; + dpp->base.caps = &dcn60_dpp_cap; + + dpp->tf_regs = tf_regs; + dpp->tf_shift = tf_shift; + dpp->tf_mask = tf_mask; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.h new file mode 100644 index 000000000000..96ff26a5ed65 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.h @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DCN60_DPP_H__ +#define __DCN60_DPP_H__ + +#include "dcn20/dcn20_dpp.h" +#include "dcn30/dcn30_dpp.h" +#include "dcn32/dcn32_dpp.h" +#include "dcn35/dcn35_dpp.h" +#include "dcn401/dcn401_dpp.h" +#include "dcn42/dcn42_dpp.h" +#include "dcn50/dcn50_dpp.h" + +#define TO_DCN60_DPP(dpp)\ + container_of(dpp, struct dcn60_dpp, base) + +#define DPP_REG_LIST_SH_MASK_DCN60(mask_sh)\ + TF_SF(CM0_CM_MEM_PWR_STATUS, GAMCOR_MEM_PWR_STATE, mask_sh),\ + TF_SF(CM0_CM_DEALPHA, CM_DEALPHA_EN, mask_sh),\ + TF_SF(CM0_CM_DEALPHA, CM_DEALPHA_ABLND, mask_sh),\ + TF_SF(CM0_CM_BIAS_CR_R, CM_BIAS_CR_R, mask_sh),\ + TF_SF(CM0_CM_BIAS_Y_G_CB_B, CM_BIAS_Y_G, mask_sh),\ + TF_SF(CM0_CM_BIAS_Y_G_CB_B, CM_BIAS_CB_B, mask_sh),\ + TF_SF(CM0_CM_MEM_PWR_CTRL, GAMCOR_MEM_PWR_DIS, mask_sh),\ + TF_SF(CM0_CM_MEM_PWR_CTRL, GAMCOR_MEM_PWR_FORCE, mask_sh),\ + TF_SF(CM0_CM_MEM_PWR_CTRL, GAMCOR_MEM_PWR_DIS, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_CONTROL, CM_GAMCOR_MODE, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_CONTROL, CM_GAMCOR_SELECT, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_CONTROL, CM_GAMCOR_PWL_DISABLE, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_CONTROL, CM_GAMCOR_MODE_CURRENT, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_CONTROL, CM_GAMCOR_SELECT_CURRENT, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_LUT_INDEX, CM_GAMCOR_LUT_INDEX, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_LUT_DATA, CM_GAMCOR_LUT_DATA, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_LUT_CONTROL, CM_GAMCOR_LUT_WRITE_COLOR_MASK, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_LUT_CONTROL, CM_GAMCOR_LUT_READ_COLOR_SEL, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_LUT_CONTROL, CM_GAMCOR_LUT_READ_DBG, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_LUT_CONTROL, CM_GAMCOR_LUT_HOST_SEL, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_START_CNTL_B, CM_GAMCOR_RAMA_EXP_REGION_START_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_START_CNTL_B, CM_GAMCOR_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_START_SLOPE_CNTL_B, CM_GAMCOR_RAMA_EXP_REGION_START_SLOPE_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_START_BASE_CNTL_B, CM_GAMCOR_RAMA_EXP_REGION_START_BASE_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_END_CNTL1_B, CM_GAMCOR_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_END_CNTL2_B, CM_GAMCOR_RAMA_EXP_REGION_END_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_END_CNTL2_B, CM_GAMCOR_RAMA_EXP_REGION_END_SLOPE_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_OFFSET_B, CM_GAMCOR_RAMA_OFFSET_B, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_REGION_0_1, CM_GAMCOR_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_REGION_0_1, CM_GAMCOR_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_REGION_0_1, CM_GAMCOR_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + TF_SF(CM0_CM_GAMCOR_RAMA_REGION_0_1, CM_GAMCOR_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP, mask_sh),\ + TF_SF(DSCL0_OTG_H_BLANK, OTG_H_BLANK_START, mask_sh),\ + TF_SF(DSCL0_OTG_H_BLANK, OTG_H_BLANK_END, mask_sh),\ + TF_SF(DSCL0_OTG_V_BLANK, OTG_V_BLANK_START, mask_sh),\ + TF_SF(DSCL0_OTG_V_BLANK, OTG_V_BLANK_END, mask_sh),\ + TF2_SF(DSCL0, LB_DATA_FORMAT__ALPHA_EN, mask_sh),\ + TF_SF(DSCL0_LB_MEMORY_CTRL, MEMORY_CONFIG, mask_sh),\ + TF_SF(DSCL0_LB_MEMORY_CTRL, LB_MAX_PARTITIONS, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_FRAC_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_NUM_PIPE, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_PIPE_ID, mask_sh),\ + TF_SF(DSCL0_DSCL_CONTROL, SCL_BOUNDARY_MODE, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_V_NUM_TAPS, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_H_NUM_TAPS, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_V_NUM_TAPS_C, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_H_NUM_TAPS_C, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_SELECT, SCL_COEF_RAM_TAP_PAIR_IDX, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_SELECT, SCL_COEF_RAM_PHASE, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_SELECT, SCL_COEF_RAM_FILTER_TYPE, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_EVEN_TAP_COEF, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_EVEN_TAP_COEF_EN, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_ODD_TAP_COEF, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_ODD_TAP_COEF_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_H_2TAP_HARDCODE_COEF_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_H_2TAP_SHARP_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_H_2TAP_SHARP_FACTOR, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_V_2TAP_HARDCODE_COEF_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_V_2TAP_SHARP_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_V_2TAP_SHARP_FACTOR, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, SCL_COEF_RAM_SELECT, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, DSCL_MODE, mask_sh),\ + TF_SF(DSCL0_RECOUT_START, RECOUT_START_X, mask_sh),\ + TF_SF(DSCL0_RECOUT_START, RECOUT_START_Y, mask_sh),\ + TF_SF(DSCL0_RECOUT_SIZE, RECOUT_WIDTH, mask_sh),\ + TF_SF(DSCL0_RECOUT_SIZE, RECOUT_HEIGHT, mask_sh),\ + TF_SF(DSCL0_MPC_SIZE, MPC_WIDTH, mask_sh),\ + TF_SF(DSCL0_MPC_SIZE, MPC_HEIGHT, mask_sh),\ + TF_SF(DSCL0_SCL_BLACK_COLOR, SCL_BLACK_COLOR_RGB_Y, mask_sh),\ + TF_SF(DSCL0_SCL_BLACK_COLOR, SCL_BLACK_COLOR_CBCR, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_SCALE_RATIO, SCL_H_SCALE_RATIO, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_SCALE_RATIO, SCL_V_SCALE_RATIO, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_SCALE_RATIO_C, SCL_H_SCALE_RATIO_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_SCALE_RATIO_C, SCL_V_SCALE_RATIO_C, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_FRAC, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_INT, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT_C, SCL_H_INIT_FRAC_C, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT_C, SCL_H_INIT_INT_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_FRAC, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_INT, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_C, SCL_V_INIT_FRAC_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_C, SCL_V_INIT_INT_C, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, SCL_CHROMA_COEF_MODE, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, SCL_COEF_RAM_SELECT_CURRENT, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_DEALPHA, PRE_DEALPHA_EN, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_DEALPHA, PRE_DEALPHA_ABLND_EN, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_REALPHA, PRE_REALPHA_EN, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_REALPHA, PRE_REALPHA_ABLND_EN, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_GAM, PRE_GAM_MODE, mask_sh),\ + TF_SF(CNVC_CFG0_PRE_GAM, PRE_DEGAM_SELECT, mask_sh),\ + TF_SF(CNVC_CFG0_PRE_GAM, PRE_REGAM_SELECT, mask_sh),\ + TF_SF(CNVC_CFG0_PRE_CSC_MODE, PRE_CSC_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_CSC_MODE, PRE_CSC_MODE_CURRENT, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_CSC_C11_C12, PRE_CSC_C11, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_CSC_C11_C12, PRE_CSC_C12, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_CSC_C33_C34, PRE_CSC_C33, mask_sh), \ + TF_SF(CNVC_CFG0_PRE_CSC_C33_C34, PRE_CSC_C34, mask_sh), \ + TF_SF(CM0_CM_POST_CSC_CONTROL, CM_POST_CSC_MODE, mask_sh), \ + TF_SF(CM0_CM_POST_CSC_CONTROL, CM_POST_CSC_MODE_CURRENT, mask_sh), \ + TF_SF(CM0_CM_POST_CSC_C11_C12, CM_POST_CSC_C11, mask_sh), \ + TF_SF(CM0_CM_POST_CSC_C11_C12, CM_POST_CSC_C12, mask_sh), \ + TF_SF(CM0_CM_POST_CSC_C33_C34, CM_POST_CSC_C33, mask_sh), \ + TF_SF(CM0_CM_POST_CSC_C33_C34, CM_POST_CSC_C34, mask_sh), \ + TF_SF(CM0_CM_TEST_DEBUG_INDEX, CM_TEST_DEBUG_INDEX, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, CNVC_BYPASS, mask_sh), \ + TF2_SF(CNVC_CFG0, FORMAT_CONTROL__ALPHA_EN, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_EXPANSION_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_SURFACE_PIXEL_FORMAT, CNVC_SURFACE_PIXEL_FORMAT, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_CONTROL, CUR0_MODE, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_CONTROL, CUR0_EXPANSION_MODE, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_CONTROL, CUR0_ENABLE, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_COLOR0, CUR0_COLOR0, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_COLOR1, CUR0_COLOR1, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y, CUR0_FP_BIAS_G_Y, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y, CUR0_FP_SCALE_G_Y, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB, CUR0_FP_BIAS_RB_CRCB, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB, CUR0_FP_SCALE_RB_CRCB, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_MODE, CUR0_MATRIX_MODE, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_MODE, CUR0_MATRIX_MODE_CURRENT, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_MODE, CUR0_MATRIX_COEF_FORMAT, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C11_C12_A, CUR0_MATRIX_C11_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C11_C12_A, CUR0_MATRIX_C12_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C13_C14_A, CUR0_MATRIX_C13_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C13_C14_A, CUR0_MATRIX_C14_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C21_C22_A, CUR0_MATRIX_C21_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C21_C22_A, CUR0_MATRIX_C22_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C23_C24_A, CUR0_MATRIX_C23_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C23_C24_A, CUR0_MATRIX_C24_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C31_C32_A, CUR0_MATRIX_C31_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C31_C32_A, CUR0_MATRIX_C32_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C33_C34_A, CUR0_MATRIX_C33_A, mask_sh), \ + TF_SF(CM_CUR0_CUR0_MATRIX_C33_C34_A, CUR0_MATRIX_C34_A, mask_sh), \ + TF_SF(DPP_TOP0_DPP_CONTROL, DPP_CLOCK_ENABLE, mask_sh), \ + TF_SF(CM0_CM_HDR_MULT_COEF, CM_HDR_MULT_COEF, mask_sh), \ + TF_SF(CM0_CM_CONTROL, CM_BYPASS, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_SEL, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_CH_EN, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_SRC1_SEL, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_SRC2_SEL, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_SRC3_SEL, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_CH1_XBAR, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_CH2_XBAR, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_CH3_XBAR, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_FORMAT, mask_sh), \ + TF_SF(CM0_CM_HIST_CNTL, CM_HIST_READ_CHANNEL_MASK, mask_sh), \ + TF_SF(CM0_CM_HIST_LOCK, CM_HIST_LOCK, mask_sh), \ + TF_SF(CM0_CM_HIST_INDEX, CM_HIST_INDEX, mask_sh), \ + TF_SF(CM0_CM_HIST_DATA, CM_HIST_DATA, mask_sh), \ + TF_SF(CM0_CM_HIST_STATUS, CM_HIST_BUFA_RDY_STATUS, mask_sh), \ + TF_SF(CM0_CM_HIST_STATUS, CM_HIST_BUFB_RDY_STATUS, mask_sh), \ + TF_SF(CM0_CM_HIST_SCALE_SRC1, CM_HIST_SCALE_SRC1, mask_sh), \ + TF_SF(CM0_CM_HIST_COEFA_SRC2, CM_HIST_COEFA_SRC2, mask_sh), \ + TF_SF(CM0_CM_HIST_COEFB_SRC2, CM_HIST_COEFB_SRC2, mask_sh), \ + TF_SF(CM0_CM_HIST_COEFC_SRC2, CM_HIST_COEFC_SRC2, mask_sh), \ + TF_SF(CM0_CM_HIST_SCALE_SRC3, CM_HIST_SCALE_SRC3, mask_sh), \ + TF_SF(CM0_CM_HIST_BIAS_SRC1, CM_HIST_BIAS_SRC1, mask_sh), \ + TF_SF(CM0_CM_HIST_BIAS_SRC2, CM_HIST_BIAS_SRC2, mask_sh), \ + TF_SF(CM0_CM_HIST_BIAS_SRC3, CM_HIST_BIAS_SRC3, mask_sh), \ + TF_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_MODE, mask_sh), \ + TF_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_PITCH, mask_sh), \ + TF_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_LINES_PER_CHUNK, mask_sh), \ + TF_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_ENABLE, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_CNV16, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, CNVC_BYPASS_MSB_ALIGN, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, CLAMP_POSITIVE, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, CLAMP_POSITIVE_C, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_CROSSBAR_R, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_CROSSBAR_G, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_CROSSBAR_B, mask_sh), \ + TF_SF(CNVC_CFG0_ALPHA_2BIT_LUT01, ALPHA_2BIT_LUT0, mask_sh), \ + TF_SF(CNVC_CFG0_ALPHA_2BIT_LUT01, ALPHA_2BIT_LUT1, mask_sh), \ + TF_SF(CNVC_CFG0_ALPHA_2BIT_LUT23, ALPHA_2BIT_LUT2, mask_sh), \ + TF_SF(CNVC_CFG0_ALPHA_2BIT_LUT23, ALPHA_2BIT_LUT3, mask_sh), \ + TF_SF(CNVC_CFG0_FCNV_FP_BIAS_R, FCNV_FP_BIAS_R, mask_sh), \ + TF_SF(CNVC_CFG0_FCNV_FP_BIAS_G, FCNV_FP_BIAS_G, mask_sh), \ + TF_SF(CNVC_CFG0_FCNV_FP_BIAS_B, FCNV_FP_BIAS_B, mask_sh), \ + TF_SF(CNVC_CFG0_FCNV_FP_SCALE_R, FCNV_FP_SCALE_R, mask_sh), \ + TF_SF(CNVC_CFG0_FCNV_FP_SCALE_G, FCNV_FP_SCALE_G, mask_sh), \ + TF_SF(CNVC_CFG0_FCNV_FP_SCALE_B, FCNV_FP_SCALE_B, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_CONTROL, COLOR_KEYER_EN, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_CONTROL, LUMA_KEYER_EN, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_CONTROL, COLOR_KEYER_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_ALPHA, COLOR_KEYER_ALPHA_LOW, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_ALPHA, COLOR_KEYER_ALPHA_HIGH, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_RED, COLOR_KEYER_RED_LOW, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_RED, COLOR_KEYER_RED_HIGH, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_GREEN, COLOR_KEYER_GREEN_LOW, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_GREEN, COLOR_KEYER_GREEN_HIGH, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_BLUE, COLOR_KEYER_BLUE_LOW, mask_sh), \ + TF_SF(CNVC_CFG0_COLOR_KEYER_BLUE, COLOR_KEYER_BLUE_HIGH, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_CONTROL, CUR0_PIX_INV_MODE, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_CONTROL, CUR0_PIXEL_ALPHA_MOD_EN, mask_sh), \ + TF_SF(CM_CUR0_CURSOR0_CONTROL, CUR0_ROM_EN, mask_sh),\ + TF_SF(DSCL0_OBUF_MEM_PWR_CTRL, OBUF_MEM_PWR_FORCE, mask_sh),\ + TF_SF(DSCL0_DSCL_MEM_PWR_CTRL, LUT_MEM_PWR_FORCE, mask_sh),\ + TF_SF(DSCL0_DSCL_MEM_PWR_CTRL, LUT_MEM_PWR_DIS, mask_sh),\ + TF_SF(DSCL0_DSCL_MEM_PWR_STATUS, LUT_MEM_PWR_STATE, mask_sh),\ + TF_SF(DSCL0_DSCL_SC_MODE, SCL_SC_MATRIX_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_SC_MODE, SCL_SC_LTONL_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_MODE, SCL_EASF_H_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_MODE, SCL_EASF_H_RINGEST_FORCE_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_MODE, SCL_EASF_H_2TAP_SHARP_FACTOR, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_CNTL, SCL_EASF_H_BF1_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_CNTL, SCL_EASF_H_BF2_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_CNTL, SCL_EASF_H_BF3_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_CNTL, SCL_EASF_H_BF2_FLAT1_GAIN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_CNTL, SCL_EASF_H_BF2_FLAT2_GAIN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_CNTL, SCL_EASF_H_BF2_ROC_GAIN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_RINGEST_EVENTAP_REDUCE, SCL_EASF_H_RINGEST_EVENTAP_REDUCEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_RINGEST_EVENTAP_REDUCE, SCL_EASF_H_RINGEST_EVENTAP_REDUCEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_RINGEST_EVENTAP_GAIN, SCL_EASF_H_RINGEST_EVENTAP_GAIN1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_RINGEST_EVENTAP_GAIN, SCL_EASF_H_RINGEST_EVENTAP_GAIN2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_FINAL_MAX_MIN, SCL_EASF_H_BF_MAXA, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_FINAL_MAX_MIN, SCL_EASF_H_BF_MAXB, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_FINAL_MAX_MIN, SCL_EASF_H_BF_MINA, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF_FINAL_MAX_MIN, SCL_EASF_H_BF_MINB, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG0, SCL_EASF_H_BF1_PWL_IN_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG0, SCL_EASF_H_BF1_PWL_BASE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG0, SCL_EASF_H_BF1_PWL_SLOPE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG1, SCL_EASF_H_BF1_PWL_IN_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG1, SCL_EASF_H_BF1_PWL_BASE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG1, SCL_EASF_H_BF1_PWL_SLOPE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG2, SCL_EASF_H_BF1_PWL_IN_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG2, SCL_EASF_H_BF1_PWL_BASE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG2, SCL_EASF_H_BF1_PWL_SLOPE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG3, SCL_EASF_H_BF1_PWL_IN_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG3, SCL_EASF_H_BF1_PWL_BASE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG3, SCL_EASF_H_BF1_PWL_SLOPE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG4, SCL_EASF_H_BF1_PWL_IN_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG4, SCL_EASF_H_BF1_PWL_BASE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG4, SCL_EASF_H_BF1_PWL_SLOPE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG5, SCL_EASF_H_BF1_PWL_IN_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG5, SCL_EASF_H_BF1_PWL_BASE_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG5, SCL_EASF_H_BF1_PWL_SLOPE_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG6, SCL_EASF_H_BF1_PWL_IN_SEG6, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG6, SCL_EASF_H_BF1_PWL_BASE_SEG6, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG6, SCL_EASF_H_BF1_PWL_SLOPE_SEG6, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG7, SCL_EASF_H_BF1_PWL_IN_SEG7, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF1_PWL_SEG7, SCL_EASF_H_BF1_PWL_BASE_SEG7, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG0, SCL_EASF_H_BF3_PWL_IN_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG0, SCL_EASF_H_BF3_PWL_BASE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG0, SCL_EASF_H_BF3_PWL_SLOPE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG1, SCL_EASF_H_BF3_PWL_IN_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG1, SCL_EASF_H_BF3_PWL_BASE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG1, SCL_EASF_H_BF3_PWL_SLOPE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG2, SCL_EASF_H_BF3_PWL_IN_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG2, SCL_EASF_H_BF3_PWL_BASE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG2, SCL_EASF_H_BF3_PWL_SLOPE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG3, SCL_EASF_H_BF3_PWL_IN_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG3, SCL_EASF_H_BF3_PWL_BASE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG3, SCL_EASF_H_BF3_PWL_SLOPE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG4, SCL_EASF_H_BF3_PWL_IN_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG4, SCL_EASF_H_BF3_PWL_BASE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG4, SCL_EASF_H_BF3_PWL_SLOPE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG5, SCL_EASF_H_BF3_PWL_IN_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_H_BF3_PWL_SEG5, SCL_EASF_H_BF3_PWL_BASE_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_MODE, SCL_EASF_V_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_MODE, SCL_EASF_V_RINGEST_FORCE_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_MODE, SCL_EASF_V_2TAP_SHARP_FACTOR, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_CNTL, SCL_EASF_V_BF1_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_CNTL, SCL_EASF_V_BF2_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_CNTL, SCL_EASF_V_BF3_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_CNTL, SCL_EASF_V_BF2_FLAT1_GAIN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_CNTL, SCL_EASF_V_BF2_FLAT2_GAIN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_CNTL, SCL_EASF_V_BF2_ROC_GAIN, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_3TAP_CNTL1, SCL_EASF_V_RINGEST_3TAP_DNTILT_UPTILT, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_3TAP_CNTL1, SCL_EASF_V_RINGEST_3TAP_UPTILT_MAXVAL, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_3TAP_CNTL2, SCL_EASF_V_RINGEST_3TAP_DNTILT_SLOPE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_3TAP_CNTL2, SCL_EASF_V_RINGEST_3TAP_UPTILT1_SLOPE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_3TAP_CNTL3, SCL_EASF_V_RINGEST_3TAP_UPTILT2_SLOPE, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_3TAP_CNTL3, SCL_EASF_V_RINGEST_3TAP_UPTILT2_OFFSET, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_EVENTAP_REDUCE, SCL_EASF_V_RINGEST_EVENTAP_REDUCEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_EVENTAP_REDUCE, SCL_EASF_V_RINGEST_EVENTAP_REDUCEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_EVENTAP_GAIN, SCL_EASF_V_RINGEST_EVENTAP_GAIN1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_RINGEST_EVENTAP_GAIN, SCL_EASF_V_RINGEST_EVENTAP_GAIN2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_FINAL_MAX_MIN, SCL_EASF_V_BF_MAXA, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_FINAL_MAX_MIN, SCL_EASF_V_BF_MAXB, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_FINAL_MAX_MIN, SCL_EASF_V_BF_MINA, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF_FINAL_MAX_MIN, SCL_EASF_V_BF_MINB, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG0, SCL_EASF_V_BF1_PWL_IN_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG0, SCL_EASF_V_BF1_PWL_BASE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG0, SCL_EASF_V_BF1_PWL_SLOPE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG1, SCL_EASF_V_BF1_PWL_IN_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG1, SCL_EASF_V_BF1_PWL_BASE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG1, SCL_EASF_V_BF1_PWL_SLOPE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG2, SCL_EASF_V_BF1_PWL_IN_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG2, SCL_EASF_V_BF1_PWL_BASE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG2, SCL_EASF_V_BF1_PWL_SLOPE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG3, SCL_EASF_V_BF1_PWL_IN_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG3, SCL_EASF_V_BF1_PWL_BASE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG3, SCL_EASF_V_BF1_PWL_SLOPE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG4, SCL_EASF_V_BF1_PWL_IN_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG4, SCL_EASF_V_BF1_PWL_BASE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG4, SCL_EASF_V_BF1_PWL_SLOPE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG5, SCL_EASF_V_BF1_PWL_IN_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG5, SCL_EASF_V_BF1_PWL_BASE_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG5, SCL_EASF_V_BF1_PWL_SLOPE_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG6, SCL_EASF_V_BF1_PWL_IN_SEG6, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG6, SCL_EASF_V_BF1_PWL_BASE_SEG6, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG6, SCL_EASF_V_BF1_PWL_SLOPE_SEG6, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG7, SCL_EASF_V_BF1_PWL_IN_SEG7, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF1_PWL_SEG7, SCL_EASF_V_BF1_PWL_BASE_SEG7, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG0, SCL_EASF_V_BF3_PWL_IN_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG0, SCL_EASF_V_BF3_PWL_BASE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG0, SCL_EASF_V_BF3_PWL_SLOPE_SEG0, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG1, SCL_EASF_V_BF3_PWL_IN_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG1, SCL_EASF_V_BF3_PWL_BASE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG1, SCL_EASF_V_BF3_PWL_SLOPE_SEG1, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG2, SCL_EASF_V_BF3_PWL_IN_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG2, SCL_EASF_V_BF3_PWL_BASE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG2, SCL_EASF_V_BF3_PWL_SLOPE_SEG2, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG3, SCL_EASF_V_BF3_PWL_IN_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG3, SCL_EASF_V_BF3_PWL_BASE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG3, SCL_EASF_V_BF3_PWL_SLOPE_SEG3, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG4, SCL_EASF_V_BF3_PWL_IN_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG4, SCL_EASF_V_BF3_PWL_BASE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG4, SCL_EASF_V_BF3_PWL_SLOPE_SEG4, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG5, SCL_EASF_V_BF3_PWL_IN_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_EASF_V_BF3_PWL_SEG5, SCL_EASF_V_BF3_PWL_BASE_SEG5, mask_sh),\ + TF_SF(DSCL0_DSCL_SC_MATRIX_C0C1, SCL_SC_MATRIX_C0, mask_sh),\ + TF_SF(DSCL0_DSCL_SC_MATRIX_C0C1, SCL_SC_MATRIX_C1, mask_sh),\ + TF_SF(DSCL0_DSCL_SC_MATRIX_C2C3, SCL_SC_MATRIX_C2, mask_sh),\ + TF_SF(DSCL0_DSCL_SC_MATRIX_C2C3, SCL_SC_MATRIX_C3, mask_sh),\ + TF_SF(DSCL0_ISHARP_DELTA_CTRL, ISHARP_DELTA_LUT_HOST_SELECT, mask_sh),\ + TF_SF(DSCL0_ISHARP_DELTA_LUT_MEM_PWR_CTRL, ISHARP_DELTA_LUT_MEM_PWR_DIS, mask_sh),\ + TF_SF(DSCL0_ISHARP_DELTA_LUT_MEM_PWR_CTRL, ISHARP_DELTA_LUT_MEM_PWR_FORCE, mask_sh),\ + TF_SF(DSCL0_ISHARP_DELTA_LUT_MEM_PWR_CTRL, ISHARP_DELTA_LUT_MEM_PWR_STATE, mask_sh),\ + TF_SF(DSCL0_ISHARP_DELTA_DATA, ISHARP_DELTA_DATA, mask_sh),\ + TF_SF(DSCL0_ISHARP_DELTA_INDEX, ISHARP_DELTA_INDEX, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_EN, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_NOISEDET_EN, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_NOISEDET_MODE, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_LBA_MODE, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_DELTA_LUT_SELECT, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_FMT_MODE, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_FMT_NORM, mask_sh),\ + TF_SF(DSCL0_ISHARP_MODE, ISHARP_DELTA_LUT_SELECT_CURRENT, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG0, ISHARP_LBA_PWL_IN_SEG0, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG0, ISHARP_LBA_PWL_BASE_SEG0, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG0, ISHARP_LBA_PWL_SLOPE_SEG0, mask_sh), \ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG1, ISHARP_LBA_PWL_IN_SEG1, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG1, ISHARP_LBA_PWL_BASE_SEG1, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG1, ISHARP_LBA_PWL_SLOPE_SEG1, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG2, ISHARP_LBA_PWL_IN_SEG2, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG2, ISHARP_LBA_PWL_BASE_SEG2, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG2, ISHARP_LBA_PWL_SLOPE_SEG2, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG3, ISHARP_LBA_PWL_IN_SEG3, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG3, ISHARP_LBA_PWL_BASE_SEG3, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG3, ISHARP_LBA_PWL_SLOPE_SEG3, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG4, ISHARP_LBA_PWL_IN_SEG4, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG4, ISHARP_LBA_PWL_BASE_SEG4, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG4, ISHARP_LBA_PWL_SLOPE_SEG4, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG5, ISHARP_LBA_PWL_IN_SEG5, mask_sh),\ + TF_SF(DSCL0_ISHARP_LBA_PWL_SEG5, ISHARP_LBA_PWL_BASE_SEG5, mask_sh),\ + TF_SF(DSCL0_ISHARP_NOISEDET_THRESHOLD, ISHARP_NOISEDET_UTHRE, mask_sh),\ + TF_SF(DSCL0_ISHARP_NOISEDET_THRESHOLD, ISHARP_NOISEDET_DTHRE, mask_sh), \ + TF_SF(DSCL0_ISHARP_NOISE_GAIN_PWL, ISHARP_NOISEDET_PWL_START_IN, mask_sh), \ + TF_SF(DSCL0_ISHARP_NOISE_GAIN_PWL, ISHARP_NOISEDET_PWL_END_IN, mask_sh), \ + TF_SF(DSCL0_ISHARP_NOISE_GAIN_PWL, ISHARP_NOISEDET_PWL_SLOPE, mask_sh), \ + TF_SF(DSCL0_ISHARP_NLDELTA_SOFT_CLIP, ISHARP_NLDELTA_SCLIP_EN_P, mask_sh), \ + TF_SF(DSCL0_ISHARP_NLDELTA_SOFT_CLIP, ISHARP_NLDELTA_SCLIP_PIVOT_P, mask_sh), \ + TF_SF(DSCL0_ISHARP_NLDELTA_SOFT_CLIP, ISHARP_NLDELTA_SCLIP_SLOPE_P, mask_sh), \ + TF_SF(DSCL0_ISHARP_NLDELTA_SOFT_CLIP, ISHARP_NLDELTA_SCLIP_EN_N, mask_sh), \ + TF_SF(DSCL0_ISHARP_NLDELTA_SOFT_CLIP, ISHARP_NLDELTA_SCLIP_PIVOT_N, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_V_NUM_TAPS, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_V_INIT_INT, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_V_INIT_FRAC, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_H_NUM_TAPS, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_H_INIT_INT, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_H_INIT_FRAC, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_MODE, UPSP_BOUNDARY_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P0, UPSP_V_COEF_TAP0_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P0, UPSP_V_COEF_TAP1_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P0, UPSP_V_COEF_TAP2_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P0, UPSP_V_COEF_TAP3_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P1, UPSP_V_COEF_TAP0_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P1, UPSP_V_COEF_TAP1_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P1, UPSP_V_COEF_TAP2_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_V_COEF_P1, UPSP_V_COEF_TAP3_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P0, UPSP_H_COEF_TAP0_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P0, UPSP_H_COEF_TAP1_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P0, UPSP_H_COEF_TAP2_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P0, UPSP_H_COEF_TAP3_P0, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P1, UPSP_H_COEF_TAP0_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P1, UPSP_H_COEF_TAP1_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P1, UPSP_H_COEF_TAP2_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_H_COEF_P1, UPSP_H_COEF_TAP3_P1, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_CLAMP, UPSP_CLAMP_MAX, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_UPSP_CLAMP, UPSP_CLAMP_MIN, mask_sh) + +#define DPP_REG_FIELD_LIST_DCN60(type) \ + DPP_REG_FIELD_LIST_DCN42(type); \ + type PRE_GAM_MODE; \ + type PRE_REGAM_SELECT; \ + type AUTOCAL_FRAC_MODE; \ + type SCL_BLACK_COLOR_RGB_Y; \ + type SCL_BLACK_COLOR_CBCR; \ + type UPSP_MODE; \ + type UPSP_V_NUM_TAPS; \ + type UPSP_V_INIT_INT; \ + type UPSP_V_INIT_FRAC; \ + type UPSP_H_NUM_TAPS; \ + type UPSP_H_INIT_INT; \ + type UPSP_H_INIT_FRAC; \ + type UPSP_BOUNDARY_MODE; \ + type UPSP_V_COEF_TAP0_P0; \ + type UPSP_V_COEF_TAP1_P0; \ + type UPSP_V_COEF_TAP2_P0; \ + type UPSP_V_COEF_TAP3_P0; \ + type UPSP_V_COEF_TAP0_P1; \ + type UPSP_V_COEF_TAP1_P1; \ + type UPSP_V_COEF_TAP2_P1; \ + type UPSP_V_COEF_TAP3_P1; \ + type UPSP_H_COEF_TAP0_P0; \ + type UPSP_H_COEF_TAP1_P0; \ + type UPSP_H_COEF_TAP2_P0; \ + type UPSP_H_COEF_TAP3_P0; \ + type UPSP_H_COEF_TAP0_P1; \ + type UPSP_H_COEF_TAP1_P1; \ + type UPSP_H_COEF_TAP2_P1; \ + type UPSP_H_COEF_TAP3_P1; \ + type UPSP_CLAMP_MAX; \ + type UPSP_CLAMP_MIN + +#define DPP_REG_VARIABLE_LIST_DCN60 \ + DPP_REG_VARIABLE_LIST_DCN42; \ + uint32_t PRE_GAM; \ + uint32_t SCL_BLACK_COLOR; \ + uint32_t UPSP_MODE; \ + uint32_t UPSP_V_COEF_P0; \ + uint32_t UPSP_V_COEF_P1; \ + uint32_t UPSP_H_COEF_P0; \ + uint32_t UPSP_H_COEF_P1; \ + uint32_t UPSP_CLAMP + +struct dcn60_dpp_registers { + DPP_REG_VARIABLE_LIST_DCN60; +}; + +struct dcn60_dpp_shift { + DPP_REG_FIELD_LIST_DCN60(uint8_t); +}; + +struct dcn60_dpp_mask { + DPP_REG_FIELD_LIST_DCN60(uint32_t); +}; + +struct dcn60_dpp { + struct dpp base; + const struct dcn60_dpp_registers *tf_regs; + const struct dcn60_dpp_shift *tf_shift; + const struct dcn60_dpp_mask *tf_mask; + const uint16_t *filter_v; + const uint16_t *filter_h; + const uint16_t *filter_v_c; + const uint16_t *filter_h_c; + int lb_pixel_depth_supported; + int lb_memory_size; + int lb_bits_per_entry; + bool is_write_to_ram_a_safe; + struct scaler_data scl_data; + struct pwl_params pwl_data; +}; + +void dpp60_dpp_setup( + struct dpp *dpp_base, + enum surface_pixel_format format, + enum expansion_mode mode, + struct dc_csc_transform input_csc_color_matrix, + enum dc_color_space input_color_space, + struct cnv_alpha_2bit_lut *alpha_2bit_lut); + +void dpp60_full_bypass(struct dpp *dpp_base); + +void dpp60_dscl_set_scaler_manual_scale( + struct dpp *dpp_base, + const struct scaler_data *scl_data); + +void dpp60_dscl_set_lb( + struct dcn60_dpp *dpp, + const struct line_buffer_params *lb_params, + enum lb_memory_config mem_size_config); + +void dpp60_dscl_set_manual_ratio_init( + struct dcn60_dpp *dpp, + const struct scaler_data *data); + +void dpp60_dscl_program_upsp( + struct dpp *dpp_base, + const struct dscl_prog_data *dscl_prog_data); + +bool dpp60_construct(struct dcn60_dpp *dpp60, + struct dc_context *ctx, + uint32_t inst, + const struct dcn60_dpp_registers *tf_regs, + const struct dcn60_dpp_shift *tf_shift, + const struct dcn60_dpp_mask *tf_mask); + + +#endif /* __DCN60_DPP_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c new file mode 100644 index 000000000000..0898148ba704 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dm_services.h" + +#include "core_types.h" + +#include "reg_helper.h" +#include "dcn60/dcn60_dpp.h" +#include "basics/conversion.h" + +#define REG(reg)\ + dpp->tf_regs->reg + +#define CTX \ + dpp->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + dpp->tf_shift->field_name, dpp->tf_mask->field_name + +static void dpp60_power_on_dscl(struct dpp *dpp_base, bool power_on) +{ + struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base); + + if (power_on) { + REG_UPDATE(DSCL_MEM_PWR_CTRL, LUT_MEM_PWR_FORCE, 0); + REG_UPDATE(DSCL_MEM_PWR_CTRL, LUT_MEM_PWR_DIS, 1); + REG_WAIT(DSCL_MEM_PWR_STATUS, LUT_MEM_PWR_STATE, 0, 1, 100); + } else { + if (dpp->base.ctx->dc->debug.enable_mem_low_power.bits.dscl) { + dpp->base.ctx->dc->optimized_required = true; + dpp->base.deferred_reg_writes.bits.disable_dscl = true; + } + } +} + +void dpp60_dscl_set_lb( + struct dcn60_dpp *dpp, + const struct line_buffer_params *lb_params, + enum lb_memory_config mem_size_config) +{ + /* LB */ + REG_SET(LB_DATA_FORMAT, 0, + LB_DATA_FORMAT__ALPHA_EN, lb_params->alpha_en); /* Alpha enable */ + + REG_SET_2(LB_MEMORY_CTRL, 0, + MEMORY_CONFIG, mem_size_config, + LB_MAX_PARTITIONS, dpp->base.caps->max_lb_partitions); +} + +void dpp60_dscl_set_manual_ratio_init( + struct dcn60_dpp *dpp, const struct scaler_data *data) +{ + uint32_t init_frac = 0; + uint32_t init_int = 0; + if ((dpp->base.ctx->dc->config.use_spl) && (!dpp->base.ctx->dc->debug.disable_spl)) { + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0, + SCL_H_SCALE_RATIO, data->dscl_prog_data.ratios.h_scale_ratio); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0, + SCL_V_SCALE_RATIO, data->dscl_prog_data.ratios.v_scale_ratio); + + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO_C, 0, + SCL_H_SCALE_RATIO_C, data->dscl_prog_data.ratios.h_scale_ratio_c); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO_C, 0, + SCL_V_SCALE_RATIO_C, data->dscl_prog_data.ratios.v_scale_ratio_c); + + REG_SET_2(SCL_HORZ_FILTER_INIT, 0, + SCL_H_INIT_FRAC, data->dscl_prog_data.init.h_filter_init_frac, + SCL_H_INIT_INT, data->dscl_prog_data.init.h_filter_init_int); + + REG_SET_2(SCL_HORZ_FILTER_INIT_C, 0, + SCL_H_INIT_FRAC_C, data->dscl_prog_data.init.h_filter_init_frac_c, + SCL_H_INIT_INT_C, data->dscl_prog_data.init.h_filter_init_int_c); + + REG_SET_2(SCL_VERT_FILTER_INIT, 0, + SCL_V_INIT_FRAC, data->dscl_prog_data.init.v_filter_init_frac, + SCL_V_INIT_INT, data->dscl_prog_data.init.v_filter_init_int); + + REG_SET_2(SCL_VERT_FILTER_INIT_C, 0, + SCL_V_INIT_FRAC_C, data->dscl_prog_data.init.v_filter_init_frac_c, + SCL_V_INIT_INT_C, data->dscl_prog_data.init.v_filter_init_int_c); + + return; + } + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0, + SCL_H_SCALE_RATIO, dc_fixpt_u3d19(data->ratios.horz) << 5); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0, + SCL_V_SCALE_RATIO, dc_fixpt_u3d19(data->ratios.vert) << 5); + + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO_C, 0, + SCL_H_SCALE_RATIO_C, dc_fixpt_u3d19(data->ratios.horz_c) << 5); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO_C, 0, + SCL_V_SCALE_RATIO_C, dc_fixpt_u3d19(data->ratios.vert_c) << 5); + + /* + * 0.24 format for fraction, first five bits zeroed + */ + init_frac = dc_fixpt_u0d19(data->inits.h) << 5; + init_int = dc_fixpt_floor(data->inits.h); + REG_SET_2(SCL_HORZ_FILTER_INIT, 0, + SCL_H_INIT_FRAC, init_frac, + SCL_H_INIT_INT, init_int); + + init_frac = dc_fixpt_u0d19(data->inits.h_c) << 5; + init_int = dc_fixpt_floor(data->inits.h_c); + REG_SET_2(SCL_HORZ_FILTER_INIT_C, 0, + SCL_H_INIT_FRAC_C, init_frac, + SCL_H_INIT_INT_C, init_int); + + init_frac = dc_fixpt_u0d19(data->inits.v) << 5; + init_int = dc_fixpt_floor(data->inits.v); + REG_SET_2(SCL_VERT_FILTER_INIT, 0, + SCL_V_INIT_FRAC, init_frac, + SCL_V_INIT_INT, init_int); + + init_frac = dc_fixpt_u0d19(data->inits.v_c) << 5; + init_int = dc_fixpt_floor(data->inits.v_c); + REG_SET_2(SCL_VERT_FILTER_INIT_C, 0, + SCL_V_INIT_FRAC_C, init_frac, + SCL_V_INIT_INT_C, init_int); +} + +/** + * dpp60_dscl_set_scaler_manual_scale - Manually program scaler and line buffer + * + * @dpp_base: High level DPP struct + * @scl_data: scalaer_data info + * + * This is the primary function to program scaler and line buffer in manual + * scaling mode. To execute the required operations for manual scale, we need + * to disable AutoCal first. + */ +void dpp60_dscl_set_scaler_manual_scale(struct dpp *dpp_base, + const struct scaler_data *scl_data) +{ + enum lb_memory_config lb_config; + struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base); + struct dcn401_dpp *dpp401 = TO_DCN401_DPP(dpp_base); + const struct rect *rect = &scl_data->recout; + uint32_t mpc_width = scl_data->h_active; + uint32_t mpc_height = scl_data->v_active; + uint32_t v_num_taps = scl_data->taps.v_taps - 1; + uint32_t v_num_taps_c = scl_data->taps.v_taps_c - 1; + uint32_t h_num_taps = scl_data->taps.h_taps - 1; + uint32_t h_num_taps_c = scl_data->taps.h_taps_c - 1; + enum dcn401_dscl_mode_sel dscl_mode = dpp401_dscl_get_dscl_mode( + dpp_base, scl_data, dpp_base->ctx->dc->debug.always_scale); + bool ycbcr = scl_data->format >= PIXEL_FORMAT_VIDEO_BEGIN + && scl_data->format <= PIXEL_FORMAT_VIDEO_END; + bool program_isharp_1dlut = false; + bool bs_coeffs_updated = false; + + if (memcmp(&dpp->scl_data, scl_data, sizeof(*scl_data)) == 0) + return; + + PERF_TRACE(); + + /* If only sharpness has changed, then only update 1dlut, then return */ + if (scl_data->dscl_prog_data.isharp_en && + (dpp->scl_data.dscl_prog_data.sharpness_level + != scl_data->dscl_prog_data.sharpness_level)) { + /* ISHARP_DELTA_LUT */ + dpp401_dscl_set_isharp_filter(dpp401, scl_data->dscl_prog_data.isharp_delta); + dpp->scl_data.dscl_prog_data.sharpness_level = scl_data->dscl_prog_data.sharpness_level; + memcpy(dpp->scl_data.dscl_prog_data.isharp_delta, scl_data->dscl_prog_data.isharp_delta, + sizeof(uint32_t) * ISHARP_LUT_TABLE_SIZE); + + if (memcmp(&dpp->scl_data, scl_data, sizeof(*scl_data)) == 0) + return; + program_isharp_1dlut = true; + } + + dpp->scl_data = *scl_data; + + if ((dpp->base.ctx->dc->config.use_spl) && (!dpp->base.ctx->dc->debug.disable_spl)) { + dscl_mode = (enum dcn401_dscl_mode_sel) scl_data->dscl_prog_data.dscl_mode; + rect = (struct rect *)&scl_data->dscl_prog_data.recout; + mpc_width = scl_data->dscl_prog_data.mpc_size.width; + mpc_height = scl_data->dscl_prog_data.mpc_size.height; + v_num_taps = scl_data->dscl_prog_data.taps.v_taps; + v_num_taps_c = scl_data->dscl_prog_data.taps.v_taps_c; + h_num_taps = scl_data->dscl_prog_data.taps.h_taps; + h_num_taps_c = scl_data->dscl_prog_data.taps.h_taps_c; + } + + /* Unconditionally power on DSCL - can be in light sleep otherwise. */ + if (dscl_mode != DCN401_DSCL_MODE_DSCL_BYPASS) + dpp60_power_on_dscl(dpp_base, true); + + /* Autocal off */ + REG_SET_4(DSCL_AUTOCAL, 0, + AUTOCAL_MODE, 0, + AUTOCAL_FRAC_MODE, 0, + AUTOCAL_NUM_PIPE, 0, + AUTOCAL_PIPE_ID, 0); + + /*clean scaler boundary mode when Autocal off*/ + REG_SET(DSCL_CONTROL, 0, + SCL_BOUNDARY_MODE, 0); + + /* Recout */ + dpp401_dscl_set_recout(dpp401, rect); + + /* MPC Size */ + REG_SET_2(MPC_SIZE, 0, + /* Number of horizontal pixels of MPC */ + MPC_WIDTH, mpc_width, + /* Number of vertical lines of MPC */ + MPC_HEIGHT, mpc_height); + + /* SCL mode */ + REG_UPDATE(SCL_MODE, DSCL_MODE, dscl_mode); + + if (dscl_mode == DCN401_DSCL_MODE_DSCL_BYPASS) { + dpp60_power_on_dscl(dpp_base, false); + return; + } + + /* LB */ + lb_config = dpp401_dscl_find_lb_memory_config(dpp401, scl_data); + dpp60_dscl_set_lb(dpp, &scl_data->lb_params, lb_config); + + if (dscl_mode == DCN401_DSCL_MODE_SCALING_444_BYPASS) { + if (dpp->base.ctx->dc->config.prefer_easf) + dpp401_dscl_disable_easf(dpp_base, scl_data); + dpp401_dscl_program_isharp(dpp_base, scl_data, program_isharp_1dlut, &bs_coeffs_updated); + return; + } + + /* Black color */ + if (ycbcr) + REG_SET_2(SCL_BLACK_COLOR, 0, + SCL_BLACK_COLOR_RGB_Y, BLACK_OFFSET_RGB_Y, + SCL_BLACK_COLOR_CBCR, BLACK_OFFSET_CBCR); + else + REG_SET_2(SCL_BLACK_COLOR, 0, + SCL_BLACK_COLOR_RGB_Y, BLACK_OFFSET_RGB_Y, + SCL_BLACK_COLOR_CBCR, BLACK_OFFSET_RGB_Y); + + /* Manually calculate scale ratio and init values */ + dpp60_dscl_set_manual_ratio_init(dpp, scl_data); + + /* HTaps/VTaps */ + REG_SET_4(SCL_TAP_CONTROL, 0, + SCL_V_NUM_TAPS, v_num_taps, + SCL_H_NUM_TAPS, h_num_taps, + SCL_V_NUM_TAPS_C, v_num_taps_c, + SCL_H_NUM_TAPS_C, h_num_taps_c); + + /* ISharp configuration + * - B&S coeffs are written to same coeff RAM as WB scaler coeffs + * - coeff RAM toggle is in EASF programming + * - if we are only programming B&S coeffs, then need to reprogram + * WB scaler coeffs and toggle coeff RAM together + */ + //if (dpp->base.ctx->dc->config.prefer_easf) + dpp401_dscl_program_isharp(dpp_base, scl_data, program_isharp_1dlut, &bs_coeffs_updated); + + dpp401_dscl_set_scl_filter(dpp401, scl_data, ycbcr, bs_coeffs_updated); + /* Edge adaptive scaler function configuration */ + if (dpp->base.ctx->dc->config.prefer_easf) + dpp401_dscl_program_easf(dpp_base, scl_data); + PERF_TRACE(); +} + +/** + * dpp60_dscl_program_upsp - Manually program UPSP registers + * + * @dpp_base: High level DPP struct + * @dscl_prog_data: dscl_prog_data info + */ +void dpp60_dscl_program_upsp(struct dpp *dpp_base, + const struct dscl_prog_data *dscl_prog_data) +{ + struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base); + + REG_SET_8(UPSP_MODE, 0, + UPSP_MODE, dscl_prog_data->upsp_mode, + UPSP_V_NUM_TAPS, dscl_prog_data->upsp_v_num_taps, + UPSP_V_INIT_INT, dscl_prog_data->upsp_v_init_int, + UPSP_V_INIT_FRAC, dscl_prog_data->upsp_v_init_frac, + UPSP_H_NUM_TAPS, dscl_prog_data->upsp_h_num_taps, + UPSP_H_INIT_INT, dscl_prog_data->upsp_h_init_int, + UPSP_H_INIT_FRAC, dscl_prog_data->upsp_h_init_frac, + UPSP_BOUNDARY_MODE, dscl_prog_data->upsp_boundary_mode); + REG_SET_4(UPSP_V_COEF_P0, 0, + UPSP_V_COEF_TAP0_P0, dscl_prog_data->upsp_v_coef_tap0_p0, + UPSP_V_COEF_TAP1_P0, dscl_prog_data->upsp_v_coef_tap1_p0, + UPSP_V_COEF_TAP2_P0, dscl_prog_data->upsp_v_coef_tap2_p0, + UPSP_V_COEF_TAP3_P0, dscl_prog_data->upsp_v_coef_tap3_p0); + REG_SET_4(UPSP_V_COEF_P1, 0, + UPSP_V_COEF_TAP0_P1, dscl_prog_data->upsp_v_coef_tap0_p1, + UPSP_V_COEF_TAP1_P1, dscl_prog_data->upsp_v_coef_tap1_p1, + UPSP_V_COEF_TAP2_P1, dscl_prog_data->upsp_v_coef_tap2_p1, + UPSP_V_COEF_TAP3_P1, dscl_prog_data->upsp_v_coef_tap3_p1); + REG_SET_4(UPSP_H_COEF_P0, 0, + UPSP_H_COEF_TAP0_P0, dscl_prog_data->upsp_h_coef_tap0_p0, + UPSP_H_COEF_TAP1_P0, dscl_prog_data->upsp_h_coef_tap1_p0, + UPSP_H_COEF_TAP2_P0, dscl_prog_data->upsp_h_coef_tap2_p0, + UPSP_H_COEF_TAP3_P0, dscl_prog_data->upsp_h_coef_tap3_p0); + REG_SET_4(UPSP_H_COEF_P1, 0, + UPSP_H_COEF_TAP0_P1, dscl_prog_data->upsp_h_coef_tap0_p1, + UPSP_H_COEF_TAP1_P1, dscl_prog_data->upsp_h_coef_tap1_p1, + UPSP_H_COEF_TAP2_P1, dscl_prog_data->upsp_h_coef_tap2_p1, + UPSP_H_COEF_TAP3_P1, dscl_prog_data->upsp_h_coef_tap3_p1); + REG_SET_2(UPSP_CLAMP, 0, + UPSP_CLAMP_MAX, dscl_prog_data->upsp_clamp_max, + UPSP_CLAMP_MIN, dscl_prog_data->upsp_clamp_min); +} diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.c new file mode 100644 index 000000000000..0cfedd4268c6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include + +#include "reg_helper.h" +#include "dcn60_dsc.h" +#include "dsc/dscc_types.h" +#include "dsc/rc_calc.h" + +static const struct dsc_funcs dcn60_dsc_funcs = { + .dsc_read_state = dsc401_read_state, + .dsc_validate_stream = dsc401_validate_stream, + .dsc_set_config = dsc60_set_config, + .dsc_get_packed_pps = dsc2_get_packed_pps, + .dsc_enable = dsc401_enable, + .dsc_disable = dsc401_disable, + .dsc_disconnect = dsc401_disconnect, + .dsc_wait_disconnect_pending_clear = dsc401_wait_disconnect_pending_clear, + .dsc_get_single_enc_caps = dsc60_get_single_enc_caps, + .dsc_read_reg_state = dsc2_read_reg_state, + .set_fgcg = dsc60_set_fgcg, +}; + +/* Macro definitios for REG_SET macros*/ +#define CTX \ + dsc60->base.ctx + +#define REG(reg)\ + dsc60->dsc_regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + dsc60->dsc_shift->field_name, dsc60->dsc_mask->field_name +#define DC_LOGGER \ + dsc->ctx->logger + +void dsc60_set_fgcg(struct display_stream_compressor *dsc, bool enable) +{ + struct dcn60_dsc *dsc60 = TO_DCN60_DSC(dsc); + + REG_UPDATE(DSC_TOP_CONTROL, DSC_FGCG_REP_DIS, !enable); +} + +/* API functions (external or via structure->function_pointer) */ + +void dsc60_construct(struct dcn60_dsc *dsc, + struct dc_context *ctx, + int inst, + const struct dcn401_dsc_registers *dsc_regs, + const struct dcn60_dsc_shift *dsc_shift, + const struct dcn60_dsc_mask *dsc_mask) +{ + dsc->base.ctx = ctx; + dsc->base.inst = inst; + dsc->base.funcs = &dcn60_dsc_funcs; + + dsc->dsc_regs = dsc_regs; + dsc->dsc_shift = dsc_shift; + dsc->dsc_mask = dsc_mask; + + dsc->max_image_width = 5184; +} + +static void dsc60_init_reg_values(struct dsc60_reg_values *reg_vals) +{ + int i; + + memset(reg_vals, 0, sizeof(struct dsc60_reg_values)); + + /* Non-PPS values */ + reg_vals->dsc_clock_enable = 1; + reg_vals->dsc_clock_gating_disable = 0; + reg_vals->underflow_recovery_en = 0; + reg_vals->underflow_occurred_int_en = 0; + reg_vals->underflow_occurred_status = 0; + reg_vals->ich_reset_at_eol = 0; + reg_vals->alternate_ich_encoding_en = 0; + reg_vals->rc_buffer_model_size = 0; + /*reg_vals->disable_ich = 0;*/ + reg_vals->dsc_dbg_en = 0; + + for (i = 0; i < 8; i++) + reg_vals->rc_buffer_model_overflow_int_en[i] = 0; + + /* PPS values */ + reg_vals->pps.dsc_version_minor = 2; + reg_vals->pps.dsc_version_major = 1; + reg_vals->pps.line_buf_depth = 9; + reg_vals->pps.bits_per_component = 8; + reg_vals->pps.block_pred_enable = 1; + reg_vals->pps.slice_chunk_size = 0; + reg_vals->pps.pic_width = 0; + reg_vals->pps.pic_height = 0; + reg_vals->pps.slice_width = 0; + reg_vals->pps.slice_height = 0; + reg_vals->pps.initial_xmit_delay = 170; + reg_vals->pps.initial_dec_delay = 0; + reg_vals->pps.initial_scale_value = 0; + reg_vals->pps.scale_increment_interval = 0; + reg_vals->pps.scale_decrement_interval = 0; + reg_vals->pps.nfl_bpg_offset = 0; + reg_vals->pps.slice_bpg_offset = 0; + reg_vals->pps.nsl_bpg_offset = 0; + reg_vals->pps.initial_offset = 6144; + reg_vals->pps.final_offset = 0; + reg_vals->pps.flatness_min_qp = 3; + reg_vals->pps.flatness_max_qp = 12; + reg_vals->pps.rc_model_size = 8192; + reg_vals->pps.rc_edge_factor = 6; + reg_vals->pps.rc_quant_incr_limit0 = 11; + reg_vals->pps.rc_quant_incr_limit1 = 11; + reg_vals->pps.rc_tgt_offset_low = 3; + reg_vals->pps.rc_tgt_offset_high = 3; +} + +/*Updates dsc_reg_values::reg_vals::xxx fields based on the values from computed params. +* This is required because dscc_compute_dsc_parameters returns a modified PPS, which in turn +* affects non - PPS register values. +*/ +static void dsc60_update_from_dsc_parameters(struct dsc60_reg_values *reg_vals, const struct dsc_parameters *dsc_params) +{ + int i; + + reg_vals->pps = dsc_params->pps; + + // pps_computed will have the "expanded" values; need to shift them to make them fit for regs. + for (i = 0; i < NUM_BUF_RANGES - 1; i++) + reg_vals->pps.rc_buf_thresh[i] = reg_vals->pps.rc_buf_thresh[i] >> 6; + + reg_vals->rc_buffer_model_size = dsc_params->rc_buffer_model_size; +} + +static bool dsc60_prepare_config(const struct dsc_config *dsc_cfg, struct dsc60_reg_values *dsc_reg_vals, + struct dsc_optc_config *dsc_optc_cfg) +{ + struct dsc_parameters dsc_params; + struct rc_params rc; + + /* Validate input parameters */ + ASSERT(dsc_cfg->dc_dsc_cfg.num_slices_h); + ASSERT(dsc_cfg->dc_dsc_cfg.num_slices_v); + ASSERT(dsc_cfg->dc_dsc_cfg.version_minor == 1 || dsc_cfg->dc_dsc_cfg.version_minor == 2); + ASSERT(dsc_cfg->pic_width); + ASSERT(dsc_cfg->pic_height); + ASSERT((dsc_cfg->dc_dsc_cfg.version_minor == 1 && + (8 <= dsc_cfg->dc_dsc_cfg.linebuf_depth && dsc_cfg->dc_dsc_cfg.linebuf_depth <= 13)) || + (dsc_cfg->dc_dsc_cfg.version_minor == 2 && + ((8 <= dsc_cfg->dc_dsc_cfg.linebuf_depth && dsc_cfg->dc_dsc_cfg.linebuf_depth <= 15) || + dsc_cfg->dc_dsc_cfg.linebuf_depth == 0))); + ASSERT(96 <= dsc_cfg->dc_dsc_cfg.bits_per_pixel && dsc_cfg->dc_dsc_cfg.bits_per_pixel <= 0x3ff); // 6.0 <= bits_per_pixel <= 63.9375 + + if (!dsc_cfg->dc_dsc_cfg.num_slices_v || !dsc_cfg->dc_dsc_cfg.num_slices_h || + !(dsc_cfg->dc_dsc_cfg.version_minor == 1 || dsc_cfg->dc_dsc_cfg.version_minor == 2) || + !dsc_cfg->pic_width || !dsc_cfg->pic_height || + !((dsc_cfg->dc_dsc_cfg.version_minor == 1 && // v1.1 line buffer depth range: + 8 <= dsc_cfg->dc_dsc_cfg.linebuf_depth && dsc_cfg->dc_dsc_cfg.linebuf_depth <= 13) || + (dsc_cfg->dc_dsc_cfg.version_minor == 2 && // v1.2 line buffer depth range: + ((8 <= dsc_cfg->dc_dsc_cfg.linebuf_depth && dsc_cfg->dc_dsc_cfg.linebuf_depth <= 15) || + dsc_cfg->dc_dsc_cfg.linebuf_depth == 0))) || + !(96 <= dsc_cfg->dc_dsc_cfg.bits_per_pixel && dsc_cfg->dc_dsc_cfg.bits_per_pixel <= 0x3ff)) { + dm_output_to_console("%s: Invalid parameters\n", __func__); + return false; + } + + dsc60_init_reg_values(dsc_reg_vals); + + /* Copy input config */ + dsc_reg_vals->pixel_format = dsc_dc_pixel_encoding_to_dsc_pixel_format(dsc_cfg->pixel_encoding, dsc_cfg->dc_dsc_cfg.ycbcr422_simple); + dsc_reg_vals->num_slices_h = dsc_cfg->dc_dsc_cfg.num_slices_h; + dsc_reg_vals->num_slices_v = dsc_cfg->dc_dsc_cfg.num_slices_v; + dsc_reg_vals->pps.dsc_version_minor = (u8)dsc_cfg->dc_dsc_cfg.version_minor; + dsc_reg_vals->pps.pic_width = (u16)dsc_cfg->pic_width; + dsc_reg_vals->pps.pic_height = (u16)dsc_cfg->pic_height; + dsc_reg_vals->pps.bits_per_component = dsc_dc_color_depth_to_dsc_bits_per_comp(dsc_cfg->color_depth); + dsc_reg_vals->pps.block_pred_enable = dsc_cfg->dc_dsc_cfg.block_pred_enable; + dsc_reg_vals->pps.line_buf_depth = (u8)dsc_cfg->dc_dsc_cfg.linebuf_depth; + dsc_reg_vals->alternate_ich_encoding_en = dsc_reg_vals->pps.dsc_version_minor == 1 ? 0 : 1; + dsc_reg_vals->ich_reset_at_eol = (dsc_cfg->is_odm || dsc_reg_vals->num_slices_h > 1) ? 0xF : 0; + + // TODO: in addition to validating slice height (pic height must be divisible by slice height), + // see what happens when the same condition doesn't apply for slice_width/pic_width. + dsc_reg_vals->pps.slice_width = (u16)(dsc_cfg->pic_width / dsc_cfg->dc_dsc_cfg.num_slices_h); + dsc_reg_vals->pps.slice_height = (u16)(dsc_cfg->pic_height / dsc_cfg->dc_dsc_cfg.num_slices_v); + + ASSERT(dsc_reg_vals->pps.slice_height * dsc_cfg->dc_dsc_cfg.num_slices_v == dsc_cfg->pic_height); + if (!(dsc_reg_vals->pps.slice_height * dsc_cfg->dc_dsc_cfg.num_slices_v == dsc_cfg->pic_height)) { + dm_output_to_console("%s: pix height %d not divisible by num_slices_v %d\n\n", __func__, dsc_cfg->pic_height, dsc_cfg->dc_dsc_cfg.num_slices_v); + return false; + } + + dsc_reg_vals->bpp_x32 = dsc_cfg->dc_dsc_cfg.bits_per_pixel << 1; + if (dsc_reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR420 || dsc_reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR422) + dsc_reg_vals->pps.bits_per_pixel = (u16)dsc_reg_vals->bpp_x32; + else + dsc_reg_vals->pps.bits_per_pixel = (u16)(dsc_reg_vals->bpp_x32 >> 1); + + dsc_reg_vals->pps.convert_rgb = dsc_reg_vals->pixel_format == DSC_PIXFMT_RGB ? 1 : 0; + dsc_reg_vals->pps.native_422 = (dsc_reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR422); + dsc_reg_vals->pps.native_420 = (dsc_reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR420); + dsc_reg_vals->pps.simple_422 = (dsc_reg_vals->pixel_format == DSC_PIXFMT_SIMPLE_YCBCR422); + + calc_rc_params(&rc, &dsc_reg_vals->pps); + + if (dsc_cfg->dc_dsc_cfg.rc_params_ovrd) + dsc_override_rc_params(&rc, dsc_cfg->dc_dsc_cfg.rc_params_ovrd); + + if (dscc_compute_dsc_parameters(&dsc_reg_vals->pps, &rc, &dsc_params)) { + dm_output_to_console("%s: DSC config failed\n", __func__); + return false; + } + + dsc60_update_from_dsc_parameters(dsc_reg_vals, &dsc_params); + + dsc_optc_cfg->bytes_per_pixel = dsc_params.bytes_per_pixel; + dsc_optc_cfg->slice_width = dsc_reg_vals->pps.slice_width; + dsc_optc_cfg->is_pixel_format_444 = dsc_reg_vals->pixel_format == DSC_PIXFMT_RGB || + dsc_reg_vals->pixel_format == DSC_PIXFMT_YCBCR444 || + dsc_reg_vals->pixel_format == DSC_PIXFMT_SIMPLE_YCBCR422; + + return true; +} + +static void dsc60_write_to_registers(struct display_stream_compressor *dsc, const struct dsc60_reg_values *reg_vals) +{ + uint32_t temp_int; + struct dcn60_dsc *dsc60 = TO_DCN60_DSC(dsc); + + REG_SET(DSC_DEBUG_CONTROL, 0, + DSC_DBG_EN, reg_vals->dsc_dbg_en); + + // dscc registers + if (dsc60->dsc_mask->ICH_RESET_AT_END_OF_LINE == 0) { + REG_SET_3(DSCC_CONFIG0, 0, + NUMBER_OF_SLICES_PER_LINE, reg_vals->num_slices_h - 1, + ALTERNATE_ICH_ENCODING_EN, reg_vals->alternate_ich_encoding_en, + NUMBER_OF_SLICES_IN_VERTICAL_DIRECTION, reg_vals->num_slices_v - 1); + } else { + REG_SET_4(DSCC_CONFIG0, 0, ICH_RESET_AT_END_OF_LINE, + reg_vals->ich_reset_at_eol, NUMBER_OF_SLICES_PER_LINE, + reg_vals->num_slices_h - 1, ALTERNATE_ICH_ENCODING_EN, + reg_vals->alternate_ich_encoding_en, NUMBER_OF_SLICES_IN_VERTICAL_DIRECTION, + reg_vals->num_slices_v - 1); + } + + REG_SET(DSCC_CONFIG1, 0, + DSCC_RATE_CONTROL_BUFFER_MODEL_SIZE, reg_vals->rc_buffer_model_size); + + REG_SET_8(DSCC_INTERRUPT_CONTROL0, 0, + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN0, reg_vals->rc_buffer_model_overflow_int_en[0], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN1, reg_vals->rc_buffer_model_overflow_int_en[1], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN2, reg_vals->rc_buffer_model_overflow_int_en[2], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN3, reg_vals->rc_buffer_model_overflow_int_en[3], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN4, reg_vals->rc_buffer_model_overflow_int_en[4], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN5, reg_vals->rc_buffer_model_overflow_int_en[5], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN6, reg_vals->rc_buffer_model_overflow_int_en[6], + DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN7, reg_vals->rc_buffer_model_overflow_int_en[7]); + + REG_SET_3(DSCC_PPS_CONFIG0, 0, + DSC_VERSION_MINOR, reg_vals->pps.dsc_version_minor, + LINEBUF_DEPTH, reg_vals->pps.line_buf_depth, + DSCC_PPS_CONFIG0__BITS_PER_COMPONENT, reg_vals->pps.bits_per_component); + + if (reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR420 || reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR422) + temp_int = reg_vals->bpp_x32; + else + temp_int = reg_vals->bpp_x32 >> 1; + + REG_SET_7(DSCC_PPS_CONFIG1, 0, + BITS_PER_PIXEL, temp_int, + SIMPLE_422, reg_vals->pixel_format == DSC_PIXFMT_SIMPLE_YCBCR422, + CONVERT_RGB, reg_vals->pixel_format == DSC_PIXFMT_RGB, + BLOCK_PRED_ENABLE, reg_vals->pps.block_pred_enable, + NATIVE_422, reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR422, + NATIVE_420, reg_vals->pixel_format == DSC_PIXFMT_NATIVE_YCBCR420, + CHUNK_SIZE, reg_vals->pps.slice_chunk_size); + + REG_SET_2(DSCC_PPS_CONFIG2, 0, + PIC_WIDTH, reg_vals->pps.pic_width, + PIC_HEIGHT, reg_vals->pps.pic_height); + + REG_SET_2(DSCC_PPS_CONFIG3, 0, + SLICE_WIDTH, reg_vals->pps.slice_width, + SLICE_HEIGHT, reg_vals->pps.slice_height); + + REG_SET(DSCC_PPS_CONFIG4, 0, + INITIAL_XMIT_DELAY, reg_vals->pps.initial_xmit_delay); + + REG_SET_2(DSCC_PPS_CONFIG5, 0, + INITIAL_SCALE_VALUE, reg_vals->pps.initial_scale_value, + SCALE_INCREMENT_INTERVAL, reg_vals->pps.scale_increment_interval); + + REG_SET_3(DSCC_PPS_CONFIG6, 0, + SCALE_DECREMENT_INTERVAL, reg_vals->pps.scale_decrement_interval, + FIRST_LINE_BPG_OFFSET, reg_vals->pps.first_line_bpg_offset, + SECOND_LINE_BPG_OFFSET, reg_vals->pps.second_line_bpg_offset); + + REG_SET_2(DSCC_PPS_CONFIG7, 0, + NFL_BPG_OFFSET, reg_vals->pps.nfl_bpg_offset, + SLICE_BPG_OFFSET, reg_vals->pps.slice_bpg_offset); + + REG_SET_2(DSCC_PPS_CONFIG8, 0, + NSL_BPG_OFFSET, reg_vals->pps.nsl_bpg_offset, + SECOND_LINE_OFFSET_ADJ, reg_vals->pps.second_line_offset_adj); + + REG_SET_2(DSCC_PPS_CONFIG9, 0, + INITIAL_OFFSET, reg_vals->pps.initial_offset, + FINAL_OFFSET, reg_vals->pps.final_offset); + + REG_SET_3(DSCC_PPS_CONFIG10, 0, + FLATNESS_MIN_QP, reg_vals->pps.flatness_min_qp, + FLATNESS_MAX_QP, reg_vals->pps.flatness_max_qp, + RC_MODEL_SIZE, reg_vals->pps.rc_model_size); + + REG_SET_5(DSCC_PPS_CONFIG11, 0, + RC_EDGE_FACTOR, reg_vals->pps.rc_edge_factor, + RC_QUANT_INCR_LIMIT0, reg_vals->pps.rc_quant_incr_limit0, + RC_QUANT_INCR_LIMIT1, reg_vals->pps.rc_quant_incr_limit1, + RC_TGT_OFFSET_LO, reg_vals->pps.rc_tgt_offset_low, + RC_TGT_OFFSET_HI, reg_vals->pps.rc_tgt_offset_high); + + REG_SET_4(DSCC_PPS_CONFIG12, 0, + RC_BUF_THRESH0, reg_vals->pps.rc_buf_thresh[0], + RC_BUF_THRESH1, reg_vals->pps.rc_buf_thresh[1], + RC_BUF_THRESH2, reg_vals->pps.rc_buf_thresh[2], + RC_BUF_THRESH3, reg_vals->pps.rc_buf_thresh[3]); + + REG_SET_4(DSCC_PPS_CONFIG13, 0, + RC_BUF_THRESH4, reg_vals->pps.rc_buf_thresh[4], + RC_BUF_THRESH5, reg_vals->pps.rc_buf_thresh[5], + RC_BUF_THRESH6, reg_vals->pps.rc_buf_thresh[6], + RC_BUF_THRESH7, reg_vals->pps.rc_buf_thresh[7]); + + REG_SET_4(DSCC_PPS_CONFIG14, 0, + RC_BUF_THRESH8, reg_vals->pps.rc_buf_thresh[8], + RC_BUF_THRESH9, reg_vals->pps.rc_buf_thresh[9], + RC_BUF_THRESH10, reg_vals->pps.rc_buf_thresh[10], + RC_BUF_THRESH11, reg_vals->pps.rc_buf_thresh[11]); + + REG_SET_5(DSCC_PPS_CONFIG15, 0, + RC_BUF_THRESH12, reg_vals->pps.rc_buf_thresh[12], + RC_BUF_THRESH13, reg_vals->pps.rc_buf_thresh[13], + RANGE_MIN_QP0, reg_vals->pps.rc_range_params[0].range_min_qp, + RANGE_MAX_QP0, reg_vals->pps.rc_range_params[0].range_max_qp, + RANGE_BPG_OFFSET0, reg_vals->pps.rc_range_params[0].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG16, 0, + RANGE_MIN_QP1, reg_vals->pps.rc_range_params[1].range_min_qp, + RANGE_MAX_QP1, reg_vals->pps.rc_range_params[1].range_max_qp, + RANGE_BPG_OFFSET1, reg_vals->pps.rc_range_params[1].range_bpg_offset, + RANGE_MIN_QP2, reg_vals->pps.rc_range_params[2].range_min_qp, + RANGE_MAX_QP2, reg_vals->pps.rc_range_params[2].range_max_qp, + RANGE_BPG_OFFSET2, reg_vals->pps.rc_range_params[2].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG17, 0, + RANGE_MIN_QP3, reg_vals->pps.rc_range_params[3].range_min_qp, + RANGE_MAX_QP3, reg_vals->pps.rc_range_params[3].range_max_qp, + RANGE_BPG_OFFSET3, reg_vals->pps.rc_range_params[3].range_bpg_offset, + RANGE_MIN_QP4, reg_vals->pps.rc_range_params[4].range_min_qp, + RANGE_MAX_QP4, reg_vals->pps.rc_range_params[4].range_max_qp, + RANGE_BPG_OFFSET4, reg_vals->pps.rc_range_params[4].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG18, 0, + RANGE_MIN_QP5, reg_vals->pps.rc_range_params[5].range_min_qp, + RANGE_MAX_QP5, reg_vals->pps.rc_range_params[5].range_max_qp, + RANGE_BPG_OFFSET5, reg_vals->pps.rc_range_params[5].range_bpg_offset, + RANGE_MIN_QP6, reg_vals->pps.rc_range_params[6].range_min_qp, + RANGE_MAX_QP6, reg_vals->pps.rc_range_params[6].range_max_qp, + RANGE_BPG_OFFSET6, reg_vals->pps.rc_range_params[6].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG19, 0, + RANGE_MIN_QP7, reg_vals->pps.rc_range_params[7].range_min_qp, + RANGE_MAX_QP7, reg_vals->pps.rc_range_params[7].range_max_qp, + RANGE_BPG_OFFSET7, reg_vals->pps.rc_range_params[7].range_bpg_offset, + RANGE_MIN_QP8, reg_vals->pps.rc_range_params[8].range_min_qp, + RANGE_MAX_QP8, reg_vals->pps.rc_range_params[8].range_max_qp, + RANGE_BPG_OFFSET8, reg_vals->pps.rc_range_params[8].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG20, 0, + RANGE_MIN_QP9, reg_vals->pps.rc_range_params[9].range_min_qp, + RANGE_MAX_QP9, reg_vals->pps.rc_range_params[9].range_max_qp, + RANGE_BPG_OFFSET9, reg_vals->pps.rc_range_params[9].range_bpg_offset, + RANGE_MIN_QP10, reg_vals->pps.rc_range_params[10].range_min_qp, + RANGE_MAX_QP10, reg_vals->pps.rc_range_params[10].range_max_qp, + RANGE_BPG_OFFSET10, reg_vals->pps.rc_range_params[10].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG21, 0, + RANGE_MIN_QP11, reg_vals->pps.rc_range_params[11].range_min_qp, + RANGE_MAX_QP11, reg_vals->pps.rc_range_params[11].range_max_qp, + RANGE_BPG_OFFSET11, reg_vals->pps.rc_range_params[11].range_bpg_offset, + RANGE_MIN_QP12, reg_vals->pps.rc_range_params[12].range_min_qp, + RANGE_MAX_QP12, reg_vals->pps.rc_range_params[12].range_max_qp, + RANGE_BPG_OFFSET12, reg_vals->pps.rc_range_params[12].range_bpg_offset); + + REG_SET_6(DSCC_PPS_CONFIG22, 0, + RANGE_MIN_QP13, reg_vals->pps.rc_range_params[13].range_min_qp, + RANGE_MAX_QP13, reg_vals->pps.rc_range_params[13].range_max_qp, + RANGE_BPG_OFFSET13, reg_vals->pps.rc_range_params[13].range_bpg_offset, + RANGE_MIN_QP14, reg_vals->pps.rc_range_params[14].range_min_qp, + RANGE_MAX_QP14, reg_vals->pps.rc_range_params[14].range_max_qp, + RANGE_BPG_OFFSET14, reg_vals->pps.rc_range_params[14].range_bpg_offset); + +} + +void dsc60_set_config(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg, + struct dsc_optc_config *dsc_optc_cfg) +{ + bool is_config_ok; + struct dcn60_dsc *dsc60 = TO_DCN60_DSC(dsc); + + DC_LOG_DSC("Setting DSC Config at DSC inst %d", dsc->inst); + dsc_config_log(dsc, dsc_cfg); + is_config_ok = dsc60_prepare_config(dsc_cfg, &dsc60->reg_vals, dsc_optc_cfg); + ASSERT(is_config_ok); + DC_LOG_DSC("programming DSC Picture Parameter Set (PPS):"); + dsc_log_pps(dsc, &dsc60->reg_vals.pps); + dsc60_write_to_registers(dsc, &dsc60->reg_vals); +} + +void dsc60_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsigned int max_dscclk_khz) +{ + dsc_enc_caps->dsc_version = 0x21; /* v1.2 - DP spec defined it in reverse order and we kept it */ + + dsc_enc_caps->slice_caps.bits.NUM_SLICES_1 = 1; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_2 = 1; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_3 = 1; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_4 = 1; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_8 = 1; + + dsc_enc_caps->lb_bit_depth = 13; + dsc_enc_caps->is_block_pred_supported = true; + + dsc_enc_caps->color_formats.bits.RGB = 1; + dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; + dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; + + dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; + dsc_enc_caps->color_depth.bits.COLOR_DEPTH_10_BPC = 1; + dsc_enc_caps->color_depth.bits.COLOR_DEPTH_12_BPC = 1; + dsc_enc_caps->max_total_throughput_mps = max_dscclk_khz * 3 / 1000; + + dsc_enc_caps->max_slice_width = 5760; /* (including 64 overlap pixels for eDP MSO mode) */ + dsc_enc_caps->bpp_increment_div = 16; /* 1/16th of a bit */ +} diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.h b/drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.h new file mode 100644 index 000000000000..b7f2d8031121 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn60/dcn60_dsc.h @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DCN60_DSC_H__ +#define __DCN60_DSC_H__ + +#include "dsc.h" +#include "dsc/dscc_types.h" +#include "dcn20/dcn20_dsc.h" +#include "dcn401/dcn401_dsc.h" +#include + +#define TO_DCN60_DSC(dsc)\ + container_of(dsc, struct dcn60_dsc, base) + +#define DSC_REG_LIST_SH_MASK_DCN60(mask_sh)\ + DSC_SF(DSC_TOP0_DSC_TOP_CONTROL, DSC_CLOCK_EN, mask_sh), \ + DSC_SF(DSC_TOP0_DSC_TOP_CONTROL, DSC_DISPCLK_R_GATE_DIS, mask_sh), \ + DSC_SF(DSC_TOP0_DSC_TOP_CONTROL, DSC_DSCCLK_R_GATE_DIS, mask_sh), \ + DSC_SF(DSC_TOP0_DSC_TOP_CONTROL, DSC_FGCG_REP_DIS, mask_sh), \ + DSC_SF(DSC_TOP0_DSC_DEBUG_CONTROL, DSC_DBG_EN, mask_sh), \ + DSC_SF(DSC_TOP0_DSC_DEBUG_CONTROL, DSC_TEST_CLOCK_MUX_SEL, mask_sh), \ + DSC_SF(DSCC0_DSCC_CONFIG0, ICH_RESET_AT_END_OF_LINE, mask_sh), \ + DSC_SF(DSCC0_DSCC_CONFIG0, NUMBER_OF_SLICES_PER_LINE, mask_sh), \ + DSC_SF(DSCC0_DSCC_CONFIG0, ALTERNATE_ICH_ENCODING_EN, mask_sh), \ + DSC_SF(DSCC0_DSCC_CONFIG0, NUMBER_OF_SLICES_IN_VERTICAL_DIRECTION, mask_sh), \ + DSC_SF(DSCC0_DSCC_CONFIG1, DSCC_RATE_CONTROL_BUFFER_MODEL_SIZE, mask_sh), \ + DSC_SF(DSCC0_DSCC_STATUS, DSCC_DOUBLE_BUFFER_REG_UPDATE_PENDING, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_CLEAR0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_CLEAR1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_CLEAR2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_CLEAR3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN4, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN5, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN6, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL0, DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN7, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_STATUS2, DSCC_END_OF_FRAME_NOT_REACHED_OCCURRED, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_OVERFLOW_CLEAR0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_OVERFLOW_CLEAR1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_OVERFLOW_CLEAR2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_OVERFLOW_CLEAR3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_UNDERFLOW_CLEAR0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_UNDERFLOW_CLEAR1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_UNDERFLOW_CLEAR2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR1, DSCC_OUTPUT_BUFFER_UNDERFLOW_CLEAR3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CLEAR2, DSCC_END_OF_FRAME_NOT_REACHED_CLEAR, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED_INT_EN0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED_INT_EN1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED_INT_EN2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_OVERFLOW_OCCURRED_INT_EN3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED_INT_EN0, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED_INT_EN1, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED_INT_EN2, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL1, DSCC_OUTPUT_BUFFER_UNDERFLOW_OCCURRED_INT_EN3, mask_sh), \ + DSC_SF(DSCC0_DSCC_INTERRUPT_CONTROL2, DSCC_END_OF_FRAME_NOT_REACHED_OCCURRED_INT_EN, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG0, DSC_VERSION_MINOR, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG0, DSC_VERSION_MAJOR, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG0, PPS_IDENTIFIER, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG0, LINEBUF_DEPTH, mask_sh), \ + DSC2_SF(DSCC0, DSCC_PPS_CONFIG0__BITS_PER_COMPONENT, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, BITS_PER_PIXEL, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, VBR_ENABLE, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, SIMPLE_422, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, CONVERT_RGB, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, BLOCK_PRED_ENABLE, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, NATIVE_422, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, NATIVE_420, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG1, CHUNK_SIZE, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG2, PIC_WIDTH, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG2, PIC_HEIGHT, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG3, SLICE_WIDTH, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG3, SLICE_HEIGHT, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG4, INITIAL_XMIT_DELAY, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG4, INITIAL_DEC_DELAY, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG5, INITIAL_SCALE_VALUE, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG5, SCALE_INCREMENT_INTERVAL, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG6, SCALE_DECREMENT_INTERVAL, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG6, FIRST_LINE_BPG_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG6, SECOND_LINE_BPG_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG7, NFL_BPG_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG7, SLICE_BPG_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG8, NSL_BPG_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG8, SECOND_LINE_OFFSET_ADJ, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG9, INITIAL_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG9, FINAL_OFFSET, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG10, FLATNESS_MIN_QP, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG10, FLATNESS_MAX_QP, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG10, RC_MODEL_SIZE, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG11, RC_EDGE_FACTOR, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG11, RC_QUANT_INCR_LIMIT0, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG11, RC_QUANT_INCR_LIMIT1, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG11, RC_TGT_OFFSET_LO, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG11, RC_TGT_OFFSET_HI, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG12, RC_BUF_THRESH0, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG12, RC_BUF_THRESH1, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG12, RC_BUF_THRESH2, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG12, RC_BUF_THRESH3, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG13, RC_BUF_THRESH4, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG13, RC_BUF_THRESH5, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG13, RC_BUF_THRESH6, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG13, RC_BUF_THRESH7, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG14, RC_BUF_THRESH8, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG14, RC_BUF_THRESH9, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG14, RC_BUF_THRESH10, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG14, RC_BUF_THRESH11, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG15, RC_BUF_THRESH12, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG15, RC_BUF_THRESH13, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG15, RANGE_MIN_QP0, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG15, RANGE_MAX_QP0, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG15, RANGE_BPG_OFFSET0, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG16, RANGE_MIN_QP1, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG16, RANGE_MAX_QP1, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG16, RANGE_BPG_OFFSET1, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG16, RANGE_MIN_QP2, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG16, RANGE_MAX_QP2, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG16, RANGE_BPG_OFFSET2, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG17, RANGE_MIN_QP3, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG17, RANGE_MAX_QP3, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG17, RANGE_BPG_OFFSET3, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG17, RANGE_MIN_QP4, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG17, RANGE_MAX_QP4, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG17, RANGE_BPG_OFFSET4, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG18, RANGE_MIN_QP5, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG18, RANGE_MAX_QP5, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG18, RANGE_BPG_OFFSET5, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG18, RANGE_MIN_QP6, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG18, RANGE_MAX_QP6, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG18, RANGE_BPG_OFFSET6, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG19, RANGE_MIN_QP7, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG19, RANGE_MAX_QP7, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG19, RANGE_BPG_OFFSET7, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG19, RANGE_MIN_QP8, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG19, RANGE_MAX_QP8, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG19, RANGE_BPG_OFFSET8, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG20, RANGE_MIN_QP9, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG20, RANGE_MAX_QP9, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG20, RANGE_BPG_OFFSET9, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG20, RANGE_MIN_QP10, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG20, RANGE_MAX_QP10, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG20, RANGE_BPG_OFFSET10, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG21, RANGE_MIN_QP11, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG21, RANGE_MAX_QP11, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG21, RANGE_BPG_OFFSET11, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG21, RANGE_MIN_QP12, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG21, RANGE_MAX_QP12, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG21, RANGE_BPG_OFFSET12, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG22, RANGE_MIN_QP13, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG22, RANGE_MAX_QP13, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG22, RANGE_BPG_OFFSET13, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG22, RANGE_MIN_QP14, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG22, RANGE_MAX_QP14, mask_sh), \ + DSC_SF(DSCC0_DSCC_PPS_CONFIG22, RANGE_BPG_OFFSET14, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL0, DSCC_DEFAULT_MEM_LOW_POWER_STATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL0, DSCC_MEM_PWR_FORCE, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL0, DSCC_MEM_PWR_DIS, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL0, DSCC_MEM_PWR_STATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL1, DSCC_DEFAULT_MEM_LOW_POWER_STATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL1, DSCC_MEM_PWR_FORCE, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL1, DSCC_MEM_PWR_DIS, mask_sh), \ + DSC_SF(DSCC0_DSCC_MEM_POWER_CONTROL1, DSCC_MEM_PWR_STATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_TEST_DEBUG_BUS_ROTATE, DSCC_TEST_DEBUG_BUS0_ROTATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_TEST_DEBUG_BUS_ROTATE, DSCC_TEST_DEBUG_BUS1_ROTATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_TEST_DEBUG_BUS_ROTATE, DSCC_TEST_DEBUG_BUS2_ROTATE, mask_sh), \ + DSC_SF(DSCC0_DSCC_TEST_DEBUG_BUS_ROTATE, DSCC_TEST_DEBUG_BUS3_ROTATE, mask_sh), \ + DSC_SF(DSCRM0_DSCRM_DSC_FORWARD_CONFIG, DSCRM_DSC_FORWARD_EN, mask_sh), \ + DSC_SF(DSCRM0_DSCRM_DSC_FORWARD_CONFIG, DSCRM_DSC_OPP_PIPE_SOURCE, mask_sh), \ + DSC_SF(DSCRM0_DSCRM_DSC_FORWARD_CONFIG, DSCRM_DSC_FORWARD_EN_STATUS, mask_sh) + +#define DSC_FIELD_LIST_DCN60(type) \ + struct { \ + DSC_FIELD_LIST_DCN401(type); \ + type DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN4; \ + type DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN5; \ + type DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN6; \ + type DSCC_RATE_CONTROL_BUFFER_MODEL_OVERFLOW_OCCURRED_INT_EN7; \ + } + +struct dcn60_dsc_shift { + DSC_FIELD_LIST_DCN60(uint8_t); +}; + +struct dcn60_dsc_mask { + DSC_FIELD_LIST_DCN60(uint32_t); +}; + +struct dsc60_reg_values { + /* PPS registers */ + struct drm_dsc_config pps; + + /* Additional registers */ + uint32_t dsc_clock_enable; + uint32_t dsc_clock_gating_disable; + uint32_t underflow_recovery_en; + uint32_t underflow_occurred_int_en; + uint32_t underflow_occurred_status; + enum dsc_pixel_format pixel_format; + uint32_t ich_reset_at_eol; + uint32_t alternate_ich_encoding_en; + uint32_t num_slices_h; + uint32_t num_slices_v; + uint32_t rc_buffer_model_size; + uint32_t disable_ich; + uint32_t bpp_x32; + uint32_t dsc_dbg_en; + uint32_t rc_buffer_model_overflow_int_en[8]; +}; + +struct dcn60_dsc { + struct display_stream_compressor base; + const struct dcn401_dsc_registers *dsc_regs; + const struct dcn60_dsc_shift *dsc_shift; + const struct dcn60_dsc_mask *dsc_mask; + struct dsc60_reg_values reg_vals; + int max_image_width; +}; + +void dsc60_construct(struct dcn60_dsc *dsc, + struct dc_context *ctx, + int inst, + const struct dcn401_dsc_registers *dsc_regs, + const struct dcn60_dsc_shift *dsc_shift, + const struct dcn60_dsc_mask *dsc_mask); + +void dsc60_set_fgcg(struct display_stream_compressor *dsc, bool enable); +void dsc60_set_config(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg, + struct dsc_optc_config *dsc_optc_cfg); + +void dsc60_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsigned int max_dscclk_khz); + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.c new file mode 100644 index 000000000000..72e4d5141901 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + +#include "../hw_gpio.h" +#include "../hw_ddc.h" +#include "../hw_hpd.h" +#include "../hw_generic.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" +#include "dpcs/dpcs_6_0_0_offset.h" +#include "dpcs/dpcs_6_0_0_sh_mask.h" + +#include "reg_helper.h" +#include "../hpd_regs.h" +#include "hw_factory_dcn60.h" + +#define DCN_BASE__INST0_SEG2 0x000034C0 + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +/* DCN */ +#define block HPD +#define reg_num 0 + +#undef BASE_INNER +#define BASE_INNER(seg) DCN_BASE__INST0_SEG ## seg + +#define BASE(seg) BASE_INNER(seg) + +#define REG(reg_name)\ + BASE(reg ## reg_name ## _BASE_IDX) + reg ## reg_name + +#define SF_HPD(reg_name, field_name, post_fix)\ + .field_name = HPD0_ ## reg_name ## __ ## field_name ## post_fix + +#define REGI(reg_name, block, id)\ + BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + +// DC_GPIO_HPD_* registers are gone. +#undef HPD_REG_LIST +#define HPD_REG_LIST(id) \ + .int_status = REGI(DC_HPD_INT_STATUS, HPD, id),\ + .toggle_filt_cntl = REGI(DC_HPD_TOGGLE_FILT_CNTL, HPD, id) + +#define hpd_regs(id) \ +{\ + HPD_REG_LIST(id)\ +} + +static const struct hpd_registers hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), +}; + +static const struct hpd_sh_mask hpd_shift = { + HPD_MASK_SH_LIST(__SHIFT) +}; + +static const struct hpd_sh_mask hpd_mask = { + HPD_MASK_SH_LIST(_MASK) +}; + +#include "../ddc_regs.h" + + /* set field name */ +#define SF_DDC(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define DDC_REG_LIST_DCN6(id)\ + .ddc_setup = REG(DC_I2C_DDC ## id ## _SETUP),\ + .phy_aux_cntl = REG(PHY_AUX_CNTL),\ + .dc_gpio_aux_ctrl_5 = REG(DC_GPIO_AUX_CTRL_5) + +#define I3CPAD_REG_LIST_DCN6(id) \ + .dc_i3cpad_control0 = REG(DC_I3C ## id ## _DC_I3CPAD_CONTROL0),\ + .dc_i3cpad_control1 = REG(DC_I3C ## id ## _DC_I3CPAD_CONTROL1) + +#define ddc_regs_dcn6(id, i3cpad_id) \ +{\ + DDC_REG_LIST_DCN6(id), \ + I3CPAD_REG_LIST_DCN6(i3cpad_id) \ +} + +static const struct ddc_registers ddc_regs[] = { + ddc_regs_dcn6(1, 0), + ddc_regs_dcn6(2, 1) +}; + +#define DDC_MASK_SH_LIST_DCN6(mask_sh) \ + {SF_DDC(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE, mask_sh),\ + SF_DDC(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_EDID_DETECT_ENABLE, mask_sh),\ + SF_DDC(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_EDID_DETECT_MODE, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_DDCCLK_MASK, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_DDCDATA_MASK, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_CLK_A, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_DATA_A, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_CLK_EN, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_DATA_EN, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_CLK_Y, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_DATA_Y, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL0, DC_I3CPAD_PD_EN, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL1, DC_I3CPAD_STR, mask_sh),\ + SF_DDC(DC_I3C0_DC_I3CPAD_CONTROL1, DC_I3CPAD_RXSEL, mask_sh)} + +static const struct ddc_sh_mask ddc_shift[] = { + DDC_MASK_SH_LIST_DCN6(__SHIFT), + DDC_MASK_SH_LIST_DCN6(__SHIFT) +}; + +static const struct ddc_sh_mask ddc_mask[] = { + DDC_MASK_SH_LIST_DCN6(_MASK), + DDC_MASK_SH_LIST_DCN6(_MASK) +}; + +#include "../generic_regs.h" + +/* set field name */ +#define SF_GENERIC(reg_name, field_name, post_fix)\ + .field_name = 0 + +static const struct generic_registers generic_regs[] = { + {{ 0 }}, + {{ 0 }}, +}; + +static const struct generic_sh_mask generic_shift[] = { + { 0 }, + { 0 }, +}; + +static const struct generic_sh_mask generic_mask[] = { + { 0 }, + { 0 }, +}; + +static void dcn60_define_generic_registers(struct hw_gpio_pin *pin, uint32_t en) +{ + struct hw_generic *generic = HW_GENERIC_FROM_BASE(pin); + + generic->regs = &generic_regs[en]; + generic->shifts = &generic_shift[en]; + generic->masks = &generic_mask[en]; + generic->base.regs = &generic_regs[en].gpio; +} + +static void dcn60_define_ddc_registers( + struct hw_gpio_pin *pin, + uint32_t en) +{ + struct hw_ddc *ddc = HW_DDC_FROM_BASE(pin); + + switch (pin->id) { + case GPIO_ID_DDC_DATA: + case GPIO_ID_DDC_CLOCK: + ddc->regs = &ddc_regs[en]; + ddc->base.regs = &ddc_regs[en].gpio; + break; + default: + ASSERT_CRITICAL(false); + return; + } + + ddc->shifts = &ddc_shift[en]; + ddc->masks = &ddc_mask[en]; + +} + +static void dcn60_define_hpd_registers(struct hw_gpio_pin *pin, uint32_t en) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(pin); + + hpd->regs = &hpd_regs[en]; + hpd->shifts = &hpd_shift; + hpd->masks = &hpd_mask; + hpd->base.regs = &hpd_regs[en].gpio; +} + +/* function table */ +static const struct hw_factory_funcs funcs = { + .init_ddc_data = dal_hw_ddc_init_i3cpad, + .init_generic = dal_hw_generic_init, + .init_hpd = dal_hw_hpd_init, + .get_ddc_pin = dal_hw_ddc_get_pin, + .get_hpd_pin = dal_hw_hpd_get_pin, + .get_generic_pin = dal_hw_generic_get_pin, + .define_hpd_registers = dcn60_define_hpd_registers, + .define_ddc_registers = dcn60_define_ddc_registers, + .define_generic_registers = dcn60_define_generic_registers +}; + +/* + * dal_hw_factory_dcn60_init + * + * @brief + * Initialize HW factory function pointers and pin info + * + * @param + * struct hw_factory *factory - [out] struct of function pointers + */ +void dal_hw_factory_dcn60_init(struct hw_factory *factory) +{ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 2; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 2; + factory->number_of_pins[GPIO_ID_GENERIC] = 2; + factory->number_of_pins[GPIO_ID_HPD] = 4; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 0; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 0; + factory->number_of_pins[GPIO_ID_GSL] = 0; + + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.h b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.h new file mode 100644 index 000000000000..092e68cdf63c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_factory_dcn60.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DAL_HW_FACTORY_DCN60_H__ +#define __DAL_HW_FACTORY_DCN60_H__ + +/* Initialize HW factory function pointers and pin info */ +void dal_hw_factory_dcn60_init(struct hw_factory *factory); + +#endif /* __DAL_HW_FACTORY_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.c new file mode 100644 index 000000000000..2639988153cb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "hw_translate_dcn60.h" + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_translate.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" +#include "dpcs/dpcs_6_0_0_offset.h" +#include "dpcs/dpcs_6_0_0_sh_mask.h" + +#define DCN_BASE__INST0_SEG2 0x000034C0 + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +/* DCN */ +#define block HPD +#define reg_num 0 + +#undef BASE_INNER +#define BASE_INNER(seg) DCN_BASE__INST0_SEG ## seg + +#define BASE(seg) BASE_INNER(seg) + +#undef REG +#define REG(reg_name)\ + BASE(reg ## reg_name ## _BASE_IDX) + reg ## reg_name +#define SF_HPD(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + +static bool dcn60_offset_to_id( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + (void)mask; + switch (offset) { + /* HPD */ + case REG(HPD0_DC_HPD_INT_STATUS): + *id = GPIO_ID_HPD; + *en = GPIO_HPD_1; + return true; + case REG(HPD1_DC_HPD_INT_STATUS): + *id = GPIO_ID_HPD; + *en = GPIO_HPD_2; + return true; + case REG(HPD2_DC_HPD_INT_STATUS): + *id = GPIO_ID_HPD; + *en = GPIO_HPD_3; + return true; + case REG(HPD3_DC_HPD_INT_STATUS): + *id = GPIO_ID_HPD; + *en = GPIO_HPD_4; + return true; + /* DDC */ + /* we don't care about the GPIO_ID for DDC + * it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method + */ + case REG(DC_I3C0_DC_I3CPAD_CONTROL0): + *en = GPIO_DDC_LINE_DDC1; + return true; + case REG(DC_I3C1_DC_I3CPAD_CONTROL0): + *en = GPIO_DDC_LINE_DDC2; + return true; + + /* UNEXPECTED */ + default: + ASSERT_CRITICAL(false); + return false; + } +} + +static bool dcn60_id_to_offset( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + bool result = true; + + switch (id) { + case GPIO_ID_DDC_DATA: + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = REG(DC_I3C0_DC_I3CPAD_CONTROL0); + break; + case GPIO_DDC_LINE_DDC2: + info->offset = REG(DC_I3C1_DC_I3CPAD_CONTROL0); + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_DDC_CLOCK: + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = REG(DC_I3C0_DC_I3CPAD_CONTROL0); + break; + case GPIO_DDC_LINE_DDC2: + info->offset = REG(DC_I3C1_DC_I3CPAD_CONTROL0); + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_HPD: + switch (en) { + case GPIO_HPD_1: + info->offset = REG(HPD0_DC_HPD_INT_STATUS); + info->mask = HPD0_DC_HPD_INT_STATUS__DC_HPD_SENSE_MASK; + break; + case GPIO_HPD_2: + info->offset = REG(HPD1_DC_HPD_INT_STATUS); + info->mask = HPD0_DC_HPD_INT_STATUS__DC_HPD_SENSE_MASK; + break; + case GPIO_HPD_3: + info->offset = REG(HPD2_DC_HPD_INT_STATUS); + info->mask = HPD0_DC_HPD_INT_STATUS__DC_HPD_SENSE_MASK; + break; + case GPIO_HPD_4: + info->offset = REG(HPD3_DC_HPD_INT_STATUS); + info->mask = HPD0_DC_HPD_INT_STATUS__DC_HPD_SENSE_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + + return result; +} + +/* function table */ +static const struct hw_translate_funcs funcs = { + .offset_to_id = dcn60_offset_to_id, + .id_to_offset = dcn60_id_to_offset, +}; + +/* + * dal_hw_translate_dcn60_init + * + * @brief + * Initialize Hw translate function pointers. + * + * @param + * struct hw_translate *tr - [out] struct of function pointers + * + */ +void dal_hw_translate_dcn60_init(struct hw_translate *tr) +{ + tr->funcs = &funcs; +} + diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.h b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.h new file mode 100644 index 000000000000..33cbee6aeb5b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn60/hw_translate_dcn60.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DAL_HW_TRANSLATE_DCN60_H__ +#define __DAL_HW_TRANSLATE_DCN60_H__ + +struct hw_translate; + +/* Initialize Hw translate function pointers */ +void dal_hw_translate_dcn60_init(struct hw_translate *tr); + +#endif /* __DAL_HW_TRANSLATE_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.c new file mode 100644 index 000000000000..404b6cf40814 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dc_bios_types.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" +#include "dcn60_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 + +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_enc60_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: + cntl.pixel_clock = 666667; + break; + case HDMI_FRL_LINK_RATE_16GBPS: + cntl.pixel_clock = 888889; + break; + case HDMI_FRL_LINK_RATE_20GBPS: + default: + cntl.pixel_clock = 1111111; + 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(); + } +} + +static struct hpo_frl_link_encoder_funcs dcn60_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_enc60_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_encoder60_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 = &dcn60_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/dcn60/dcn60_hpo_frl_link_encoder.h b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.h new file mode 100644 index 000000000000..40ecad3af406 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_link_encoder.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DAL_DCN60_HPO_FRL_LINK_ENCODER_H__ +#define __DAL_DCN60_HPO_FRL_LINK_ENCODER_H__ + +#include "link_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" + +void hpo_frl_link_encoder60_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/dcn60/dcn60_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c new file mode 100644 index 000000000000..3f3704b68021 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dc_bios_types.h" +#include "core_types.h" +#include "dcn60_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn401/dcn401_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 + +/* setup stream encoder in hdmi mode */ +/* Precondition: link is trained */ +static void hpo_enc60_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_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING_TYPE, 0, + FIFO_UNCOMPRESSED_PIXEL_FORMAT, 0); + break; + case PIXEL_ENCODING_YCBCR420: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 2); + REG_UPDATE_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING_TYPE, 0, + FIFO_UNCOMPRESSED_PIXEL_FORMAT, 1); + break; + default: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 0); + REG_UPDATE_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING_TYPE, 0, + 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 */ +// 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_DEBUG("Exiting [%s]\n", __func__); +} + +static void hpo_enc60_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); +} + +//Covered both, rounding up or rounding down from FRL Link Rate /18. +static const struct frl_audio_clock_info frl_audio_clock_info_table[16] = { + {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}, + {888888, 4032, 875000, 3969, 625000, 6048, 875000}, + {888889, 4032, 875000, 3969, 625000, 6048, 875000}, + {1111110, 3456, 937500, 3969, 781250, 5184, 937500}, + {1111111, 3456, 937500, 3969, 781250, 5184, 937500}, + {1333332, 3072, 1000000, 3969, 937500, 4752, 1031250}, + {1333333, 3072, 1000000, 3969, 937500, 4752, 1031250} +}; + +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(); +} + +static void hpo_enc60_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); + + /* 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__); +} + +static void hpo_enc60_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_enc60_setup_hdmi_audio(enc, audio_crtc_info); + ASSERT (enc->apg); + enc->apg->funcs->se_audio_setup(enc->apg, az_inst, info); +} + +static void hpo_enc60_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + ASSERT (enc->apg); + if (enc->apg && enc->apg->funcs->disable_apg) + enc->apg->funcs->disable_apg(enc->apg); + + REG_UPDATE(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_APG_CLOCK_EN, 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_enc60_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_enc60_audio_mute_control, + .hdmi_audio_setup = hpo_enc60_hdmi_audio_setup, + .hdmi_audio_disable = hpo_enc60_hdmi_audio_disable, + .set_avmute = enc401_stream_encoder_set_avmute, + .read_state = hpo_enc401_read_state, + .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config, + .set_dynamic_metadata = hpo_enc401_set_dynamic_metadata, +}; + +void dcn60_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 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) +{ + 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.apg = apg; + 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/dcn60/dcn60_hpo_frl_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.h new file mode 100644 index 000000000000..c524277b5853 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_HPO_FRL_STREAM_ENCODER_DCN60_H__ +#define __DC_HPO_FRL_STREAM_ENCODER_DCN60_H__ + +#include "dcn30/dcn30_vpg.h" +#include "dcn30/dcn30_afmt.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 DCN60_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) + +void dcn60_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 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_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c new file mode 100644 index 000000000000..3994e73a2992 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c @@ -0,0 +1,1816 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dcn60_hubbub.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed31_32.h" + +#define CTX \ + hubbub2->base.ctx +#define DC_LOGGER \ + hubbub2->base.ctx->logger +#define REG(reg)\ + hubbub2->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hubbub2->shifts->field_name, hubbub2->masks->field_name + +static void dcn60_init_crb(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_GET(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, + &hubbub2->det0_size); + + REG_GET(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, + &hubbub2->det1_size); + + REG_GET(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, + &hubbub2->det2_size); + + REG_GET(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, + &hubbub2->det3_size); + + REG_GET(DCHUBBUB_COMPBUF_CTRL, COMPBUF_SIZE_CURRENT, + &hubbub2->compbuf_size_segments); + + REG_SET(COMPBUF_RESERVED_SPACE, 0, + COMPBUF_RESERVED_SPACE_64B, hubbub2->pixel_chunk_size / 32); // 256 64Bytes +} + +static void dcn60_program_det_segments(struct hubbub *hubbub, int hubp_inst, unsigned int det_buffer_size_seg) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + switch (hubp_inst) { + case 0: + REG_UPDATE(DCHUBBUB_DET0_CTRL, + DET0_SIZE, det_buffer_size_seg); + hubbub2->det0_size = det_buffer_size_seg; + break; + case 1: + REG_UPDATE(DCHUBBUB_DET1_CTRL, + DET1_SIZE, det_buffer_size_seg); + hubbub2->det1_size = det_buffer_size_seg; + break; + case 2: + REG_UPDATE(DCHUBBUB_DET2_CTRL, + DET2_SIZE, det_buffer_size_seg); + hubbub2->det2_size = det_buffer_size_seg; + break; + case 3: + REG_UPDATE(DCHUBBUB_DET3_CTRL, + DET3_SIZE, det_buffer_size_seg); + hubbub2->det3_size = det_buffer_size_seg; + break; + default: + break; + } + if (hubbub2->det0_size + hubbub2->det1_size + hubbub2->det2_size + + hubbub2->det3_size + hubbub2->compbuf_size_segments > hubbub2->crb_size_segs) { + /* This may happen during seamless transition from ODM 2:1 to ODM4:1 */ + DC_LOG_WARNING("CRB Config Warning: DET size (%d,%d,%d,%d) + Compbuf size (%d) > CRB segments (%d)\n", + hubbub2->det0_size, hubbub2->det1_size, hubbub2->det2_size, hubbub2->det3_size, + hubbub2->compbuf_size_segments, hubbub2->crb_size_segs); + } +} + +static void dcn60_program_compbuf_segments(struct hubbub *hubbub, unsigned int compbuf_size_seg, bool safe_to_increase) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + if (safe_to_increase || compbuf_size_seg <= hubbub2->compbuf_size_segments) { + if (compbuf_size_seg > hubbub2->compbuf_size_segments) { + REG_WAIT(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, hubbub2->det0_size, 1, 100); + REG_WAIT(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, hubbub2->det1_size, 1, 100); + REG_WAIT(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, hubbub2->det2_size, 1, 100); + REG_WAIT(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, hubbub2->det3_size, 1, 100); + } + /* Should never be hit, if it is we have an erroneous hw config*/ + ASSERT(hubbub2->det0_size + hubbub2->det1_size + hubbub2->det2_size + + hubbub2->det3_size + compbuf_size_seg <= hubbub2->crb_size_segs); + REG_UPDATE(DCHUBBUB_COMPBUF_CTRL, COMPBUF_SIZE, compbuf_size_seg); + hubbub2->compbuf_size_segments = compbuf_size_seg; + } +} + +static void dcn60_wait_for_det_update(struct hubbub *hubbub, int hubp_inst) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + switch (hubp_inst) { + case 0: + REG_WAIT(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, hubbub2->det0_size, 1, 100000); /* 1 vupdate at 10hz */ + break; + case 1: + REG_WAIT(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, hubbub2->det1_size, 1, 100000); + break; + case 2: + REG_WAIT(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, hubbub2->det2_size, 1, 100000); + break; + case 3: + REG_WAIT(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, hubbub2->det3_size, 1, 100000); + break; + default: + break; + } +} + +static bool hubbub60_program_urgent_watermarks( + struct hubbub *hubbub, + union dcn_watermark_set *watermarks, + unsigned int refclk_mhz, + bool safe_to_lower) +{ + (void)refclk_mhz; + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + bool wm_pending = false; + + /* Repeat for water mark set A and B */ + /* clock state A */ + if (safe_to_lower || watermarks->dcn4x.a.urgent > hubbub2->watermarks.dcn4x.a.urgent) { + hubbub2->watermarks.dcn4x.a.urgent = watermarks->dcn4x.a.urgent; + REG_SET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, 0, + DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, watermarks->dcn4x.a.urgent); + DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.a.urgent, watermarks->dcn4x.a.urgent); + } else if (watermarks->dcn4x.a.urgent < hubbub2->watermarks.dcn4x.a.urgent) + wm_pending = true; + + /* determine the transfer time for a quantity of data for a particular requestor.*/ + if (safe_to_lower || watermarks->dcn4x.a.frac_urg_bw_flip + > hubbub2->watermarks.dcn4x.a.frac_urg_bw_flip) { + hubbub2->watermarks.dcn4x.a.frac_urg_bw_flip = watermarks->dcn4x.a.frac_urg_bw_flip; + REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, 0, + DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, watermarks->dcn4x.a.frac_urg_bw_flip); + } else if (watermarks->dcn4x.a.frac_urg_bw_flip + < hubbub2->watermarks.dcn4x.a.frac_urg_bw_flip) + wm_pending = true; + + if (safe_to_lower || watermarks->dcn4x.a.frac_urg_bw_nom + > hubbub2->watermarks.dcn4x.a.frac_urg_bw_nom) { + hubbub2->watermarks.dcn4x.a.frac_urg_bw_nom = watermarks->dcn4x.a.frac_urg_bw_nom; + REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, 0, + DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, watermarks->dcn4x.a.frac_urg_bw_nom); + } else if (watermarks->dcn4x.a.frac_urg_bw_nom + < hubbub2->watermarks.dcn4x.a.frac_urg_bw_nom) + wm_pending = true; + + if (safe_to_lower || + watermarks->dcn4x.a.refcyc_per_trip_to_mem > hubbub2->watermarks.dcn4x.a.refcyc_per_trip_to_mem) { + hubbub2->watermarks.dcn4x.a.refcyc_per_trip_to_mem = watermarks->dcn4x.a.refcyc_per_trip_to_mem; + REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, 0, + DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, watermarks->dcn4x.a.refcyc_per_trip_to_mem); + } else if (watermarks->dcn4x.a.refcyc_per_trip_to_mem < hubbub2->watermarks.dcn4x.a.refcyc_per_trip_to_mem) + wm_pending = true; + + if (safe_to_lower || + watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem > hubbub2->watermarks.dcn4x.a.refcyc_per_meta_trip_to_mem) { + hubbub2->watermarks.dcn4x.a.refcyc_per_meta_trip_to_mem = watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem; + REG_SET(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A, 0, + DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A, watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem); + } else if (watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem < + hubbub2->watermarks.dcn4x.a.refcyc_per_meta_trip_to_mem) + wm_pending = true; + + if (safe_to_lower || watermarks->dcn4x.a.buffer_fullness > hubbub2->watermarks.dcn4x.a.buffer_fullness) { + hubbub2->watermarks.dcn4x.a.buffer_fullness = watermarks->dcn4x.a.buffer_fullness; + REG_SET(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A, 0, + DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A, watermarks->dcn4x.a.buffer_fullness); + } else if (watermarks->dcn4x.a.buffer_fullness < hubbub2->watermarks.dcn4x.a.buffer_fullness) + wm_pending = true; + + /* clock state B */ + if (safe_to_lower || watermarks->dcn4x.b.urgent > hubbub2->watermarks.dcn4x.b.urgent) { + hubbub2->watermarks.dcn4x.b.urgent = watermarks->dcn4x.b.urgent; + REG_SET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, 0, + DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, watermarks->dcn4x.b.urgent); + DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.b.urgent, watermarks->dcn4x.b.urgent); + } else if (watermarks->dcn4x.b.urgent < hubbub2->watermarks.dcn4x.b.urgent) + wm_pending = true; + + /* determine the transfer time for a quantity of data for a particular requestor.*/ + if (safe_to_lower || watermarks->dcn4x.b.frac_urg_bw_flip + > hubbub2->watermarks.dcn4x.b.frac_urg_bw_flip) { + hubbub2->watermarks.dcn4x.b.frac_urg_bw_flip = watermarks->dcn4x.b.frac_urg_bw_flip; + REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, 0, + DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, watermarks->dcn4x.b.frac_urg_bw_flip); + } else if (watermarks->dcn4x.b.frac_urg_bw_flip + < hubbub2->watermarks.dcn4x.b.frac_urg_bw_flip) + wm_pending = true; + + if (safe_to_lower || watermarks->dcn4x.b.frac_urg_bw_nom + > hubbub2->watermarks.dcn4x.b.frac_urg_bw_nom) { + hubbub2->watermarks.dcn4x.b.frac_urg_bw_nom = watermarks->dcn4x.b.frac_urg_bw_nom; + REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, 0, + DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, watermarks->dcn4x.b.frac_urg_bw_nom); + } else if (watermarks->dcn4x.b.frac_urg_bw_nom + < hubbub2->watermarks.dcn4x.b.frac_urg_bw_nom) + wm_pending = true; + + if (safe_to_lower || + watermarks->dcn4x.b.refcyc_per_trip_to_mem > hubbub2->watermarks.dcn4x.b.refcyc_per_trip_to_mem) { + hubbub2->watermarks.dcn4x.b.refcyc_per_trip_to_mem = watermarks->dcn4x.b.refcyc_per_trip_to_mem; + REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, 0, + DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, watermarks->dcn4x.b.refcyc_per_trip_to_mem); + } else if (watermarks->dcn4x.b.refcyc_per_trip_to_mem < hubbub2->watermarks.dcn4x.b.refcyc_per_trip_to_mem) + wm_pending = true; + + if (safe_to_lower || + watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem > hubbub2->watermarks.dcn4x.b.refcyc_per_meta_trip_to_mem) { + hubbub2->watermarks.dcn4x.b.refcyc_per_meta_trip_to_mem = watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem; + REG_SET(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, 0, + DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem); + } else if (watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem < + hubbub2->watermarks.dcn4x.b.refcyc_per_meta_trip_to_mem) + wm_pending = true; + + if (safe_to_lower || watermarks->dcn4x.b.buffer_fullness > hubbub2->watermarks.dcn4x.b.buffer_fullness) { + hubbub2->watermarks.dcn4x.b.buffer_fullness = watermarks->dcn4x.b.buffer_fullness; + REG_SET(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, 0, + DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, watermarks->dcn4x.b.buffer_fullness); + } else if (watermarks->dcn4x.b.buffer_fullness < hubbub2->watermarks.dcn4x.b.buffer_fullness) + wm_pending = true; + + return wm_pending; +} + +static bool hubbub60_program_pstate_watermarks( + struct hubbub *hubbub, + union dcn_watermark_set *watermarks, + unsigned int refclk_mhz, + bool safe_to_lower) +{ + (void)refclk_mhz; + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + bool wm_pending = false; + + /* Section for UCLK_PSTATE_CHANGE_WATERMARKS, used for UCLK change (UCLK/FCLK PState) */ + /* clock state A */ + if (safe_to_lower || watermarks->dcn4x.a.uclk_pstate + > hubbub2->watermarks.dcn4x.a.uclk_pstate) { + hubbub2->watermarks.dcn4x.a.uclk_pstate = + watermarks->dcn4x.a.uclk_pstate; + REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, 0, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, watermarks->dcn4x.a.uclk_pstate); + DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.a.uclk_pstate, watermarks->dcn4x.a.uclk_pstate); + } else if (watermarks->dcn4x.a.uclk_pstate + < hubbub2->watermarks.dcn4x.a.uclk_pstate) + wm_pending = true; + + /* clock state B */ + if (safe_to_lower || watermarks->dcn4x.b.uclk_pstate + > hubbub2->watermarks.dcn4x.b.uclk_pstate) { + hubbub2->watermarks.dcn4x.b.uclk_pstate = + watermarks->dcn4x.b.uclk_pstate; + REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, 0, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, watermarks->dcn4x.b.uclk_pstate); + DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.b.uclk_pstate, watermarks->dcn4x.b.uclk_pstate); + } else if (watermarks->dcn4x.b.uclk_pstate + < hubbub2->watermarks.dcn4x.b.uclk_pstate) + wm_pending = true; + + /* Section for UCLK_PSTATE_CHANGE_WATERMARKS1 (Reserved set, can be used for FCLK Pstate only) */ + if (safe_to_lower || watermarks->dcn4x.a.fclk_pstate + > hubbub2->watermarks.dcn4x.a.fclk_pstate) { + hubbub2->watermarks.dcn4x.a.fclk_pstate = + watermarks->dcn4x.a.fclk_pstate; + REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, 0, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, watermarks->dcn4x.a.fclk_pstate); + DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK1_A calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.a.fclk_pstate, watermarks->dcn4x.a.fclk_pstate); + } else if (watermarks->dcn4x.a.fclk_pstate + < hubbub2->watermarks.dcn4x.a.fclk_pstate) + wm_pending = true; + + /* clock state B */ + if (safe_to_lower || watermarks->dcn4x.b.fclk_pstate + > hubbub2->watermarks.dcn4x.b.fclk_pstate) { + hubbub2->watermarks.dcn4x.b.fclk_pstate = + watermarks->dcn4x.b.fclk_pstate; + REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, 0, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, watermarks->dcn4x.b.fclk_pstate); + DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK1_B calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.b.fclk_pstate, watermarks->dcn4x.b.fclk_pstate); + } else if (watermarks->dcn4x.b.fclk_pstate + < hubbub2->watermarks.dcn4x.b.fclk_pstate) + wm_pending = true; + + /* Section for FCLK_PSTATE_CHANGE_WATERMARKS, instance 0 used for G7 PPT */ + /* clock state A */ + if (safe_to_lower || watermarks->dcn4x.a.ppt + > hubbub2->watermarks.dcn4x.a.ppt) { + hubbub2->watermarks.dcn4x.a.ppt = + watermarks->dcn4x.a.ppt; + REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A, 0, + DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A, watermarks->dcn4x.a.ppt); + DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.a.ppt, watermarks->dcn4x.a.ppt); + } else if (watermarks->dcn4x.a.ppt + < hubbub2->watermarks.dcn4x.a.ppt) + wm_pending = true; + + /* clock state B */ + if (safe_to_lower || watermarks->dcn4x.b.ppt + > hubbub2->watermarks.dcn4x.b.ppt) { + hubbub2->watermarks.dcn4x.b.ppt = + watermarks->dcn4x.b.ppt; + REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, 0, + DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, watermarks->dcn4x.b.ppt); + DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.b.ppt, watermarks->dcn4x.b.ppt); + } else if (watermarks->dcn4x.b.ppt + < hubbub2->watermarks.dcn4x.b.ppt) + wm_pending = true; + + /* Section for FCLK_CHANGE_WATERMARKS1, instance 1 used for G7 Temp Read */ + if (safe_to_lower || watermarks->dcn4x.a.temp_read + > hubbub2->watermarks.dcn4x.a.temp_read) { + hubbub2->watermarks.dcn4x.a.temp_read = + watermarks->dcn4x.a.temp_read; + REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A, 0, + DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A, watermarks->dcn4x.a.temp_read); + DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK1_A calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.a.temp_read, watermarks->dcn4x.a.temp_read); + } else if (watermarks->dcn4x.a.temp_read + < hubbub2->watermarks.dcn4x.a.temp_read) + wm_pending = true; + + /* clock state B */ + if (safe_to_lower || watermarks->dcn4x.b.temp_read + > hubbub2->watermarks.dcn4x.b.temp_read) { + hubbub2->watermarks.dcn4x.b.temp_read = + watermarks->dcn4x.b.temp_read; + REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, 0, + DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, watermarks->dcn4x.b.temp_read); + DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK1_B calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->dcn4x.b.temp_read, watermarks->dcn4x.b.temp_read); + } else if (watermarks->dcn4x.b.temp_read + < hubbub2->watermarks.dcn4x.b.temp_read) + wm_pending = true; + + return wm_pending; +} + +static bool hubbub60_program_stutter_watermarks( + struct hubbub *hubbub, + union dcn_watermark_set *watermarks, + unsigned int refclk_mhz, + bool safe_to_lower) +{ + (void)refclk_mhz; + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + bool wm_pending = false; + + /* clock state A */ + if (safe_to_lower || watermarks->dcn4x.a.sr_enter + > hubbub2->watermarks.dcn4x.a.sr_enter) { + hubbub2->watermarks.dcn4x.a.sr_enter = + watermarks->dcn4x.a.sr_enter; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, watermarks->dcn4x.a.sr_enter); + DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.a.sr_enter, watermarks->dcn4x.a.sr_enter); + // On dGPU Z states are N/A, so program unused Stutter Enter wm A with the same value + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, watermarks->dcn4x.a.sr_enter); + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, watermarks->dcn4x.a.sr_enter); + + } + /* possible 2nd stutter watermark. PMFW to choose which one to use */ + if (safe_to_lower || watermarks->dcn4x.a.sr_enter_low_power + > hubbub2->watermarks.dcn4x.a.sr_enter_low_power) { + hubbub2->watermarks.dcn4x.a.sr_enter_low_power = + watermarks->dcn4x.a.sr_enter_low_power; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, watermarks->dcn4x.a.sr_enter_low_power); + DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK1_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.a.sr_enter_low_power, watermarks->dcn4x.a.sr_enter_low_power); + } + + if (watermarks->dcn4x.a.sr_enter + < hubbub2->watermarks.dcn4x.a.sr_enter || + watermarks->dcn4x.a.sr_enter_low_power + < hubbub2->watermarks.dcn4x.a.sr_enter_low_power) + wm_pending = true; + + if (safe_to_lower || watermarks->dcn4x.a.sr_exit + > hubbub2->watermarks.dcn4x.a.sr_exit) { + hubbub2->watermarks.dcn4x.a.sr_exit = + watermarks->dcn4x.a.sr_exit; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, watermarks->dcn4x.a.sr_exit); + DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.a.sr_exit, watermarks->dcn4x.a.sr_exit); + // On dGPU Z states are N/A, so program unused Stutter Exit wm A with the same value + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, watermarks->dcn4x.a.sr_exit); + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, watermarks->dcn4x.a.sr_exit); + } + /* possible 2nd stutter exit watermark. PMFW to choose which one to use */ + if (safe_to_lower || watermarks->dcn4x.a.sr_exit_low_power + > hubbub2->watermarks.dcn4x.a.sr_exit_low_power) { + hubbub2->watermarks.dcn4x.a.sr_exit_low_power = + watermarks->dcn4x.a.sr_exit_low_power; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, watermarks->dcn4x.a.sr_exit_low_power); + DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK1_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.a.sr_exit_low_power, watermarks->dcn4x.a.sr_exit_low_power); + } + + if (watermarks->dcn4x.a.sr_exit + < hubbub2->watermarks.dcn4x.a.sr_exit || + watermarks->dcn4x.a.sr_exit_low_power + < hubbub2->watermarks.dcn4x.a.sr_exit_low_power) + wm_pending = true; + + /* clock state B */ + if (safe_to_lower || watermarks->dcn4x.b.sr_enter + > hubbub2->watermarks.dcn4x.b.sr_enter) { + hubbub2->watermarks.dcn4x.b.sr_enter = + watermarks->dcn4x.b.sr_enter; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, watermarks->dcn4x.b.sr_enter); + DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.b.sr_enter, watermarks->dcn4x.b.sr_enter); + // On dGPU Z states are N/A, so program unused Stutter Enter wm B with the same value + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, watermarks->dcn4x.b.sr_enter); + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, watermarks->dcn4x.b.sr_enter); + } + /* possible 2nd stutter enter watermark. PMFW to choose which one to use */ + if (safe_to_lower || watermarks->dcn4x.b.sr_enter_low_power + > hubbub2->watermarks.dcn4x.b.sr_enter_low_power) { + hubbub2->watermarks.dcn4x.b.sr_enter_low_power = + watermarks->dcn4x.b.sr_enter_low_power; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, 0, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, watermarks->dcn4x.b.sr_enter_low_power); + DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK1_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.b.sr_enter_low_power, watermarks->dcn4x.b.sr_enter_low_power); + } + + if (watermarks->dcn4x.b.sr_enter + < hubbub2->watermarks.dcn4x.b.sr_enter || + watermarks->dcn4x.b.sr_enter_low_power + < hubbub2->watermarks.dcn4x.b.sr_enter_low_power) + wm_pending = true; + + if (safe_to_lower || watermarks->dcn4x.b.sr_exit + > hubbub2->watermarks.dcn4x.b.sr_exit) { + hubbub2->watermarks.dcn4x.b.sr_exit = + watermarks->dcn4x.b.sr_exit; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, watermarks->dcn4x.b.sr_exit); + DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.b.sr_exit, watermarks->dcn4x.b.sr_exit); + // On dGPU Z states are N/A, so program unused Stutter Exit wm B with the same value + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, watermarks->dcn4x.b.sr_exit); + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, watermarks->dcn4x.b.sr_exit); + } + /* possible 2nd stutter exit watermark. PMFW to choose which one to use */ + if (safe_to_lower || watermarks->dcn4x.b.sr_exit_low_power + > hubbub2->watermarks.dcn4x.b.sr_exit_low_power) { + hubbub2->watermarks.dcn4x.b.sr_exit_low_power = + watermarks->dcn4x.b.sr_exit_low_power; + REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, 0, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, watermarks->dcn4x.b.sr_exit_low_power); + DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK1_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->dcn4x.b.sr_exit_low_power, watermarks->dcn4x.b.sr_exit_low_power); + } + + if (watermarks->dcn4x.b.sr_exit + < hubbub2->watermarks.dcn4x.b.sr_exit || + watermarks->dcn4x.b.sr_exit_low_power + < hubbub2->watermarks.dcn4x.b.sr_exit_low_power) + wm_pending = true; + + return wm_pending; +} + +static bool hubbub60_program_watermarks( + struct hubbub *hubbub, + union dcn_watermark_set *watermarks, + unsigned int refclk_mhz, + bool safe_to_lower) +{ + bool wm_pending = false; + + if (hubbub60_program_urgent_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower)) + wm_pending = true; + + if (hubbub60_program_stutter_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower)) + wm_pending = true; + + if (hubbub60_program_pstate_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower)) + wm_pending = true; + + hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter); + + return wm_pending; +} + +/* Copy values from WM set A to all other sets */ +static void hubbub60_init_watermarks(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t reg; + + reg = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A); + REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A); + REG_WRITE(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A); + REG_WRITE(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A); + REG_WRITE(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A); + REG_WRITE(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, reg); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A); + REG_WRITE(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, reg); + reg = REG_READ(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A); + REG_WRITE(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A); + REG_WRITE(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, reg); + reg = REG_READ(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A); + REG_WRITE(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, reg); + + reg = REG_READ(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A); + REG_WRITE(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, reg); +} + +static void hubbub60_force_wm_propagate_to_pipes(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_SET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, 0, + DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, hubbub2->watermarks.dcn4x.a.urgent); + +} + +static void hubbub60_wm_read_state(struct hubbub *hubbub, + struct dcn_hubbub_wm *wm) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + struct dcn_hubbub_wm_set *s; + + memset(wm, 0, sizeof(struct dcn_hubbub_wm)); + + s = &wm->sets[0]; + s->wm_set = 0; + REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, + DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, &s->data_urgent); + + REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, &s->sr_enter); + + REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, &s->sr_exit); + + REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, &s->dram_clk_change); + + REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, &s->fclk_pstate_change); + + s = &wm->sets[1]; + s->wm_set = 1; + REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, + DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, &s->data_urgent); + + REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, + DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, &s->sr_enter); + + REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, + DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, &s->sr_exit); + + REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, &s->dram_clk_change); + + REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, &s->fclk_pstate_change); +} + +/** + * @brief Resets the performance monitor for the DCN 6.0 hubbub. + * + * This function resets the performance monitoring counters and related + * state for the specified hubbub instance. It is typically called to + * clear performance statistics before starting a new measurement period. + * + * @param hubbub Pointer to the hubbub structure to reset performance monitoring + * for. + */ +static void hubbub60_perfmon_reset(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_WRITE(DC_PERFMON5_PERFMON_CNTL, 0); + REG_WRITE(DC_PERFMON5_PERFMON_CNTL2, 0); + REG_WRITE(DC_PERFMON5_PERFCOUNTER_STATE, 0); + REG_WRITE(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, 0xFF00); + REG_WRITE(DC_PERFMON5_PERFMON_CVALUE_LOW, 0); + REG_WRITE(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0); + REG_WRITE(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0); +} + +/** + * Starts measuring memory latencies (maximum, minimum, and average) in + * nanoseconds using performance counters. + * + * This function configures and enables performance monitoring counters 4, 5, + * 6, and 7 to track memory latency within the hubbub hardware block. The + * function sets up: + * + * Counter 4: Counts the number of memory latency samples + * (PERFCOUNTER_INC_MODE = 0x3 for positive edge counting) + * Counter 5: Accumulates total latency cycles for average calculation + * (PERFCOUNTER_INC_MODE = 0x2 for LSB level counting) + * Counter 6: Tracks maximum latency values (PERFCOUNTER_COUNTED_VALUE_TYPE = + * 0x1, event 74 for frame_window_refclk) + * Counter 7: Tracks minimum latency values (PERFCOUNTER_COUNTED_VALUE_TYPE = + * 0x2, event 74 for frame_window_refclk) + * + * Configuration details: + * - Uses Data Fabric latency source (LATENCY_SOURCE_SEL = 0x2) + * - Monitors all request types (UTM_FILTER_SEL = 0) + * - Event 79 for memory latency monitoring (counters 4, 5) + * - Event 74 for frame_window_refclk timing (counters 6, 7) + * - Counters 6 and 7 use independent state mode with restart enabled + * - All counters run on refclk cycles for consistent timing measurement + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + */ +static void hubbub60_perfmon_start_measuring_memory_latencies( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + /* configure measurement control */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0, + DCHUBBUB_LATENCY_CNT_EN, 0x1, + DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1); + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0, + LATENCY_SOURCE_SEL, 0x2, + UTM_FILTER_SEL, 0); + + /* program counter 4 to count until duration */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x4, + PERFCOUNTER_EVENT_SEL, 79, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x3, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x0, + PERFCOUNTER_RESTART_EN, 0x0, + PERFCOUNTER_ACTIVE, 0x1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x4, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* program counter 5 to measure accumulated latency */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x5, + PERFCOUNTER_EVENT_SEL, 79, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x2, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x0, + PERFCOUNTER_RESTART_EN, 0x0, + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x5, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* program counter 6 to measure max latency */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x6, + PERFCOUNTER_EVENT_SEL, 74, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x2, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x0, + PERFCOUNTER_RESTART_EN, 0x1, + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x6, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x1, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* program counter 7 to measure min latency */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x7, + PERFCOUNTER_EVENT_SEL, 74, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x2, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x0, + PERFCOUNTER_RESTART_EN, 0x1, + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x7, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x2, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* Program perfcounter states */ + REG_SET_4(DC_PERFMON5_PERFCOUNTER_STATE, 0, + PERFCOUNTER_STATE_SEL6, 0x1, + PERFCOUNTER_CNT6_STATE, 0x3, + PERFCOUNTER_STATE_SEL7, 0x1, + PERFCOUNTER_CNT7_STATE, 0x3); + + REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0, + PERFMON_RUN_ENABLE_START_SEL, 0x0, + PERFMON_RUN_ENABLE_STOP_SEL, 0); + + /* start the counters */ + REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0, + PERFMON_STATE, 0x1, + PERFMON_RPT_COUNT, 0xFFFFF); +} + +/** + * @brief Reads the current performance monitor results and calculates + * memory latencies in nanoseconds. + * + * This function reads the values from the DCN 6.0 hubbub performance monitor + * counters, then calculates the minimum, maximum, and average observed + * memory latencies in nanoseconds. + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + * @param refclk_mhz Reference clock frequency in MHz, used for time + * conversion. + * @param min_latency_ns Optional pointer to store the minimum latency in + * nanoseconds. + * @param max_latency_ns Optional pointer to store the maximum latency in + * nanoseconds. + * @param avg_latency_ns Optional pointer to store the average latency in + * nanoseconds. + * + * @return The number of memory latency samples measured as a 32-bit + * unsigned integer. + */ +static uint32_t hubbub60_perfmon_get_memory_latencies_ns( + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *min_latency_ns, uint32_t *max_latency_ns, + uint32_t *avg_latency_ns) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t count4 = 0, count5 = 0, count6 = 0, count7 = 0; + struct fixed31_32 temp; + + ASSERT(refclk_mhz != 0); + if (refclk_mhz == 0) + return 0; + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x5); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count5); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x6); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count6); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x7); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count7); + + if (avg_latency_ns && count4) { + temp = dc_fixpt_from_fraction(count5, count4); + temp = dc_fixpt_div_int(temp, refclk_mhz); + *avg_latency_ns = dc_fixpt_ceil(dc_fixpt_mul_int(temp, 1000)); + } + + if (max_latency_ns) { + temp = dc_fixpt_from_fraction(count6, refclk_mhz); + *max_latency_ns = dc_fixpt_ceil(dc_fixpt_mul_int(temp, 1000)); + } + + if (min_latency_ns) { + temp = dc_fixpt_from_fraction(count7, refclk_mhz); + *min_latency_ns = dc_fixpt_ceil(dc_fixpt_mul_int(temp, 1000)); + } + + return count4; +} + +/** + * Starts measuring urgent assertion and deassertion counts using + * performance counters. + * + * This function configures and enables performance monitoring counters 0, + * 1, and 4 to track urgent assertion and deassertion events within the + * hubbub hardware block. The function sets up: + * + * Counter 0: Counts urgent assertion events (PERFCOUNTER_INC_MODE = 0x3 for + * positive edge counting) + * Counter 1: Counts urgent deassertion events (PERFCOUNTER_INC_MODE = 0x4 for + * negative edge counting) + * Counter 4: Gets the current timestamp in reference clock cycles + * + * Configuration details: + * - Uses UTM urgent latency source (LATENCY_SOURCE_SEL = 0x4) + * - Monitors all request types (UTM_FILTER_SEL = 0) + * - Event 65 for urgent assertion/deassertion monitoring (counters 0, 1) + * - Event 19 for current timestamp (counter 4) + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + */ +static void hubbub60_perfmon_start_measuring_urgent_assertion_count( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + /* configure measurement control */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0, + DCHUBBUB_LATENCY_CNT_EN, 0x1, + DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1); + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0, + LATENCY_SOURCE_SEL, 0x4, + UTM_FILTER_SEL, 0); + + /* program counter 0 to urgent assertions */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x0, + PERFCOUNTER_EVENT_SEL, 65, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x3, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x1, + PERFCOUNTER_RESTART_EN, 0x0, + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x0, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* program counter 1 to urgent assertions */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x1, + PERFCOUNTER_EVENT_SEL, 65, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x4, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x1, + PERFCOUNTER_RESTART_EN, 0x0, + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x1, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* program counter 4 to get current timestamp in refclk cycles */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x4, + PERFCOUNTER_EVENT_SEL, 19, + PERFCOUNTER_CVALUE_SEL, 0x0, + PERFCOUNTER_INC_MODE, 0x2, + PERFCOUNTER_HW_CNTL_SEL, 0x0, + PERFCOUNTER_RUNEN_MODE, 0x0, + PERFCOUNTER_RESTART_EN, 0x0, + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x4, + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, + PERFCOUNTER_HW_STOP1_SEL, 0x0, + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + REG_WRITE(DC_PERFMON5_PERFCOUNTER_STATE, 0); + + /* start the counters */ + REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0, + PERFMON_STATE, 0x1, + PERFMON_RPT_COUNT, 0xFFFFF); +} + +/** + * @brief Reads the urgent assertion and deassertion counts from the + * performance monitor. + * + * This function reads the values from the DCN 6.0 hubbub performance monitor + * counters for urgent assertion and deassertion events. It also retrieves + * a timestamp in microseconds based on the reference clock frequency. + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + * @param refclk_mhz Reference clock frequency in MHz, used for time + * conversion. + * @param assertion_count Optional pointer to store the urgent assertion count. + * @param deassertion_count Optional pointer to store the urgent deassertion + * count. + * @param timestamp_us Optional pointer to store the timestamp in microseconds. + * + * @return A boolean indicating whether the assertion or deassertion counts + * have been updated since the last read. + */ +static bool hubbub60_perfmon_get_urgent_assertion_count( + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *assertion_count, uint32_t *deassertion_count, + uint32_t *timestamp_us) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t count0 = 0, count1 = 0, count4 = 0; + bool updated = false; + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x1); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count1); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4); + + if (refclk_mhz != 0 && timestamp_us) + *timestamp_us = count4 / refclk_mhz; + + if (assertion_count && (*assertion_count != count0)) { + *assertion_count = count0; + updated = true; + } + + if (deassertion_count && (*deassertion_count != count1)) { + *deassertion_count = count1; + updated = true; + } + + return updated; +} + +/** + * @brief Configures the performance monitor to measure the urgent ramp + * latency. + * + * This function configures the performance monitoring counters to measure + * the urgent ramp latency. It uses hardware counters 0, 1, 2, 4, 5, 6, 7 + * for this purpose: + * + * Counter 4: + * - Purpose: Begins counting on the first urgent data return, marking + * the start of the first rolling window (t_win). + * - Behavior: Signals a stop event at the end of every t_win period. + * + * Counter 5: + * - Purpose: Starts counting at the beginning of t_win. + * - Behavior: Signals a stop event at 1/3 of t_win. + * + * Counter 6: + * - Purpose: Starts counting at the beginning of t_win. + * - Behavior: Signals a stop event at 2/3 of t_win. + * + * After the first t_win period, counters 4, 5, and 6 will generate stop + * events every 1/3 t_win. These stop events trigger counters 0, 1, and 2 + * respectively: + * + * Counter 0, 1, 2: + * - Purpose: Track the total accumulated data received during each 1/3 + * t_win interval. + * - Behavior: Each counter starts on its respective stop event and stops + * after 1/3 t_win. + * - Threshold: If the total data size exceeds a precalculated threshold, + * it indicates bandwidth higher than the target for that 1/3 t_win + * period. + * - When the counter reaches the threshold cvalue, it signals a stop + * interrupt for counter 7. + * + * Counter 7: + * - Purpose: Measures the total time in reference clock (refclk) cycles. + * - Behavior: Starts from the first data return and stops when any of + * counters 0, 1, or 2 signal a stop interrupt. + * - Usage: The total time counted in refclk cycles is converted into + * urgent ramp latency. + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + * @param params Pointer to the urgent latency measurement parameters. + */ +static void hubbub60_perfmon_start_measuring_urgent_ramp_latency( + struct hubbub *hubbub, + const struct hubbub_urgent_latency_params *params) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + /* configure measurement control */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0, + DCHUBBUB_LATENCY_CNT_EN, 0x1, + DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1); + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0, + LATENCY_SOURCE_SEL, 0x4, // df_urgent + UTM_FILTER_SEL, 0x1); // urgent requests + + /* program counter 0 as the first bw counter */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0 + PERFCOUNTER_EVENT_SEL, 257, // ROB output valid event + PERFCOUNTER_CVALUE_SEL, 0x7, // use cvalue bits 47-36 + PERFCOUNTER_HW_CNTL_SEL, 0x1, // individual mode + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it is active + PERFCOUNTER_INT_EN, 0x1, // signal when the measurement is complete + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0 + PERFCOUNTER_CNTOFF_SEL, 0x4, // start when counter 4 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue + REG_SET_2(DC_PERFMON5_PERFCOUNTER_STATE, 0, + PERFCOUNTER_STATE_SEL0, 0x1, // independent state mode + PERFCOUNTER_CNT0_STATE, 0x3); // hw mode + + /* program counter 1 as the second bw counter */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x1, // select counter 1 + PERFCOUNTER_EVENT_SEL, 257, // ROB output valid event + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached. + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_CVALUE_SEL, 0x7, // use cvalue bits 47-36 + PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it is active + PERFCOUNTER_INT_EN, 0x1, // signal when the measurement is complete + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x1, // select counter 1 + PERFCOUNTER_CNTOFF_SEL, 0x5, // start when counter 5 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue + REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE, + PERFCOUNTER_STATE_SEL1, 0x1, // independent state mode + PERFCOUNTER_CNT1_STATE, 0x3); // hw mode + + /* program counter 2 as the third bw counter */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x2, // select counter 2 + PERFCOUNTER_EVENT_SEL, 257, // ROB output valid event + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached. + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_CVALUE_SEL, 0x7, // use cvalue bits 47-36 + PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it is active + PERFCOUNTER_INT_EN, 0x1, // signal when the measurement is complete + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x2, // select counter 2 + PERFCOUNTER_CNTOFF_SEL, 0x6, // start when counter 6 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue + REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE, + PERFCOUNTER_STATE_SEL2, 0x1, // independent state mode + PERFCOUNTER_CNT2_STATE, 0x3); // hw mode + + /* program counter 4 as the first time window */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4 + PERFCOUNTER_EVENT_SEL, 19, // Always 1 event + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached. + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_CVALUE_SEL, 0x4, // use cvalue bits 11-0 + PERFCOUNTER_RESTART_EN, 0x0, // do not restart the counter while active. + PERFCOUNTER_INT_EN, 0x0, // used for timing only, do not enable interrupt + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4 + PERFCOUNTER_CNTOFF_SEL, 0x8, // start on custom signal: latency start | counter interrupt + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue + REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE, + PERFCOUNTER_STATE_SEL4, 0x1, // independent state mode + PERFCOUNTER_CNT4_STATE, 0x3); // hw mode + + /* program counter 5 as the second time window */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x5, // select counter 5 + PERFCOUNTER_EVENT_SEL, 19, // Always 1 event + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached. + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_CVALUE_SEL, 0x5, // use cvalue bits 23-12 + PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it's active + PERFCOUNTER_OFF_MASK, 0x1, // Don't allow this counter to trigger counter 4 restart + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x5, // select counter 5 + PERFCOUNTER_CNTOFF_SEL, 0x4, // start when counter 4 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue + REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE, + PERFCOUNTER_STATE_SEL5, 0x1, // independent state mode + PERFCOUNTER_CNT5_STATE, 0x3); // hw mode + + /* program counter 6 as the third time window */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x6, // select counter 6 + PERFCOUNTER_EVENT_SEL, 19, // Always 1 event + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached. + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_CVALUE_SEL, 0x6, // use cvalue bits 35-24 + PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it's active + PERFCOUNTER_OFF_MASK, 0x1, // Don't allow this counter to trigger counter 4 restart + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x6, // select counter 6 + PERFCOUNTER_CNTOFF_SEL, 0x4, // start when counter 4 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue + REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE, + PERFCOUNTER_STATE_SEL6, 0x1, // independent state mode + PERFCOUNTER_CNT6_STATE, 0x3); // hw mode + + /* program counter 7 to count urgent ramp latency from urg asserted until bw reaches threshold */ + REG_SET_10(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x7, // select counter 7 + PERFCOUNTER_EVENT_SEL, 19, // Always 1 event + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached. + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_CVALUE_SEL, 0x0, // default/not used + PERFCOUNTER_RESTART_EN, 0x0, // do not restart counter + PERFCOUNTER_INT_EN, 0x0, // Final measurement; doesn't need to interrupt + PERFCOUNTER_OFF_MASK, 0x0, // default + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x7, // select counter 7 + PERFCOUNTER_CNTOFF_SEL, 0x8, // start on custom signal: latency start | counter interrupt + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode + PERFCOUNTER_HW_STOP2_SEL, 0x1); // stop on external event + REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE, + PERFCOUNTER_STATE_SEL7, 0x1, // independent state mode + PERFCOUNTER_CNT7_STATE, 0x3); // hw mode + + REG_SET_3(DC_PERFMON5_PERFMON_CNTL2, 0, + PERFMON_RUN_ENABLE_START_SEL, 0x0, // start on first urgent request from lat mon + PERFMON_RUN_ENABLE_STOP_SEL, 11, // stop on counter interrupt + PERFMON_CNTOFF_INT_TYPE, 0x0); + + // Program Perfmon cvalue registers (example values, should be + // calculated as per doc) refclk_hz = refclk_mhz * 1,000,000 t_win_s = + // t_win_ns / 1,000,000,000 CVALUE[11:0] = refclk_hz * t_win_s = + // (refclk_mhz * t_win_ns) / 1000 + struct fixed31_32 slice_size = + dc_fixpt_from_fraction((uint64_t)params->refclk_mhz * params->t_win_ns, 3000); + struct fixed31_32 threshold_bytes = dc_fixpt_div_int( + dc_fixpt_from_int(params->bandwidth_mbps * params->t_win_ns), + 1000); + + uint32_t cvalue_0 = dc_fixpt_floor(dc_fixpt_mul_int(slice_size, 3)); + uint32_t cvalue_1 = dc_fixpt_floor(slice_size); + uint32_t cvalue_2 = dc_fixpt_floor(dc_fixpt_mul_int(slice_size, 2)); + uint32_t cvalue_3 = dc_fixpt_floor(dc_fixpt_mul( + dc_fixpt_div_int(threshold_bytes, 64), + dc_fixpt_from_fraction(params->bw_factor_x1000, 1000))); + + // Pack into 48 bits: [47:36][35:24][23:12][11:0] + uint64_t cvalue = ((uint64_t)cvalue_3 << 36) | + ((uint64_t)cvalue_2 << 24) | + ((uint64_t)cvalue_1 << 12) | + ((uint64_t)cvalue_0); + + REG_SET(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, 0, PERFMON_CVALUE_HI, (uint32_t) (cvalue >> 32)); + REG_SET(DC_PERFMON5_PERFMON_CVALUE_LOW, 0, PERFMON_CVALUE_LOW, (uint32_t) (cvalue & 0xFFFFFFFF)); + + /* start the counters */ + REG_SET_3(DC_PERFMON5_PERFMON_CNTL, 0, + PERFMON_STATE, 0x3, + PERFMON_RPT_COUNT, 1, + PERFMON_CNTOFF_INT_EN, 0x1); +} + +/** + * @brief Reads the current performance monitor result and calculates + * the urgent ramp latency in nanoseconds. + * + * This function reads the values from the DCN 6.0 hubbub performance + * monitor counters, then calculates the urgent ramp latency in + * nanoseconds. + * + * @param hubbub Pointer to the hubbub structure representing the + * hardware instance. + * @param refclk_mhz Reference clock frequency in MHz, used for time + * conversion. + * + * @return The urgent ramp latency in nanoseconds as a 32-bit unsigned + * integer. + */ +static uint32_t hubbub60_perfmon_get_urgent_ramp_latency_ns( + struct hubbub *hubbub, uint32_t refclk_mhz) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t count7 = 0, latency_ns = 0; + struct fixed31_32 temp; + + if (refclk_mhz == 0) + return 0; + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x7); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count7); + + temp = dc_fixpt_from_fraction(count7, refclk_mhz); + temp = dc_fixpt_mul_int(temp, 1000); + latency_ns = dc_fixpt_ceil(temp); + + return latency_ns; +} + +/** + * hubbub60_perfmon_arm_measuring_out_of_order_bandwidth - Configure the out-of-order BW counter. + * @hubbub: pointer to the hubbub hardware instance + * + * Configures the performance monitoring counters to measure out-of-order + * (peak prefetch) bandwidth using hardware counters 0, 1, and 4: + * + * Counter 0: count-off counter — counts response-valid events and gates the + * measurement window once it reaches its target value. + * Counter 1: data counter — tracks total data received during the measurement + * period; generates an interrupt when measurement completes. + * Counter 4: duration timer — measures elapsed time in refclk cycles. + * + * UTM_FILTER_SEL is set to 0 so that isolation comes from OTG-vblank gating + * rather than the silicon-broken HW filter. The first 200 prefetch requests + * are skipped (ramp-up), and the subsequent 200 are the measurement window. + * + * This function configures counters only; call + * hubbub60_perfmon_start_measuring_out_of_order_bandwidth() to enable them. + */ +static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + /* configure measurement control */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0, + DCHUBBUB_LATENCY_CNT_EN, 0x1, + DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1); + /* + * UTM_FILTER_SEL = 0: time-domain gating (OTG vblank) replaces the + * silicon-broken HW filter that can no longer isolate prefetch traffic. + */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0, + LATENCY_SOURCE_SEL, 0x2, + UTM_FILTER_SEL, 0x0); + + /* Program counter 0 as the count off counter */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0 + PERFCOUNTER_EVENT_SEL, 259, // response vld + PERFCOUNTER_CVALUE_SEL, 0x1, // use cvalue bits 15-0 + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0 + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x1, // the stop trigger is that perfcounter meet the target CVALUE + PERFCOUNTER_HW_STOP2_SEL, 0x0); // ignored + + /* Program counter 1 to count total data received */ + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x1, // select counter 1 + PERFCOUNTER_EVENT_SEL, 259, // response vld + PERFCOUNTER_CVALUE_SEL, 0x2, // use cvalue bits 31-16 + PERFCOUNTER_INC_MODE, 0x2, // count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done + PERFCOUNTER_INT_EN, 1, // signal when the measurement is complete + PERFCOUNTER_ACTIVE, 0x1); + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x1, + PERFCOUNTER_CNTOFF_SEL, 0, // start when count0 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored + PERFCOUNTER_HW_STOP2_SEL, 0x0); // the stop trigger is that perfcounter meet the target CVALUE + + /* Program counter 4 to count the measuring time */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4 + PERFCOUNTER_EVENT_SEL, 19, // always 1 event + PERFCOUNTER_CVALUE_SEL, 0x0, // ignored + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done + PERFCOUNTER_ACTIVE, 1); + REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4 + PERFCOUNTER_CNTOFF_SEL, 0, // start when count0 stops + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored + PERFCOUNTER_HW_STOP2_SEL, 0x1); // the stop trigger is from the external 64 pairs of start/stop events + + /* Program perfcounter states */ + REG_SET_6(DC_PERFMON5_PERFCOUNTER_STATE, 0, + PERFCOUNTER_STATE_SEL0, 0x1, // independent state mode + PERFCOUNTER_CNT0_STATE, 0x3, // hw mode + PERFCOUNTER_STATE_SEL1, 0x1, // independent state mode + PERFCOUNTER_CNT1_STATE, 0x3, // hw mode + PERFCOUNTER_STATE_SEL4, 0x1, // independent state mode + PERFCOUNTER_CNT4_STATE, 0x3); // hw mode + + /* + * The cvalue is derived based on experimental results at the lowest + * clock state. Out-of-order bandwidth requires ~200 prefetch requests + * to ramp up to full speed; the subsequent 200 requests form the + * measurement window. + */ + REG_SET(DC_PERFMON5_PERFMON_CVALUE_LOW, 0, PERFMON_CVALUE_LOW, 200 | 200 << 16); + + REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0, + PERFMON_RUN_ENABLE_START_SEL, 0x0, + PERFMON_RUN_ENABLE_STOP_SEL, 11); // perfmon counter off event +} + +/** + * hubbub60_perfmon_start_measuring_out_of_order_bandwidth - Enable the out-of-order BW counter. + * @hubbub: pointer to the hubbub hardware instance + * + * Enables the performance monitor counters previously configured by + * hubbub60_perfmon_arm_measuring_out_of_order_bandwidth(). The counter + * self-stops once the count-off counter reaches its target; there is no + * explicit stop step for the peak-BW path. + */ +static void hubbub60_perfmon_start_measuring_out_of_order_bandwidth( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0, + PERFMON_STATE, 0x3, + PERFMON_RPT_COUNT, 1); +} + +/** + * hubbub60_perfmon_get_out_of_order_bandwidth_mbps - Read out-of-order BW counter result. + * @hubbub: pointer to the hubbub hardware instance + * @refclk_mhz: reference clock frequency in MHz, used for duration conversion + * @duration_ns: output parameter; receives the measured duration in nanoseconds + * + * Reads hardware counters 1 (data) and 4 (duration) and converts the raw + * refclk-cycle count into bandwidth in Mbps. + * + * Return: out-of-order bandwidth in Mbps + */ +static uint32_t hubbub60_perfmon_get_out_of_order_bandwidth_mbps( + struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *duration_ns) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t count0 = 0, count1 = 0, count4 = 0, + out_of_order_bandwidth_mbps = 0, measuring_duration_ns = 0; + struct fixed31_32 temp; + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x1); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count1); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4); + + if (refclk_mhz == 0) + return 0; + + temp = dc_fixpt_from_fraction(count4, refclk_mhz); + temp = dc_fixpt_mul_int(temp, 1000); + measuring_duration_ns = dc_fixpt_ceil(temp); + + if (duration_ns) + *duration_ns = measuring_duration_ns; + if (measuring_duration_ns == 0) + return 0; + + temp = dc_fixpt_from_fraction(count1, measuring_duration_ns); + temp = dc_fixpt_mul_int(temp, 64 * 1000); + out_of_order_bandwidth_mbps = dc_fixpt_floor(temp); + + return out_of_order_bandwidth_mbps; +} + +/** + * @brief Configures the performance monitor to measure in-order + * bandwidth in megabits per second (Mbps). + * + * This function sets up performance monitoring counters to measure + * in-order bandwidth. It uses hardware counters 0 and 4 for this + * purpose: + * + * Counter 0: + * - Purpose: Tracks the total data received during the measurement + * period. + * - Behavior: Starts counting when the measurement starts and stops + * when the measurement is stopped manually. + * + * Counter 4: + * - Purpose: Measures the total time taken during the measurement + * period in reference clock (refclk) cycles. + * - Behavior: Starts counting when the measurement starts and stops + * when the measurement is stopped manually. + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + */ +static void hubbub60_perfmon_start_measuring_in_order_bandwidth( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + /* Program Latency Monitor Registers */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0, + DCHUBBUB_LATENCY_CNT_EN, 0x1, + DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1); + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0, + LATENCY_SOURCE_SEL, 0x8, // ROB latency + UTM_FILTER_SEL, 0x0); + + /* Program counter 0 to count total data received */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0 + PERFCOUNTER_EVENT_SEL, 257, // in order data + PERFCOUNTER_CVALUE_SEL, 0x0, // ignored + PERFCOUNTER_INC_MODE, 0x2, // count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done + PERFCOUNTER_ACTIVE, 0x1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0 + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // stop when other counters are done + PERFCOUNTER_HW_STOP2_SEL, 0x0); + + /* Program counter 4 to count the measuring time */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4 + PERFCOUNTER_EVENT_SEL, 19, // always 1 event + PERFCOUNTER_CVALUE_SEL, 0x0, // all cvalue range + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x0, // stops after counting is done + PERFCOUNTER_ACTIVE, 0x1); // enable the counter + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4 + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // stop when count reaches cvalue + PERFCOUNTER_HW_STOP2_SEL, 0x0); // ignored + + /* Program perfcounter states */ + REG_SET_4(DC_PERFMON5_PERFCOUNTER_STATE, 0, + PERFCOUNTER_STATE_SEL0, 0x0, // global state mode + PERFCOUNTER_CNT0_STATE, 0x0, // ignored + PERFCOUNTER_STATE_SEL4, 0x0, // global state mode + PERFCOUNTER_CNT4_STATE, 0x0); // ignored + + /* start the counters */ + REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0, + PERFMON_STATE, 1, + PERFMON_RPT_COUNT, 0xFFFFF); +} + +/** + * @brief Reads the current performance monitor result and calculates + * the in-order bandwidth in Mbps. + * + * This function reads the values from the DCN 6.0 hubbub performance + * monitor counters, then calculates the in-order bandwidth in Mbps. + * + * @param hubbub Pointer to the hubbub structure representing the + * hardware instance. + * @param refclk_mhz Reference clock frequency in MHz, used for time + * conversion. + * @param min_duration_ns Minimum duration in nanoseconds required for + * a valid measurement. + * @param duration_ns Measured duration in nanoseconds. + * + * @return The in-order bandwidth in Mbps as a 32-bit unsigned integer. + */ +static uint32_t hubbub60_perfmon_get_in_order_bandwidth_mbps( + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t min_duration_ns, uint32_t *duration_ns) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t count0 = 0, count4 = 0, in_order_bandwidth_mbps = 0, + measuring_duration_ns = 0; + struct fixed31_32 temp; + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4); + + if (refclk_mhz == 0) + return 0; + + measuring_duration_ns = count4 * 1000 / refclk_mhz; + *duration_ns = measuring_duration_ns; + if (min_duration_ns > measuring_duration_ns) + return 0; + + /* stop the counters */ + REG_SET(DC_PERFMON5_PERFMON_CNTL, 0, PERFMON_STATE, 2); + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0); + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4); + + ASSERT(count4); + temp = dc_fixpt_from_fraction(count4, refclk_mhz); + temp = dc_fixpt_mul_int(temp, 1000); + measuring_duration_ns = dc_fixpt_ceil(temp); + if (duration_ns) + *duration_ns = measuring_duration_ns; + + temp = dc_fixpt_from_fraction(count0, measuring_duration_ns); + temp = dc_fixpt_mul_int(temp, 64 * 1000); + in_order_bandwidth_mbps = dc_fixpt_floor(temp); + + return in_order_bandwidth_mbps; +} + +/** + * @brief Configures the performance monitor to measure prefetch data size. + * + * This function sets up performance monitoring counter 0 to measure + * the total prefetch data size in bytes. It configures the necessary + * registers to count the number of valid ROB output events, which + * correspond to prefetch data. + * + * @param hubbub Pointer to the hubbub structure representing the hardware + * instance. + */ +static void hubbub60_perfmon_start_measuring_prefetch_data_size( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + /* configure measurement control */ + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0, + DCHUBBUB_LATENCY_CNT_EN, 0x1, + DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1); + REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0, + LATENCY_SOURCE_SEL, 0x2, + UTM_FILTER_SEL, 0x2); // prefetch only + + /* program counter 0 to count prefetch data size */ + REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0 + PERFCOUNTER_EVENT_SEL, 259, // ROB output valid event + PERFCOUNTER_CVALUE_SEL, 0x0, // ignored + PERFCOUNTER_INC_MODE, 0x2, // Count LSB level + PERFCOUNTER_HW_CNTL_SEL, 0x0, // simultaneous mode + PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high + PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done + PERFCOUNTER_ACTIVE, 1); + REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0 + PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value + PERFCOUNTER_HW_STOP1_SEL, 0x0, // stop when counting is done + PERFCOUNTER_HW_STOP2_SEL, 0x0); // ignored + + REG_SET_2(DC_PERFMON5_PERFCOUNTER_STATE, 0, + PERFCOUNTER_STATE_SEL0, 0x1, // simultaneous state mode + PERFCOUNTER_CNT0_STATE, 0x3); // hw mode + + REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0, + PERFMON_RUN_ENABLE_START_SEL, 0x0, + PERFMON_RUN_ENABLE_STOP_SEL, 0); + + /* start the counters */ + REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0, + PERFMON_STATE, 0x3, + PERFMON_RPT_COUNT, 0x1); +} + +/** + * @brief Reads the current performance monitor result and calculates + * the prefetch data size in bytes. + * + * This function reads the value from the DCN 6.0 hubbub performance + * monitor counter 0, then calculates the prefetch data size in bytes. + * + * @param hubbub Pointer to the hubbub structure representing the + * hardware instance. + * + * @return The prefetch data size in bytes as a 32-bit unsigned integer. + */ +static uint32_t hubbub60_perfmon_get_prefetch_data_size( + struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + uint32_t count0 = 0; + uint32_t prefetch_data_size_bytes = 0; + + REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0); + REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0); + + prefetch_data_size_bytes = count0 * 64; + return prefetch_data_size_bytes; +} + +/** + * @brief Forces the display to use the nominal QoS profile. + * + * This function configures the hubbub to force the display to use + * the nominal QoS profile by updating the relevant registers. + * + * @param hubbub Pointer to the hubbub structure representing the + * hardware instance. + */ +static void hubbub60_force_display_nominal_profile(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, 0); + REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, 1); +} + +/** + * @brief Forces the display to use the urgent QoS profile. + * + * This function configures the hubbub to force the display to use + * the urgent QoS profile by updating the relevant registers. + * + * @param hubbub Pointer to the hubbub structure representing the + * hardware instance. + */ +static void hubbub60_force_display_urgent_profile(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, 1); + REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, 1); +} + +/** + * @brief Resets the display QoS profile to default. + * + * This function resets the display QoS profile by disabling any + * forced QoS settings in the hubbub. + * + * @param hubbub Pointer to the hubbub structure representing the + * hardware instance. + */ +static void hubbub60_reset_display_qos_profile(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + + REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, 0); + REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, 0); +} + +static const struct hubbub_funcs hubbub60_funcs = { + .update_dchub = hubbub2_update_dchub, + .init_dchub_sys_ctx = hubbub3_init_dchub_sys_ctx, + .init_vm_ctx = hubbub2_init_vm_ctx, + .dcc_support_swizzle_addr3 = hubbub401_dcc_support_swizzle, + .dcc_support_pixel_format_plane0_plane1 = + hubbub401_dcc_support_pixel_format, + .get_dcc_compression_cap = hubbub401_get_dcc_compression_cap, + .wm_read_state = hubbub60_wm_read_state, + .get_dchub_ref_freq = hubbub2_get_dchub_ref_freq, + .program_watermarks = hubbub60_program_watermarks, + .allow_self_refresh_control = hubbub1_allow_self_refresh_control, + .is_allow_self_refresh_enabled = hubbub1_is_allow_self_refresh_enabled, + .verify_allow_pstate_change_high = NULL, + .force_wm_propagate_to_pipes = hubbub60_force_wm_propagate_to_pipes, + .force_pstate_change_control = hubbub3_force_pstate_change_control, + .init_watermarks = hubbub60_init_watermarks, + .init_crb = dcn60_init_crb, + .hubbub_read_state = hubbub2_read_state, + .force_usr_retraining_allow = NULL, + .set_request_limit = hubbub32_set_request_limit, + .program_det_segments = dcn60_program_det_segments, + .program_compbuf_segments = dcn60_program_compbuf_segments, + .wait_for_det_update = dcn60_wait_for_det_update, + .program_arbiter = dcn401_program_arbiter, + .hubbub_read_reg_state = hubbub3_read_reg_state, + .perfmon = { + .reset = hubbub60_perfmon_reset, + .start_measuring_memory_latencies = + hubbub60_perfmon_start_measuring_memory_latencies, + .get_memory_latencies_ns = + hubbub60_perfmon_get_memory_latencies_ns, + .start_measuring_urgent_assertion_count = + hubbub60_perfmon_start_measuring_urgent_assertion_count, + .get_urgent_assertion_count = + hubbub60_perfmon_get_urgent_assertion_count, + .start_measuring_urgent_ramp_latency = + hubbub60_perfmon_start_measuring_urgent_ramp_latency, + .get_urgent_ramp_latency_ns = + hubbub60_perfmon_get_urgent_ramp_latency_ns, + .arm_measuring_out_of_order_bandwidth = + hubbub60_perfmon_arm_measuring_out_of_order_bandwidth, + .start_measuring_out_of_order_bandwidth = + hubbub60_perfmon_start_measuring_out_of_order_bandwidth, + .get_out_of_order_bandwidth_mbps = + hubbub60_perfmon_get_out_of_order_bandwidth_mbps, + .start_measuring_in_order_bandwidth = + hubbub60_perfmon_start_measuring_in_order_bandwidth, + .get_in_order_bandwidth_mbps = + hubbub60_perfmon_get_in_order_bandwidth_mbps, + .start_measuring_prefetch_data_size = + hubbub60_perfmon_start_measuring_prefetch_data_size, + .get_prefetch_data_size = + hubbub60_perfmon_get_prefetch_data_size, + }, + .qos = { + .force_display_nominal_profile = + hubbub60_force_display_nominal_profile, + .force_display_urgent_profile = + hubbub60_force_display_urgent_profile, + .reset_display_qos_profile = + hubbub60_reset_display_qos_profile, + }, +}; + +void hubbub60_construct(struct dcn20_hubbub *hubbub2, + struct dc_context *ctx, + const struct dcn_hubbub_registers *hubbub_regs, + const struct dcn_hubbub_shift *hubbub_shift, + const struct dcn_hubbub_mask *hubbub_mask, + int det_size_kb, + int pixel_chunk_size_kb, + int config_return_buffer_size_kb) +{ + hubbub2->base.ctx = ctx; + hubbub2->base.funcs = &hubbub60_funcs; + hubbub2->regs = hubbub_regs; + hubbub2->shifts = hubbub_shift; + hubbub2->masks = hubbub_mask; + + hubbub2->detile_buf_size = det_size_kb * 1024; + hubbub2->pixel_chunk_size = pixel_chunk_size_kb * 1024; + hubbub2->crb_size_segs = config_return_buffer_size_kb / DCN6_0_CRB_SEGMENT_SIZE_KB; +} diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.h new file mode 100644 index 000000000000..204f422c3383 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.h @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + + +#ifndef __DC_HUBBUB_DCN60_H__ +#define __DC_HUBBUB_DCN60_H__ + +#include "dcn30/dcn30_hubbub.h" +#include "dcn32/dcn32_hubbub.h" +#include "dcn401/dcn401_hubbub.h" + + +#define DCN6_0_CRB_SIZE_KB 2112 +#define DCN6_0_DEFAULT_DET_SIZE 512 +#define DCN6_0_CRB_SEGMENT_SIZE_KB 64 + + #define HUBBUB_MASK_SH_LIST_DCN6_0(mask_sh)\ + HUBBUB_SF(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_SOFT_RESET, DCHUBBUB_GLOBAL_SOFT_RESET, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, DCHUBBUB_ARB_WATERMARK_CHANGE_DONE_INTERRUPT_DISABLE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_VALUE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_VALUE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_ENABLE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_SAT_LEVEL, DCHUBBUB_ARB_SAT_LEVEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DF_REQ_OUTSTAND, DCHUBBUB_ARB_MIN_REQ_OUTSTAND, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DF_REQ_OUTSTAND, DCHUBBUB_ARB_MAX_REQ_OUTSTAND, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HUBBUB_SF(DCN_VM_FB_LOCATION_BASE, FB_BASE, mask_sh), \ + HUBBUB_SF(DCN_VM_FB_LOCATION_TOP, FB_TOP, mask_sh), \ + HUBBUB_SF(DCN_VM_FB_OFFSET, FB_OFFSET, mask_sh), \ + HUBBUB_SF(DCN_VM_AGP_BOT, AGP_BOT, mask_sh), \ + HUBBUB_SF(DCN_VM_AGP_TOP, AGP_TOP, mask_sh), \ + HUBBUB_SF(DCN_VM_AGP_BASE, AGP_BASE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A, DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_DEBUG_CTRL_0, DET_DEPTH, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET0_CTRL, DET0_SIZE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET1_CTRL, DET1_SIZE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET2_CTRL, DET2_SIZE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET3_CTRL, DET3_SIZE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, mask_sh),\ + HUBBUB_SF(DCHUBBUB_COMPBUF_CTRL, COMPBUF_SIZE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_COMPBUF_CTRL, COMPBUF_SIZE_CURRENT, mask_sh),\ + HUBBUB_SF(DCHUBBUB_COMPBUF_CTRL, CONFIG_ERROR, mask_sh),\ + HUBBUB_SF(COMPBUF_RESERVED_SPACE, COMPBUF_RESERVED_SPACE_64B, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A, DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A, DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A, DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A, mask_sh),\ + HUBBUB_SF(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, mask_sh),\ + HUBBUB_SF(DCN_VM_FAULT_ADDR_MSB, DCN_VM_FAULT_ADDR_MSB, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_ADDR_LSB, DCN_VM_FAULT_ADDR_LSB, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_CNTL, DCN_VM_ERROR_STATUS_CLEAR, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_CNTL, DCN_VM_ERROR_STATUS_MODE, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_CNTL, DCN_VM_ERROR_INTERRUPT_ENABLE, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_CNTL, DCN_VM_RANGE_FAULT_DISABLE, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_CNTL, DCN_VM_PRQ_FAULT_DISABLE, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_STATUS, DCN_VM_ERROR_STATUS, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_STATUS, DCN_VM_ERROR_VMID, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_STATUS, DCN_VM_ERROR_TABLE_LEVEL, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_STATUS, DCN_VM_ERROR_PIPE, mask_sh), \ + HUBBUB_SF(DCN_VM_FAULT_STATUS, DCN_VM_ERROR_INTERRUPT_STATUS, mask_sh),\ + HUBBUB_SF(SDPIF_REQUEST_RATE_LIMIT, SDPIF_REQUEST_RATE_LIMIT, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CLOCK_CNTL, DISPCLK_R_DCHUBBUB_GATE_DIS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CLOCK_CNTL, DCFCLK_R_DCHUBBUB_GATE_DIS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_SDPIF_CFG0, SDPIF_PORT_CONTROL, mask_sh),\ + HUBBUB_SF(DCHUBBUB_SDPIF_CFG1, SDPIF_MAX_NUM_OUTSTANDING, mask_sh),\ + HUBBUB_SF(DCHUBBUB_MEM_PWR_MODE_CTRL, DET_MEM_PWR_LS_MODE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL1, DCHUBBUB_TIMEOUT_ERROR_STATUS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL1, DCHUBBUB_TIMEOUT_REQ_STALL_THRESHOLD, mask_sh),\ + HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_PSTATE_STALL_THRESHOLD, mask_sh),\ + HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_DETECTION_EN, mask_sh),\ + HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_TIMER_RESET, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_UNDERFLOW_STATUS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_OVERFLOW_STATUS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_OVERFLOW_CLEAR, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, DCHUBBUB_HW_DEBUG, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, CSTATE_SWATH_CHK_GOOD_MODE, mask_sh),\ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, DCHUBBUB_LATENCY_CNT_EN, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, ARB_LATENCY_PIPE_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, ARB_LATENCY_REQ_TYPE_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, ROB_FIFO_LEVEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, REQ_UNIT_ID_MASK, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, REQ_UNIT_ID_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, DCHUBBUB_LATENCY_FRAME_WIN_EN, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, DCHUBBUB_LATENCY_FRAME_WIN_SRC_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, DCHUBBUB_LATENCY_FRAME_WIN_DUR, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, LATENCY_SOURCE_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, LATENCY_DEBUG_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, UTM_FILTER_SEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, ROB_MAX_FIFO_LEVEL, mask_sh), \ + HUBBUB_SF(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, ROB_MAX_FIFO_LEVEL_RESET, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_EVENT_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_CVALUE_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_INC_MODE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_HW_CNTL_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_RUNEN_MODE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_CNTOFF_START_DIS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_RESTART_EN, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_INT_EN, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_OFF_MASK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_ACTIVE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL, PERFCOUNTER_CNTL_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL2, PERFCOUNTER_COUNTED_VALUE_TYPE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL2, PERFCOUNTER_HW_STOP1_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL2, PERFCOUNTER_HW_STOP2_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL2, PERFCOUNTER_CNTOFF_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_CNTL2, PERFCOUNTER_CNTL2_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT0_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL0, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT1_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL1, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT2_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL2, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT3_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL3, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT4_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL4, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT5_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL5, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT6_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL6, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_CNT7_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFCOUNTER_STATE, PERFCOUNTER_STATE_SEL7, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL, PERFMON_STATE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL, PERFMON_RPT_COUNT, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL, PERFMON_CNTOFF_AND_OR, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL, PERFMON_CNTOFF_INT_EN, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL, PERFMON_CNTOFF_INT_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL, PERFMON_CNTOFF_INT_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL2, PERFMON_CNTOFF_INT_TYPE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL2, PERFMON_CLK_ENABLE, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL2, PERFMON_RUN_ENABLE_START_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CNTL2, PERFMON_RUN_ENABLE_STOP_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT0_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT1_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT2_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT3_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT4_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT5_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT6_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT7_STATUS, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT0_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT1_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT2_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT3_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT4_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT5_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT6_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFCOUNTER_INT7_ACK, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, PERFMON_CVALUE_HI, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_CVALUE_LOW, PERFMON_CVALUE_LOW, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_HI, PERFMON_HI, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_HI, PERFMON_READ_SEL, mask_sh), \ + HUBBUB_SF(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, mask_sh), \ + HUBBUB_SF(FMON_CTRL, FMON_MODE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_BANDWIDTH_OVERHEAD, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, mask_sh), \ + HUBBUB_SF(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_DO_NOT_FORCE_URGENCY_DURING_PSTATE_CHANGE_REQUEST, mask_sh) + +void hubbub60_construct(struct dcn20_hubbub *hubbub2, + struct dc_context *ctx, + const struct dcn_hubbub_registers *hubbub_regs, + const struct dcn_hubbub_shift *hubbub_shift, + const struct dcn_hubbub_mask *hubbub_mask, + int det_size_kb, + int pixel_chunk_size_kb, + int config_return_buffer_size_kb); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c new file mode 100644 index 000000000000..e704ce5cd0a0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dce_calcs.h" +#include "reg_helper.h" +#include "basics/conversion.h" +#include "dcn50_hubp.h" + +#define REG(reg)\ + hubp2->hubp_regs->reg + +#define CTX \ + hubp2->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + hubp2->hubp_shift->field_name, hubp2->hubp_mask->field_name + +bool hubp50_program_surface_flip_and_addr( + struct hubp *hubp, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + //program flip type + REG_UPDATE(DCSURF_FLIP_CONTROL, + SURFACE_FLIP_TYPE, flip_immediate); + + // Program VMID reg + if (flip_immediate == 0) + REG_UPDATE(VMID_SETTINGS_0, + VMID, address->vmid); + + if (address->type == PLN_ADDR_TYPE_GRPH_STEREO) { + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_MODE_FOR_STEREOSYNC, 0); + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_IN_STEREOSYNC, 0x1); + + } else { + // turn off stereo if not in stereo + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_MODE_FOR_STEREOSYNC, 0x0); + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_IN_STEREOSYNC, 0x0); + } + + /* HW automatically latch rest of address register on write to + * DCSURF_PRIMARY_SURFACE_ADDRESS if SURFACE_UPDATE_LOCK is not used + * + * program high first and then the low addr, order matters! + */ + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + if (address->grph.addr.quad_part == 0) + break; + + REG_UPDATE(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->grph.addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->grph.addr.low_part); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + if (address->video_progressive.luma_addr.quad_part == 0 + || address->video_progressive.chroma_addr.quad_part == 0) + break; + + REG_UPDATE_2(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface, + PRIMARY_SURFACE_TMZ_C, address->tmz_surface); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, 0, + PRIMARY_SURFACE_ADDRESS_HIGH_C, + address->video_progressive.chroma_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_C, 0, + PRIMARY_SURFACE_ADDRESS_C, + address->video_progressive.chroma_addr.low_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->video_progressive.luma_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->video_progressive.luma_addr.low_part); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + if (address->grph_stereo.left_addr.quad_part == 0 + || address->grph_stereo.right_addr.quad_part == 0) + break; + + REG_UPDATE_4(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface, + PRIMARY_SURFACE_TMZ_C, address->tmz_surface, + SECONDARY_SURFACE_TMZ, address->tmz_surface, + SECONDARY_SURFACE_TMZ_C, address->tmz_surface); + + REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, 0, + SECONDARY_SURFACE_ADDRESS_HIGH_C, + address->grph_stereo.right_alpha_addr.high_part); + + REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS_C, 0, + SECONDARY_SURFACE_ADDRESS_C, + address->grph_stereo.right_alpha_addr.low_part); + + REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, 0, + SECONDARY_SURFACE_ADDRESS_HIGH, + address->grph_stereo.right_addr.high_part); + + REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS, 0, + SECONDARY_SURFACE_ADDRESS, + address->grph_stereo.right_addr.low_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, 0, + PRIMARY_SURFACE_ADDRESS_HIGH_C, + address->grph_stereo.left_alpha_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_C, 0, + PRIMARY_SURFACE_ADDRESS_C, + address->grph_stereo.left_alpha_addr.low_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->grph_stereo.left_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->grph_stereo.left_addr.low_part); + break; + case PLN_ADDR_TYPE_RGBEA: + if (address->rgbea.addr.quad_part == 0 + || address->rgbea.alpha_addr.quad_part == 0) + break; + + REG_UPDATE_2(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface, + PRIMARY_SURFACE_TMZ_C, address->tmz_surface); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, 0, + PRIMARY_SURFACE_ADDRESS_HIGH_C, + address->rgbea.alpha_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_C, 0, + PRIMARY_SURFACE_ADDRESS_C, + address->rgbea.alpha_addr.low_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->rgbea.addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->rgbea.addr.low_part); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + hubp->request_address = *address; + + return true; +} + +static enum swizzle_mode_values hubp50_addr3_to_swizzle_mode_mapping(enum swizzle_mode_addr3_values addr3_swizzle_mode) +{ + enum swizzle_mode_values swizzle_mode = DC_SW_UNKNOWN; + + switch (addr3_swizzle_mode) { + case DC_ADDR3_SW_LINEAR: + swizzle_mode = DC_SW_LINEAR; + break; + case DC_ADDR3_SW_256B_2D: + swizzle_mode = DC_SW_256_R; + break; + case DC_ADDR3_SW_4KB_2D: + swizzle_mode = DC_SW_4KB_R; + break; + case DC_ADDR3_SW_64KB_2D: + case DC_ADDR3_SW_64KB_2D_Z: + swizzle_mode = DC_SW_64KB_R; + break; + case DC_ADDR3_SW_256KB_2D: + case DC_ADDR3_SW_256KB_2D_Z: + swizzle_mode = DC_SW_VAR_R; + break; + case DC_ADDR3_SW_4KB_3D: + case DC_ADDR3_SW_64KB_3D: + case DC_ADDR3_SW_256KB_3D: + default: + BREAK_TO_DEBUGGER(); + break; + } + + return swizzle_mode; +} + +static void hubp50_program_tiling( + struct dcn20_hubp *hubp2, + const struct dc_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + (void)pixel_format; + /* Tiling address-generation compatibility level programmed into the + * DCSURF_TILING_CONFIG.COMPAT_LEVEL register field: + * 0 - gfx8 bank_height == 1 + * 1 - gfx8 bank_height == 2 + * 2 - gfx9/10/11 + * 5 - gfx addr3 + */ + unsigned int compat_level = 0; + enum swizzle_mode_values swizzle_mode; + + switch (info->gfxversion) { + case DcGfxVersion7: + case DcGfxVersion8: + + REG_UPDATE_8(DCSURF_LEGACY_ADDR_CONFIG, + LEGACY_NUM_BANKS, info->gfx8.num_banks, + BANK_WIDTH, info->gfx8.bank_width, + BANK_HEIGHT, info->gfx8.bank_height, + MACRO_TILE_ASPECT, info->gfx8.tile_aspect, + TILE_SPLIT, info->gfx8.tile_split, + MICRO_TILE_MODE_NEW, info->gfx8.tile_mode, + PIPE_CONFIG, info->gfx8.pipe_config, + ARRAY_MODE, info->gfx8.array_mode); + if (info->gfx8.bank_height == 1) { + compat_level = 0; + } else if (info->gfx8.bank_height == 2) { + compat_level = 1; + } + break; + + case DcGfxVersion9: + case DcGfxVersion10: + case DcGfxVersion11: + REG_UPDATE_4(DCSURF_ADDR_CONFIG, + NUM_PIPES, log_2(info->gfx9.num_pipes), + PIPE_INTERLEAVE, info->gfx9.pipe_interleave, + MAX_COMPRESSED_FRAGS, log_2(info->gfx9.max_compressed_frags), + NUM_PKRS, log_2(info->gfx9.num_pkrs)); + REG_UPDATE(DCSURF_TILING_CONFIG, SW_MODE, info->gfx9.swizzle); + compat_level = 2; + + break; + case DcGfxAddr3: + swizzle_mode = hubp50_addr3_to_swizzle_mode_mapping(info->gfx_addr3.swizzle); + REG_UPDATE(DCSURF_TILING_CONFIG, SW_MODE, swizzle_mode); + compat_level = 5; + break; + } + + REG_UPDATE(DCSURF_TILING_CONFIG, COMPAT_LEVEL, compat_level); +} +static void hubp50_program_pixel_format( + struct hubp *hubp, + enum surface_pixel_format format) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + uint32_t red_bar = 3; + uint32_t blue_bar = 2; + + /* swap for ABGR format */ + if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR8888 + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010 + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616 + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { + red_bar = 2; + blue_bar = 3; + } + + REG_UPDATE_2(HUBPRET_CONTROL, + CROSSBAR_SRC_CB_B, blue_bar, + CROSSBAR_SRC_CR_R, red_bar); + + /* Mapping is same as ipp programming (cnvc) */ + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 1); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 3); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 8); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 10); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: /* we use crossbar already */ + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 26); /* ARGB16161616_UNORM */ + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:/*we use crossbar already*/ + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 24); + break; + + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 65); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 64); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 67); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 66); + break; + /* Planar 422 formats*/ + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P208: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 64); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P208: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 65); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P210: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 66); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P210: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 67); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P212: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 68); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P212: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 69); + break; + /* Packed 422 formats*/ + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCrYCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 72); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCbYCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 73); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrYCbY: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 74); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbYCrY: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 75); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCrYCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 76); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCbYCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 77); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CrYCbY: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 78); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CbYCrY: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 79); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCbYCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 80); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCrYCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 81); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CrYCbY: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 82); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CbYCrY: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 83); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_AYCrCb8888: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 12); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB111110_FIX: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 112); + break; + case SURFACE_PIXEL_FORMAT_GRPH_BGR101111_FIX: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 113); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_ACrYCb2101010: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 114); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB111110_FLOAT: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 118); + break; + case SURFACE_PIXEL_FORMAT_GRPH_BGR101111_FLOAT: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 119); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGBE: + REG_UPDATE_2(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 116, + ALPHA_PLANE_EN, 0); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA: + REG_UPDATE_2(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 116, + ALPHA_PLANE_EN, 1); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +void hubp50_program_surface_config( + struct hubp *hubp, + enum surface_pixel_format format, + struct dc_tiling_info *tiling_info, + struct plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror, + unsigned int compat_level) +{ + (void)compat_level; + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + hubp401_dcc_control(hubp, dcc); + hubp50_program_tiling(hubp2, tiling_info, format); + hubp401_program_size(hubp, format, plane_size, dcc); + hubp2_program_rotation(hubp, rotation, horizontal_mirror); + hubp50_program_pixel_format(hubp, format); +} + +void hubp50_read_state(struct hubp *hubp) +{ + hubp401_read_state(hubp); +} + +static struct hubp_funcs dcn50_hubp_funcs = { + .hubp_enable_tripleBuffer = hubp2_enable_triplebuffer, + .hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled, + .hubp_program_surface_flip_and_addr = hubp50_program_surface_flip_and_addr, + .hubp_program_surface_config = hubp50_program_surface_config, + .hubp_is_flip_pending = hubp2_is_flip_pending, + .hubp_setup2 = hubp401_setup, + .hubp_setup_interdependent2 = hubp401_setup_interdependent, + .hubp_set_vm_system_aperture_settings = hubp3_set_vm_system_aperture_settings, + .set_blank = hubp2_set_blank, + .set_blank_regs = hubp2_set_blank_regs, + .hubp_reset = hubp_reset, + .mem_program_viewport = hubp401_set_viewport, + .set_cursor_attributes = hubp32_cursor_set_attributes, + .set_cursor_position = hubp401_cursor_set_position, + .hubp_clk_cntl = hubp2_clk_cntl, + .hubp_vtg_sel = hubp2_vtg_sel, + .dmdata_set_attributes = hubp3_dmdata_set_attributes, + .dmdata_load = hubp2_dmdata_load, + .dmdata_status_done = hubp2_dmdata_status_done, + .hubp_read_state = hubp50_read_state, + .hubp_clear_underflow = hubp2_clear_underflow, + .hubp_set_flip_control_surface_gsl = hubp2_set_flip_control_surface_gsl, + .hubp_init = hubp401_init, + .set_unbounded_requesting = hubp401_set_unbounded_requesting, + .hubp_soft_reset = hubp31_soft_reset, + .hubp_set_flip_int = hubp401_set_flip_int, + .hubp_in_blank = hubp401_in_blank, + .phantom_hubp_post_enable = hubp32_phantom_hubp_post_enable, + .hubp_update_mall_sel = hubp401_update_mall_sel, + .hubp_prepare_subvp_buffering = hubp32_prepare_subvp_buffering, + .hubp_program_mcache_id_and_split_coordinate = hubp401_program_mcache_id_and_split_coordinate, + .hubp_update_3dlut_fl_bias_scale = hubp401_update_3dlut_fl_bias_scale, + .hubp_program_3dlut_fl_mode = hubp401_program_3dlut_fl_mode, + .hubp_program_3dlut_fl_format = hubp401_program_3dlut_fl_format, + .hubp_program_3dlut_fl_addr = hubp401_program_3dlut_fl_addr, + .hubp_program_3dlut_fl_dlg_param = hubp401_program_3dlut_fl_dlg_param, + .hubp_enable_3dlut_fl = hubp401_enable_3dlut_fl, + .hubp_program_3dlut_fl_addressing_mode = hubp401_program_3dlut_fl_addressing_mode, + .hubp_program_3dlut_fl_width = hubp401_program_3dlut_fl_width, + .hubp_program_3dlut_fl_tmz_protected = hubp401_program_3dlut_fl_tmz_protected, + .hubp_program_3dlut_fl_crossbar = hubp401_program_3dlut_fl_crossbar, + .hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done, + .hubp_clear_tiling = hubp401_clear_tiling, + .hubp_read_reg_state = hubp3_read_reg_state +}; + +bool hubp50_construct( + struct dcn20_hubp *hubp2, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_hubp2_registers *hubp_regs, + const struct dcn_hubp2_shift *hubp_shift, + const struct dcn_hubp2_mask *hubp_mask) +{ + hubp2->base.funcs = &dcn50_hubp_funcs; + hubp2->base.ctx = ctx; + hubp2->hubp_regs = hubp_regs; + hubp2->hubp_shift = hubp_shift; + hubp2->hubp_mask = hubp_mask; + hubp2->base.inst = inst; + hubp2->base.opp_id = OPP_ID_INVALID; + hubp2->base.mpcc_id = 0xf; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h new file mode 100644 index 000000000000..e7d9bb3623b9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + */ + +#ifndef __DC_HUBP_DCN50_H__ +#define __DC_HUBP_DCN50_H__ + +#include "dcn20/dcn20_hubp.h" +#include "dcn21/dcn21_hubp.h" +#include "dcn30/dcn30_hubp.h" +#include "dcn31/dcn31_hubp.h" +#include "dcn32/dcn32_hubp.h" +#include "dcn401/dcn401_hubp.h" +#include "dml2_0/dml21/inc/dml_top_dchub_registers.h" + +#define HUBP_MASK_SH_LIST_DCN50(mask_sh)\ + HUBP_MASK_SH_LIST_DCN401(mask_sh) + +bool hubp50_program_surface_flip_and_addr( + struct hubp *hubp, + const struct dc_plane_address *address, + bool flip_immediate); + +void hubp50_program_surface_config( + struct hubp *hubp, + enum surface_pixel_format format, + struct dc_tiling_info *tiling_info, + struct plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror, + unsigned int compat_level); + + +void hubp50_read_state(struct hubp *hubp); + + +bool hubp50_construct( + struct dcn20_hubp *hubp2, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_hubp2_registers *hubp_regs, + const struct dcn_hubp2_shift *hubp_shift, + const struct dcn_hubp2_mask *hubp_mask); + +#endif /* __DC_HUBP_DCN50_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.c new file mode 100644 index 000000000000..1065303f1d72 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dce_calcs.h" +#include "reg_helper.h" +#include "basics/conversion.h" +#include "dcn60_hubp.h" + +#define REG(reg)\ + hubp2->hubp_regs->reg + +#define CTX \ + hubp2->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + hubp2->hubp_shift->field_name, hubp2->hubp_mask->field_name + +static void hubp60_program_deadline( + struct hubp *hubp, + struct dml2_display_dlg_regs *dlg_attr, + struct dml2_display_ttu_regs *ttu_attr) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + /* DLG - Per hubp */ + REG_SET_2(BLANK_OFFSET_0, 0, + REFCYC_H_BLANK_END, dlg_attr->refcyc_h_blank_end, + DLG_V_BLANK_END, dlg_attr->dlg_vblank_end); + + REG_SET(BLANK_OFFSET_1, 0, + MIN_DST_Y_NEXT_START, dlg_attr->min_dst_y_next_start); + + REG_SET(DST_DIMENSIONS, 0, + REFCYC_PER_HTOTAL, dlg_attr->refcyc_per_htotal); + + REG_SET_2(DST_AFTER_SCALER, 0, + REFCYC_X_AFTER_SCALER, dlg_attr->refcyc_x_after_scaler, + DST_Y_AFTER_SCALER, dlg_attr->dst_y_after_scaler); + + REG_SET(REF_FREQ_TO_PIX_FREQ, 0, + REF_FREQ_TO_PIX_FREQ, dlg_attr->ref_freq_to_pix_freq); + + /* DLG - Per luma/chroma */ + REG_SET(VBLANK_PARAMETERS_1, 0, + REFCYC_PER_PTE_GROUP_VBLANK_L, dlg_attr->refcyc_per_pte_group_vblank_l); + + if (REG(NOM_PARAMETERS_0)) + REG_SET(NOM_PARAMETERS_0, 0, + DST_Y_PER_PTE_ROW_NOM_L, dlg_attr->dst_y_per_pte_row_nom_l); + + if (REG(NOM_PARAMETERS_1)) + REG_SET(NOM_PARAMETERS_1, 0, + REFCYC_PER_PTE_GROUP_NOM_L, dlg_attr->refcyc_per_pte_group_nom_l); + + REG_SET(NOM_PARAMETERS_4, 0, + DST_Y_PER_META_ROW_NOM_L, dlg_attr->dst_y_per_meta_row_nom_l); + + REG_SET(NOM_PARAMETERS_5, 0, + REFCYC_PER_META_CHUNK_NOM_L, dlg_attr->refcyc_per_meta_chunk_nom_l); + + REG_SET_2(PER_LINE_DELIVERY, 0, + REFCYC_PER_LINE_DELIVERY_L, dlg_attr->refcyc_per_line_delivery_l, + REFCYC_PER_LINE_DELIVERY_C, dlg_attr->refcyc_per_line_delivery_c); + + REG_SET(VBLANK_PARAMETERS_2, 0, + REFCYC_PER_PTE_GROUP_VBLANK_C, dlg_attr->refcyc_per_pte_group_vblank_c); + + if (REG(NOM_PARAMETERS_2)) + REG_SET(NOM_PARAMETERS_2, 0, + DST_Y_PER_PTE_ROW_NOM_C, dlg_attr->dst_y_per_pte_row_nom_c); + + if (REG(NOM_PARAMETERS_3)) + REG_SET(NOM_PARAMETERS_3, 0, + REFCYC_PER_PTE_GROUP_NOM_C, dlg_attr->refcyc_per_pte_group_nom_c); + + REG_SET(NOM_PARAMETERS_6, 0, + DST_Y_PER_META_ROW_NOM_C, dlg_attr->dst_y_per_meta_row_nom_c); + + REG_SET(NOM_PARAMETERS_7, 0, + REFCYC_PER_META_CHUNK_NOM_C, dlg_attr->refcyc_per_meta_chunk_nom_c); + + /* TTU - per luma/chroma */ + /* TO DO: Set URGENT_FORCE_x fields with values from DML if/when available */ + REG_SET_3(DCN_SURF0_TTU_CNTL0, 0, + REFCYC_PER_REQ_DELIVERY, ttu_attr->refcyc_per_req_delivery_l, + URGENT_FORCE_VALUE, 0, + URGENT_FORCE_EN, 0); + + REG_SET_3(DCN_SURF1_TTU_CNTL0, 0, + REFCYC_PER_REQ_DELIVERY, ttu_attr->refcyc_per_req_delivery_c, + URGENT_FORCE_VALUE, 0, + URGENT_FORCE_EN, 0); + + REG_SET_3(DCN_CUR0_TTU_CNTL0, 0, + REFCYC_PER_REQ_DELIVERY, ttu_attr->refcyc_per_req_delivery_cur0, + URGENT_FORCE_VALUE, 0, + URGENT_FORCE_EN, 0); + + REG_SET(FLIP_PARAMETERS_1, 0, + REFCYC_PER_PTE_GROUP_FLIP_L, dlg_attr->refcyc_per_pte_group_flip_l); + REG_SET(HUBP_3DLUT_DLG_PARAM, 0, REFCYC_PER_3DLUT_GROUP, dlg_attr->refcyc_per_tdlut_group); + + REG_UPDATE(DCN_DMDATA_VM_CNTL, + REFCYC_PER_VM_DMDATA, dlg_attr->refcyc_per_vm_dmdata); +} + +void hubp60_setup( + struct hubp *hubp, + struct dml2_dchub_per_pipe_register_set *pipe_regs, + union dml2_global_sync_programming *pipe_global_sync, + struct dc_crtc_timing *timing) +{ + /* otg is locked when this func is called. Register are double buffered. + * disable the requestors is not needed + */ + hubp401_vready_at_or_After_vsync(hubp, pipe_global_sync, timing); + hubp401_program_requestor(hubp, &pipe_regs->rq_regs); + hubp60_program_deadline(hubp, &pipe_regs->dlg_regs, &pipe_regs->ttu_regs); +} + +void hubp60_setup_interdependent( + struct hubp *hubp, + struct dml2_dchub_per_pipe_register_set *pipe_regs) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + REG_SET_3(PREFETCH_SETTINGS, 0, + DST_Y_PREFETCH, pipe_regs->dlg_regs.dst_y_prefetch, + VRATIO_PREFETCH, pipe_regs->dlg_regs.vratio_prefetch, + FORCE_DISP_PREF_TO_VBLANK, pipe_regs->dlg_regs.force_prefetch_to_vblank); + + REG_SET(PREFETCH_SETTINGS_C, 0, + VRATIO_PREFETCH_C, pipe_regs->dlg_regs.vratio_prefetch_c); + + REG_SET_2(VBLANK_PARAMETERS_0, 0, + DST_Y_PER_VM_VBLANK, pipe_regs->dlg_regs.dst_y_per_vm_vblank, + DST_Y_PER_ROW_VBLANK, pipe_regs->dlg_regs.dst_y_per_row_vblank); + + REG_SET_2(FLIP_PARAMETERS_0, 0, + DST_Y_PER_VM_FLIP, pipe_regs->dlg_regs.dst_y_per_vm_flip, + DST_Y_PER_ROW_FLIP, pipe_regs->dlg_regs.dst_y_per_row_flip); + + REG_SET(VBLANK_PARAMETERS_3, 0, + REFCYC_PER_META_CHUNK_VBLANK_L, pipe_regs->dlg_regs.refcyc_per_meta_chunk_vblank_l); + + REG_SET(VBLANK_PARAMETERS_4, 0, + REFCYC_PER_META_CHUNK_VBLANK_C, pipe_regs->dlg_regs.refcyc_per_meta_chunk_vblank_c); + + REG_SET(FLIP_PARAMETERS_2, 0, + REFCYC_PER_META_CHUNK_FLIP_L, pipe_regs->dlg_regs.refcyc_per_meta_chunk_flip_l); + + REG_SET_2(PER_LINE_DELIVERY_PRE, 0, + REFCYC_PER_LINE_DELIVERY_PRE_L, pipe_regs->dlg_regs.refcyc_per_line_delivery_pre_l, + REFCYC_PER_LINE_DELIVERY_PRE_C, pipe_regs->dlg_regs.refcyc_per_line_delivery_pre_c); + + REG_SET(DCN_SURF0_TTU_CNTL1, 0, + REFCYC_PER_REQ_DELIVERY_PRE, + pipe_regs->ttu_regs.refcyc_per_req_delivery_pre_l); + + REG_SET(DCN_SURF1_TTU_CNTL1, 0, + REFCYC_PER_REQ_DELIVERY_PRE, + pipe_regs->ttu_regs.refcyc_per_req_delivery_pre_c); + + REG_SET(DCN_CUR0_TTU_CNTL1, 0, + REFCYC_PER_REQ_DELIVERY_PRE, pipe_regs->ttu_regs.refcyc_per_req_delivery_pre_cur0); + + REG_SET(DCN_GLOBAL_TTU_CNTL, 0, + MIN_TTU_VBLANK, pipe_regs->ttu_regs.min_ttu_vblank); + + REG_SET(DST_Y_DELTA_DRQ_LIMIT, 0, + DST_Y_DELTA_DRQ_LIMIT, pipe_regs->dlg_regs.dst_y_delta_drq_limit); + + REG_SET(DST_Y_ALT_CH_DRQ_LIMIT, 0, + DST_Y_ALT_CH_DRQ_LIMIT, pipe_regs->dlg_regs.dst_y_svp_drq_limit); +} + +void hubp60_cursor_set_attributes( + struct hubp *hubp, + const struct dc_cursor_attributes *attr) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + enum cursor_pitch hw_pitch = hubp1_get_cursor_pitch(attr->pitch); + enum cursor_lines_per_chunk lpc = hubp2_get_lines_per_chunk( + attr->width, attr->color_format); + + //Round cursor width up to next multiple of 64 + uint32_t cursor_width = ((attr->width + 63) / 64) * 64; + + hubp->curs_attr = *attr; + + if (!hubp->cursor_offload) { + REG_UPDATE(CURSOR_SURFACE_ADDRESS_HIGH, + CURSOR_SURFACE_ADDRESS_HIGH, attr->address.high_part); + REG_UPDATE(CURSOR_SURFACE_ADDRESS, + CURSOR_SURFACE_ADDRESS, attr->address.low_part); + + REG_UPDATE_2(CURSOR_SIZE, + CURSOR_WIDTH, cursor_width, + CURSOR_HEIGHT, attr->height); + + REG_UPDATE_4(CURSOR_CONTROL, + CURSOR_MODE, attr->color_format, + CURSOR_2X_MAGNIFY, attr->attribute_flags.bits.ENABLE_MAGNIFICATION, + CURSOR_PITCH, hw_pitch, + CURSOR_LINES_PER_CHUNK, lpc); + + REG_SET_3(CURSOR_SETTINGS, 0, + /* no shift of the cursor HDL schedule */ + CURSOR0_DST_Y_OFFSET, 0, + /* used to shift the cursor chunk request deadline */ + CURSOR0_CHUNK_HDL_ADJUST, 3, + FORCE_CURSOR_TO_DISP_PREF, attr->force_cursor_to_disp_pref); + } + hubp->att.SURFACE_ADDR_HIGH = attr->address.high_part; + hubp->att.SURFACE_ADDR = attr->address.low_part; + hubp->att.size.bits.width = attr->width; + hubp->att.size.bits.height = attr->height; + hubp->att.cur_ctl.bits.mode = attr->color_format; + + hubp->cur_rect.w = attr->width; + hubp->cur_rect.h = attr->height; + + hubp->att.cur_ctl.bits.pitch = hw_pitch; + hubp->att.cur_ctl.bits.line_per_chunk = lpc; + hubp->att.cur_ctl.bits.cur_2x_magnify = attr->attribute_flags.bits.ENABLE_MAGNIFICATION; + hubp->att.settings.bits.dst_y_offset = 0; + hubp->att.settings.bits.chunk_hdl_adjust = 3; + hubp->att.settings.bits.force_cursor_to_disp_pref = attr->force_cursor_to_disp_pref; +} + +void hubp60_read_state(struct hubp *hubp) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + struct dcn_hubp_state *s = &hubp2->state; + struct _vcs_dpi_display_dlg_regs_st *dlg_attr = &s->dlg_attr; + struct _vcs_dpi_display_ttu_regs_st *ttu_attr = &s->ttu_attr; + struct _vcs_dpi_display_rq_regs_st *rq_regs = &s->rq_regs; + + /* Requester */ + REG_GET(HUBPRET_CONTROL, + DET_BUF_PLANE1_BASE_ADDRESS, &rq_regs->plane1_base_address); + REG_GET_4(DCN_EXPANSION_MODE, + DRQ_EXPANSION_MODE, &rq_regs->drq_expansion_mode, + PRQ_EXPANSION_MODE, &rq_regs->prq_expansion_mode, + MRQ_EXPANSION_MODE, &rq_regs->mrq_expansion_mode, + CRQ_EXPANSION_MODE, &rq_regs->crq_expansion_mode); + + REG_GET_5(DCHUBP_REQ_SIZE_CONFIG, + CHUNK_SIZE, &rq_regs->rq_regs_l.chunk_size, + MIN_CHUNK_SIZE, &rq_regs->rq_regs_l.min_chunk_size, + DPTE_GROUP_SIZE, &rq_regs->rq_regs_l.dpte_group_size, + SWATH_HEIGHT, &rq_regs->rq_regs_l.swath_height, + PTE_ROW_HEIGHT_LINEAR, &rq_regs->rq_regs_l.pte_row_height_linear); + + REG_GET_5(DCHUBP_REQ_SIZE_CONFIG_C, + CHUNK_SIZE_C, &rq_regs->rq_regs_c.chunk_size, + MIN_CHUNK_SIZE_C, &rq_regs->rq_regs_c.min_chunk_size, + DPTE_GROUP_SIZE_C, &rq_regs->rq_regs_c.dpte_group_size, + SWATH_HEIGHT_C, &rq_regs->rq_regs_c.swath_height, + PTE_ROW_HEIGHT_LINEAR_C, &rq_regs->rq_regs_c.pte_row_height_linear); + + REG_GET(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR, + MC_VM_SYSTEM_APERTURE_HIGH_ADDR, &rq_regs->aperture_high_addr); + + REG_GET(DCN_VM_SYSTEM_APERTURE_LOW_ADDR, + MC_VM_SYSTEM_APERTURE_LOW_ADDR, &rq_regs->aperture_low_addr); + + /* DLG - Per hubp */ + REG_GET_2(BLANK_OFFSET_0, + REFCYC_H_BLANK_END, &dlg_attr->refcyc_h_blank_end, + DLG_V_BLANK_END, &dlg_attr->dlg_vblank_end); + + REG_GET(BLANK_OFFSET_1, + MIN_DST_Y_NEXT_START, &dlg_attr->min_dst_y_next_start); + + REG_GET(DST_DIMENSIONS, + REFCYC_PER_HTOTAL, &dlg_attr->refcyc_per_htotal); + + REG_GET_2(DST_AFTER_SCALER, + REFCYC_X_AFTER_SCALER, &dlg_attr->refcyc_x_after_scaler, + DST_Y_AFTER_SCALER, &dlg_attr->dst_y_after_scaler); + + REG_GET_2(PREFETCH_SETTINGS, + DST_Y_PREFETCH, &dlg_attr->dst_y_prefetch, + VRATIO_PREFETCH, &dlg_attr->vratio_prefetch); + + REG_GET_2(VBLANK_PARAMETERS_0, + DST_Y_PER_VM_VBLANK, &dlg_attr->dst_y_per_vm_vblank, + DST_Y_PER_ROW_VBLANK, &dlg_attr->dst_y_per_row_vblank); + + REG_GET(REF_FREQ_TO_PIX_FREQ, + REF_FREQ_TO_PIX_FREQ, &dlg_attr->ref_freq_to_pix_freq); + + /* DLG - Per luma/chroma */ + REG_GET(VBLANK_PARAMETERS_1, + REFCYC_PER_PTE_GROUP_VBLANK_L, &dlg_attr->refcyc_per_pte_group_vblank_l); + + REG_GET(VBLANK_PARAMETERS_3, + REFCYC_PER_META_CHUNK_VBLANK_L, &dlg_attr->refcyc_per_meta_chunk_vblank_l); + + REG_GET(NOM_PARAMETERS_0, + DST_Y_PER_PTE_ROW_NOM_L, &dlg_attr->dst_y_per_pte_row_nom_l); + + REG_GET(NOM_PARAMETERS_1, + REFCYC_PER_PTE_GROUP_NOM_L, &dlg_attr->refcyc_per_pte_group_nom_l); + + REG_GET(NOM_PARAMETERS_4, + DST_Y_PER_META_ROW_NOM_L, &dlg_attr->dst_y_per_meta_row_nom_l); + + REG_GET(NOM_PARAMETERS_5, + REFCYC_PER_META_CHUNK_NOM_L, &dlg_attr->refcyc_per_meta_chunk_nom_l); + + REG_GET_2(PER_LINE_DELIVERY_PRE, + REFCYC_PER_LINE_DELIVERY_PRE_L, &dlg_attr->refcyc_per_line_delivery_pre_l, + REFCYC_PER_LINE_DELIVERY_PRE_C, &dlg_attr->refcyc_per_line_delivery_pre_c); + + REG_GET_2(PER_LINE_DELIVERY, + REFCYC_PER_LINE_DELIVERY_L, &dlg_attr->refcyc_per_line_delivery_l, + REFCYC_PER_LINE_DELIVERY_C, &dlg_attr->refcyc_per_line_delivery_c); + + REG_GET(PREFETCH_SETTINGS_C, + VRATIO_PREFETCH_C, &dlg_attr->vratio_prefetch_c); + + REG_GET(VBLANK_PARAMETERS_2, + REFCYC_PER_PTE_GROUP_VBLANK_C, &dlg_attr->refcyc_per_pte_group_vblank_c); + + REG_GET(VBLANK_PARAMETERS_4, + REFCYC_PER_META_CHUNK_VBLANK_C, &dlg_attr->refcyc_per_meta_chunk_vblank_c); + + REG_GET(NOM_PARAMETERS_2, + DST_Y_PER_PTE_ROW_NOM_C, &dlg_attr->dst_y_per_pte_row_nom_c); + + REG_GET(NOM_PARAMETERS_3, + REFCYC_PER_PTE_GROUP_NOM_C, &dlg_attr->refcyc_per_pte_group_nom_c); + + REG_GET(NOM_PARAMETERS_6, + DST_Y_PER_META_ROW_NOM_C, &dlg_attr->dst_y_per_meta_row_nom_c); + + REG_GET(NOM_PARAMETERS_7, + REFCYC_PER_META_CHUNK_NOM_C, &dlg_attr->refcyc_per_meta_chunk_nom_c); + + REG_GET(DCN_GLOBAL_TTU_CNTL, + MIN_TTU_VBLANK, &ttu_attr->min_ttu_vblank); + + /* TTU - per luma/chroma */ + /* Assumed surf0 is luma and 1 is chroma */ + + REG_GET(DCN_SURF0_TTU_CNTL0, + REFCYC_PER_REQ_DELIVERY, &ttu_attr->refcyc_per_req_delivery_l); + + REG_GET(DCN_SURF0_TTU_CNTL1, + REFCYC_PER_REQ_DELIVERY_PRE, + &ttu_attr->refcyc_per_req_delivery_pre_l); + + REG_GET(DCN_SURF1_TTU_CNTL0, + REFCYC_PER_REQ_DELIVERY, &ttu_attr->refcyc_per_req_delivery_c); + + REG_GET(DCN_SURF1_TTU_CNTL1, + REFCYC_PER_REQ_DELIVERY_PRE, + &ttu_attr->refcyc_per_req_delivery_pre_c); + + /* Rest of hubp */ + REG_GET(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, &s->pixel_format); + + REG_GET(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, + SURFACE_EARLIEST_INUSE_ADDRESS_HIGH, &s->inuse_addr_hi); + + REG_GET(DCSURF_SURFACE_EARLIEST_INUSE, + SURFACE_EARLIEST_INUSE_ADDRESS, &s->inuse_addr_lo); + + REG_GET_2(DCSURF_PRI_VIEWPORT_DIMENSION, + PRI_VIEWPORT_WIDTH, &s->viewport_width, + PRI_VIEWPORT_HEIGHT, &s->viewport_height); + + REG_GET_2(DCSURF_SURFACE_CONFIG, + ROTATION_ANGLE, &s->rotation_angle, + H_MIRROR_EN, &s->h_mirror_en); + + REG_GET(DCSURF_TILING_CONFIG, + SW_MODE, &s->sw_mode); + + REG_GET(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_DCC_EN, &s->dcc_en); + + REG_GET_3(DCHUBP_CNTL, + HUBP_BLANK_EN, &s->blank_en, + HUBP_TTU_DISABLE, &s->ttu_disable, + HUBP_UNDERFLOW_STATUS, &s->underflow_status); + + REG_GET(HUBP_CLK_CNTL, + HUBP_CLOCK_ENABLE, &s->clock_en); + + REG_GET(DCN_GLOBAL_TTU_CNTL, + MIN_TTU_VBLANK, &s->min_ttu_vblank); + + REG_GET(DCSURF_PRIMARY_SURFACE_ADDRESS, + PRIMARY_SURFACE_ADDRESS, &s->primary_surface_addr_lo); + + REG_GET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, + PRIMARY_SURFACE_ADDRESS, &s->primary_surface_addr_hi); + + s->uclk_pstate_force = REG_READ(UCLK_PSTATE_FORCE); + + s->hubp_cntl = REG_READ(DCHUBP_CNTL); + s->flip_control = REG_READ(DCSURF_FLIP_CONTROL); +} + +static struct hubp_funcs dcn60_hubp_funcs = { + .hubp_enable_tripleBuffer = hubp2_enable_triplebuffer, + .hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled, + .hubp_program_surface_flip_and_addr = hubp50_program_surface_flip_and_addr, + .hubp_program_surface_config = hubp50_program_surface_config, + .hubp_is_flip_pending = hubp2_is_flip_pending, + .hubp_setup2 = hubp60_setup, + .hubp_setup_interdependent2 = hubp60_setup_interdependent, + .hubp_set_vm_system_aperture_settings = hubp3_set_vm_system_aperture_settings, + .set_blank = hubp2_set_blank, + .set_blank_regs = hubp2_set_blank_regs, + .hubp_reset = hubp_reset, + .mem_program_viewport = hubp401_set_viewport, + .set_cursor_attributes = hubp60_cursor_set_attributes, + .set_cursor_position = hubp401_cursor_set_position, + .hubp_clk_cntl = hubp2_clk_cntl, + .hubp_vtg_sel = hubp2_vtg_sel, + .dmdata_set_attributes = hubp3_dmdata_set_attributes, + .dmdata_load = hubp2_dmdata_load, + .dmdata_status_done = hubp2_dmdata_status_done, + .hubp_read_state = hubp60_read_state, + .hubp_clear_underflow = hubp2_clear_underflow, + .hubp_set_flip_control_surface_gsl = hubp2_set_flip_control_surface_gsl, + .hubp_init = hubp401_init, + .set_unbounded_requesting = hubp401_set_unbounded_requesting, + .hubp_soft_reset = hubp31_soft_reset, + .hubp_set_flip_int = hubp401_set_flip_int, + .hubp_in_blank = hubp401_in_blank, + .phantom_hubp_post_enable = hubp32_phantom_hubp_post_enable, + .hubp_update_mall_sel = NULL, + .hubp_prepare_subvp_buffering = hubp32_prepare_subvp_buffering, + .hubp_program_mcache_id_and_split_coordinate = hubp401_program_mcache_id_and_split_coordinate, + .hubp_update_3dlut_fl_bias_scale = hubp401_update_3dlut_fl_bias_scale, + .hubp_program_3dlut_fl_mode = hubp401_program_3dlut_fl_mode, + .hubp_program_3dlut_fl_format = hubp401_program_3dlut_fl_format, + .hubp_program_3dlut_fl_addr = hubp401_program_3dlut_fl_addr, + .hubp_program_3dlut_fl_dlg_param = hubp401_program_3dlut_fl_dlg_param, + .hubp_enable_3dlut_fl = hubp401_enable_3dlut_fl, + .hubp_program_3dlut_fl_addressing_mode = hubp401_program_3dlut_fl_addressing_mode, + .hubp_program_3dlut_fl_width = hubp401_program_3dlut_fl_width, + .hubp_program_3dlut_fl_tmz_protected = hubp401_program_3dlut_fl_tmz_protected, + .hubp_program_3dlut_fl_crossbar = hubp42_program_3dlut_fl_crossbar, + .hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done, + .hubp_clear_tiling = hubp401_clear_tiling, + .hubp_read_reg_state = hubp3_read_reg_state +}; + +bool hubp60_construct( + struct dcn20_hubp *hubp2, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_hubp2_registers *hubp_regs, + const struct dcn_hubp2_shift *hubp_shift, + const struct dcn_hubp2_mask *hubp_mask) +{ + hubp2->base.funcs = &dcn60_hubp_funcs; + hubp2->base.ctx = ctx; + hubp2->hubp_regs = hubp_regs; + hubp2->hubp_shift = hubp_shift; + hubp2->hubp_mask = hubp_mask; + hubp2->base.inst = inst; + hubp2->base.opp_id = OPP_ID_INVALID; + hubp2->base.mpcc_id = 0xf; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.h new file mode 100644 index 000000000000..e502c9330cef --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn60/dcn60_hubp.h @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_HUBP_DCN60_H__ +#define __DC_HUBP_DCN60_H__ + +#include "dcn20/dcn20_hubp.h" +#include "dcn21/dcn21_hubp.h" +#include "dcn30/dcn30_hubp.h" +#include "dcn31/dcn31_hubp.h" +#include "dcn32/dcn32_hubp.h" +#include "dcn401/dcn401_hubp.h" +#include "dcn42/dcn42_hubp.h" +#include "dcn50/dcn50_hubp.h" +#include "dml2_0/dml21/inc/dml_top_dchub_registers.h" + +#include "dcn/dcn_6_0_0_sh_mask.h" + +#define HUBP_MASK_SH_LIST_DCN60(mask_sh)\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, REFCYC_PER_VM_DMDATA, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, DMDATA_VM_FAULT_STATUS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, DMDATA_VM_FAULT_STATUS_CLEAR, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, DMDATA_VM_UNDERFLOW_STATUS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, DMDATA_VM_LATE_STATUS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, DMDATA_VM_UNDERFLOW_STATUS_CLEAR, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_DMDATA_VM_CNTL, DMDATA_VM_DONE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_BLANK_EN, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_TTU_DISABLE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_UNDERFLOW_STATUS, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_UNDERFLOW_CLEAR, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_NO_OUTSTANDING_REQ, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_VTG_SEL, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_UNBOUNDED_REQ_MODE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_IN_BLANK, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_SOFT_RESET, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_ADDR_CONFIG, NUM_PIPES, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_ADDR_CONFIG, PIPE_INTERLEAVE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_ADDR_CONFIG, MAX_COMPRESSED_FRAGS, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_ADDR_CONFIG, NUM_PKRS, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, LEGACY_PIPE_INTERLEAVE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, ARRAY_MODE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, PIPE_CONFIG, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, MICRO_TILE_MODE_NEW, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, TILE_SPLIT, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, BANK_WIDTH, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, BANK_HEIGHT, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, MACRO_TILE_ASPECT, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_LEGACY_ADDR_CONFIG, LEGACY_NUM_BANKS, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_TILING_CONFIG, SW_MODE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_TILING_CONFIG, COMPAT_LEVEL, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_PITCH, PITCH, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_PITCH_C, PITCH_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SURFACE_CONFIG, SURFACE_PIXEL_FORMAT, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_MODE_FOR_STEREOSYNC, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_IN_STEREOSYNC, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_PENDING, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_UPDATE_LOCK, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_WIDTH, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_HEIGHT, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_START, PRI_VIEWPORT_X_START, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_START, PRI_VIEWPORT_Y_START, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_DIMENSION, SEC_VIEWPORT_WIDTH, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_DIMENSION, SEC_VIEWPORT_HEIGHT, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_START, SEC_VIEWPORT_X_START, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_START, SEC_VIEWPORT_Y_START, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION_C, PRI_VIEWPORT_WIDTH_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION_C, PRI_VIEWPORT_HEIGHT_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_START_C, PRI_VIEWPORT_X_START_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_START_C, PRI_VIEWPORT_Y_START_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_DIMENSION_C, SEC_VIEWPORT_WIDTH_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_DIMENSION_C, SEC_VIEWPORT_HEIGHT_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_START_C, SEC_VIEWPORT_X_START_C, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SEC_VIEWPORT_START_C, SEC_VIEWPORT_Y_START_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, PRIMARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS, PRIMARY_SURFACE_ADDRESS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, SECONDARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SECONDARY_SURFACE_ADDRESS, SECONDARY_SURFACE_ADDRESS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, PRIMARY_SURFACE_ADDRESS_HIGH_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS_C, PRIMARY_SURFACE_ADDRESS_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, SECONDARY_SURFACE_ADDRESS_HIGH_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SECONDARY_SURFACE_ADDRESS_C, SECONDARY_SURFACE_ADDRESS_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_INUSE, SURFACE_INUSE_ADDRESS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_INUSE_HIGH, SURFACE_INUSE_ADDRESS_HIGH, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_INUSE_C, SURFACE_INUSE_ADDRESS_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_INUSE_HIGH_C, SURFACE_INUSE_ADDRESS_HIGH_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE, SURFACE_EARLIEST_INUSE_ADDRESS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE_HIGH, SURFACE_EARLIEST_INUSE_ADDRESS_HIGH, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE_C, SURFACE_EARLIEST_INUSE_ADDRESS_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, SURFACE_EARLIEST_INUSE_ADDRESS_HIGH_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_TMZ, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_TMZ_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_DCC_EN, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_TMZ, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_TMZ_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, SECONDARY_SURFACE_DCC_EN, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_SURFACE_FLIP_INTERRUPT, SURFACE_FLIP_INT_MASK, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_CONTROL, DET_BUF_PLANE1_BASE_ADDRESS, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_CB_B, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_CR_R, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_Y_G, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_ALPHA, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_CONTROL, PACK_3TO2_ELEMENT_DISABLE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_EXPANSION_MODE, DRQ_EXPANSION_MODE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_EXPANSION_MODE, PRQ_EXPANSION_MODE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_EXPANSION_MODE, MRQ_EXPANSION_MODE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_EXPANSION_MODE, CRQ_EXPANSION_MODE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, CHUNK_SIZE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, MIN_CHUNK_SIZE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, DPTE_GROUP_SIZE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, SWATH_HEIGHT, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, PTE_ROW_HEIGHT_LINEAR, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, CHUNK_SIZE_C, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, MIN_CHUNK_SIZE_C, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, DPTE_GROUP_SIZE_C, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, SWATH_HEIGHT_C, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, PTE_ROW_HEIGHT_LINEAR_C, mask_sh),\ + HUBP_SF(HUBPREQ0_BLANK_OFFSET_0, REFCYC_H_BLANK_END, mask_sh),\ + HUBP_SF(HUBPREQ0_BLANK_OFFSET_0, DLG_V_BLANK_END, mask_sh),\ + HUBP_SF(HUBPREQ0_BLANK_OFFSET_1, MIN_DST_Y_NEXT_START, mask_sh),\ + HUBP_SF(HUBPREQ0_DST_DIMENSIONS, REFCYC_PER_HTOTAL, mask_sh),\ + HUBP_SF(HUBPREQ0_DST_AFTER_SCALER, REFCYC_X_AFTER_SCALER, mask_sh),\ + HUBP_SF(HUBPREQ0_DST_AFTER_SCALER, DST_Y_AFTER_SCALER, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_0, DST_Y_PER_VM_VBLANK, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_0, DST_Y_PER_ROW_VBLANK, mask_sh),\ + HUBP_SF(HUBPREQ0_REF_FREQ_TO_PIX_FREQ, REF_FREQ_TO_PIX_FREQ, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_1, REFCYC_PER_PTE_GROUP_VBLANK_L, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_3, REFCYC_PER_META_CHUNK_VBLANK_L, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_4, DST_Y_PER_META_ROW_NOM_L, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_5, REFCYC_PER_META_CHUNK_NOM_L, mask_sh),\ + HUBP_SF(HUBPREQ0_PER_LINE_DELIVERY_PRE, REFCYC_PER_LINE_DELIVERY_PRE_L, mask_sh),\ + HUBP_SF(HUBPREQ0_PER_LINE_DELIVERY_PRE, REFCYC_PER_LINE_DELIVERY_PRE_C, mask_sh),\ + HUBP_SF(HUBPREQ0_PER_LINE_DELIVERY, REFCYC_PER_LINE_DELIVERY_L, mask_sh),\ + HUBP_SF(HUBPREQ0_PER_LINE_DELIVERY, REFCYC_PER_LINE_DELIVERY_C, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_2, REFCYC_PER_PTE_GROUP_VBLANK_C, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_4, REFCYC_PER_META_CHUNK_VBLANK_C, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_6, DST_Y_PER_META_ROW_NOM_C, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_7, REFCYC_PER_META_CHUNK_NOM_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_GLOBAL_TTU_CNTL, MIN_TTU_VBLANK, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_GLOBAL_TTU_CNTL, ROW_TTU_MODE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_GLOBAL_TTU_CNTL, FORCE_URGENT_FLIP, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_GLOBAL_TTU_CNTL, FORCE_URGENT_VM_PREFETCH, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL0, REFCYC_PER_REQ_DELIVERY, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL0, URGENT_FORCE_VALUE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL0, URGENT_FORCE_EN, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL1, REFCYC_PER_REQ_DELIVERY_PRE, mask_sh),\ + HUBP_SF(HUBP0_HUBP_CLK_CNTL, HUBP_CLOCK_ENABLE, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_0, DST_Y_PER_PTE_ROW_NOM_L, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_1, REFCYC_PER_PTE_GROUP_NOM_L, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_2, DST_Y_PER_PTE_ROW_NOM_C, mask_sh),\ + HUBP_SF(HUBPREQ0_NOM_PARAMETERS_3, REFCYC_PER_PTE_GROUP_NOM_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_VM_MX_L1_TLB_CNTL, ENABLE_L1_TLB, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SURFACE_CONFIG, ROTATION_ANGLE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SURFACE_CONFIG, H_MIRROR_EN, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_SURFACE_CONFIG, ALPHA_PLANE_EN, mask_sh),\ + HUBP_SF(HUBPREQ0_PREFETCH_SETTINGS, DST_Y_PREFETCH, mask_sh),\ + HUBP_SF(HUBPREQ0_PREFETCH_SETTINGS, VRATIO_PREFETCH, mask_sh),\ + HUBP_SF(HUBPREQ0_PREFETCH_SETTINGS, FORCE_DISP_PREF_TO_VBLANK, mask_sh),\ + HUBP_SF(HUBPREQ0_PREFETCH_SETTINGS_C, VRATIO_PREFETCH_C, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_LOW_ADDR, MC_VM_SYSTEM_APERTURE_LOW_ADDR, mask_sh),\ + HUBP_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR, MC_VM_SYSTEM_APERTURE_HIGH_ADDR, mask_sh),\ + HUBP_SF(HUBPREQ0_CURSOR_SETTINGS, CURSOR0_DST_Y_OFFSET, mask_sh), \ + HUBP_SF(HUBPREQ0_CURSOR_SETTINGS, CURSOR0_CHUNK_HDL_ADJUST, mask_sh), \ + HUBP_SF(HUBPREQ0_CURSOR_SETTINGS, FORCE_CURSOR_TO_DISP_PREF, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_SIZE, CURSOR_WIDTH, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_SIZE, CURSOR_HEIGHT, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_MODE, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_REQ_MODE, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_PITCH, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_LINES_PER_CHUNK, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_CONTROL, CURSOR_ENABLE, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_POSITION, CURSOR_X_POSITION, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + HUBP_SF(CURSOR0_0_CURSOR_DST_OFFSET, CURSOR_DST_X_OFFSET, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_ADDRESS_HIGH, DMDATA_ADDRESS_HIGH, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_CNTL, DMDATA_MODE, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_CNTL, DMDATA_UPDATED, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_CNTL, DMDATA_REPEAT, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_CNTL, DMDATA_SIZE, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_SW_CNTL, DMDATA_SW_UPDATED, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_SW_CNTL, DMDATA_SW_REPEAT, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_SW_CNTL, DMDATA_SW_SIZE, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_QOS_CNTL, DMDATA_QOS_MODE, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_QOS_CNTL, DMDATA_DL_DELTA, mask_sh), \ + HUBP_SF(CURSOR0_0_DMDATA_STATUS, DMDATA_DONE, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_0, DST_Y_PER_VM_FLIP, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_0, DST_Y_PER_ROW_FLIP, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_1, REFCYC_PER_PTE_GROUP_FLIP_L, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_2, REFCYC_PER_META_CHUNK_FLIP_L, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_VREADY_AT_OR_AFTER_VSYNC, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_DISABLE_STOP_DATA_DURING_VM, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, HUBPREQ_MASTER_UPDATE_LOCK_STATUS, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL2, SURFACE_GSL_ENABLE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL2, SURFACE_TRIPLE_BUFFER_ENABLE, mask_sh),\ + HUBP_SF(HUBPREQ0_VMID_SETTINGS_0, VMID, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_3, REFCYC_PER_VM_GROUP_FLIP, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_4, REFCYC_PER_VM_REQ_FLIP, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_5, REFCYC_PER_PTE_GROUP_FLIP_C, mask_sh),\ + HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_6, REFCYC_PER_META_CHUNK_FLIP_C, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_5, REFCYC_PER_VM_GROUP_VBLANK, mask_sh),\ + HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_6, REFCYC_PER_VM_REQ_VBLANK, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, VM_GROUP_SIZE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_VMPG_CONFIG, VMPG_SIZE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_VMPG_CONFIG, PTE_BUFFER_MODE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_VMPG_CONFIG, BIGK_FRAGMENT_SIZE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_VMPG_CONFIG, FORCE_ONE_ROW_FOR_FRAME, mask_sh),\ + HUBP_SF(HUBPREQ0_UCLK_PSTATE_FORCE, DATA_UCLK_PSTATE_FORCE_EN, mask_sh),\ + HUBP_SF(HUBPREQ0_UCLK_PSTATE_FORCE, DATA_UCLK_PSTATE_FORCE_VALUE, mask_sh),\ + HUBP_SF(HUBPREQ0_UCLK_PSTATE_FORCE, CURSOR_UCLK_PSTATE_FORCE_EN, mask_sh),\ + HUBP_SF(HUBPREQ0_UCLK_PSTATE_FORCE, CURSOR_UCLK_PSTATE_FORCE_VALUE, mask_sh),\ + HUBP_SF(HUBP0_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_MODE, mask_sh),\ + HUBP_SF(HUBP0_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_FORMAT, mask_sh),\ + HUBP_SF(HUBP0_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_BIAS, mask_sh),\ + HUBP_SF(HUBP0_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_SCALE, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_ENABLE, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_DONE, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_ADDRESSING_MODE, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_WIDTH, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_MPC_WIDTH, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_TMZ, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_CROSSBAR_SEL_B, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_CROSSBAR_SEL_G, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_CROSSBAR_SEL_R, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_ADDRESS_HIGH, HUBP_3DLUT_ADDRESS_HIGH, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_ADDRESS_LOW, HUBP_3DLUT_ADDRESS_LOW, mask_sh),\ + HUBP_SF(CURSOR0_0_HUBP_3DLUT_DLG_PARAM, REFCYC_PER_3DLUT_GROUP, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_VIEWPORT_MCACHE_SPLIT_COORDINATE, VIEWPORT_MCACHE_SPLIT_COORDINATE, mask_sh),\ + HUBP_SF(HUBP0_DCSURF_VIEWPORT_MCACHE_SPLIT_COORDINATE, VIEWPORT_MCACHE_SPLIT_COORDINATE_C, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_REG_READ_1H_P0, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_REG_READ_2H_P0, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_REG_READ_1H_P1, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_REG_READ_2H_P1, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_1H_P0, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_2H_P0, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_1H_P1, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_2H_P1, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_SEG_ALLOC_ERR_STATUS, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_READ_LINE_VALUE, PIPE_READ_LINE, mask_sh),\ + HUBP_SF(HUBPREQ0_DST_Y_DELTA_DRQ_LIMIT, DST_Y_DELTA_DRQ_LIMIT, mask_sh),\ + HUBP_SF(HUBPREQ0_DST_Y_ALT_CH_DRQ_LIMIT, DST_Y_ALT_CH_DRQ_LIMIT, mask_sh),\ + +void hubp60_setup( + struct hubp *hubp, + struct dml2_dchub_per_pipe_register_set *pipe_regs, + union dml2_global_sync_programming *pipe_global_sync, + struct dc_crtc_timing *timing); + +void hubp60_setup_interdependent( + struct hubp *hubp, + struct dml2_dchub_per_pipe_register_set *pipe_regs); + +void hubp60_cursor_set_attributes(struct hubp *hubp, + const struct dc_cursor_attributes *attr); + +void hubp60_read_state(struct hubp *hubp); + +bool hubp60_construct( + struct dcn20_hubp *hubp2, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_hubp2_registers *hubp_regs, + const struct dcn_hubp2_shift *hubp_shift, + const struct dcn_hubp2_mask *hubp_mask); + +#endif /* __DC_HUBP_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c new file mode 100644 index 000000000000..a7f8fd03faea --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c @@ -0,0 +1,759 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dm_helpers.h" +#include "core_types.h" +#include "resource.h" +#include "dccg.h" +#include "dce/dce_hwseq.h" +#include "reg_helper.h" +#include "abm.h" +#include "hubp.h" +#include "dchubbub.h" +#include "timing_generator.h" +#include "opp.h" +#include "ipp.h" +#include "mpc.h" +#include "mcif_wb.h" +#include "dc_dmub_srv.h" +#include "link_hwss.h" +#include "dpcd_defs.h" +#include "clk_mgr.h" +#include "dsc.h" +#include "link_service.h" + +#include "dce/dmub_hw_lock_mgr.h" +#include "dcn10/dcn10_cm_common.h" +#include "dcn20/dcn20_optc.h" +#include "dcn30/dcn30_cm_common.h" +#include "dcn32/dcn32_hwseq.h" +#include "dcn50_hwseq.h" +#include "dcn60/dcn60_resource.h" +#include "dcn401/dcn401_hwseq.h" +#include "dcn401/dcn401_resource.h" +#include "dc_state_priv.h" +#include "link_enc_cfg.h" +#include "dio/dcn10/dcn10_dio.h" + +#define DC_LOGGER_INIT(logger) + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg +#define DC_LOGGER \ + dc->ctx->logger + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +static void dcn50_initialize_min_clocks(struct dc *dc) +{ + struct dc_clocks *clocks = &dc->current_state->bw_ctx.bw.dcn.clk; + + clocks->dcfclk_deep_sleep_khz = DCN3_2_DCFCLK_DS_INIT_KHZ; + clocks->dcfclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz * 1000; + clocks->socclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].socclk_mhz * 1000; + clocks->dramclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].memclk_mhz * 1000; + clocks->dppclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dppclk_mhz * 1000; + if (dc->debug.disable_boot_optimizations) { + clocks->dispclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dispclk_mhz * 1000; + } else { + /* Even though DPG_EN = 1 for the connected display, it still requires the + * correct timing so we cannot set DISPCLK to min freq or it could cause + * audio corruption. Read current DISPCLK from DENTIST and request the same + * freq to ensure that the timing is valid and unchanged. + */ + clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr); + } + clocks->ref_dtbclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dtbclk_mhz * 1000; + clocks->fclk_p_state_change_support = true; + clocks->p_state_change_support = true; + + dc->clk_mgr->funcs->update_clocks( + dc->clk_mgr, + dc->current_state, + true); +} + +void dcn50_update_dchubp_dpp( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + struct dpp *dpp = pipe_ctx->plane_res.dpp; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct dccg *dccg = dc->res_pool->dccg; + bool viewport_changed = false; + enum mall_stream_type pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe_ctx); + + if (pipe_ctx->update_flags.bits.dppclk) + dpp->funcs->dpp_dppclk_control(dpp, false, true); + + if (pipe_ctx->update_flags.bits.enable) + dccg->funcs->update_dpp_dto(dccg, dpp->inst, pipe_ctx->plane_res.bw.dppclk_khz); + + /* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG + * VTG is within DCHUBBUB which is commond block share by each pipe HUBP. + * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG + */ + + if (pipe_ctx->update_flags.bits.hubp_rq_dlg_ttu) { + hubp->funcs->hubp_vtg_sel(hubp, pipe_ctx->stream_res.tg->inst); + + if (hubp->funcs->hubp_setup2) { + hubp->funcs->hubp_setup2( + hubp, + &pipe_ctx->hubp_regs, + &pipe_ctx->global_sync, + &pipe_ctx->stream->timing); + } else { + hubp->funcs->hubp_setup( + hubp, + &pipe_ctx->dlg_regs, + &pipe_ctx->ttu_regs, + &pipe_ctx->rq_regs, + &pipe_ctx->pipe_dlg_param); + } + } + + if (pipe_ctx->update_flags.bits.unbounded_req && hubp->funcs->set_unbounded_requesting) + hubp->funcs->set_unbounded_requesting(hubp, pipe_ctx->unbounded_req); + + if (pipe_ctx->update_flags.bits.hubp_interdependent) { + if (hubp->funcs->hubp_setup_interdependent2) { + hubp->funcs->hubp_setup_interdependent2( + hubp, + &pipe_ctx->hubp_regs); + } else { + hubp->funcs->hubp_setup_interdependent( + hubp, + &pipe_ctx->dlg_regs, + &pipe_ctx->ttu_regs); + } + } + + if (pipe_ctx->update_flags.bits.enable || + pipe_ctx->update_flags.bits.plane_changed || + plane_state->update_bits.bpp_change || + plane_state->update_bits.input_csc_change || + plane_state->update_bits.color_space_change || + plane_state->update_bits.coeff_reduction_change) { + struct dc_bias_and_scale bns_params = plane_state->bias_and_scale; + + // program the input csc + dpp->funcs->dpp_setup(dpp, + plane_state->format, + EXPANSION_MODE_ZERO, + plane_state->input_csc_color_matrix, + plane_state->color_space, + NULL); + + if (dpp->funcs->set_cursor_matrix) { + dpp->funcs->set_cursor_matrix(dpp, + plane_state->color_space, + plane_state->cursor_csc_color_matrix); + } + if (dpp->funcs->dpp_program_bias_and_scale) { + //TODO :for CNVC set scale and bias registers if necessary + dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params); + } + } + + if (pipe_ctx->update_flags.bits.mpcc + || pipe_ctx->update_flags.bits.plane_changed + || plane_state->update_bits.global_alpha_change + || plane_state->update_bits.per_pixel_alpha_change) { + // MPCC inst is equal to pipe index in practice + hws->funcs.update_mpcc(dc, pipe_ctx); + } + + if (pipe_ctx->update_flags.bits.scaler || + plane_state->update_bits.scaling_change || + plane_state->update_bits.position_change || + plane_state->update_bits.per_pixel_alpha_change || + pipe_ctx->stream->update_flags.bits.scaling) { + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha; + ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP); + /* scaler configuration */ + pipe_ctx->plane_res.dpp->funcs->dpp_set_scaler( + pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data); + } + + if (pipe_ctx->update_flags.bits.viewport || + (context == dc->current_state && plane_state->update_bits.position_change) || + (context == dc->current_state && plane_state->update_bits.scaling_change) || + (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) { + + hubp->funcs->mem_program_viewport( + hubp, + &pipe_ctx->plane_res.scl_data.viewport, + &pipe_ctx->plane_res.scl_data.viewport_c); + viewport_changed = true; + } + + /* Any updates are handled in dc interface, just need to apply existing for plane enable */ + if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || + pipe_ctx->update_flags.bits.scaler || viewport_changed == true) && + pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { + if (dc->hwss.abort_cursor_offload_update) + dc->hwss.abort_cursor_offload_update(dc, pipe_ctx); + + dc->hwss.set_cursor_attribute(pipe_ctx); + dc->hwss.set_cursor_position(pipe_ctx); + + if (dc->hwss.set_cursor_sdr_white_level) + dc->hwss.set_cursor_sdr_white_level(pipe_ctx); + } + + /* Any updates are handled in dc interface, just need + * to apply existing for plane enable / opp change */ + if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed + || pipe_ctx->update_flags.bits.plane_changed + || pipe_ctx->stream->update_flags.bits.gamut_remap + || plane_state->update_bits.gamut_remap_change + || pipe_ctx->stream->update_flags.bits.out_csc) { + /* dpp/cm gamut remap*/ + hwss_program_gamut_remap(pipe_ctx); + + /*call the dcn2 method which uses mpc csc*/ + dc->hwss.program_output_csc(dc, + pipe_ctx, + pipe_ctx->stream->output_color_space, + pipe_ctx->stream->csc_color_matrix.matrix, + hubp->opp_id); + } + + if (pipe_ctx->update_flags.bits.enable || + pipe_ctx->update_flags.bits.plane_changed || + plane_state->update_bits.addr_update) { + if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && + pipe_mall_type == SUBVP_MAIN) { + union block_sequence_params params; + + params.subvp_save_surf_addr.dc_dmub_srv = dc->ctx->dmub_srv; + params.subvp_save_surf_addr.addr = &pipe_ctx->plane_state->address; + params.subvp_save_surf_addr.subvp_index = pipe_ctx->subvp_index; + hwss_subvp_save_surf_addr(¶ms); + } + dc->hwss.update_plane_addr(dc, pipe_ctx); + } + + if (pipe_ctx->update_flags.bits.enable) + hubp->funcs->set_blank(hubp, false); + /* If the stream paired with this plane is phantom, the plane is also phantom */ + if (pipe_mall_type == SUBVP_PHANTOM && hubp->funcs->phantom_hubp_post_enable) + hubp->funcs->phantom_hubp_post_enable(hubp); +} + +void dcn50_update_dchubp_dpp_sequence(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct block_sequence_state *seq_state) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + struct dpp *dpp = pipe_ctx->plane_res.dpp; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct dccg *dccg = dc->res_pool->dccg; + bool viewport_changed = false; + enum mall_stream_type pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe_ctx); + + if (!hubp || !dpp || !plane_state) + return; + + /* Step 1: DPP DPPCLK control */ + if (pipe_ctx->update_flags.bits.dppclk) + hwss_add_dpp_dppclk_control(seq_state, dpp, false, true); + + /* Step 2: DCCG update DPP DTO */ + if (pipe_ctx->update_flags.bits.enable) + hwss_add_dccg_update_dpp_dto(seq_state, dccg, dpp->inst, pipe_ctx->plane_res.bw.dppclk_khz); + + /* Step 3: HUBP VTG selection */ + if (pipe_ctx->update_flags.bits.hubp_rq_dlg_ttu) { + hwss_add_hubp_vtg_sel(seq_state, hubp, pipe_ctx->stream_res.tg->inst); + + /* Step 4: HUBP setup (choose setup2 or setup) */ + if (hubp->funcs->hubp_setup2) { + hwss_add_hubp_setup2(seq_state, hubp, &pipe_ctx->hubp_regs, + &pipe_ctx->global_sync, &pipe_ctx->stream->timing); + } else if (hubp->funcs->hubp_setup) { + hwss_add_hubp_setup(seq_state, hubp, &pipe_ctx->dlg_regs, + &pipe_ctx->ttu_regs, &pipe_ctx->rq_regs, &pipe_ctx->pipe_dlg_param); + } + } + + /* Step 5: Set unbounded requesting */ + if (pipe_ctx->update_flags.bits.unbounded_req && hubp->funcs->set_unbounded_requesting) + hwss_add_hubp_set_unbounded_requesting(seq_state, hubp, pipe_ctx->unbounded_req); + + /* Step 6: HUBP interdependent setup */ + if (pipe_ctx->update_flags.bits.hubp_interdependent) { + if (hubp->funcs->hubp_setup_interdependent2) + hwss_add_hubp_setup_interdependent2(seq_state, hubp, &pipe_ctx->hubp_regs); + else if (hubp->funcs->hubp_setup_interdependent) + hwss_add_hubp_setup_interdependent(seq_state, hubp, &pipe_ctx->dlg_regs, &pipe_ctx->ttu_regs); + } + + /* Step 7: DPP setup - input CSC and format setup */ + if (pipe_ctx->update_flags.bits.enable || + pipe_ctx->update_flags.bits.plane_changed || + plane_state->update_bits.bpp_change || + plane_state->update_bits.input_csc_change || + plane_state->update_bits.color_space_change || + plane_state->update_bits.coeff_reduction_change) { + hwss_add_dpp_setup_dpp(seq_state, pipe_ctx); + + /* Step 8: DPP cursor matrix setup */ + if (dpp->funcs->set_cursor_matrix) { + hwss_add_dpp_set_cursor_matrix(seq_state, dpp, plane_state->color_space, + &plane_state->cursor_csc_color_matrix); + } + + /* Step 9: DPP program bias and scale */ + if (dpp->funcs->dpp_program_bias_and_scale) + hwss_add_dpp_program_bias_and_scale(seq_state, pipe_ctx); + } + + /* Step 10: MPCC updates */ + if (pipe_ctx->update_flags.bits.mpcc || + pipe_ctx->update_flags.bits.plane_changed || + plane_state->update_bits.global_alpha_change || + plane_state->update_bits.per_pixel_alpha_change) { + + /* Check if update_mpcc_sequence is implemented and prefer it over single MPC_UPDATE_MPCC step */ + if (hws->funcs.update_mpcc_sequence) + hws->funcs.update_mpcc_sequence(dc, pipe_ctx, seq_state); + } + + /* Step 11: DPP scaler setup */ + if (pipe_ctx->update_flags.bits.scaler || + plane_state->update_bits.scaling_change || + plane_state->update_bits.position_change || + plane_state->update_bits.per_pixel_alpha_change || + pipe_ctx->stream->update_flags.bits.scaling) { + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha; + ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP); + hwss_add_dpp_set_scaler(seq_state, pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data); + } + + /* Step 12: HUBP viewport programming */ + if (pipe_ctx->update_flags.bits.viewport || + (context == dc->current_state && plane_state->update_bits.position_change) || + (context == dc->current_state && plane_state->update_bits.scaling_change) || + (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) { + hwss_add_hubp_mem_program_viewport(seq_state, hubp, + &pipe_ctx->plane_res.scl_data.viewport, &pipe_ctx->plane_res.scl_data.viewport_c); + viewport_changed = true; + } + + /* Step 13: Cursor attribute setup */ + if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || + pipe_ctx->update_flags.bits.scaler || viewport_changed == true) && + pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { + + hwss_add_abort_cursor_offload_update(seq_state, dc, pipe_ctx); + + hwss_add_set_cursor_attribute(seq_state, dc, pipe_ctx); + + /* Step 14: Cursor position setup */ + hwss_add_set_cursor_position(seq_state, dc, pipe_ctx); + + /* Step 15: Cursor SDR white level */ + if (dc->hwss.set_cursor_sdr_white_level) + hwss_add_set_cursor_sdr_white_level(seq_state, dc, pipe_ctx); + } + + /* Step 16: Gamut remap and output CSC */ + if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || + pipe_ctx->update_flags.bits.plane_changed || + pipe_ctx->stream->update_flags.bits.gamut_remap || + plane_state->update_bits.gamut_remap_change || + pipe_ctx->stream->update_flags.bits.out_csc) { + + /* Gamut remap */ + hwss_add_dpp_program_gamut_remap(seq_state, pipe_ctx); + + /* Output CSC */ + hwss_add_program_output_csc(seq_state, dc, pipe_ctx, pipe_ctx->stream->output_color_space, + pipe_ctx->stream->csc_color_matrix.matrix, hubp->opp_id); + } + + /* Step 17: Update plane address (with SubVP support) */ + if (pipe_ctx->update_flags.bits.enable || + pipe_ctx->update_flags.bits.plane_changed || + plane_state->update_bits.addr_update) { + + /* SubVP save surface address if needed */ + if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_mall_type == SUBVP_MAIN) { + hwss_add_dmub_subvp_save_surf_addr(seq_state, dc->ctx->dmub_srv, + &pipe_ctx->plane_state->address, pipe_ctx->subvp_index); + } + + /* Update plane address */ + hwss_add_hubp_update_plane_addr(seq_state, dc, pipe_ctx); + } + + /* Step 18: HUBP set blank - enable plane */ + if (pipe_ctx->update_flags.bits.enable) + hwss_add_hubp_set_blank(seq_state, hubp, false); + + /* Step 19: Phantom HUBP post enable */ + if (pipe_mall_type == SUBVP_PHANTOM && hubp->funcs->phantom_hubp_post_enable) + hwss_add_phantom_hubp_post_enable(seq_state, hubp); +} + +void dcn50_update_mpcc_sequence(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct block_sequence_state *seq_state) +{ + struct hubp *hubp = pipe_ctx->plane_res.hubp; + struct mpcc_blnd_cfg blnd_cfg = {0}; + bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha; + int mpcc_id; + struct mpcc *new_mpcc; + struct mpc *mpc = dc->res_pool->mpc; + struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params); + + if (!hubp || !pipe_ctx->plane_state) + return; + + /* Initialize blend configuration */ + blnd_cfg.overlap_only = false; + blnd_cfg.global_gain = 0xfff; + + if (per_pixel_alpha) { + blnd_cfg.pre_multiplied_alpha = pipe_ctx->plane_state->pre_multiplied_alpha; + if (pipe_ctx->plane_state->global_alpha) { + blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN; + blnd_cfg.global_gain = pipe_ctx->plane_state->global_alpha_value; + } else { + blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; + } + } else { + blnd_cfg.pre_multiplied_alpha = false; + blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; + } + + if (pipe_ctx->plane_state->global_alpha) + blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value; + else + blnd_cfg.global_alpha = 0xfff; + + blnd_cfg.background_color_bpc = 4; + blnd_cfg.bottom_gain_mode = 0; + blnd_cfg.top_gain = 0x1f000; + blnd_cfg.bottom_inside_gain = 0x1f000; + blnd_cfg.bottom_outside_gain = 0x1f000; + + if (pipe_ctx->plane_state->format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA) + blnd_cfg.pre_multiplied_alpha = false; + + /* MPCC instance is equal to HUBP instance */ + mpcc_id = hubp->inst; + + /* Step 1: Update blending if no full update needed */ + if (!pipe_ctx->plane_state->update_bits.full_update && + !pipe_ctx->update_flags.bits.mpcc) { + + /* Update blending configuration */ + hwss_add_mpc_update_blending(seq_state, mpc, blnd_cfg, mpcc_id); + + /* Update visual confirm color */ + hwss_add_mpc_update_visual_confirm(seq_state, dc, pipe_ctx, mpcc_id); + return; + } + + /* Step 2: Get existing MPCC for DPP */ + new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id); + + /* Step 3: Remove MPCC if being used */ + if (new_mpcc != NULL) { + hwss_add_mpc_remove_mpcc(seq_state, mpc, mpc_tree_params, new_mpcc); + } else { + /* Step 4: Assert MPCC idle (debug only) */ + if (dc->debug.sanity_checks) + hwss_add_mpc_assert_idle_mpcc(seq_state, mpc, mpcc_id); + } + + /* Step 5: Insert new plane into MPC tree */ + hwss_add_mpc_insert_plane(seq_state, mpc, mpc_tree_params, blnd_cfg, NULL, NULL, hubp->inst, mpcc_id); + + /* Step 6: Update visual confirm color */ + hwss_add_mpc_update_visual_confirm(seq_state, dc, pipe_ctx, mpcc_id); + + /* Step 7: Set HUBP OPP and MPCC IDs */ + hubp->opp_id = pipe_ctx->stream_res.opp->inst; + hubp->mpcc_id = mpcc_id; +} + +static void dcn50_setup_hpo_hw_control(const struct dce_hwseq *hws, bool enable) +{ + REG_UPDATE(HPO_TOP_HW_CONTROL, HPO_IO_EN, enable); +} + +void dcn50_program_front_end_for_ctx( + struct dc *dc, + struct dc_state *context) +{ + if (resource_is_pipe_topology_changed(dc->current_state, context)) + resource_log_pipe_topology_update(dc, context); + + hwss_build_full_sequence(dc, + context->block_sequence, + &(context->block_sequence_steps), + context, false); + hwss_execute_sequence(dc, + context->block_sequence, + context->block_sequence_steps); +} + +void dcn50_post_unlock_program_front_end( + struct dc *dc, + struct dc_state *context) +{ + hwss_build_post_unlock_full_sequence(dc, + context->block_sequence, + &(context->block_sequence_steps), + context); + hwss_execute_sequence(dc, + context->block_sequence, + context->block_sequence_steps); +} + +void dcn50_init_hw(struct dc *dc) +{ + struct abm **abms = dc->res_pool->multiple_abms; + struct dce_hwseq *hws = dc->hwseq; + struct dc_bios *dcb = dc->ctx->dc_bios; + struct resource_pool *res_pool = dc->res_pool; + unsigned int i; + unsigned int edp_num; + uint32_t backlight = MAX_BACKLIGHT_LEVEL; + uint32_t user_level = MAX_BACKLIGHT_LEVEL; + int current_dchub_ref_freq = 0; + + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { + dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); + + // mark dcmode limits present if any clock has distinct AC and DC values from SMU + dc->caps.dcmode_power_limits_present = dc->clk_mgr->funcs->is_dc_mode_present && + dc->clk_mgr->funcs->is_dc_mode_present(dc->clk_mgr); + } + + // Initialize the dccg + if (res_pool->dccg->funcs->dccg_init) + res_pool->dccg->funcs->dccg_init(res_pool->dccg); + + // Disable DMUB Initialization until IPS state programming is finalized + //if (!dcb->funcs->is_accelerated_mode(dcb)) { + // hws->funcs.bios_golden_init(dc); + //} + + // Set default OPTC memory power states + if (dc->debug.enable_mem_low_power.bits.optc) { + // Shutdown when unassigned and light sleep in VBLANK + REG_SET_2(ODM_MEM_PWR_CTRL3, 0, ODM_MEM_UNASSIGNED_PWR_MODE, 3, ODM_MEM_VBLANK_PWR_MODE, 1); + } + + if (dc->debug.enable_mem_low_power.bits.vga) { + // Power down VGA memory + REG_UPDATE(MMHUBBUB_MEM_PWR_CNTL, VGA_MEM_PWR_FORCE, 1); + } + + if (dc->ctx->dc_bios->fw_info_valid) { + res_pool->ref_clocks.xtalin_clock_inKhz = + dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; + + if (res_pool->hubbub) { + (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg, + dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency, + &res_pool->ref_clocks.dccg_ref_clock_inKhz); + + current_dchub_ref_freq = res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; + + (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub, + res_pool->ref_clocks.dccg_ref_clock_inKhz, + &res_pool->ref_clocks.dchub_ref_clock_inKhz); + } else { + // Not all ASICs have DCCG sw component + res_pool->ref_clocks.dccg_ref_clock_inKhz = + res_pool->ref_clocks.xtalin_clock_inKhz; + res_pool->ref_clocks.dchub_ref_clock_inKhz = + res_pool->ref_clocks.xtalin_clock_inKhz; + } + } else + ASSERT_CRITICAL(false); + + for (i = 0; i < dc->link_count; i++) { + /* Power up AND update implementation according to the + * required signal (which may be different from the + * default signal on connector). + */ + struct dc_link *link = dc->links[i]; + + link->link_enc->funcs->hw_init(link->link_enc); + + /* Check for enabled DIG to identify enabled display */ + if (link->link_enc->funcs->is_dig_enabled && + link->link_enc->funcs->is_dig_enabled(link->link_enc)) { + link->link_status.link_active = true; + link->phy_state.symclk_state = SYMCLK_ON_TX_ON; + if (link->link_enc->funcs->fec_is_active && + link->link_enc->funcs->fec_is_active(link->link_enc)) + link->fec_state = dc_link_fec_enabled; + } + } + + /* enable_power_gating_plane before dsc_pg_control because + * FORCEON = 1 with hw default value on bootup, resume from s3 + */ + if (hws->funcs.enable_power_gating_plane) + hws->funcs.enable_power_gating_plane(dc->hwseq, true); + + /* we want to turn off all dp displays before doing detection */ + dc->link_srv->blank_all_dp_displays(dc); + + /* If taking control over from VBIOS, we may want to optimize our first + * mode set, so we need to skip powering down pipes until we know which + * pipes we want to use. + * Otherwise, if taking control is not possible, we need to power + * everything down. + */ + if (dcb->funcs->is_accelerated_mode(dcb) || !dc->config.seamless_boot_edp_requested) { + /* Disable boot optimizations means power down everything including PHY, DIG, + * and OTG (i.e. the boot is not optimized because we do a full power down). + */ + if (dc->hwss.enable_accelerated_mode && dc->debug.disable_boot_optimizations) + dc->hwss.enable_accelerated_mode(dc, dc->current_state); + else + hws->funcs.init_pipes(dc, dc->current_state); + + if (dc->res_pool->hubbub->funcs->allow_self_refresh_control) + dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, + !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); + + dcn50_initialize_min_clocks(dc); + + /* On HW init, allow idle optimizations after pipes have been turned off. + * + * In certain D3 cases (i.e. BOCO / BOMACO) it's possible that hardware state + * is reset (i.e. not in idle at the time hw init is called), but software state + * still has idle_optimizations = true, so we must disable idle optimizations first + * (i.e. set false), then re-enable (set true). + */ + dc_allow_idle_optimizations(dc, false); + dc_allow_idle_optimizations(dc, true); + } + + /* In headless boot cases, DIG may be turned + * on which causes HW/SW discrepancies. + * To avoid this, power down hardware on boot + * if DIG is turned on and seamless boot not enabled + */ + if (!dc->config.seamless_boot_edp_requested) { + struct dc_link *edp_links[MAX_NUM_EDP]; + struct dc_link *edp_link; + + dc_get_edp_links(dc, edp_links, &edp_num); + if (edp_num) { + for (i = 0; i < edp_num; i++) { + edp_link = edp_links[i]; + if (edp_link->link_enc->funcs->is_dig_enabled && + edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) && + dc->hwss.edp_backlight_control && + hws->funcs.power_down && + dc->hwss.edp_power_control) { + dc->hwss.edp_backlight_control(edp_link, false); + hws->funcs.power_down(dc); + dc->hwss.edp_power_control(edp_link, false); + } + } + } else { + for (i = 0; i < dc->link_count; i++) { + struct dc_link *link = dc->links[i]; + + if (link->link_enc->funcs->is_dig_enabled && + link->link_enc->funcs->is_dig_enabled(link->link_enc) && + hws->funcs.power_down) { + hws->funcs.power_down(dc); + break; + } + + } + } + } + + for (i = 0; i < res_pool->audio_count; i++) { + struct audio *audio = res_pool->audios[i]; + + audio->funcs->hw_init(audio); + } + + for (i = 0; i < dc->link_count; i++) { + struct dc_link *link = dc->links[i]; + + if (link->panel_cntl) { + backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl); + user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL; + } + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (abms[i] != NULL && abms[i]->funcs != NULL) + abms[i]->funcs->abm_init(abms[i], backlight, user_level); + } + + /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ + if (dc->res_pool->dio && dc->res_pool->dio->funcs->mem_pwr_ctrl) + dc->res_pool->dio->funcs->mem_pwr_ctrl(dc->res_pool->dio, false); + + if (!dc->debug.disable_clock_gate) { + /* enable all DCN clock gating */ + if (dc->res_pool->dccg && dc->res_pool->dccg->funcs && dc->res_pool->dccg->funcs->allow_clock_gating) + dc->res_pool->dccg->funcs->allow_clock_gating(dc->res_pool->dccg, true); + + REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); + } + + dcn50_setup_hpo_hw_control(hws, true); + + if (!dcb->funcs->is_accelerated_mode(dcb) && dc->res_pool->hubbub->funcs->init_watermarks) + dc->res_pool->hubbub->funcs->init_watermarks(dc->res_pool->hubbub); + + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_wm_ranges) + dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); + + if (dc->res_pool->hubbub->funcs->force_pstate_change_control) + dc->res_pool->hubbub->funcs->force_pstate_change_control( + dc->res_pool->hubbub, false, false); + + if (dc->res_pool->hubbub->funcs->init_crb) + dc->res_pool->hubbub->funcs->init_crb(dc->res_pool->hubbub); + + if (dc->res_pool->hubbub->funcs->set_request_limit && dc->config.sdpif_request_limit_words_per_umc > 0) + dc->res_pool->hubbub->funcs->set_request_limit(dc->res_pool->hubbub, + dc->ctx->dc_bios->vram_info.num_chans, dc->config.sdpif_request_limit_words_per_umc); + + // Get DMCUB capabilities + if (dc->ctx->dmub_srv) { + dc_dmub_srv_query_caps_cmd(dc->ctx->dmub_srv); + dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr; + dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0; + dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver; + dc->debug.fams2_config.bits.enable &= + dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; // sw & fw fams versions must match for support + if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box) + || res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) { + /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ + if (dc->clk_mgr) + dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); + } + } +} diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.h new file mode 100644 index 000000000000..79b5a4794ac1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DC_HWSS_DCN50_H__ +#define __DC_HWSS_DCN50_H__ + +#include "inc/core_types.h" +#include "dc.h" +#include "dc_stream.h" +#include "hw_sequencer_private.h" +#include "dcn401/dcn401_dccg.h" + +struct dc; + +void dcn50_init_hw(struct dc *dc); + +void dcn50_update_dchubp_dpp( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context); +void dcn50_update_dchubp_dpp_sequence(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct block_sequence_state *seq_state); +void dcn50_update_mpcc_sequence(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct block_sequence_state *seq_state); +void dcn50_program_front_end_for_ctx(struct dc *dc, struct dc_state *context); +void dcn50_post_unlock_program_front_end(struct dc *dc, struct dc_state *context); + +#endif /* __DC_HWSS_DCN50_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c new file mode 100644 index 000000000000..05239e2f086f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c @@ -0,0 +1,1168 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dm_helpers.h" +#include "core_types.h" +#include "resource.h" +#include "dccg.h" +#include "dce/dce_hwseq.h" +#include "reg_helper.h" +#include "abm.h" +#include "hubp.h" +#include "dchubbub.h" +#include "timing_generator.h" +#include "opp.h" +#include "ipp.h" +#include "mpc.h" +#include "mcif_wb.h" +#include "dc_dmub_srv.h" +#include "link_hwss.h" +#include "dpcd_defs.h" +#include "clk_mgr.h" +#include "dsc.h" +#include "link_service.h" + +#include "dce/dmub_hw_lock_mgr.h" +#include "dcn10/dcn10_cm_common.h" +#include "dcn20/dcn20_optc.h" +#include "dcn30/dcn30_cm_common.h" +#include "dce110/dce110_hwseq.h" +#include "dcn32/dcn32_hwseq.h" +#include "dcn401/dcn401_hwseq.h" +#include "dcn50/dcn50_hwseq.h" +#include "dcn60_hwseq.h" +#include "dcn401/dcn401_resource.h" +#include "dcn60/dcn60_resource.h" +#include "dc_state_priv.h" +#include "link_enc_cfg.h" +#include "dio/dcn10/dcn10_dio.h" + +#define DC_LOGGER_INIT(logger) + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg +#define DC_LOGGER \ + dc->ctx->logger + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +static void dcn60_build_audio_output( + struct dc_state *state, + const struct pipe_ctx *pipe_ctx, + struct audio_output *audio_output) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + audio_output->engine_id = pipe_ctx->stream_res.stream_enc->id; + + audio_output->signal = pipe_ctx->stream->signal; + + /* audio_crtc_info */ + + audio_output->crtc_info.h_total = + stream->timing.h_total; + + /* + * Audio packets are sent during actual CRTC blank physical signal, we + * need to specify actual active signal portion + */ + audio_output->crtc_info.h_active = + stream->timing.h_addressable + + stream->timing.h_border_left + + stream->timing.h_border_right; + + audio_output->crtc_info.v_active = + stream->timing.v_addressable + + stream->timing.v_border_top + + stream->timing.v_border_bottom; + + audio_output->crtc_info.pixel_repetition = 1; + + audio_output->crtc_info.interlaced = + (stream->timing.flags.INTERLACE != 0); + + audio_output->crtc_info.refresh_rate = + (uint16_t)((stream->timing.pix_clk_100hz*100)/ + (stream->timing.h_total*stream->timing.v_total)); + + audio_output->crtc_info.color_depth = + stream->timing.display_color_depth; + + audio_output->crtc_info.requested_pixel_clock_100Hz = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz; + + audio_output->crtc_info.calculated_pixel_clock_100Hz = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz; + + audio_output->crtc_info.pixel_encoding = + stream->timing.pixel_encoding; + + audio_output->crtc_info.dsc_bits_per_pixel = + stream->timing.dsc_cfg.bits_per_pixel; + + audio_output->crtc_info.dsc_num_slices = + stream->timing.dsc_cfg.num_slices_h; + +/*for HDMI, audio ACR is with deep color ratio factor*/ + if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal) && + audio_output->crtc_info.requested_pixel_clock_100Hz == + (stream->timing.pix_clk_100hz)) { + if (pipe_ctx->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420) { + audio_output->crtc_info.requested_pixel_clock_100Hz = + audio_output->crtc_info.requested_pixel_clock_100Hz/2; + audio_output->crtc_info.calculated_pixel_clock_100Hz = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz/2; + + } + } + 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: + audio_output->crtc_info.frl_character_clock_kHz = 666667; + break; + case HDMI_FRL_LINK_RATE_16GBPS: + audio_output->crtc_info.frl_character_clock_kHz = 888889; + break; + case HDMI_FRL_LINK_RATE_20GBPS: + default: + audio_output->crtc_info.frl_character_clock_kHz = 1111111; + 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( + state->clk_mgr); + } + + audio_output->pll_info.dto_source = + translate_to_dto_source( + pipe_ctx->stream_res.tg->inst + 1); + + /* TODO hard code to enable for now. Need get from stream */ + audio_output->pll_info.ss_enabled = true; + + audio_output->pll_info.ss_percentage = + pipe_ctx->pll_settings.ss_percentage; + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) { + populate_audio_dp_link_info(pipe_ctx, &audio_output->dp_link_info); + } +} + +enum dc_status dcn60_apply_single_controller_ctx_to_hw( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct drr_params params = {0}; + unsigned int event_triggers = 0; + struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; + struct dce_hwseq *hws = dc->hwseq; + const struct link_hwss *link_hwss = get_link_hwss( + link, &pipe_ctx->link_res); + + if (hws->funcs.disable_stream_gating) { + hws->funcs.disable_stream_gating(dc, pipe_ctx); + } + + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output = {0}; + + dcn60_build_audio_output(context, pipe_ctx, &audio_output); + + link_hwss->setup_audio_output(pipe_ctx, &audio_output, + pipe_ctx->stream_res.audio->inst); + + pipe_ctx->stream_res.audio->funcs->az_configure( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &pipe_ctx->stream->audio_info, + &audio_output.dp_link_info); + + if (dc->config.disable_hbr_audio_dp2) + if (pipe_ctx->stream_res.audio->funcs->az_disable_hbr_audio && + dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) + pipe_ctx->stream_res.audio->funcs->az_disable_hbr_audio(pipe_ctx->stream_res.audio); + } + + /* make sure no pipes syncd to the pipe being enabled */ + if (!pipe_ctx->stream->apply_seamless_boot_optimization && dc->config.use_pipe_ctx_sync_logic) + check_syncd_pipes_for_disabled_master_pipe(dc, context, pipe_ctx->pipe_idx); + + pipe_ctx->stream_res.opp->funcs->opp_program_fmt( + pipe_ctx->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + + pipe_ctx->stream_res.opp->funcs->opp_set_dyn_expansion( + pipe_ctx->stream_res.opp, + COLOR_SPACE_YCBCR601, + stream->timing.display_color_depth, + stream->signal); + + while (odm_pipe) { + odm_pipe->stream_res.opp->funcs->opp_set_dyn_expansion( + odm_pipe->stream_res.opp, + COLOR_SPACE_YCBCR601, + stream->timing.display_color_depth, + stream->signal); + + odm_pipe->stream_res.opp->funcs->opp_program_fmt( + odm_pipe->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + odm_pipe = odm_pipe->next_odm_pipe; + } + + /* DCN3.1 FPGA Workaround + * Need to enable HPO DP Stream Encoder before setting OTG master enable. + * To do so, move calling function enable_stream_timing to only be done AFTER calling + * function core_link_enable_stream + */ + if (!(hws->wa.dp_hpo_and_otg_sequence && dc->link_srv->dp_is_128b_132b_signal(pipe_ctx))) + /* */ + /* Do not touch stream timing on seamless boot optimization. */ + if (!pipe_ctx->stream->apply_seamless_boot_optimization) + hws->funcs.enable_stream_timing(pipe_ctx, context, dc); + + if (hws->funcs.setup_vupdate_interrupt) + hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx); + + params.vertical_total_min = stream->adjust.v_total_min; + params.vertical_total_max = stream->adjust.v_total_max; + set_drr_and_clear_adjust_pending(pipe_ctx, stream, ¶ms); + + // DRR should set trigger event to monitor surface update event + if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0) + event_triggers = 0x80; + /* Event triggers and num frames initialized for DRR, but can be + * later updated for PSR use. Note DRR trigger events are generated + * regardless of whether num frames met. + */ + if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control) + 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) + && !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); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_CONNECT_DIG_FE_OTG); + + /* Temporary workaround to perform DSC programming ahead of stream enablement + * for smartmux/SPRS + * TODO: Remove SmartMux/SPRS checks once movement of DSC programming is generalized + */ + if (pipe_ctx->stream->timing.flags.DSC) { + if ((pipe_ctx->stream->signal == SIGNAL_TYPE_EDP && + ((link->dc->config.smart_mux_version && link->dc->is_switch_in_progress_dest) + || link->is_dds || link->skip_implict_edp_power_control)) && + (dc_is_dp_signal(pipe_ctx->stream->signal) || + dc_is_virtual_signal(pipe_ctx->stream->signal))) + dc->link_srv->set_dsc_enable(pipe_ctx, true); + } + if (!stream->dpms_off) + dc->link_srv->set_dpms_on(context, pipe_ctx); + + /* DCN3.1 FPGA Workaround + * Need to enable HPO DP Stream Encoder before setting OTG master enable. + * To do so, move calling function enable_stream_timing to only be done AFTER calling + * function core_link_enable_stream + */ + if (hws->wa.dp_hpo_and_otg_sequence && dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { + if (!pipe_ctx->stream->apply_seamless_boot_optimization) + hws->funcs.enable_stream_timing(pipe_ctx, context, dc); + } + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != NULL; + + /* Phantom and main stream share the same link (because the stream + * is constructed with the same sink). Make sure not to override + * and link programming on the main. + */ + if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) { + pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false; + pipe_ctx->stream->link->replay_settings.replay_feature_enabled = false; + } + return DC_OK; +} + +static void dcn60_setup_audio_dto( + struct dc *dc, + struct dc_state *context) +{ + unsigned int i; + + /* program audio wall clock. use HDMI as clock source if HDMI + * audio active. Otherwise, use DP as clock source + * first, loop to find any HDMI audio, if not, loop find DP audio + */ + /* Setup audio rate clock source */ + /* Issue: + * Audio lag happened on DP monitor when unplug a HDMI monitor + * + * Cause: + * In case of DP and HDMI connected or HDMI only, DCCG_AUDIO_DTO_SEL + * is set to either dto0 or dto1, audio should work fine. + * In case of DP connected only, DCCG_AUDIO_DTO_SEL should be dto1, + * set to dto0 will cause audio lag. + * + * Solution: + * Not optimized audio wall dto setup. When mode set, iterate pipe_ctx, + * find first available pipe with audio, setup audio wall DTO per topology + * instead of per pipe. + */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->top_pipe) + continue; + 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; + + dcn60_build_audio_output(context, pipe_ctx, &audio_output); + + 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 (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_FRL) { + /* For DCN3.1, audio to HPO FRL encoder is using audio DTBCLK DTO */ + /* set audio DTBCLK DTO to 24MHz */ + dto_params.req_audio_dtbclk_khz = 24000; + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, + &dto_params); + } else { + /* Audio DTBCLK params default to disabled */ + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, + &dto_params); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + } + } else + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + break; + } + } + + /* no HDMI audio is found, try DP audio */ + if (i == dc->res_pool->pipe_count) { + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->top_pipe) + continue; + + if (!dc_is_dp_signal(pipe_ctx->stream->signal)) + continue; + + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output = {0}; + + dcn60_build_audio_output(context, pipe_ctx, &audio_output); + + /* 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; + } + } + } +} + +enum dc_status dcn60_apply_ctx_to_hw( + struct dc *dc, + struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + struct dc_bios *dcb = dc->ctx->dc_bios; + enum dc_status status; + uint8_t i; + bool was_hpo_acquired = resource_is_hpo_acquired(dc->current_state); + bool is_hpo_acquired = resource_is_hpo_acquired(context); + + /* reset syncd pipes from disabled pipes */ + if (dc->config.use_pipe_ctx_sync_logic) + reset_syncd_pipes_from_disabled_pipes(dc, context); + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + hws->funcs.reset_hw_ctx_wrap(dc, context); + + /* Skip applying if no targets */ + if (context->stream_count <= 0) + return DC_OK; + + /* Apply new context */ + dcb->funcs->set_scratch_critical_state(dcb, true); + + /* below is for real asic only */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL || pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) { + if (pipe_ctx_old->clock_source != pipe_ctx->clock_source) + dce_crtc_switch_to_clk_src(dc->hwseq, + pipe_ctx->clock_source, i); + continue; + } + + hws->funcs.enable_display_power_gating( + dc, i, dc->ctx->dc_bios, + PIPE_GATING_CONTROL_DISABLE); + } + + dcn60_setup_audio_dto(dc, context); + + if (dc->hwseq->funcs.setup_hpo_hw_control && was_hpo_acquired != is_hpo_acquired) { + dc->hwseq->funcs.setup_hpo_hw_control(dc->hwseq, is_hpo_acquired); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream && + pipe_ctx->stream->link->link_state_valid) { + continue; + } + + if (pipe_ctx_old->stream && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + continue; + + if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe) + continue; + + status = dcn60_apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + if (DC_OK != status) + return status; + +#ifdef CONFIG_DRM_AMD_DC_FP + if (hws->funcs.resync_fifo_dccg_dio) + hws->funcs.resync_fifo_dccg_dio(hws, dc, context, i); +#endif + } + + dcb->funcs->set_scratch_critical_state(dcb, false); + + return DC_OK; +} + +void dcn60_init_hw(struct dc *dc) +{ + struct abm **abms = dc->res_pool->multiple_abms; + struct dce_hwseq *hws = dc->hwseq; + struct dc_bios *dcb = dc->ctx->dc_bios; + struct resource_pool *res_pool = dc->res_pool; + unsigned int i; + unsigned int edp_num; + uint32_t backlight = MAX_BACKLIGHT_LEVEL; + uint32_t user_level = MAX_BACKLIGHT_LEVEL; + int current_dchub_ref_freq = 0; + + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { + dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); + + // mark dcmode limits present if any clock has distinct AC and DC values from SMU + dc->caps.dcmode_power_limits_present = dc->clk_mgr->funcs->is_dc_mode_present && + dc->clk_mgr->funcs->is_dc_mode_present(dc->clk_mgr); + } + + // Initialize the dccg + if (res_pool->dccg->funcs->dccg_init) + res_pool->dccg->funcs->dccg_init(res_pool->dccg); + + // Set default OPTC memory power states + if (dc->debug.enable_mem_low_power.bits.optc) { + // Shutdown when unassigned and light sleep in VBLANK + REG_SET_2(ODM_MEM_PWR_CTRL3, 0, ODM_MEM_UNASSIGNED_PWR_MODE, 3, ODM_MEM_VBLANK_PWR_MODE, 1); + } + + if (dc->debug.enable_mem_low_power.bits.vga) { + // Power down VGA memory + REG_UPDATE(MMHUBBUB_MEM_PWR_CNTL, VGA_MEM_PWR_FORCE, 1); + } + + for (i = 0; i < (unsigned int)dc->res_pool->res_cap->num_dsc; i++) { + struct display_stream_compressor *dsc = dc->res_pool->dscs[i]; + + if (dsc->funcs->set_fgcg) + dsc->funcs->set_fgcg(dsc, dc->ctx->dc->debug.enable_fine_grain_clock_gating.bits.dsc); + } + + if (dc->ctx->dc_bios->fw_info_valid) { + res_pool->ref_clocks.xtalin_clock_inKhz = + dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; + + if (res_pool->hubbub) { + (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg, + dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency, + &res_pool->ref_clocks.dccg_ref_clock_inKhz); + + current_dchub_ref_freq = res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; + + (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub, + res_pool->ref_clocks.dccg_ref_clock_inKhz, + &res_pool->ref_clocks.dchub_ref_clock_inKhz); + } else { + // Not all ASICs have DCCG sw component + res_pool->ref_clocks.dccg_ref_clock_inKhz = + res_pool->ref_clocks.xtalin_clock_inKhz; + res_pool->ref_clocks.dchub_ref_clock_inKhz = + res_pool->ref_clocks.xtalin_clock_inKhz; + } + } else + ASSERT_CRITICAL(false); + + for (i = 0; i < dc->link_count; i++) { + /* Power up AND update implementation according to the + * required signal (which may be different from the + * default signal on connector). + */ + struct dc_link *link = dc->links[i]; + + link->link_enc->funcs->hw_init(link->link_enc); + + /* Check for enabled DIG to identify enabled display */ + if (link->link_enc->funcs->is_dig_enabled && + link->link_enc->funcs->is_dig_enabled(link->link_enc)) { + link->link_status.link_active = true; + link->phy_state.symclk_state = SYMCLK_ON_TX_ON; + if (link->link_enc->funcs->fec_is_active && + link->link_enc->funcs->fec_is_active(link->link_enc)) + link->fec_state = dc_link_fec_enabled; + } + } + + /* we want to turn off all dp displays before doing detection */ + dc->link_srv->blank_all_dp_displays(dc); + + /* If taking control over from VBIOS, we may want to optimize our first + * mode set, so we need to skip powering down pipes until we know which + * pipes we want to use. + * Otherwise, if taking control is not possible, we need to power + * everything down. + */ + if (dcb->funcs->is_accelerated_mode(dcb) || !dc->config.seamless_boot_edp_requested) { + /* Disable boot optimizations means power down everything including PHY, DIG, + * and OTG (i.e. the boot is not optimized because we do a full power down). + */ + if (dc->hwss.enable_accelerated_mode && dc->debug.disable_boot_optimizations) + dc->hwss.enable_accelerated_mode(dc, dc->current_state); + else + hws->funcs.init_pipes(dc, dc->current_state); + + if (dc->res_pool->hubbub->funcs->allow_self_refresh_control) + dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, + !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); + + dcn401_initialize_min_clocks(dc); + + /* On HW init, allow idle optimizations after pipes have been turned off. + * + * In certain D3 cases (i.e. BOCO / BOMACO) it's possible that hardware state + * is reset (i.e. not in idle at the time hw init is called), but software state + * still has idle_optimizations = true, so we must disable idle optimizations first + * (i.e. set false), then re-enable (set true). + */ + dc_allow_idle_optimizations(dc, false); + dc_allow_idle_optimizations(dc, true); + } + + /* In headless boot cases, DIG may be turned + * on which causes HW/SW discrepancies. + * To avoid this, power down hardware on boot + * if DIG is turned on and seamless boot not enabled + */ + if (!dc->config.seamless_boot_edp_requested) { + struct dc_link *edp_links[MAX_NUM_EDP]; + struct dc_link *edp_link; + + dc_get_edp_links(dc, edp_links, &edp_num); + if (edp_num) { + for (i = 0; i < edp_num; i++) { + edp_link = edp_links[i]; + if (edp_link->link_enc->funcs->is_dig_enabled && + edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) && + dc->hwss.edp_backlight_control && + hws->funcs.power_down && + dc->hwss.edp_power_control) { + dc->hwss.edp_backlight_control(edp_link, false); + hws->funcs.power_down(dc); + dc->hwss.edp_power_control(edp_link, false); + } + } + } else { + for (i = 0; i < dc->link_count; i++) { + struct dc_link *link = dc->links[i]; + + if (link->link_enc->funcs->is_dig_enabled && + link->link_enc->funcs->is_dig_enabled(link->link_enc) && + hws->funcs.power_down) { + hws->funcs.power_down(dc); + break; + } + + } + } + } + + for (i = 0; i < res_pool->audio_count; i++) { + struct audio *audio = res_pool->audios[i]; + + audio->funcs->hw_init(audio); + } + + for (i = 0; i < dc->link_count; i++) { + struct dc_link *link = dc->links[i]; + + if (link->panel_cntl) { + backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl); + user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL; + } + + if (link->force_to_use_aux) { + //Setup corresponding HDCP XFER DEST interrupt to go to DMUCB + dc_dmub_srv_ihc_set_dig_hdcp_interrupt_dest( + dc->ctx->dmub_srv, + link->eng_id, + true); + } + + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (abms[i] != NULL && abms[i]->funcs != NULL) + abms[i]->funcs->abm_init(abms[i], backlight, user_level); + } + + /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ + if (dc->res_pool->dio && dc->res_pool->dio->funcs->mem_pwr_ctrl) + dc->res_pool->dio->funcs->mem_pwr_ctrl(dc->res_pool->dio, false); + + if (!dc->debug.disable_clock_gate) { + /* enable all DCN clock gating */ + if (dc->res_pool->dccg && dc->res_pool->dccg->funcs && dc->res_pool->dccg->funcs->allow_clock_gating) + dc->res_pool->dccg->funcs->allow_clock_gating(dc->res_pool->dccg, true); + + REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); + } + + dcn401_setup_hpo_hw_control(hws, true); + + if (!dcb->funcs->is_accelerated_mode(dcb) && dc->res_pool->hubbub->funcs->init_watermarks) + dc->res_pool->hubbub->funcs->init_watermarks(dc->res_pool->hubbub); + + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_wm_ranges) + dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); + + if (dc->res_pool->hubbub->funcs->force_pstate_change_control) + dc->res_pool->hubbub->funcs->force_pstate_change_control( + dc->res_pool->hubbub, false, false); + + if (dc->res_pool->hubbub->funcs->init_crb) + dc->res_pool->hubbub->funcs->init_crb(dc->res_pool->hubbub); + + if (dc->res_pool->hubbub->funcs->set_request_limit && dc->config.sdpif_request_limit_words_per_umc > 0) + dc->res_pool->hubbub->funcs->set_request_limit(dc->res_pool->hubbub, dc->ctx->dc_bios->vram_info.num_chans, dc->config.sdpif_request_limit_words_per_umc); + + // Get DMCUB capabilities + if (dc->ctx->dmub_srv) { + dc_dmub_srv_query_caps_cmd(dc->ctx->dmub_srv); + dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr; + dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0; + dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver; + dc->debug.fams2_config.bits.enable &= + dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; // sw & fw fams versions must match for support + if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box) + || res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) { + /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ + if (dc->clk_mgr) + dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); + } + } +} + +void dcn60_set_cursor_attribute(struct pipe_ctx *pipe_ctx) +{ + struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes; + + attributes->force_cursor_to_disp_pref = pipe_ctx->hubp_regs.dlg_regs.force_cursor_to_disp_pref; + pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes( + pipe_ctx->plane_res.hubp, attributes); + pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes( + pipe_ctx->plane_res.dpp, attributes); +} + +void dcn60_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe) +{ + volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1; + const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe); + const struct hubp *hubp = pipe->plane_res.hubp; + const struct dpp *dpp = pipe->plane_res.dpp; + volatile struct dmub_cursor_offload_pipe_data_dcn60_v1 *p; + uint32_t stream_idx, write_idx, payload_idx; + + if (!top_pipe || !hubp || !dpp) + return; + + stream_idx = top_pipe->pipe_idx; + write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */ + payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads); + + p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn60; + + p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR; + p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH; + p->CURSOR0_0_CURSOR_SIZE__CURSOR_WIDTH = hubp->att.size.bits.width; + p->CURSOR0_0_CURSOR_SIZE__CURSOR_HEIGHT = hubp->att.size.bits.height; + p->CURSOR0_0_CURSOR_POSITION__CURSOR_X_POSITION = hubp->pos.position.bits.x_pos; + p->CURSOR0_0_CURSOR_POSITION__CURSOR_Y_POSITION = hubp->pos.position.bits.y_pos; + p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_X = hubp->pos.hot_spot.bits.x_hot; + p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_Y = hubp->pos.hot_spot.bits.y_hot; + p->CURSOR0_0_CURSOR_DST_OFFSET__CURSOR_DST_X_OFFSET = hubp->pos.dst_offset.bits.dst_x_offset; + p->CURSOR0_0_CURSOR_CONTROL__CURSOR_ENABLE = hubp->pos.cur_ctl.bits.cur_enable; + p->CURSOR0_0_CURSOR_CONTROL__CURSOR_MODE = hubp->att.cur_ctl.bits.mode; + p->CURSOR0_0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY = hubp->pos.cur_ctl.bits.cur_2x_magnify; + p->CURSOR0_0_CURSOR_CONTROL__CURSOR_PITCH = hubp->att.cur_ctl.bits.pitch; + p->CURSOR0_0_CURSOR_CONTROL__CURSOR_LINES_PER_CHUNK = hubp->att.cur_ctl.bits.line_per_chunk; + + p->CM_CUR0_CURSOR0_CONTROL__CUR0_ENABLE = dpp->att.cur0_ctl.bits.cur0_enable; + p->CM_CUR0_CURSOR0_CONTROL__CUR0_MODE = dpp->att.cur0_ctl.bits.mode; + p->CM_CUR0_CURSOR0_CONTROL__CUR0_EXPANSION_MODE = dpp->att.cur0_ctl.bits.expansion_mode; + p->CM_CUR0_CURSOR0_CONTROL__CUR0_ROM_EN = dpp->att.cur0_ctl.bits.cur0_rom_en; + p->CM_CUR0_CURSOR0_COLOR0__CUR0_COLOR0 = 0x000000; + p->CM_CUR0_CURSOR0_COLOR1__CUR0_COLOR1 = 0xFFFFFF; + + p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_BIAS_G_Y = + dpp->att.fp_scale_bias_g_y.bits.fp_bias_g_y; + p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_SCALE_G_Y = + dpp->att.fp_scale_bias_g_y.bits.fp_scale_g_y; + p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_BIAS_RB_CRCB = + dpp->att.fp_scale_bias_rb_crcb.bits.fp_bias_rb_crcb; + p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_SCALE_RB_CRCB = + dpp->att.fp_scale_bias_rb_crcb.bits.fp_scale_rb_crcb; + + p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET = hubp->att.settings.bits.dst_y_offset; + p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust; + p->HUBPREQ0_CURSOR_SETTINGS__FORCE_CURSOR_TO_DISP_PREF = hubp->att.settings.bits.force_cursor_to_disp_pref; + + cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx); +} + +/** + * dcn60_get_ref_tg_for_hubbub_probe - Resolve the OTG master for hubbub probing. + * @context: committed dc state to resolve streams from + * + * All probe types gate their measurement window to frame edges of the OTG + * master of stream 0. Returns NULL when no active stream is present. + */ +static struct timing_generator *dcn60_get_ref_tg_for_hubbub_probe( + struct dc_state *context) +{ + struct pipe_ctx *otg_pipe; + + if (!context || !context->stream_count) + return NULL; + + otg_pipe = resource_get_otg_master_for_stream(&context->res_ctx, + context->streams[0]); + if (!otg_pipe) + return NULL; + + return otg_pipe->stream_res.tg; +} + +/** + * dcn60_build_hubbub_perfmon_sequence - Build the hubbub perfmon BLS sequence. + * @dc: DC structure + * @context: Committed dc state to resolve streams from + * @probe: Probe state to build sequence for + * @status: Perfmon status to update with probe results + * @block_sequence: Block sequence to append steps to + * @num_steps: Number of steps in the block sequence + * + * Appends BLS steps for the given probe into @block_sequence. No steps are + * added when the probe type is unsupported or prerequisites are not met. + */ +static void dcn60_build_hubbub_perfmon_sequence( + struct dc *dc, + struct dc_state *context, + const struct dc_probe_state *probe, + struct dc_probe_status *status, + struct block_sequence *block_sequence, + unsigned int *num_steps) +{ + struct hubbub *hubbub = dc->res_pool->hubbub; + uint32_t refclk_mhz = dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; + struct timing_generator *ref_tg = dcn60_get_ref_tg_for_hubbub_probe(context); + struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps }; + uint32_t duration_ns = 0; + + if (!hubbub || !hubbub->funcs || !hubbub->funcs->perfmon.reset) + return; + + status->type = probe->type; + + if (probe->target_state == DC_PROBE_NOT_MEASURING) { + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + return; + } + + if (probe->target_state != DC_PROBE_MEASURED || !ref_tg) + return; + + /* Peak BW needs a single timing group. The out-of-order counter spans one + * prefetch window, which is meaningless when streams in separate timing + * groups have non-overlapping prefetch windows. */ + if (probe->type == DC_PROBE_PEAK_MEM_BW) { + int group_size = context->stream_status[0].timing_sync_info.group_size; + + if (group_size != context->stream_count) + return; + } + + switch (probe->type) { + case DC_PROBE_PEAK_MEM_BW: + /* Start at the vblank edge and stop at the next vactive so the counter + * spans exactly one prefetch window, capturing prefetch traffic only. */ + if (!hubbub->funcs->perfmon.arm_measuring_out_of_order_bandwidth || + !hubbub->funcs->perfmon.start_measuring_out_of_order_bandwidth || + !hubbub->funcs->perfmon.get_out_of_order_bandwidth_mbps) + return; + + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + hwss_add_hubbub_perfmon_arm_out_of_order_bw(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_start_out_of_order_bw(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_hubbub_perfmon_get_out_of_order_bw(&seq_state, hubbub, + refclk_mhz, &status->u.bandwidth_mbps, &duration_ns); + break; + + case DC_PROBE_AVG_MEM_BW: + /* In-order counter accumulates over a full frame, so no timing group + * restriction applies (unlike the prefetch-windowed peak BW above). */ + if (!hubbub->funcs->perfmon.start_measuring_in_order_bandwidth || + !hubbub->funcs->perfmon.get_in_order_bandwidth_mbps) + return; + + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_start_in_order_bw(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_get_in_order_bw(&seq_state, hubbub, + refclk_mhz, 0, &status->u.bandwidth_mbps, &duration_ns); + break; + + case DC_PROBE_MEM_LATENCY: + if (!hubbub->funcs->perfmon.start_measuring_memory_latencies || + !hubbub->funcs->perfmon.get_memory_latencies_ns) + return; + + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_start_memory_latencies(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_get_memory_latencies(&seq_state, hubbub, + refclk_mhz, &status->u.latency); + break; + + case DC_PROBE_URGENT_ASSERTION_COUNT: + if (!hubbub->funcs->perfmon.start_measuring_urgent_assertion_count || + !hubbub->funcs->perfmon.get_urgent_assertion_count) + return; + + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_start_urgent_assertion_count(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_get_urgent_assertion_count(&seq_state, hubbub, + refclk_mhz, &status->u.urgent_assertion_count); + break; + + case DC_PROBE_PREFETCH_DATA_SIZE: + if (!hubbub->funcs->perfmon.start_measuring_prefetch_data_size || + !hubbub->funcs->perfmon.get_prefetch_data_size) + return; + + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_start_prefetch_data_size(&seq_state, hubbub); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); + hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); + hwss_add_hubbub_perfmon_get_prefetch_data_size(&seq_state, hubbub, + &status->u.prefetch_data_size); + break; + + case DC_PROBE_URGENT_RAMP_LATENCY: + /* Requires caller-supplied window params not available in probe model. */ + return; + + default: + return; + } +} + +/** + * dcn60_update_probe_status - Set the valid flag on a latched probe result. + * @status: result sink whose u was written by the GET BLS step during execute + */ +static void dcn60_update_probe_status(struct dc_probe_status *status) +{ + switch (status->type) { + case DC_PROBE_PEAK_MEM_BW: + case DC_PROBE_AVG_MEM_BW: + /* Zero bandwidth means the counter did not fire — treat as invalid. */ + status->valid = (status->u.bandwidth_mbps != 0); + break; + case DC_PROBE_MEM_LATENCY: + case DC_PROBE_URGENT_ASSERTION_COUNT: + case DC_PROBE_PREFETCH_DATA_SIZE: + status->valid = true; + break; + default: + status->valid = false; + break; + } +} + +/** + * is_probe_measurement_type_for_hubbub - Returns true if the probe type is + * served by the hubbub perfmon block on DCN60. + */ +static bool is_probe_measurement_type_for_hubbub(enum dc_probe_type type) +{ + switch (type) { + case DC_PROBE_PEAK_MEM_BW: + case DC_PROBE_AVG_MEM_BW: + case DC_PROBE_MEM_LATENCY: + case DC_PROBE_URGENT_ASSERTION_COUNT: + case DC_PROBE_PREFETCH_DATA_SIZE: + case DC_PROBE_URGENT_RAMP_LATENCY: + return true; + default: + return false; + } +} + +/** + * dcn60_program_perfmon - Program/transition perfmon probes for a commit. + * @dc: DC structure + * @context: target state; probes, probe_count, and probe_status are + * read from and written to this object + * + * Routes each probe to the HW-block builder that owns its measurement type, + * builds a single combined BLS sequence, executes it once, then updates + * context->probe_status in plain C. + */ +void dcn60_program_perfmon(struct dc *dc, struct dc_state *context) +{ + int i; + + if (!context) + return; + + context->block_sequence_steps = 0; + memset(context->probe_status, 0, sizeof(context->probe_status)); + + for (i = 0; i < context->probe_count; i++) { + if (is_probe_measurement_type_for_hubbub(context->probes[i].type)) + dcn60_build_hubbub_perfmon_sequence(dc, context, &context->probes[i], + &context->probe_status[i], + context->block_sequence, + &context->block_sequence_steps); + } + + hwss_execute_sequence(dc, context->block_sequence, context->block_sequence_steps); + + for (i = 0; i < context->probe_count; i++) + dcn60_update_probe_status(&context->probe_status[i]); +} + +static bool dcn60_has_active_memory_request(const struct dc *dc) +{ + int i; + + /* Check for any streams with active planes but no static panel power features. */ + for (i = 0; i < dc->current_state->stream_count; i++) { + const struct dc_link *link = dc->current_state->streams[i]->link; + bool panel_power_feature = + link && (link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED || + link->replay_settings.replay_feature_enabled); + + if (dc->current_state->stream_status[i].plane_count && !panel_power_feature) + return true; + } + + return false; +} + +static bool dcn60_has_active_display(const struct dc *dc) +{ + int i; + + for (i = 0; i < dc->current_state->stream_count; ++i) { + const struct dc_stream_state *stream = dc->current_state->streams[i]; + + if (dc_is_virtual_signal(stream->signal) || + dc_is_hdmi_tmds_signal(stream->signal) || + (dc_is_dp_signal(stream->signal) && !stream->dpms_off)) { + return true; + } + } + + for (i = 0; i < dc->link_count; i++) { + const struct dc_link *link = dc->links[i]; + + if (link->link_status.link_active || + (link->link_enc && link->link_enc->funcs->is_dig_enabled && + link->link_enc->funcs->is_dig_enabled(link->link_enc))) { + return true; + } + } + + return false; +} + +static void dcn60_notify_dmub_of_cab_status(struct dc *dc, bool enable) +{ + union dmub_rb_cmd cmd; + + if (!dc->ctx->dmub_srv || !dc->current_state) + return; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cab.header.type = DMUB_CMD__CAB_FOR_SS; + cmd.cab.header.payload_bytes = sizeof(cmd.cab) - sizeof(cmd.cab.header); + + if (enable) { + if (!dcn60_has_active_memory_request(dc)) { + DC_LOG_MALL("sending CAB action NO_DCN_REQ\n"); + cmd.cab.header.sub_type = DMUB_CMD__CAB_NO_DCN_REQ; + } else { + cmd.cab.header.sub_type = DMUB_CMD__CAB_DCN_SS_NOT_FIT_IN_CAB; + DC_LOG_MALL("MALL unsupported, frame does not fit in CAB\n"); + } + } else { + /* Disable CAB */ + cmd.cab.header.sub_type = DMUB_CMD__CAB_NO_IDLE_OPTIMIZATION; + DC_LOG_MALL("CAB idle optimization disabled\n"); + } + + dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); +} + +bool dcn60_apply_idle_power_optimizations(struct dc *dc, bool enable) +{ + struct clk_mgr *clk_mgr = dc->clk_mgr; + + dcn60_notify_dmub_of_cab_status(dc, enable); + + /* Notify clock manager and PMFW to disable PHY refclk or DF coupling. */ + if (dc->clk_mgr && dc->clk_mgr->funcs->set_idle_power_optimizations) { + const bool allow_idle = enable && !dcn60_has_active_display(dc); + + clk_mgr->funcs->set_idle_power_optimizations(clk_mgr, allow_idle); + } + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h new file mode 100644 index 000000000000..b57d22d310b0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DC_HWSS_DCN60_H__ +#define __DC_HWSS_DCN60_H__ + +#include "inc/core_types.h" +#include "dc.h" +#include "dc_stream.h" +#include "hw_sequencer_private.h" +#include "dcn401/dcn401_dccg.h" + +struct dc; + +enum dc_status dcn60_apply_ctx_to_hw( + struct dc *dc, + struct dc_state *context); + +enum dc_status dcn60_apply_single_controller_ctx_to_hw( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc); + +void dcn60_init_hw(struct dc *dc); +void dcn60_set_cursor_attribute(struct pipe_ctx *pipe_ctx); +void dcn60_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe); + +void dcn60_program_perfmon(struct dc *dc, struct dc_state *context); +bool dcn60_apply_idle_power_optimizations(struct dc *dc, bool enable); + +#endif /* __DC_HWSS_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c new file mode 100644 index 000000000000..5f4f32d516d5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dce110/dce110_hwseq.h" +#include "dcn10/dcn10_hwseq.h" +#include "dcn20/dcn20_hwseq.h" +#include "dcn21/dcn21_hwseq.h" +#include "dcn30/dcn30_hwseq.h" +#include "dcn31/dcn31_hwseq.h" +#include "dcn32/dcn32_hwseq.h" +#include "dcn35/dcn35_hwseq.h" +#include "dcn401/dcn401_hwseq.h" +#include "dcn42/dcn42_hwseq.h" +#include "dcn50/dcn50_hwseq.h" +#include "dcn60_init.h" +#include "dcn60_hwseq.h" + +static const struct hw_sequencer_funcs dcn60_funcs = { + .program_gamut_remap = dcn401_program_gamut_remap, + .init_hw = dcn60_init_hw, + .apply_ctx_to_hw = dcn60_apply_ctx_to_hw, + .apply_ctx_for_surface = NULL, + .program_front_end_for_ctx = dcn50_program_front_end_for_ctx, + .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, + .post_unlock_program_front_end = dcn50_post_unlock_program_front_end, + .update_plane_addr = dcn20_update_plane_addr, + .update_dchub = dcn10_update_dchub, + .update_pending_status = dcn10_update_pending_status, + .program_output_csc = dcn20_program_output_csc, + .trigger_3dlut_dma_load = dcn401_trigger_3dlut_dma_load, + .enable_accelerated_mode = dce110_enable_accelerated_mode, + .enable_timing_synchronization = dcn10_enable_timing_synchronization, + .enable_per_frame_crtc_position_reset = dcn10_enable_per_frame_crtc_position_reset, + .update_info_frame = dcn31_update_info_frame, + .send_immediate_sdp_message = dcn10_send_immediate_sdp_message, + .enable_stream = dcn401_enable_stream, + .disable_stream = dce110_disable_stream, + .unblank_stream = dcn401_unblank_stream, + .blank_stream = dce110_blank_stream, + .enable_audio_stream = dce110_enable_audio_stream, + .disable_audio_stream = dce110_disable_audio_stream, + .disable_plane = dcn20_disable_plane, + .disable_plane_sequence = dcn401_disable_plane_sequence, + .pipe_control_lock = dcn20_pipe_control_lock, + .interdependent_update_lock = dcn401_interdependent_update_lock, + .cursor_lock = dcn10_cursor_lock, + .prepare_bandwidth = dcn401_prepare_bandwidth, + .prepare_bandwidth_sequence = dcn401_prepare_bandwidth_sequence, + .optimize_bandwidth = dcn401_optimize_bandwidth, + .optimize_bandwidth_sequence = dcn401_optimize_bandwidth_sequence, + .update_bandwidth = dcn20_update_bandwidth, + .set_drr = dcn10_set_drr, + .get_position = dcn10_get_position, + .set_static_screen_control = dcn35_set_static_screen_control, + .setup_stereo = dcn42_setup_stereo, + .set_avmute = dcn30_set_avmute, + .log_hw_state = dcn10_log_hw_state, + .get_hw_state = dcn10_get_hw_state, + .clear_status_bits = dcn10_clear_status_bits, + .wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect, + .wait_for_mpcc_disconnect_sequence = dcn401_wait_for_mpcc_disconnect_sequence, + .edp_backlight_control = dce110_edp_backlight_control, + .edp_power_control = dce110_edp_power_control, + .edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready, + .edp_wait_for_T12 = dce110_edp_wait_for_T12, + .set_cursor_position = dcn401_set_cursor_position, + .set_cursor_attribute = dcn60_set_cursor_attribute, + .set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level, + .abort_cursor_offload_update = dcn35_abort_cursor_offload_update, + .begin_cursor_offload_update = dcn35_begin_cursor_offload_update, + .commit_cursor_offload_update = dcn35_commit_cursor_offload_update, + .update_cursor_offload_pipe = dcn60_update_cursor_offload_pipe, + .notify_cursor_offload_drr_update = dcn35_notify_cursor_offload_drr_update, + .program_cursor_offload_now = dcn35_program_cursor_offload_now, + .setup_periodic_interrupt = dcn10_setup_periodic_interrupt, + .set_clock = dcn10_set_clock, + .get_clock = dcn10_get_clock, + .program_triplebuffer = dcn20_program_triple_buffer, + .enable_writeback = dcn30_enable_writeback, + .disable_writeback = dcn30_disable_writeback, + .update_writeback = dcn30_update_writeback, + .dmdata_status_done = dcn20_dmdata_status_done, + .program_dmdata_engine = dcn30_program_dmdata_engine, + .set_dmdata_attributes = dcn20_set_dmdata_attributes, + .init_sys_ctx = dcn31_init_sys_ctx, + .init_vm_ctx = dcn20_init_vm_ctx, + .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 = dcn60_apply_idle_power_optimizations, + .does_plane_fit_in_mall = NULL, + .set_backlight_level = dcn21_set_backlight_level, + .set_abm_immediate_disable = dcn21_set_abm_immediate_disable, + .hardware_release = dcn401_hardware_release, + .set_pipe = dcn21_set_pipe, + .enable_lvds_link_output = dce110_enable_lvds_link_output, + .enable_tmds_link_output = dce110_enable_tmds_link_output, + .enable_dp_link_output = dce110_enable_dp_link_output, + .disable_link_output = dcn401_disable_link_output, + .set_disp_pattern_generator = dcn30_set_disp_pattern_generator, + .get_dcc_en_bits = dcn10_get_dcc_en_bits, + .enable_phantom_streams = NULL, + .disable_phantom_streams = NULL, + .update_visual_confirm_color = dcn10_update_visual_confirm_color, + .update_phantom_vp_position = NULL, + .update_dsc_pg = NULL, + .apply_update_flags_for_phantom = NULL, + .wait_for_dcc_meta_propagation = dcn401_wait_for_dcc_meta_propagation, + .is_pipe_topology_transition_seamless = dcn32_is_pipe_topology_transition_seamless, + .dmub_hw_control_lock = dcn401_dmub_hw_control_lock, + .fams2_update_config = dcn401_fams2_update_config, + .dmub_hw_control_lock_fast = dcn401_dmub_hw_control_lock_fast, + .program_outstanding_updates = dcn401_program_outstanding_updates, + .wait_for_all_pending_updates = dcn30_wait_for_all_pending_updates, + .detect_pipe_changes = dcn401_detect_pipe_changes, + .enable_plane = dcn20_enable_plane, + .enable_plane_sequence = dcn401_enable_plane_sequence, + .update_dchubp_dpp = dcn50_update_dchubp_dpp, + .update_dchubp_dpp_sequence = dcn50_update_dchubp_dpp_sequence, + .post_unlock_reset_opp = dcn20_post_unlock_reset_opp, + .post_unlock_reset_opp_sequence = dcn401_post_unlock_reset_opp_sequence, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, + .program_perfmon = dcn60_program_perfmon, +}; + +static const struct hwseq_private_funcs dcn60_private_funcs = { + .init_pipes = dcn10_init_pipes, + .plane_atomic_disconnect = dcn10_plane_atomic_disconnect, + .plane_atomic_disconnect_sequence = dcn401_plane_atomic_disconnect_sequence, + .update_mpcc = dcn42_update_mpcc, + .update_mpcc_sequence = dcn50_update_mpcc_sequence, + .set_input_transfer_func = dcn32_set_input_transfer_func, + .set_output_transfer_func = dcn401_set_output_transfer_func, + .power_down = dce110_power_down, + .enable_display_power_gating = dcn10_dummy_display_power_gating, + .blank_pixel_data = dcn20_blank_pixel_data, + .blank_pixel_data_sequence = dcn401_blank_pixel_data_sequence, + .reset_hw_ctx_wrap = dcn401_reset_hw_ctx_wrap, + .enable_stream_timing = dcn401_enable_stream_timing, + .edp_backlight_control = dce110_edp_backlight_control, + .setup_vupdate_interrupt = dcn20_setup_vupdate_interrupt, + .setup_vupdate_interrupt_sequence = dcn401_setup_vupdate_interrupt_sequence, + .did_underflow_occur = dcn10_did_underflow_occur, + .init_blank = dcn32_init_blank, + .disable_vga = NULL, + .bios_golden_init = dcn10_bios_golden_init, + .plane_atomic_disable = dcn20_plane_atomic_disable, + .plane_atomic_power_down = dcn401_plane_atomic_power_down, + .plane_atomic_power_down_sequence = dcn401_plane_atomic_power_down_sequence, + .enable_power_gating_plane = NULL, + .hubp_pg_control = NULL, + .program_all_writeback_pipes_in_tree = dcn30_program_all_writeback_pipes_in_tree, + .program_all_writeback_pipes_in_tree_sequence = dcn401_program_all_writeback_pipes_in_tree_sequence, + .update_odm = dcn401_update_odm, + .update_odm_sequence = dcn401_update_odm_sequence, + .dsc_pg_control = NULL, + .dsc_pg_status = NULL, + .set_hdr_multiplier = dcn10_set_hdr_multiplier, + .set_hdr_multiplier_sequence = dcn401_set_hdr_multiplier_sequence, + .wait_for_blank_complete = dcn20_wait_for_blank_complete, + .set_mcm_luts = dcn42_set_mcm_luts, + .program_mall_pipe_config = NULL, + .update_mall_sel = NULL, + .calculate_dccg_k1_k2_values = NULL, + .apply_single_controller_ctx_to_hw = dcn60_apply_single_controller_ctx_to_hw, + .reset_back_end_for_pipe = dcn401_reset_back_end_for_pipe, + .populate_mcm_luts = NULL, + .perform_3dlut_wa_unlock = dcn401_perform_3dlut_wa_unlock, + .program_cm_hist = dcn42_program_cm_hist, + .program_pipe = dcn401_program_pipe, + .program_pipe_sequence = dcn401_program_pipe_sequence, + .wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed, + .set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe, +}; + +void dcn60_hw_sequencer_init_functions(struct dc *dc) +{ + dc->hwss = dcn60_funcs; + dc->hwseq->funcs = dcn60_private_funcs; + +} diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.h new file mode 100644 index 000000000000..e245e78c53f6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __DC_DCN60_INIT_H__ +#define __DC_DCN60_INIT_H__ + +struct dc; + +void dcn60_hw_sequencer_init_functions(struct dc *dc); + +#endif /* __DC_DCN60_INIT_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c b/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c new file mode 100644 index 000000000000..99163346e7d9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "include/logger_interface.h" +#include "../dce110/irq_service_dce110.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" + +#include "irq_service_dcn60.h" + +#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h" + +#define DCN_BASE__INST0_SEG2 0x000034C0 + +static enum dc_irq_source to_dal_irq_source_dcn60( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id) +{ + (void)irq_service; + (void)src_id; + (void)ext_id; + switch (src_id) { + case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK1; + case DCN_1_0__SRCID__DC_D2_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK2; + case DCN_1_0__SRCID__DC_D3_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK3; + case DCN_1_0__SRCID__DC_D4_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK4; + case DCN_1_0__SRCID__DC_D5_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK5; + case DCN_1_0__SRCID__DC_D6_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK6; + case DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL: + return DC_IRQ_SOURCE_DC1_VLINE0; + case DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL: + return DC_IRQ_SOURCE_DC2_VLINE0; + case DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL: + return DC_IRQ_SOURCE_DC3_VLINE0; + case DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL: + return DC_IRQ_SOURCE_DC4_VLINE0; + case DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL: + return DC_IRQ_SOURCE_DC5_VLINE0; + case DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL: + return DC_IRQ_SOURCE_DC6_VLINE0; + case DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP1; + case DCN_1_0__SRCID__HUBP1_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP2; + case DCN_1_0__SRCID__HUBP2_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP3; + case DCN_1_0__SRCID__HUBP3_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP4; + case DCN_1_0__SRCID__HUBP4_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP5; + case DCN_1_0__SRCID__HUBP5_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP6; + case DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT: + return DC_IRQ_SOURCE_VUPDATE1; + case DCN_1_0__SRCID__OTG1_IHC_V_UPDATE_NO_LOCK_INTERRUPT: + return DC_IRQ_SOURCE_VUPDATE2; + case DCN_1_0__SRCID__OTG2_IHC_V_UPDATE_NO_LOCK_INTERRUPT: + return DC_IRQ_SOURCE_VUPDATE3; + case DCN_1_0__SRCID__OTG3_IHC_V_UPDATE_NO_LOCK_INTERRUPT: + return DC_IRQ_SOURCE_VUPDATE4; + case DCN_1_0__SRCID__OTG4_IHC_V_UPDATE_NO_LOCK_INTERRUPT: + return DC_IRQ_SOURCE_VUPDATE5; + case DCN_1_0__SRCID__OTG5_IHC_V_UPDATE_NO_LOCK_INTERRUPT: + return DC_IRQ_SOURCE_VUPDATE6; + case DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT: + return DC_IRQ_SOURCE_DMCUB_OUTBOX; + + case DCN_1_0__SRCID__DC_HPD1_INT: + /* generic src_id for all HPD and HPDRX interrupts */ + switch (ext_id) { + case DCN_1_0__CTXID__DC_HPD1_INT: + return DC_IRQ_SOURCE_HPD1; + case DCN_1_0__CTXID__DC_HPD2_INT: + return DC_IRQ_SOURCE_HPD2; + case DCN_1_0__CTXID__DC_HPD3_INT: + return DC_IRQ_SOURCE_HPD3; + case DCN_1_0__CTXID__DC_HPD4_INT: + return DC_IRQ_SOURCE_HPD4; + case DCN_1_0__CTXID__DC_HPD5_INT: + return DC_IRQ_SOURCE_HPD5; + case DCN_1_0__CTXID__DC_HPD6_INT: + return DC_IRQ_SOURCE_HPD6; + case DCN_1_0__CTXID__DC_HPD1_RX_INT: + return DC_IRQ_SOURCE_HPD1RX; + case DCN_1_0__CTXID__DC_HPD2_RX_INT: + return DC_IRQ_SOURCE_HPD2RX; + case DCN_1_0__CTXID__DC_HPD3_RX_INT: + return DC_IRQ_SOURCE_HPD3RX; + case DCN_1_0__CTXID__DC_HPD4_RX_INT: + return DC_IRQ_SOURCE_HPD4RX; + case DCN_1_0__CTXID__DC_HPD5_RX_INT: + return DC_IRQ_SOURCE_HPD5RX; + case DCN_1_0__CTXID__DC_HPD6_RX_INT: + return DC_IRQ_SOURCE_HPD6RX; + default: + return DC_IRQ_SOURCE_INVALID; + } + break; + + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +static struct irq_source_info_funcs hpd_irq_info_funcs = { + .set = NULL, + .ack = hpd0_ack +}; + +static struct irq_source_info_funcs hpd_rx_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs pflip_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs vupdate_no_lock_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs vblank_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs outbox_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs vline0_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs vline1_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static struct irq_source_info_funcs vline2_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +#undef BASE_INNER +#define BASE_INNER(seg) DCN_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define SRI(reg_name, block, id)\ + BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRI_DMUB(reg_name)\ + BASE(reg ## reg_name ## _BASE_IDX) + \ + reg ## reg_name + +#define IRQ_REG_ENTRY(block, reg_num, reg1, mask1, reg2, mask2)\ + .enable_reg = SRI(reg1, block, reg_num),\ + .enable_mask = \ + block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ + .enable_value = {\ + block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + },\ + .ack_reg = SRI(reg2, block, reg_num),\ + .ack_mask = \ + block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ + .ack_value = \ + block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK \ + +#define IRQ_REG_ENTRY_DMUB(reg1, mask1, reg2, mask2)\ + .enable_reg = SRI_DMUB(reg1),\ + .enable_mask = \ + reg1 ## __ ## mask1 ## _MASK,\ + .enable_value = {\ + reg1 ## __ ## mask1 ## _MASK,\ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ + },\ + .ack_reg = SRI_DMUB(reg2),\ + .ack_mask = \ + reg2 ## __ ## mask2 ## _MASK,\ + .ack_value = \ + reg2 ## __ ## mask2 ## _MASK \ + +#define hpd_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1 + reg_num] = {\ + IRQ_REG_ENTRY(HPD, reg_num,\ + DC_HPD_INT_CONTROL, DC_HPD_INT_EN,\ + DC_HPD_INT_CONTROL, DC_HPD_INT_ACK),\ + .status_reg = SRI(DC_HPD_INT_STATUS, HPD, reg_num),\ + .funcs = &hpd_irq_info_funcs\ + } + +#define hpd_rx_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1RX + reg_num] = {\ + IRQ_REG_ENTRY(HPD, reg_num,\ + DC_HPD_INT_CONTROL, DC_HPD_RX_INT_EN,\ + DC_HPD_INT_CONTROL, DC_HPD_RX_INT_ACK),\ + .status_reg = SRI(DC_HPD_INT_STATUS, HPD, reg_num),\ + .funcs = &hpd_rx_irq_info_funcs\ + } +#define pflip_int_entry(reg_num)\ + [DC_IRQ_SOURCE_PFLIP1 + reg_num] = {\ + IRQ_REG_ENTRY(HUBPREQ, reg_num,\ + DCSURF_SURFACE_FLIP_INTERRUPT, SURFACE_FLIP_INT_MASK,\ + DCSURF_SURFACE_FLIP_INTERRUPT, SURFACE_FLIP_CLEAR),\ + .funcs = &pflip_irq_info_funcs\ + } + +#define vblank_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VBLANK1 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_GLOBAL_SYNC_STATUS, VSTARTUP_INT_EN,\ + OTG_GLOBAL_SYNC_STATUS, VSTARTUP_EVENT_CLEAR),\ + .funcs = &vblank_irq_info_funcs\ + } +/* vupdate_no_lock_int_entry maps to DC_IRQ_SOURCE_VUPDATEx, to match semantic + * of DCE's DC_IRQ_SOURCE_VUPDATEx. + */ +#define vupdate_no_lock_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VUPDATE1 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_GLOBAL_SYNC_STATUS, VUPDATE_NO_LOCK_INT_EN,\ + OTG_GLOBAL_SYNC_STATUS, VUPDATE_NO_LOCK_EVENT_CLEAR),\ + .funcs = &vupdate_no_lock_irq_info_funcs\ + } + +#define vline0_int_entry(reg_num)\ + [DC_IRQ_SOURCE_DC1_VLINE0 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_VERTICAL_INTERRUPT0_CONTROL, OTG_VERTICAL_INTERRUPT0_INT_ENABLE,\ + OTG_VERTICAL_INTERRUPT0_CONTROL, OTG_VERTICAL_INTERRUPT0_CLEAR),\ + .funcs = &vline0_irq_info_funcs\ + } +#define vline1_int_entry(reg_num)\ + [DC_IRQ_SOURCE_DC1_VLINE1 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_VERTICAL_INTERRUPT1_CONTROL, OTG_VERTICAL_INTERRUPT1_INT_ENABLE,\ + OTG_VERTICAL_INTERRUPT1_CONTROL, OTG_VERTICAL_INTERRUPT1_CLEAR),\ + .funcs = &vline1_irq_info_funcs\ + } +#define vline2_int_entry(reg_num)\ + [DC_IRQ_SOURCE_DC1_VLINE2 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_VERTICAL_INTERRUPT2_CONTROL, OTG_VERTICAL_INTERRUPT2_INT_ENABLE,\ + OTG_VERTICAL_INTERRUPT2_CONTROL, OTG_VERTICAL_INTERRUPT2_CLEAR),\ + .funcs = &vline2_irq_info_funcs\ + } +#define dmub_outbox_int_entry()\ + [DC_IRQ_SOURCE_DMCUB_OUTBOX] = {\ + IRQ_REG_ENTRY_DMUB(\ + DMCUB_INTERRUPT_ENABLE, DMCUB_OUTBOX1_READY_INT_EN,\ + DMCUB_INTERRUPT_ACK, DMCUB_OUTBOX1_READY_INT_ACK),\ + .funcs = &outbox_irq_info_funcs\ + } + +#define dummy_irq_entry() \ + {\ + .funcs = &dummy_irq_info_funcs\ + } + +#define i2c_int_entry(reg_num) \ + [DC_IRQ_SOURCE_I2C_DDC ## reg_num] = dummy_irq_entry() + +#define dp_sink_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DPSINK ## reg_num] = dummy_irq_entry() + +#define gpio_pad_int_entry(reg_num) \ + [DC_IRQ_SOURCE_GPIOPAD ## reg_num] = dummy_irq_entry() + +#define dc_underflow_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DC ## reg_num ## UNDERFLOW] = dummy_irq_entry() + +static struct irq_source_info_funcs dummy_irq_info_funcs = { + .set = dal_irq_service_dummy_set, + .ack = dal_irq_service_dummy_ack +}; + +static const struct irq_source_info +irq_source_info_dcn60[DAL_IRQ_SOURCES_NUMBER] = { + [DC_IRQ_SOURCE_INVALID] = dummy_irq_entry(), + hpd_int_entry(0), + hpd_int_entry(1), + hpd_int_entry(2), + hpd_int_entry(3), + hpd_rx_int_entry(0), + hpd_rx_int_entry(1), + hpd_rx_int_entry(2), + hpd_rx_int_entry(3), + i2c_int_entry(1), + i2c_int_entry(2), + i2c_int_entry(3), + i2c_int_entry(4), + i2c_int_entry(5), + i2c_int_entry(6), + dp_sink_int_entry(1), + dp_sink_int_entry(2), + dp_sink_int_entry(3), + dp_sink_int_entry(4), + dp_sink_int_entry(5), + dp_sink_int_entry(6), + [DC_IRQ_SOURCE_TIMER] = dummy_irq_entry(), + pflip_int_entry(0), + pflip_int_entry(1), + pflip_int_entry(2), + pflip_int_entry(3), + [DC_IRQ_SOURCE_PFLIP5] = dummy_irq_entry(), + [DC_IRQ_SOURCE_PFLIP6] = dummy_irq_entry(), + [DC_IRQ_SOURCE_PFLIP_UNDERLAY0] = dummy_irq_entry(), + gpio_pad_int_entry(0), + gpio_pad_int_entry(1), + gpio_pad_int_entry(2), + gpio_pad_int_entry(3), + gpio_pad_int_entry(4), + gpio_pad_int_entry(5), + gpio_pad_int_entry(6), + gpio_pad_int_entry(7), + gpio_pad_int_entry(8), + gpio_pad_int_entry(9), + gpio_pad_int_entry(10), + gpio_pad_int_entry(11), + gpio_pad_int_entry(12), + gpio_pad_int_entry(13), + gpio_pad_int_entry(14), + gpio_pad_int_entry(15), + gpio_pad_int_entry(16), + gpio_pad_int_entry(17), + gpio_pad_int_entry(18), + gpio_pad_int_entry(19), + gpio_pad_int_entry(20), + gpio_pad_int_entry(21), + gpio_pad_int_entry(22), + gpio_pad_int_entry(23), + gpio_pad_int_entry(24), + gpio_pad_int_entry(25), + gpio_pad_int_entry(26), + gpio_pad_int_entry(27), + gpio_pad_int_entry(28), + gpio_pad_int_entry(29), + gpio_pad_int_entry(30), + dc_underflow_int_entry(1), + dc_underflow_int_entry(2), + dc_underflow_int_entry(3), + dc_underflow_int_entry(4), + dc_underflow_int_entry(5), + dc_underflow_int_entry(6), + [DC_IRQ_SOURCE_DMCU_SCP] = dummy_irq_entry(), + [DC_IRQ_SOURCE_VBIOS_SW] = dummy_irq_entry(), + vblank_int_entry(0), + vblank_int_entry(1), + vblank_int_entry(2), + vblank_int_entry(3), + [DC_IRQ_SOURCE_DC5_VLINE1] = dummy_irq_entry(), + [DC_IRQ_SOURCE_DC6_VLINE1] = dummy_irq_entry(), + dmub_outbox_int_entry(), + vupdate_no_lock_int_entry(0), + vupdate_no_lock_int_entry(1), + vupdate_no_lock_int_entry(2), + vupdate_no_lock_int_entry(3), + vline0_int_entry(0), + vline0_int_entry(1), + vline0_int_entry(2), + vline0_int_entry(3), + vline1_int_entry(0), + vline1_int_entry(1), + vline1_int_entry(2), + vline1_int_entry(3), + vline2_int_entry(0), + vline2_int_entry(1), + vline2_int_entry(2), + vline2_int_entry(3), +}; + +static const struct irq_service_funcs irq_service_funcs_dcn60 = { + .to_dal_irq_source = to_dal_irq_source_dcn60 +}; + +static void dcn60_irq_construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data) +{ + dal_irq_service_construct(irq_service, init_data); + + irq_service->info = irq_source_info_dcn60; + irq_service->funcs = &irq_service_funcs_dcn60; +} + +struct irq_service *dal_irq_service_dcn60_create( + struct irq_service_init_data *init_data) +{ + struct irq_service *irq_service = kzalloc(sizeof(*irq_service), + GFP_KERNEL); + + if (!irq_service) + return NULL; + + dcn60_irq_construct(irq_service, init_data); + return irq_service; +} diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.h b/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.h new file mode 100644 index 000000000000..256c15c485fe --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. +#ifndef __DAL_IRQ_SERVICE_DCN60_H__ +#define __DAL_IRQ_SERVICE_DCN60_H__ + +#include "../irq_service.h" + +struct irq_service *dal_irq_service_dcn60_create( + struct irq_service_init_data *init_data); + +#endif /* __DAL_IRQ_SERVICE_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c new file mode 100644 index 000000000000..f8a878ead67d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. +#include "reg_helper.h" +#include "dc.h" +#include "dcn60_mpc.h" +#include "dcn10/dcn10_cm_common.h" +#include "basics/conversion.h" +#include "mpc.h" + +#define REG(reg)\ + mpc60->mpc_regs->reg + +#define CTX \ + mpc60->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + mpc60->mpc_shift->field_name, mpc60->mpc_mask->field_name + +/* + * Insert DPP into MPC tree based on specified blending position. + * Only used for planes that are part of blending chain for OPP output + * + * Parameters: + * [in/out] mpc - MPC context. + * [in/out] tree - MPC tree structure that plane will be added to. + * [in] blnd_cfg - MPCC blending configuration for the new blending layer. + * [in] sm_cfg - MPCC stereo mix configuration for the new blending layer. + * stereo mix must disable for the very bottom layer of the tree config. + * [in] insert_above_mpcc - Insert new plane above this MPCC. If NULL, insert as bottom plane. + * [in] dpp_id - DPP instance for the plane to be added. + * [in] mpcc_id - The MPCC physical instance to use for blending. + * + * Return: struct mpcc* - MPCC that was added. + */ +static struct mpcc *mpc60_insert_plane( + struct mpc *mpc, + struct mpc_tree *tree, + struct mpcc_blnd_cfg *blnd_cfg, + struct mpcc_sm_cfg *sm_cfg, + struct mpcc *insert_above_mpcc, + int dpp_id, + int mpcc_id) +{ + (void)sm_cfg; + struct dcn60_mpc *mpc60 = TO_DCN60_MPC(mpc); + struct mpcc *new_mpcc = NULL; + + /* sanity check parameters */ + ASSERT(mpcc_id < mpc60->num_mpcc); + ASSERT(!(mpc60->mpcc_in_use_mask & 1 << mpcc_id)); + + if (insert_above_mpcc) { + /* check insert_above_mpcc exist in tree->opp_list */ + struct mpcc *temp_mpcc = tree->opp_list; + + if (temp_mpcc != insert_above_mpcc) + while (temp_mpcc && temp_mpcc->mpcc_bot != insert_above_mpcc) + temp_mpcc = temp_mpcc->mpcc_bot; + if (temp_mpcc == NULL) + return NULL; + } + + /* Get and update MPCC struct parameters */ + new_mpcc = mpc1_get_mpcc(mpc, mpcc_id); + new_mpcc->dpp_id = dpp_id; + + /* program mux and MPCC_MODE */ + if (insert_above_mpcc) { + new_mpcc->mpcc_bot = insert_above_mpcc; + REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, insert_above_mpcc->mpcc_id); + REG_UPDATE(MPCC_CONTROL[mpcc_id], MPCC_MODE, MPCC_BLEND_MODE_TOP_BOT_BLENDING); + } else { + new_mpcc->mpcc_bot = NULL; + REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf); + REG_UPDATE(MPCC_CONTROL[mpcc_id], MPCC_MODE, MPCC_BLEND_MODE_TOP_LAYER_ONLY); + } + REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, dpp_id); + REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, tree->opp_id); + + /* Configure VUPDATE lock set for this MPCC to map to the OPP */ + REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, tree->opp_id); + + /* update mpc tree mux setting */ + if (tree->opp_list == insert_above_mpcc) { + /* insert the toppest mpcc */ + tree->opp_list = new_mpcc; + REG_UPDATE(MUX[tree->opp_id], MPC_OUT_MUX, mpcc_id); + } else { + /* find insert position */ + struct mpcc *temp_mpcc = tree->opp_list; + + while (temp_mpcc && temp_mpcc->mpcc_bot != insert_above_mpcc) + temp_mpcc = temp_mpcc->mpcc_bot; + if (temp_mpcc && temp_mpcc->mpcc_bot == insert_above_mpcc) { + REG_SET(MPCC_BOT_SEL[temp_mpcc->mpcc_id], 0, MPCC_BOT_SEL, mpcc_id); + temp_mpcc->mpcc_bot = new_mpcc; + if (!insert_above_mpcc) + REG_UPDATE(MPCC_CONTROL[temp_mpcc->mpcc_id], + MPCC_MODE, MPCC_BLEND_MODE_TOP_BOT_BLENDING); + } + } + + /* update the blending configuration */ + mpc->funcs->update_blending(mpc, blnd_cfg, mpcc_id); + + /* mark this mpcc as in use */ + mpc60->mpcc_in_use_mask |= 1 << mpcc_id; + + return new_mpcc; +} + +void mpc60_program_rmcm_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, + bool lut_bank_a, bool enabled, int mpcc_id) +{ + struct dcn60_mpc *mpc60 = TO_DCN60_MPC(mpc); + + switch (id) { + case MCM_LUT_3DLUT: + REG_UPDATE(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_MODE, + (!enabled) ? 0 : + (lut_bank_a) ? 1 : 2); + break; + + case MCM_LUT_SHAPER: + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], + MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); + + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], + MPC_RMCM_SHAPER_LUT_WRITE_SEL, + lut_bank_a == true ? 0 : 1); + + REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[mpcc_id], 0, + MPC_RMCM_SHAPER_LUT_INDEX, 0); + break; + default: + break; + } +} + +void mpc60_select_3dlut_ram( + struct mpc *mpc, + enum dc_lut_mode mode, + bool is_color_channel_12bits, + uint32_t mpcc_id) +{ + (void)mode; + struct dcn60_mpc *mpc60 = TO_DCN60_MPC(mpc); + + REG_UPDATE(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], + MPCC_MCM_3DLUT_30BIT_EN, is_color_channel_12bits == true ? 0 : 1); +} + +static enum dc_lut_mode get3dlut_config( + struct mpc *mpc, + bool *is_17x17x17, + bool *is_12bits_color_channel, + int mpcc_id) +{ + uint32_t i_mode, i_enable_10bits, lut_size; + enum dc_lut_mode mode; + struct dcn60_mpc *mpc60 = TO_DCN60_MPC(mpc); + + REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], + MPCC_MCM_3DLUT_MODE_CURRENT, &i_mode); + + REG_GET(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], + MPCC_MCM_3DLUT_30BIT_EN, &i_enable_10bits); + + switch (i_mode) { + case 0: + mode = LUT_BYPASS; + break; + case 1: + mode = LUT_RAM_A; + break; + case 2: + mode = LUT_RAM_B; + break; + default: + mode = LUT_BYPASS; + break; + } + if (i_enable_10bits > 0) + *is_12bits_color_channel = false; + else + *is_12bits_color_channel = true; + + REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_SIZE, &lut_size); + + if (lut_size == 0) + *is_17x17x17 = true; + else + *is_17x17x17 = false; + + return mode; +} + +bool mpc60_program_3dlut( + struct mpc *mpc, + const struct tetrahedral_params *params, + int mpcc_id) +{ + enum dc_lut_mode mode; + bool is_17x17x17; + bool is_12bits_color_channel; + const struct dc_rgb *lut0; + const struct dc_rgb *lut1; + const struct dc_rgb *lut2; + const struct dc_rgb *lut3; + int lut_size0; + int lut_size; + + if (params == NULL) { + mpc32_set_3dlut_mode(mpc, LUT_BYPASS, false, false, mpcc_id); + return false; + } + mpc32_power_on_shaper_3dlut(mpc, mpcc_id, true); + + mode = get3dlut_config(mpc, &is_17x17x17, &is_12bits_color_channel, mpcc_id); + + if (mode == LUT_BYPASS || mode == LUT_RAM_B) + mode = LUT_RAM_A; + else + mode = LUT_RAM_B; + + is_17x17x17 = !params->use_tetrahedral_9; + is_12bits_color_channel = params->use_12bits; + if (is_17x17x17) { + lut0 = params->tetrahedral_17.lut0; + lut1 = params->tetrahedral_17.lut1; + lut2 = params->tetrahedral_17.lut2; + lut3 = params->tetrahedral_17.lut3; + lut_size0 = sizeof(params->tetrahedral_17.lut0) / + sizeof(params->tetrahedral_17.lut0[0]); + lut_size = sizeof(params->tetrahedral_17.lut1) / + sizeof(params->tetrahedral_17.lut1[0]); + } else { + lut0 = params->tetrahedral_9.lut0; + lut1 = params->tetrahedral_9.lut1; + lut2 = params->tetrahedral_9.lut2; + lut3 = params->tetrahedral_9.lut3; + lut_size0 = sizeof(params->tetrahedral_9.lut0) / + sizeof(params->tetrahedral_9.lut0[0]); + lut_size = sizeof(params->tetrahedral_9.lut1) / + sizeof(params->tetrahedral_9.lut1[0]); + } + + mpc60_select_3dlut_ram(mpc, mode, + is_12bits_color_channel, mpcc_id); + mpc32_select_3dlut_ram_mask(mpc, 0x1, mpcc_id); + if (is_12bits_color_channel) + mpc32_set3dlut_ram12(mpc, lut0, lut_size0, mpcc_id); + else + mpc32_set3dlut_ram10(mpc, lut0, lut_size0, mpcc_id); + + mpc32_select_3dlut_ram_mask(mpc, 0x2, mpcc_id); + if (is_12bits_color_channel) + mpc32_set3dlut_ram12(mpc, lut1, lut_size, mpcc_id); + else + mpc32_set3dlut_ram10(mpc, lut1, lut_size, mpcc_id); + + mpc32_select_3dlut_ram_mask(mpc, 0x4, mpcc_id); + if (is_12bits_color_channel) + mpc32_set3dlut_ram12(mpc, lut2, lut_size, mpcc_id); + else + mpc32_set3dlut_ram10(mpc, lut2, lut_size, mpcc_id); + + mpc32_select_3dlut_ram_mask(mpc, 0x8, mpcc_id); + if (is_12bits_color_channel) + mpc32_set3dlut_ram12(mpc, lut3, lut_size, mpcc_id); + else + mpc32_set3dlut_ram10(mpc, lut3, lut_size, mpcc_id); + + mpc32_set_3dlut_mode(mpc, mode, is_12bits_color_channel, + is_17x17x17, mpcc_id); + + if (mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) + mpc32_power_on_shaper_3dlut(mpc, mpcc_id, false); + + return true; +} + +void mpc60_program_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, bool lut_bank_a, int mpcc_id) +{ + + switch (id) { + case MCM_LUT_3DLUT: + mpc32_select_3dlut_ram_mask(mpc, 0xf, mpcc_id); + break; + case MCM_LUT_SHAPER: + mpc32_configure_shaper_lut(mpc, lut_bank_a, mpcc_id); + break; + case MCM_LUT_1DLUT: + mpc32_configure_post1dlut(mpc, lut_bank_a, mpcc_id); + break; + } +} + +static const struct mpc_funcs dcn60_mpc_funcs = { + .read_mpcc_state = mpc1_read_mpcc_state, + .insert_plane = mpc60_insert_plane, + .remove_mpcc = mpc1_remove_mpcc, + .mpc_init = mpc32_mpc_init, + .mpc_init_single_inst = mpc3_mpc_init_single_inst, + .update_blending = mpc42_update_blending, + .cursor_lock = mpc1_cursor_lock, + .get_mpcc_for_dpp = mpc1_get_mpcc_for_dpp, + .wait_for_idle = mpc2_assert_idle_mpcc, + .assert_mpcc_idle_before_connect = mpc2_assert_mpcc_idle_before_connect, + .init_mpcc_list_from_hw = mpc1_init_mpcc_list_from_hw, + .set_denorm = mpc3_set_denorm, + .set_denorm_clamp = mpc3_set_denorm_clamp, + .set_output_csc = mpc3_set_output_csc, + .set_ocsc_default = mpc3_set_ocsc_default, + .set_output_gamma = mpc3_set_output_gamma, + .set_gamut_remap = mpc401_set_gamut_remap, + .program_shaper = mpc32_program_shaper, + .program_3dlut = mpc60_program_3dlut, + .program_1dlut = mpc32_program_post1dlut, + .power_on_mpc_mem_pwr = mpc3_power_on_ogam_lut, + .get_mpc_out_mux = mpc1_get_mpc_out_mux, + .mpc_read_reg_state = mpc3_read_reg_state, + .set_bg_color = mpc1_set_bg_color, + .set_movable_cm_location = mpc401_set_movable_cm_location, + .update_3dlut_fast_load_select = mpc401_update_3dlut_fast_load_select, + .get_3dlut_fast_load_status = mpc401_get_3dlut_fast_load_status, + .populate_lut = mpc401_populate_lut, + .program_lut_read_write_control = mpc60_program_lut_read_write_control, + .program_lut_mode = mpc401_program_lut_mode, + .mcm = { + .program_lut_read_write_control = mpc42_program_lut_read_write_control, + .program_3dlut_size = mpc42_program_3dlut_size, + .program_bias_scale = mpc42_program_3dlut_fl_bias_scale, + .program_bit_depth = mpc42_program_bit_depth, + .is_config_supported = mpc42_is_config_supported, + .populate_lut = mpc42_populate_lut, + }, + .rmcm = { + .enable_3dlut_fl = mpc42_enable_3dlut_fl, + .update_3dlut_fast_load_select = mpc42_update_3dlut_fast_load_select, + .program_lut_read_write_control = mpc60_program_rmcm_lut_read_write_control, + .program_lut_mode = mpc42_program_lut_mode, + .program_3dlut_size = mpc42_program_rmcm_3dlut_size, + .program_bias_scale = mpc42_program_rmcm_3dlut_fast_load_bias_scale, + .program_bit_depth = mpc42_program_rmcm_bit_depth, + .is_config_supported = mpc42_is_rmcm_config_supported, + .power_on_shaper_3dlut = mpc42_power_on_rmcm_shaper_3dlut, + .populate_lut = mpc42_populate_rmcm_lut, + }, +}; + +void dcn60_mpc_construct(struct dcn60_mpc *mpc60, + struct dc_context *ctx, + const struct dcn60_mpc_registers *mpc_regs, + const struct dcn60_mpc_shift *mpc_shift, + const struct dcn60_mpc_mask *mpc_mask, + int num_mpcc, + int num_rmu) +{ + int i; + + mpc60->base.ctx = ctx; + + mpc60->base.funcs = &dcn60_mpc_funcs; + + mpc60->mpc_regs = mpc_regs; + mpc60->mpc_shift = mpc_shift; + mpc60->mpc_mask = mpc_mask; + + mpc60->mpcc_in_use_mask = 0; + mpc60->num_mpcc = num_mpcc; + mpc60->num_rmu = num_rmu; + + for (i = 0; i < MAX_MPCC; i++) + mpc42_init_mpcc(&mpc60->base.mpcc_array[i], i); +} + diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h new file mode 100644 index 000000000000..76d5c721f742 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_MPCC_DCN60_H__ +#define __DC_MPCC_DCN60_H__ + +#include "dcn401/dcn401_mpc.h" +#include "dcn42/dcn42_mpc.h" + +#define TO_DCN60_MPC(mpc_base) \ + container_of(mpc_base, struct dcn60_mpc, base) + +#define MPC_REG_VARIABLE_LIST_DCN6_0 \ + MPC_REG_VARIABLE_LIST_DCN42 + +#define MPC_COMMON_MASK_SH_LIST_DCN6_0(mask_sh) \ + SF(MPCC0_MPCC_TOP_SEL, MPCC_TOP_SEL, mask_sh),\ + SF(MPCC0_MPCC_BOT_SEL, MPCC_BOT_SEL, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_MODE, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_ALPHA_BLND_MODE, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_ALPHA_MULTIPLIED_MODE, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_BLND_ACTIVE_OVERLAP_ONLY, mask_sh),\ + SF(MPCC0_MPCC_CONTROL2, MPCC_GLOBAL_ALPHA, mask_sh),\ + SF(MPCC0_MPCC_CONTROL2, MPCC_GLOBAL_GAIN, mask_sh),\ + SF(MPCC0_MPCC_STATUS, MPCC_IDLE, mask_sh),\ + SF(MPCC0_MPCC_STATUS, MPCC_BUSY, mask_sh),\ + SF(MPCC0_MPCC_OPP_ID, MPCC_OPP_ID, mask_sh),\ + SF(MPCC0_MPCC_BG_G_Y, MPCC_BG_G_Y, mask_sh),\ + SF(MPCC0_MPCC_BG_R_CR, MPCC_BG_R_CR, mask_sh),\ + SF(MPCC0_MPCC_BG_B_CB, MPCC_BG_B_CB, mask_sh),\ + SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_EN, mask_sh),\ + SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_MODE, mask_sh),\ + SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FRAME_ALT, mask_sh),\ + SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FORCE_NEXT_FRAME_POL, mask_sh),\ + SF(MPCC0_MPCC_UPDATE_LOCK_SEL, MPCC_UPDATE_LOCK_SEL, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_BG_BPC, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_BOT_GAIN_MODE, mask_sh),\ + SF(MPCC0_MPCC_TOP_GAIN, MPCC_TOP_GAIN, mask_sh),\ + SF(MPCC0_MPCC_BOT_GAIN_INSIDE, MPCC_BOT_GAIN_INSIDE, mask_sh),\ + SF(MPCC0_MPCC_BOT_GAIN_OUTSIDE, MPCC_BOT_GAIN_OUTSIDE, mask_sh),\ + SF(MPCC0_MPCC_MOVABLE_CM_LOCATION_CONTROL, MPCC_MOVABLE_CM_LOCATION_CNTL, mask_sh),\ + SF(MPCC0_MPCC_MOVABLE_CM_LOCATION_CONTROL, MPCC_MOVABLE_CM_LOCATION_CNTL_CURRENT, mask_sh),\ + SF(MPCC0_MPCC_STATUS, MPCC_DISABLED, mask_sh),\ + SF(MPCC0_MPCC_MEM_PWR_CTRL, MPCC_OGAM_MEM_PWR_FORCE, mask_sh),\ + SF(MPCC0_MPCC_MEM_PWR_CTRL, MPCC_OGAM_MEM_PWR_DIS, mask_sh),\ + SF(MPCC0_MPCC_MEM_PWR_CTRL, MPCC_OGAM_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPCC0_MPCC_MEM_PWR_CTRL, MPCC_OGAM_MEM_PWR_STATE, mask_sh),\ + SF(MPCC_OGAM0_MPCC_GAMUT_REMAP_MODE, MPCC_GAMUT_REMAP_MODE, mask_sh),\ + SF(MPCC_OGAM0_MPCC_GAMUT_REMAP_MODE, MPCC_GAMUT_REMAP_MODE_CURRENT, mask_sh),\ + SF(MPCC_OGAM0_MPCC_GAMUT_REMAP_COEF_FORMAT, MPCC_GAMUT_REMAP_COEF_FORMAT, mask_sh),\ + SF(MPCC_OGAM0_MPC_GAMUT_REMAP_C11_C12_A, MPCC_GAMUT_REMAP_C11_A, mask_sh),\ + SF(MPCC_OGAM0_MPC_GAMUT_REMAP_C11_C12_A, MPCC_GAMUT_REMAP_C12_A, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_REGION_0_1, MPCC_OGAM_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_REGION_0_1, MPCC_OGAM_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_REGION_0_1, MPCC_OGAM_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_REGION_0_1, MPCC_OGAM_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_END_CNTL2_B, MPCC_OGAM_RAMA_EXP_REGION_END_SLOPE_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_END_CNTL2_B, MPCC_OGAM_RAMA_EXP_REGION_END_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_END_CNTL1_B, MPCC_OGAM_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_START_SLOPE_CNTL_B, MPCC_OGAM_RAMA_EXP_REGION_START_SLOPE_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_START_BASE_CNTL_B, MPCC_OGAM_RAMA_EXP_REGION_START_BASE_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_START_CNTL_B, MPCC_OGAM_RAMA_EXP_REGION_START_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_START_CNTL_B, MPCC_OGAM_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_OFFSET_B, MPCC_OGAM_RAMA_OFFSET_B, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_OFFSET_G, MPCC_OGAM_RAMA_OFFSET_G, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_RAMA_OFFSET_R, MPCC_OGAM_RAMA_OFFSET_R, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_LUT_INDEX, MPCC_OGAM_LUT_INDEX, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_CONTROL, MPCC_OGAM_MODE, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_CONTROL, MPCC_OGAM_SELECT, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_CONTROL, MPCC_OGAM_PWL_DISABLE, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_CONTROL, MPCC_OGAM_MODE_CURRENT, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_CONTROL, MPCC_OGAM_SELECT_CURRENT, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_LUT_CONTROL, MPCC_OGAM_LUT_WRITE_COLOR_MASK, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_LUT_CONTROL, MPCC_OGAM_LUT_READ_COLOR_SEL, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_LUT_CONTROL, MPCC_OGAM_LUT_HOST_SEL, mask_sh),\ + SF(MPCC_OGAM0_MPCC_OGAM_LUT_DATA, MPCC_OGAM_LUT_DATA, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_MODE, MPCC_MCM_3DLUT_MODE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_MODE, MPCC_MCM_3DLUT_SIZE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_MODE, MPCC_MCM_3DLUT_MODE_CURRENT, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_READ_WRITE_CONTROL, MPCC_MCM_3DLUT_WRITE_EN_MASK, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_READ_WRITE_CONTROL, MPCC_MCM_3DLUT_30BIT_EN, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_READ_WRITE_CONTROL, MPCC_MCM_3DLUT_READ_SEL, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_INDEX, MPCC_MCM_3DLUT_INDEX, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_DATA, MPCC_MCM_3DLUT_DATA0, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_DATA, MPCC_MCM_3DLUT_DATA1, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_DATA_30BIT, MPCC_MCM_3DLUT_DATA_30BIT, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_CONTROL, MPCC_MCM_SHAPER_LUT_MODE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_CONTROL, MPCC_MCM_SHAPER_MODE_CURRENT, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_OFFSET_R, MPCC_MCM_SHAPER_OFFSET_R, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_OFFSET_G, MPCC_MCM_SHAPER_OFFSET_G, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_OFFSET_B, MPCC_MCM_SHAPER_OFFSET_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_SCALE_R, MPCC_MCM_SHAPER_SCALE_R, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_SCALE_G_B, MPCC_MCM_SHAPER_SCALE_G, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_SCALE_G_B, MPCC_MCM_SHAPER_SCALE_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_LUT_INDEX, MPCC_MCM_SHAPER_LUT_INDEX, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_LUT_DATA, MPCC_MCM_SHAPER_LUT_DATA, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_LUT_WRITE_EN_MASK, MPCC_MCM_SHAPER_LUT_WRITE_EN_MASK, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_LUT_WRITE_EN_MASK, MPCC_MCM_SHAPER_LUT_WRITE_SEL, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_START_CNTL_B, MPCC_MCM_SHAPER_RAMA_EXP_REGION_START_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_START_CNTL_B, MPCC_MCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_END_CNTL_B, MPCC_MCM_SHAPER_RAMA_EXP_REGION_END_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_END_CNTL_B, MPCC_MCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_REGION_0_1, MPCC_MCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_REGION_0_1, MPCC_MCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_REGION_0_1, MPCC_MCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_SHAPER_RAMA_REGION_0_1, MPCC_MCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_CONTROL, MPCC_MCM_1DLUT_MODE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_CONTROL, MPCC_MCM_1DLUT_SELECT, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_CONTROL, MPCC_MCM_1DLUT_PWL_DISABLE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_CONTROL, MPCC_MCM_1DLUT_MODE_CURRENT, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_CONTROL, MPCC_MCM_1DLUT_SELECT_CURRENT, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_LUT_INDEX, MPCC_MCM_1DLUT_LUT_INDEX, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_LUT_DATA, MPCC_MCM_1DLUT_LUT_DATA, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_LUT_CONTROL, MPCC_MCM_1DLUT_LUT_WRITE_COLOR_MASK, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_LUT_CONTROL, MPCC_MCM_1DLUT_LUT_READ_COLOR_SEL, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_LUT_CONTROL, MPCC_MCM_1DLUT_LUT_HOST_SEL, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_START_CNTL_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_START_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_START_CNTL_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_START_SLOPE_CNTL_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_START_SLOPE_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_START_BASE_CNTL_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_START_BASE_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_END_CNTL1_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_END_CNTL2_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_END_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_END_CNTL2_B, MPCC_MCM_1DLUT_RAMA_EXP_REGION_END_SLOPE_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_OFFSET_B, MPCC_MCM_1DLUT_RAMA_OFFSET_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_REGION_0_1, MPCC_MCM_1DLUT_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_REGION_0_1, MPCC_MCM_1DLUT_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_REGION_0_1, MPCC_MCM_1DLUT_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_1DLUT_RAMA_REGION_0_1, MPCC_MCM_1DLUT_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_SHAPER_MEM_PWR_FORCE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_SHAPER_MEM_PWR_DIS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_SHAPER_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_3DLUT_MEM_PWR_FORCE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_3DLUT_MEM_PWR_DIS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_3DLUT_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_1DLUT_MEM_PWR_FORCE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_1DLUT_MEM_PWR_DIS, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_1DLUT_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_SHAPER_MEM_PWR_STATE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_3DLUT_MEM_PWR_STATE, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_MEM_PWR_CTRL, MPCC_MCM_1DLUT_MEM_PWR_STATE, mask_sh),\ + SF(CUR_VUPDATE_LOCK_SET0, CUR_VUPDATE_LOCK_SET, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_FIRST_GAMUT_REMAP_COEF_FORMAT, MPCC_MCM_FIRST_GAMUT_REMAP_COEF_FORMAT, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_FIRST_GAMUT_REMAP_MODE, MPCC_MCM_FIRST_GAMUT_REMAP_MODE, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_FIRST_GAMUT_REMAP_MODE, MPCC_MCM_FIRST_GAMUT_REMAP_MODE_CURRENT, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C11_C12_A, MPCC_MCM_FIRST_GAMUT_REMAP_C11_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C11_C12_A, MPCC_MCM_FIRST_GAMUT_REMAP_C12_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C13_C14_A, MPCC_MCM_FIRST_GAMUT_REMAP_C13_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C13_C14_A, MPCC_MCM_FIRST_GAMUT_REMAP_C14_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C21_C22_A, MPCC_MCM_FIRST_GAMUT_REMAP_C21_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C21_C22_A, MPCC_MCM_FIRST_GAMUT_REMAP_C22_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C23_C24_A, MPCC_MCM_FIRST_GAMUT_REMAP_C23_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C23_C24_A, MPCC_MCM_FIRST_GAMUT_REMAP_C24_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C31_C32_A, MPCC_MCM_FIRST_GAMUT_REMAP_C31_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C31_C32_A, MPCC_MCM_FIRST_GAMUT_REMAP_C32_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C33_C34_A, MPCC_MCM_FIRST_GAMUT_REMAP_C33_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_FIRST_GAMUT_REMAP_C33_C34_A, MPCC_MCM_FIRST_GAMUT_REMAP_C34_A, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_SECOND_GAMUT_REMAP_COEF_FORMAT, MPCC_MCM_SECOND_GAMUT_REMAP_COEF_FORMAT, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_SECOND_GAMUT_REMAP_MODE, MPCC_MCM_SECOND_GAMUT_REMAP_MODE, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_SECOND_GAMUT_REMAP_MODE, MPCC_MCM_SECOND_GAMUT_REMAP_MODE_CURRENT, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C11_C12_A, MPCC_MCM_SECOND_GAMUT_REMAP_C11_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C11_C12_A, MPCC_MCM_SECOND_GAMUT_REMAP_C12_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C13_C14_A, MPCC_MCM_SECOND_GAMUT_REMAP_C13_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C13_C14_A, MPCC_MCM_SECOND_GAMUT_REMAP_C14_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C21_C22_A, MPCC_MCM_SECOND_GAMUT_REMAP_C21_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C21_C22_A, MPCC_MCM_SECOND_GAMUT_REMAP_C22_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C23_C24_A, MPCC_MCM_SECOND_GAMUT_REMAP_C23_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C23_C24_A, MPCC_MCM_SECOND_GAMUT_REMAP_C24_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C31_C32_A, MPCC_MCM_SECOND_GAMUT_REMAP_C31_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C31_C32_A, MPCC_MCM_SECOND_GAMUT_REMAP_C32_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C33_C34_A, MPCC_MCM_SECOND_GAMUT_REMAP_C33_A, mask_sh), \ + SF(MPCC_MCM0_MPC_MCM_SECOND_GAMUT_REMAP_C33_C34_A, MPCC_MCM_SECOND_GAMUT_REMAP_C34_A, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_FAST_LOAD_SELECT, MPCC_MCM_3DLUT_FL_SEL, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_FAST_LOAD_STATUS, MPCC_MCM_3DLUT_FL_DONE, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_FAST_LOAD_STATUS, MPCC_MCM_3DLUT_FL_SOFT_UNDERFLOW, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_FAST_LOAD_STATUS, MPCC_MCM_3DLUT_FL_HARD_UNDERFLOW, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_R, MPCC_MCM_3DLUT_OUT_OFFSET_R, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_R, MPCC_MCM_3DLUT_OUT_SCALE_R, mask_sh), \ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_G, MPCC_MCM_3DLUT_OUT_OFFSET_G, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_G, MPCC_MCM_3DLUT_OUT_SCALE_G, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_OFFSET_B, mask_sh),\ + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_SCALE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_LUT_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_MODE_CURRENT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_R, MPC_RMCM_SHAPER_OFFSET_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_G, MPC_RMCM_SHAPER_OFFSET_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_B, MPC_RMCM_SHAPER_OFFSET_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_R, MPC_RMCM_SHAPER_SCALE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_INDEX, MPC_RMCM_SHAPER_LUT_INDEX, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_DATA, MPC_RMCM_SHAPER_LUT_DATA, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_SIZE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE_CURRENT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_INDEX, MPC_RMCM_3DLUT_INDEX, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA0, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA1, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA_30BIT, MPC_RMCM_3DLUT_DATA_30BIT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_WRITE_EN_MASK, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_30BIT_EN, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_READ_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_NORM_FACTOR, MPC_RMCM_3DLUT_OUT_NORM_FACTOR, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_OFFSET_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_SCALE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_OFFSET_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_SCALE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_OFFSET_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_SCALE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE_CURRENT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C11_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C12_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C13_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C14_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C21_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C22_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C23_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C24_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C31_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C32_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C33_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C34_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C11_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C12_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C13_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C14_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C21_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C22_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C23_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C24_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C31_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C32_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C33_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C34_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_FORCE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_DIS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_FORCE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_DIS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_STATE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_STATE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_SELECT, MPC_RMCM_3DLUT_FL_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_DONE, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_CNTL, MPC_RMCM_CNTL, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_INDEX, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_WRITE_EN, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_DATA, MPC_RMCM_TEST_DEBUG_DATA, mask_sh), \ + SF(MPC_OUT0_CSC_MODE, MPC_OCSC_MODE, mask_sh),\ + SF(MPC_OUT0_CSC_C11_C12_A, MPC_OCSC_C11_A, mask_sh),\ + SF(MPC_OUT0_CSC_C11_C12_A, MPC_OCSC_C12_A, mask_sh),\ + SF(MPC_OUT0_DENORM_CONTROL, MPC_OUT_DENORM_MODE, mask_sh),\ + SF(MPC_OUT0_DENORM_CONTROL, MPC_OUT_DENORM_CLAMP_MAX_R_CR, mask_sh),\ + SF(MPC_OUT0_DENORM_CONTROL, MPC_OUT_DENORM_CLAMP_MIN_R_CR, mask_sh),\ + SF(MPC_OUT0_DENORM_CLAMP_G_Y, MPC_OUT_DENORM_CLAMP_MAX_G_Y, mask_sh),\ + SF(MPC_OUT0_DENORM_CLAMP_G_Y, MPC_OUT_DENORM_CLAMP_MIN_G_Y, mask_sh),\ + SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MAX_B_CB, mask_sh),\ + SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MIN_B_CB, mask_sh),\ + SF(MPC_OUT0_MUX, MPC_OUT_MUX, mask_sh),\ + SF(MPC_OUT0_MUX, MPC_OUT_RATE_CONTROL, mask_sh),\ + SF(MPC_OUT0_MUX, MPC_OUT_RATE_CONTROL_DISABLE, mask_sh),\ + SF(MPC_OUT0_MUX, MPC_OUT_FLOW_CONTROL_MODE, mask_sh),\ + SF(MPC_OUT0_MUX, MPC_OUT_FLOW_CONTROL_COUNT, mask_sh) + +#define MPC_REG_LIST_DCN6_0_RI(inst) \ + MPC_REG_LIST_DCN42(inst) + +#define MPC_REG_FIELD_LIST_DCN6_0(type) \ + MPC_REG_FIELD_LIST_DCN42(type) + +struct dcn60_mpc_shift { + MPC_REG_FIELD_LIST_DCN6_0(uint8_t); +}; + +struct dcn60_mpc_mask { + MPC_REG_FIELD_LIST_DCN6_0(uint32_t); +}; + +struct dcn60_mpc_registers { + MPC_REG_VARIABLE_LIST_DCN6_0; +}; + +struct dcn60_mpc { + struct mpc base; + int mpcc_in_use_mask; + int num_mpcc; + const struct dcn60_mpc_registers *mpc_regs; + const struct dcn60_mpc_shift *mpc_shift; + const struct dcn60_mpc_mask *mpc_mask; + int num_rmu; +}; + +void dcn60_mpc_construct(struct dcn60_mpc *mpc401, + struct dc_context *ctx, + const struct dcn60_mpc_registers *mpc_regs, + const struct dcn60_mpc_shift *mpc_shift, + const struct dcn60_mpc_mask *mpc_mask, + int num_mpcc, + int num_rmu); + +void mpc60_program_rmcm_lut_read_write_control(struct mpc *mpc, + const enum MCM_LUT_ID id, + bool lut_bank_a, + bool enabled, + int mpcc_id); + +void mpc60_select_3dlut_ram(struct mpc *mpc, + enum dc_lut_mode mode, + bool is_color_channel_12bits, + uint32_t mpcc_id); + +bool mpc60_program_3dlut(struct mpc *mpc, + const struct tetrahedral_params *params, + int mpcc_id); + +void mpc60_program_lut_read_write_control(struct mpc *mpc, + const enum MCM_LUT_ID id, + bool lut_bank_a, + int mpcc_id); + +#endif /* DCN60_MPC_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.c new file mode 100644 index 000000000000..964639d6e260 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +/* Copyright 2025 Advanced Micro Devices, Inc. */ + +#include "dcn60_opp.h" +#include "reg_helper.h" + +#define REG(reg) ((const struct dcn60_opp_registers *)(oppn20->regs))->reg + +#undef FN +#define FN(reg_name, field_name) \ + ((const struct dcn60_opp_shift *)(oppn20->opp_shift))->field_name, \ + ((const struct dcn60_opp_mask *)(oppn20->opp_mask))->field_name + +#define CTX oppn20->base.ctx + +void dcn60_opp_construct(struct dcn20_opp *oppn20, struct dc_context *ctx, + uint32_t inst, const struct dcn60_opp_registers *regs, + const struct dcn60_opp_shift *opp_shift, + const struct dcn60_opp_mask *opp_mask) +{ + dcn20_opp_construct(oppn20, ctx, inst, + (const struct dcn20_opp_registers *)regs, + (const struct dcn20_opp_shift *)opp_shift, + (const struct dcn20_opp_mask *)opp_mask); +} diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.h b/drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.h new file mode 100644 index 000000000000..1a228b72837c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/opp/dcn60/dcn60_opp.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright 2025 Advanced Micro Devices, Inc. */ + +#ifndef __DCN60_OPP_H +#define __DCN60_OPP_H + +#include "dcn20/dcn20_opp.h" +#include "dcn35/dcn35_opp.h" + +// Separate initializer list is required for DCN6 because OPPBUF_3D_PARAMETERS_0::OPPBUF_3D_VACT_SPACE2_SIZE +// is removed in DCN6. Keep the field, but leave it uninitialized. +#define OPP_MASK_SH_LIST_DCN60(mask_sh) \ + OPP_DPG_MASK_SH_LIST(mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_SUBSAMPLING_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_CBCR_BIT_REDUCTION_BYPASS, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh), \ + OPP_SF(FMT0_FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh), \ + OPP_SF(FMT0_FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh), \ + OPP_SF(FMT0_FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh), \ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh), \ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh), \ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh), \ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_MAP420_MEMORY_CONTROL, FMT_MAP420MEM_PWR_FORCE, mask_sh), \ + OPP_SF(OPPBUF0_OPPBUF_CONTROL, OPPBUF_ACTIVE_WIDTH, mask_sh),\ + OPP_SF(OPPBUF0_OPPBUF_CONTROL, OPPBUF_PIXEL_REPETITION, mask_sh),\ + OPP_SF(OPPBUF0_OPPBUF_3D_PARAMETERS_0, OPPBUF_3D_VACT_SPACE1_SIZE, mask_sh), \ + OPP_SF(OPP_PIPE0_OPP_PIPE_CONTROL, OPP_PIPE_CLOCK_EN, mask_sh), \ + OPP_SF(OPPBUF0_OPPBUF_CONTROL, OPPBUF_DISPLAY_SEGMENTATION, mask_sh),\ + OPP_SF(OPPBUF0_OPPBUF_CONTROL, OPPBUF_OVERLAP_PIXEL_NUM, mask_sh), \ + OPP_SF(FMT0_FMT_422_CONTROL, FMT_LEFT_EDGE_EXTRA_PIXEL_COUNT, mask_sh), \ + OPP_SF(OPP_TOP_CLK_CONTROL, OPP_FGCG_REP_DIS, mask_sh) + +struct dcn60_opp_registers { + OPP_REG_VARIABLE_LIST_DCN3_5; +}; + +struct dcn60_opp_shift { + OPP_DCN35_REG_FIELD_LIST(uint8_t); +}; + +struct dcn60_opp_mask { + OPP_DCN35_REG_FIELD_LIST(uint32_t); +}; + +void dcn60_opp_construct(struct dcn20_opp *oppn20, struct dc_context *ctx, + uint32_t inst, const struct dcn60_opp_registers *regs, + const struct dcn60_opp_shift *opp_shift, + const struct dcn60_opp_mask *opp_mask); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.c new file mode 100644 index 000000000000..bfeb3c8fc8f5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dcn30/dcn30_optc.h" +#include "dcn31/dcn31_optc.h" +#include "dcn32/dcn32_optc.h" +#include "dcn401/dcn401_optc.h" +#include "dcn42/dcn42_optc.h" +#include "dcn60_optc.h" +#include "reg_helper.h" +#include "dc.h" +#include "dcn_calc_math.h" +#include "dc_dmub_srv.h" + +#define REG(reg)\ + optc1->tg_regs->reg + +#define CTX \ + optc1->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + optc1->tg_shift->field_name, optc1->tg_mask->field_name + +static const struct timing_generator_funcs dcn60_tg_funcs = { + .validate_timing = optc1_validate_timing, + .program_timing = optc1_program_timing, + .setup_vertical_interrupt0 = optc1_setup_vertical_interrupt0, + .setup_vertical_interrupt1 = optc1_setup_vertical_interrupt1, + .setup_vertical_interrupt2 = optc1_setup_vertical_interrupt2, + .program_global_sync = optc401_program_global_sync, + .enable_crtc = optc401_enable_crtc, + .disable_crtc = optc401_disable_crtc, + .phantom_crtc_post_enable = optc401_phantom_crtc_post_enable, + .disable_phantom_crtc = optc401_disable_phantom_otg, + /* used by enable_timing_synchronization. Not need for FPGA */ + .is_counter_moving = optc1_is_counter_moving, + .get_position = optc1_get_position, + .get_frame_count = optc1_get_vblank_counter, + .get_scanoutpos = optc1_get_crtc_scanoutpos, + .get_otg_active_size = optc1_get_otg_active_size, + .set_early_control = optc1_set_early_control, + /* used by enable_timing_synchronization. Not need for FPGA */ + .wait_for_state = optc1_wait_for_state, + .did_triggered_reset_occur = optc1_did_triggered_reset_occur, + .triplebuffer_lock = optc3_triplebuffer_lock, + .triplebuffer_unlock = optc2_triplebuffer_unlock, + .enable_reset_trigger = optc1_enable_reset_trigger, + .enable_crtc_reset = optc1_enable_crtc_reset, + .disable_reset_trigger = optc1_disable_reset_trigger, + .lock = optc3_lock, + .unlock = optc1_unlock, + .lock_doublebuffer_enable = optc3_lock_doublebuffer_enable, + .lock_doublebuffer_disable = optc3_lock_doublebuffer_disable, + .enable_optc_clock = optc1_enable_optc_clock, + .set_drr = optc401_set_drr, + .get_last_used_drr_vtotal = optc2_get_last_used_drr_vtotal, + .set_vtotal_min_max = optc401_set_vtotal_min_max, + .set_static_screen_control = optc1_set_static_screen_control, + .program_stereo = optc1_program_stereo, + .is_stereo_left_eye = optc1_is_stereo_left_eye, + .tg_init = optc3_tg_init, + .is_tg_enabled = optc1_is_tg_enabled, + .is_optc_underflow_occurred = optc1_is_optc_underflow_occurred, + .clear_optc_underflow = optc1_clear_optc_underflow, + .get_crc = optc42_get_crc, + .configure_crc = optc1_configure_crc, + .set_dsc_config = optc3_set_dsc_config, + .get_dsc_status = optc2_get_dsc_status, + .set_odm_bypass = optc401_set_odm_bypass, + .set_odm_combine = optc401_set_odm_combine, + .wait_odm_doublebuffer_pending_clear = optc32_wait_odm_doublebuffer_pending_clear, + .set_h_timing_div_manual_mode = optc401_set_h_timing_div_manual_mode, + .get_optc_source = optc2_get_optc_source, + .set_out_mux = optc401_set_out_mux, + .set_drr_trigger_window = optc3_set_drr_trigger_window, + .set_vtotal_change_limit = optc3_set_vtotal_change_limit, + .set_gsl = optc2_set_gsl, + .set_gsl_source_select = NULL, + .set_vtg_params = optc1_set_vtg_params, + .program_manual_trigger = optc2_program_manual_trigger, + .setup_manual_trigger = optc2_setup_manual_trigger, + .get_hw_timing = optc1_get_hw_timing, + .is_two_pixels_per_container = optc1_is_two_pixels_per_container, + .get_optc_double_buffer_pending = optc3_get_optc_double_buffer_pending, + .get_otg_double_buffer_pending = optc3_get_otg_update_pending, + .get_pipe_update_pending = optc3_get_pipe_update_pending, + .set_vupdate_keepout = optc401_set_vupdate_keepout, + .wait_update_lock_status = optc401_wait_update_lock_status, + .read_otg_state = optc31_read_otg_state, + .optc_read_reg_state = optc31_read_reg_state, +}; + +void dcn60_timing_generator_init(struct optc *optc1) +{ + optc1->base.funcs = &dcn60_tg_funcs; + + optc1->max_h_total = optc1->tg_mask->OTG_H_TOTAL + 1; + optc1->max_v_total = optc1->tg_mask->OTG_V_TOTAL + 1; + + optc1->min_h_blank = 32; + optc1->min_v_blank = 3; + optc1->min_v_blank_interlace = 5; + optc1->min_h_sync_width = 4; + optc1->min_v_sync_width = 1; +} diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.h b/drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.h new file mode 100644 index 000000000000..ba207c0e498e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn60/dcn60_optc.h @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_OPTC_DCN60_H__ +#define __DC_OPTC_DCN60_H__ + +#include "dcn10/dcn10_optc.h" + +#define OPTC_COMMON_MASK_SH_LIST_DCN60(mask_sh)\ + SF(OTG0_OTG_VSTARTUP_PARAM, VSTARTUP_START, mask_sh),\ + SF(OTG0_OTG_VUPDATE_PARAM, VUPDATE_OFFSET, mask_sh),\ + SF(OTG0_OTG_VUPDATE_PARAM, VUPDATE_WIDTH, mask_sh),\ + SF(OTG0_OTG_VREADY_PARAM, VREADY_OFFSET, mask_sh),\ + SF(OTG0_OTG_MASTER_UPDATE_LOCK, OTG_MASTER_UPDATE_LOCK, mask_sh),\ + SF(OTG0_OTG_MASTER_UPDATE_LOCK, UPDATE_LOCK_STATUS, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL0, MASTER_UPDATE_LOCK_DB_START_X, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL0, MASTER_UPDATE_LOCK_DB_END_X, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL0, MASTER_UPDATE_LOCK_DB_EN, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL1, MASTER_UPDATE_LOCK_DB_START_Y, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL1, MASTER_UPDATE_LOCK_DB_END_Y, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL2, OTG_MASTER_UPDATE_LOCK_SEL, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL4, DIG_UPDATE_POSITION_X, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL4, DIG_UPDATE_POSITION_Y, mask_sh),\ + SF(OTG0_OTG_DOUBLE_BUFFER_CONTROL, OTG_UPDATE_PENDING, mask_sh),\ + SF(OTG0_OTG_H_TOTAL, OTG_H_TOTAL, mask_sh),\ + SF(OTG0_OTG_H_BLANK_START_END, OTG_H_BLANK_START, mask_sh),\ + SF(OTG0_OTG_H_BLANK_START_END, OTG_H_BLANK_END, mask_sh),\ + SF(OTG0_OTG_H_SYNC_A, OTG_H_SYNC_A_START, mask_sh),\ + SF(OTG0_OTG_H_SYNC_A, OTG_H_SYNC_A_END, mask_sh),\ + SF(OTG0_OTG_H_SYNC_A_CNTL, OTG_H_SYNC_A_POL, mask_sh),\ + SF(OTG0_OTG_V_TOTAL, OTG_V_TOTAL, mask_sh),\ + SF(OTG0_OTG_V_BLANK_START_END, OTG_V_BLANK_START, mask_sh),\ + SF(OTG0_OTG_V_BLANK_START_END, OTG_V_BLANK_END, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A, OTG_V_SYNC_A_START, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A, OTG_V_SYNC_A_END, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A_CNTL, OTG_V_SYNC_A_POL, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_MASTER_EN, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_START_POINT_CNTL, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_DISABLE_POINT_CNTL, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_FIELD_NUMBER_CNTL, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_OUT_MUX, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_EN, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_LINE_NUM, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_POLARITY, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_EYE_FLAG_POLARITY, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_DISABLE_STEREOSYNC_OUTPUT_FOR_DP, mask_sh),\ + SF(OTG0_OTG_STEREO_STATUS, OTG_STEREO_CURRENT_EYE, mask_sh),\ + SF(OTG0_OTG_3D_STRUCTURE_CONTROL, OTG_3D_STRUCTURE_EN, mask_sh),\ + SF(OTG0_OTG_3D_STRUCTURE_CONTROL, OTG_3D_STRUCTURE_V_UPDATE_MODE, mask_sh),\ + SF(OTG0_OTG_3D_STRUCTURE_CONTROL, OTG_3D_STRUCTURE_STEREO_SEL_OVR, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_MAX, OTG_V_TOTAL_MAX, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_MIN, OTG_V_TOTAL_MIN, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_V_TOTAL_MIN_SEL, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_V_TOTAL_MAX_SEL, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_FORCE_LOCK_ON_EVENT, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_SET_V_TOTAL_MIN_MASK, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_VTOTAL_MID_REPLACING_MIN_EN, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_VTOTAL_MID_REPLACING_MAX_EN, mask_sh),\ + SF(OTG0_OTG_FORCE_COUNT_NOW_CNTL, OTG_FORCE_COUNT_NOW_CLEAR, mask_sh),\ + SF(OTG0_OTG_FORCE_COUNT_NOW_CNTL, OTG_FORCE_COUNT_NOW_MODE, mask_sh),\ + SF(OTG0_OTG_FORCE_COUNT_NOW_CNTL, OTG_FORCE_COUNT_NOW_OCCURRED, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_SOURCE_SELECT, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_SOURCE_PIPE_SELECT, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_RISING_EDGE_DETECT_CNTL, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_FALLING_EDGE_DETECT_CNTL, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_POLARITY_SELECT, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_FREQUENCY_SELECT, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_DELAY, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_CLEAR, mask_sh),\ + SF(OTG0_OTG_STATIC_SCREEN_CONTROL, OTG_STATIC_SCREEN_EVENT_MASK, mask_sh),\ + SF(OTG0_OTG_STATIC_SCREEN_CONTROL, OTG_STATIC_SCREEN_FRAME_COUNT, mask_sh),\ + SF(OTG0_OTG_STATUS_FRAME_COUNT, OTG_FRAME_COUNT, mask_sh),\ + SF(OTG0_OTG_STATUS, OTG_V_BLANK, mask_sh),\ + SF(OTG0_OTG_STATUS, OTG_V_ACTIVE_DISP, mask_sh),\ + SF(OTG0_OTG_STATUS_POSITION, OTG_HORZ_COUNT, mask_sh),\ + SF(OTG0_OTG_STATUS_POSITION, OTG_VERT_COUNT, mask_sh),\ + SF(OTG0_OTG_NOM_VERT_POSITION, OTG_VERT_COUNT_NOM, mask_sh),\ + SF(OTG0_OTG_M_CONST_DTO0, OTG_M_CONST_DTO_PHASE, mask_sh),\ + SF(OTG0_OTG_M_CONST_DTO1, OTG_M_CONST_DTO_MODULO, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_BUSY, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_CLOCK_EN, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_CLOCK_ON, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_CLOCK_GATE_DIS, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT0_CONTROL, OTG_VERTICAL_INTERRUPT0_INT_ENABLE, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT0_POSITION, OTG_VERTICAL_INTERRUPT0_LINE_START, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT0_POSITION, OTG_VERTICAL_INTERRUPT0_LINE_END, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT1_CONTROL, OTG_VERTICAL_INTERRUPT1_INT_ENABLE, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT1_POSITION, OTG_VERTICAL_INTERRUPT1_LINE_START, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT2_CONTROL, OTG_VERTICAL_INTERRUPT2_INT_ENABLE, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT2_POSITION, OTG_VERTICAL_INTERRUPT2_LINE_START, mask_sh),\ + SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_EN, mask_sh),\ + SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_ON, mask_sh),\ + SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_GATE_DIS, mask_sh),\ + SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_OCCURRED_STATUS, mask_sh),\ + SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_DOUBLE_BUFFER_PENDING, mask_sh),\ + SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_CLEAR, mask_sh),\ + SF(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\ + SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\ + SF(VTG0_CONTROL, VTG0_VCOUNT_INIT, mask_sh),\ + SF(OTG0_OTG_VERT_SYNC_CONTROL, OTG_FORCE_VSYNC_NEXT_LINE_OCCURRED, mask_sh),\ + SF(OTG0_OTG_VERT_SYNC_CONTROL, OTG_FORCE_VSYNC_NEXT_LINE_CLEAR, mask_sh),\ + SF(OTG0_OTG_VERT_SYNC_CONTROL, OTG_AUTO_FORCE_VSYNC_MODE, mask_sh),\ + SF(OTG0_OTG_GSL_CONTROL, OTG_GSL0_EN, mask_sh),\ + SF(OTG0_OTG_GSL_CONTROL, OTG_GSL1_EN, mask_sh),\ + SF(OTG0_OTG_GSL_CONTROL, OTG_GSL2_EN, mask_sh),\ + SF(OTG0_OTG_GSL_CONTROL, OTG_GSL_MASTER_EN, mask_sh),\ + SF(OTG0_OTG_GSL_CONTROL, OTG_GSL_FORCE_DELAY, mask_sh),\ + SF(OTG0_OTG_CRC_CNTL, OTG_CRC_CONT_EN, mask_sh),\ + SF(OTG0_OTG_CRC_CNTL, OTG_CRC0_SELECT, mask_sh),\ + SF(OTG0_OTG_CRC_CNTL, OTG_CRC_EN, mask_sh),\ + SF(OTG0_OTG_CRC0_DATA_R, CRC0_R_CR, mask_sh),\ + SF(OTG0_OTG_CRC0_DATA_G, CRC0_G_Y, mask_sh),\ + SF(OTG0_OTG_CRC0_DATA_B, CRC0_B_CB, mask_sh),\ + SF(OTG0_OTG_CRC1_DATA_R, CRC1_R_CR, mask_sh),\ + SF(OTG0_OTG_CRC1_DATA_G, CRC1_G_Y, mask_sh),\ + SF(OTG0_OTG_CRC1_DATA_B, CRC1_B_CB, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWA_X_CONTROL, OTG_CRC0_WINDOWA_X_START, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWA_X_CONTROL, OTG_CRC0_WINDOWA_X_END, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWA_Y_CONTROL, OTG_CRC0_WINDOWA_Y_START, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWA_Y_CONTROL, OTG_CRC0_WINDOWA_Y_END, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWB_X_CONTROL, OTG_CRC0_WINDOWB_X_START, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWB_X_CONTROL, OTG_CRC0_WINDOWB_X_END, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWB_Y_CONTROL, OTG_CRC0_WINDOWB_Y_START, mask_sh),\ + SF(OTG0_OTG_CRC0_WINDOWB_Y_CONTROL, OTG_CRC0_WINDOWB_Y_END, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWA_X_CONTROL, OTG_CRC1_WINDOWA_X_START, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWA_X_CONTROL, OTG_CRC1_WINDOWA_X_END, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWA_Y_CONTROL, OTG_CRC1_WINDOWA_Y_START, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWA_Y_CONTROL, OTG_CRC1_WINDOWA_Y_END, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWB_X_CONTROL, OTG_CRC1_WINDOWB_X_START, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWB_X_CONTROL, OTG_CRC1_WINDOWB_X_END, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWB_Y_CONTROL, OTG_CRC1_WINDOWB_Y_START, mask_sh),\ + SF(OTG0_OTG_CRC1_WINDOWB_Y_CONTROL, OTG_CRC1_WINDOWB_Y_END, mask_sh),\ + SF(OTG0_OTG_TRIGA_MANUAL_TRIG, OTG_TRIGA_MANUAL_TRIG, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL2, MANUAL_FLOW_CONTROL_SEL, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL2, GLOBAL_UPDATE_LOCK_EN, mask_sh),\ + SF(OTG0_OTG_GSL_WINDOW_X, OTG_GSL_WINDOW_START_X, mask_sh),\ + SF(OTG0_OTG_GSL_WINDOW_X, OTG_GSL_WINDOW_END_X, mask_sh), \ + SF(OTG0_OTG_GSL_WINDOW_Y, OTG_GSL_WINDOW_START_Y, mask_sh),\ + SF(OTG0_OTG_GSL_WINDOW_Y, OTG_GSL_WINDOW_END_Y, mask_sh),\ + SF(OTG0_OTG_VUPDATE_KEEPOUT, OTG_MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_EN, mask_sh), \ + SF(OTG0_OTG_VUPDATE_KEEPOUT, MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_START_OFFSET, mask_sh), \ + SF(OTG0_OTG_VUPDATE_KEEPOUT, MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_END_OFFSET, mask_sh), \ + SF(OTG0_OTG_GSL_CONTROL, OTG_GSL_MASTER_MODE, mask_sh), \ + SF(OTG0_OTG_GSL_CONTROL, OTG_MASTER_UPDATE_LOCK_GSL_EN, mask_sh), \ + SF(ODM0_OPTC_DATA_SOURCE_SELECT, OPTC_SEG0_SRC_SEL, mask_sh),\ + SF(ODM0_OPTC_DATA_SOURCE_SELECT, OPTC_SEG1_SRC_SEL, mask_sh),\ + SF(ODM0_OPTC_DATA_SOURCE_SELECT, OPTC_SEG2_SRC_SEL, mask_sh),\ + SF(ODM0_OPTC_DATA_SOURCE_SELECT, OPTC_SEG3_SRC_SEL, mask_sh),\ + SF(ODM0_OPTC_DATA_SOURCE_SELECT, OPTC_NUM_OF_INPUT_SEGMENT, mask_sh),\ + SF(ODM0_OPTC_MEMORY_CONFIG, OPTC_MEM_SEL, mask_sh),\ + SF(ODM0_OPTC_DATA_FORMAT_CONTROL, OPTC_DATA_FORMAT, mask_sh),\ + SF(ODM0_OPTC_DATA_FORMAT_CONTROL, OPTC_DSC_MODE, mask_sh),\ + SF(ODM0_OPTC_BYTES_PER_PIXEL, OPTC_DSC_BYTES_PER_PIXEL, mask_sh),\ + SF(ODM0_OPTC_WIDTH_CONTROL, OPTC_DSC_SLICE_WIDTH, mask_sh),\ + SF(ODM0_OPTC_WIDTH_CONTROL, OPTC_SEGMENT_WIDTH, mask_sh),\ + SF(ODM0_OPTC_WIDTH_CONTROL2, OPTC_SEGMENT_WIDTH_LAST, mask_sh),\ + SF(OTG0_OTG_DRR_TRIGGER_WINDOW, OTG_DRR_TRIGGER_WINDOW_START_X, mask_sh),\ + SF(OTG0_OTG_DRR_TRIGGER_WINDOW, OTG_DRR_TRIGGER_WINDOW_END_X, mask_sh),\ + SF(OTG0_OTG_DRR_V_TOTAL_CHANGE, OTG_DRR_V_TOTAL_CHANGE_LIMIT, mask_sh),\ + SF(OTG0_OTG_H_TIMING_CNTL, OTG_H_TIMING_DIV_MODE, mask_sh),\ + SF(OTG0_OTG_H_TIMING_CNTL, OTG_H_TIMING_DIV_MODE_MANUAL, mask_sh),\ + SF(OTG0_OTG_DOUBLE_BUFFER_CONTROL, OTG_DRR_TIMING_DBUF_UPDATE_MODE, mask_sh),\ + SF(OTG0_OTG_DRR_CONTROL, OTG_V_TOTAL_LAST_USED_BY_DRR, mask_sh),\ + SF(OTG0_OTG_PSTATE_REGISTER, OTG_PSTATE_KEEPOUT_START, mask_sh),\ + SF(OTG0_OTG_PSTATE_REGISTER, OTG_PSTATE_EXTEND, mask_sh),\ + SF(OTG0_OTG_PSTATE_REGISTER, OTG_UNBLANK, mask_sh),\ + SF(OTG0_OTG_PSTATE_REGISTER, OTG_PSTATE_ALLOW_WIDTH_MIN, mask_sh),\ + SF(OTG0_OTG_PIPE_UPDATE_STATUS, OTG_FLIP_PENDING, mask_sh),\ + SF(OTG0_OTG_PIPE_UPDATE_STATUS, OTG_DC_REG_UPDATE_PENDING, mask_sh),\ + SF(OTG0_OTG_PIPE_UPDATE_STATUS, OTG_CURSOR_UPDATE_PENDING, mask_sh),\ + SF(OTG0_OTG_PIPE_UPDATE_STATUS, OTG_VUPDATE_KEEPOUT_STATUS, mask_sh),\ + SF(OTG0_INTERRUPT_DEST, OTG0_IHC_OTG_VERTICAL_INTERRUPT2_DEST, mask_sh) + +void dcn60_timing_generator_init(struct optc *optc1); + +#endif /* __DC_OPTC_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c new file mode 100644 index 000000000000..9dc6e4192943 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c @@ -0,0 +1,2375 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dc.h" + +#include "dcn32/dcn32_init.h" +#include "dcn401/dcn401_init.h" +#include "dcn60/dcn60_init.h" + +#include "resource.h" +#include "include/irq_service_interface.h" + +#include "dcn20/dcn20_resource.h" +#include "dcn30/dcn30_resource.h" +#include "dcn32/dcn32_resource.h" +#include "dcn321/dcn321_resource.h" +#include "dcn401/dcn401_resource.h" +#include "dcn42/dcn42_resource.h" +#include "dcn60_resource.h" + +#include "dcn10/dcn10_ipp.h" +#include "dcn60/dcn60_hubbub.h" +#include "dcn60/dcn60_mpc.h" +#include "dcn60/dcn60_hubp.h" +#include "irq/dcn60/irq_service_dcn60.h" +#include "dcn60/dcn60_dpp.h" +#include "dcn60/dcn60_optc.h" +#include "dcn20/dcn20_hwseq.h" +#include "dcn30/dcn30_hwseq.h" +#include "dcn10/dcn10_hwseq.h" +#include "dcn60/dcn60_opp.h" +#include "dcn60/dcn60_dsc.h" +#include "dcn30/dcn30_vpg.h" +#include "dcn31/dcn31_vpg.h" +#include "dcn30/dcn30_dio_stream_encoder.h" +#include "dcn401/dcn401_dio_stream_encoder.h" +#include "dcn60/dcn60_dio_stream_encoder.h" +#include "dcn401/dcn401_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" +#include "dcn60/dcn60_hpo_frl_link_encoder.h" +#include "dcn60/dcn60_hpo_frl_stream_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" +#include "dcn31/dcn31_dio_link_encoder.h" +#include "dcn401/dcn401_dio_link_encoder.h" +#include "dcn60/dcn60_dio_link_encoder.h" +#include "dcn10/dcn10_link_encoder.h" +#include "dcn321/dcn321_dio_link_encoder.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "clk_mgr.h" +#include "dio/virtual/virtual_stream_encoder.h" +#include "dml/display_mode_vba.h" +#include "dcn60/dcn60_dccg.h" +#include "dcn10/dcn10_resource.h" +#include "link_service.h" +#include "link_enc_cfg.h" +#include "dcn31/dcn31_panel_cntl.h" + +#include "dcn30/dcn30_dwb.h" +#include "dcn32/dcn32_mmhubbub.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" +#include "dpcs/dpcs_6_0_0_offset.h" +#include "dpcs/dpcs_6_0_0_sh_mask.h" + +#include "reg_helper.h" +#include "dce/dmub_abm.h" +#include "dce/dmub_psr.h" +#include "dce/dce_aux.h" +#include "dce/dce_i2c.h" + +#include "dml/dcn30/display_mode_vba_30.h" +#include "vm_helper.h" +#include "dcn20/dcn20_vmid.h" + +#include "dc_state_priv.h" + +#include "dml2_0/dml2_wrapper.h" +#include "dml2_0/dml21/dml21_wrapper.h" + +#define DC_LOGGER_INIT(logger) + +/* begin ********************* + * macros to expend register list macro defined in HW object header file + */ + +enum dcn60_clk_src_array_id { + DCN60_CLK_SRC_PLL0, + DCN60_CLK_SRC_PLL1, + DCN60_CLK_SRC_PLL2, + DCN60_CLK_SRC_PLL3, + //DCN60_CLK_SRC_PLL4, + DCN60_CLK_SRC_TOTAL +}; + +static const uint32_t MCACHE_ID_UNASSIGNED = 0xF; +static const uint32_t SPLIT_LOCATION_UNDEFINED = 0xFFFF; + +/* DCN */ +#define BASE_INNER(seg) ctx->dcn_reg_offsets[seg] + +#define BASE(seg) BASE_INNER(seg) + +#define SR(reg_name)\ + REG_STRUCT.reg_name = BASE(reg ## reg_name ## _BASE_IDX) + \ + reg ## reg_name +#define SR_ARR(reg_name, id)\ + REG_STRUCT[id].reg_name = BASE(reg ## reg_name ## _BASE_IDX) + \ + reg ## reg_name +#define SR_ARR_INIT(reg_name, id, value)\ + REG_STRUCT[id].reg_name = value + +#define SRI(reg_name, block, id)\ + REG_STRUCT.reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRI_ARR(reg_name, block, id)\ + REG_STRUCT[id].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRI_ARR_DDC(reg_name, block, id)\ + REG_STRUCT[id-1].reg_name = BASE(reg ## reg_name ## id ## _ ## block ## _BASE_IDX) + \ + reg ## reg_name ## id ## _ ## block + +/* + * Used when a reg_name would otherwise begin with an integer + */ +#define SRI_ARR_US(reg_name, block, id)\ + REG_STRUCT[id].reg_name = BASE(reg ## block ## id ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## reg_name +#define SR_ARR_I2C(reg_name, id) \ + REG_STRUCT[id-1].reg_name = BASE(reg##reg_name##_BASE_IDX) + reg##reg_name + +#define SRI_ARR_I2C(reg_name, block, 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 + +#define SRI2(reg_name, block, id)\ + .reg_name = BASE(reg ## reg_name ## _BASE_IDX) + \ + reg ## reg_name +#define SRI2_ARR(reg_name, block, id)\ + REG_STRUCT[id].reg_name = BASE(reg ## reg_name ## _BASE_IDX) + \ + reg ## reg_name + +#define SRIR(var_name, reg_name, block, id)\ + .var_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRII(reg_name, block, id)\ + REG_STRUCT.reg_name[id] = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRII_ARR_2(reg_name, block, id, inst)\ + REG_STRUCT[inst].reg_name[id] = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRII_MPC_RMU(reg_name, block, id)\ + .RMU##_##reg_name[id] = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SRII_DWB(reg_name, temp_name, block, id)\ + REG_STRUCT.reg_name[id] = BASE(reg ## block ## id ## _ ## temp_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## temp_name + +#define DCCG_SRII(reg_name, block, id)\ + REG_STRUCT.block ## _ ## reg_name[id] = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +#define SF_DWB2(reg_name, block, id, field_name, post_fix) \ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define VUPDATE_SRII(reg_name, block, id)\ + REG_STRUCT.reg_name[id] = BASE(reg ## reg_name ## _ ## block ## id ## _BASE_IDX) + \ + reg ## reg_name ## _ ## block ## id + +/* bringup_nbif_7_10_0_offset registers */ +#define regBIF_BX0_BIOS_SCRATCH_3 0x003b +#define regBIF_BX0_BIOS_SCRATCH_3_BASE_IDX 1 +#define regBIF_BX0_BIOS_SCRATCH_6 0x003e +#define regBIF_BX0_BIOS_SCRATCH_6_BASE_IDX 1 + +/* NBIO */ +#define NBIO_BASE_INNER(seg) ctx->nbio_reg_offsets[seg] + +#define NBIO_BASE(seg) \ + NBIO_BASE_INNER(seg) + +#define NBIO_SR(reg_name)\ + REG_STRUCT.reg_name = NBIO_BASE(regBIF_BX0_ ## reg_name ## _BASE_IDX) + \ + regBIF_BX0_ ## reg_name +#define NBIO_SR_ARR(reg_name, id)\ + REG_STRUCT[id].reg_name = NBIO_BASE(regBIF_BX0_ ## reg_name ## _BASE_IDX) + \ + regBIF_BX0_ ## reg_name + +#define CTX ctx +#define REG(reg_name) \ + (ctx->dcn_reg_offsets[reg ## reg_name ## _BASE_IDX] + reg ## reg_name) + +static struct bios_registers bios_regs; + +#define bios_regs_init() \ + NBIO_SR(BIOS_SCRATCH_3),\ + NBIO_SR(BIOS_SCRATCH_6) + +#define clk_src_regs_init(index, pllid)\ + CS_COMMON_REG_LIST_DCN3_0_RI(index, pllid) + +static struct dce110_clk_src_regs clk_src_regs[5]; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCN3_2(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCN3_2(_MASK) +}; + +#define abm_regs_init(id)\ + ABM_DCN42_REG_LIST_RI(id) + +static struct dce_abm_registers abm_regs[4]; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCN42(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCN42(_MASK) +}; + +#define audio_regs_init(id)\ + AUD_COMMON_REG_LIST_RI(id) + +static struct dce_audio_registers audio_regs[5]; + +#define DCE120_AUD_COMMON_MASK_SH_LIST(mask_sh)\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_SOURCE_SEL, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO_SEL, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_USE_512FBR_DTO, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO1_USE_512FBR_DTO, mask_sh),\ + SF(DCCG_AUDIO_DTO0_MODULE, DCCG_AUDIO_DTO0_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO0_PHASE, DCCG_AUDIO_DTO0_PHASE, mask_sh),\ + SF(DCCG_AUDIO_DTO1_MODULE, DCCG_AUDIO_DTO1_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO1_PHASE, DCCG_AUDIO_DTO1_PHASE, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, AUDIO_RATE_CAPABILITIES, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, CLKSTOP, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, EPSS, mask_sh) + +static const struct dce_audio_shift audio_shift = { + DCE120_AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + DCE120_AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define vpg_regs_init(id)\ + VPG_DCN401_REG_LIST_RI(id) + +static struct dcn31_vpg_registers vpg_regs[9]; + +static const struct dcn31_vpg_shift vpg_shift = { + DCN31_VPG_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn31_vpg_mask vpg_mask = { + DCN31_VPG_MASK_SH_LIST(_MASK) +}; + +#define apg_regs_init(id)\ + APG_DCN31_REG_LIST_RI(id) + +static struct dcn31_apg_registers apg_regs[4]; + +static const struct dcn31_apg_shift apg_shift = { + DCN31_APG_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn31_apg_mask apg_mask = { + DCN31_APG_MASK_SH_LIST(_MASK) +}; + +#define stream_enc_regs_init(id)\ + SE_REG_LIST_DCN60_RI(id) + +static struct dcn10_stream_enc_registers stream_enc_regs[4]; + +static const struct dcn10_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct dcn10_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCN60(_MASK) +}; + +#define aux_regs_init(id, ddc_id)\ + DCN60_AUX_REG_LIST_RI(id, ddc_id) + +static struct dcn10_link_enc_aux_registers link_enc_aux_regs[5]; + +#define hpd_regs_init(id)\ + HPD_REG_LIST_DCN60_RI(id) + +static struct dcn10_link_enc_hpd_registers link_enc_hpd_regs[5]; + +#define link_regs_init(id, phyid)\ + LE_DCN401_REG_LIST_RI(id), \ + LE_DCN60_REG_LIST_RI(id) + +static struct dcn10_link_enc_registers link_enc_regs[4]; + +static const struct dcn10_link_enc_shift le_shift = { + LINK_ENCODER_MASK_SH_LIST_DCN401(__SHIFT), \ + LINK_ENCODER_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct dcn10_link_enc_mask le_mask = { + LINK_ENCODER_MASK_SH_LIST_DCN401(_MASK), \ + LINK_ENCODER_MASK_SH_LIST_DCN60(_MASK) +}; + +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN60_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), + DCN60_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), + DCN60_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)\ + DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) + +static struct dcn31_hpo_dp_stream_encoder_registers hpo_dp_stream_enc_regs[4]; + +static const struct dcn31_hpo_dp_stream_encoder_shift hpo_dp_se_shift = { + DCN3_1_HPO_DP_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn31_hpo_dp_stream_encoder_mask hpo_dp_se_mask = { + DCN3_1_HPO_DP_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_dp_link_encoder_reg_init(id)\ + DCN3_1_HPO_DP_LINK_ENC_REG_LIST_RI(id) + /*DCN3_1_RDPCSTX_REG_LIST(0),*/ + /*DCN3_1_RDPCSTX_REG_LIST(1),*/ + /*DCN3_1_RDPCSTX_REG_LIST(2),*/ + /*DCN3_1_RDPCSTX_REG_LIST(3),*/ + +static struct dcn31_hpo_dp_link_encoder_registers hpo_dp_link_enc_regs[4]; + +static const struct dcn31_hpo_dp_link_encoder_shift hpo_dp_le_shift = { + DCN3_2_HPO_DP_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn31_hpo_dp_link_encoder_mask hpo_dp_le_mask = { + DCN3_2_HPO_DP_LINK_ENC_MASK_SH_LIST(_MASK) +}; + +#define dpp_regs_init(id)\ + DPP_REG_LIST_DCN60_RI(id) + +static struct dcn60_dpp_registers dpp_regs[4]; + +static const struct dcn60_dpp_shift tf_shift = { + DPP_REG_LIST_SH_MASK_DCN60(__SHIFT) +}; + +static const struct dcn60_dpp_mask tf_mask = { + DPP_REG_LIST_SH_MASK_DCN60(_MASK) +}; + +#define opp_regs_init(id)\ + OPP_REG_LIST_DCN401_RI(id) + +static struct dcn60_opp_registers opp_regs[4]; + +static const struct dcn60_opp_shift opp_shift = { + OPP_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct dcn60_opp_mask opp_mask = { + OPP_MASK_SH_LIST_DCN60(_MASK) +}; + +#define aux_engine_regs_init(id) \ + AUX_COMMON_REG_LIST0_RI(id), \ + SR_ARR_INIT(AUXN_IMPCAL, id, 0), \ + SR_ARR_INIT(AUXP_IMPCAL, id, 0), \ + SR_ARR_INIT(AUX_RESET_MASK, id, DP_AUX0_AUX_CONTROL__AUX_RESET_MASK) + +static struct dce110_aux_registers aux_engine_regs[4]; + +static const struct dce110_aux_registers_shift aux_shift = { + DCN_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCN_AUX_MASK_SH_LIST(_MASK) +}; + +#define dsc_regs_init(id)\ + DSC_REG_LIST_DCN60_RI(id) + +static struct dcn401_dsc_registers dsc_regs[4]; + +static const struct dcn60_dsc_shift dsc_shift = { + DSC_REG_LIST_SH_MASK_DCN60(__SHIFT) +}; + +static const struct dcn60_dsc_mask dsc_mask = { + DSC_REG_LIST_SH_MASK_DCN60(_MASK) +}; + +static struct dcn60_mpc_registers mpc_regs; + +#define dcn_mpc_regs_init()\ + MPC_REG_LIST_DCN6_0_RI(0),\ + MPC_REG_LIST_DCN6_0_RI(1),\ + MPC_REG_LIST_DCN6_0_RI(2),\ + MPC_REG_LIST_DCN6_0_RI(3),\ + MPC_OUT_MUX_REG_LIST_DCN3_0_RI(0),\ + MPC_OUT_MUX_REG_LIST_DCN3_0_RI(1),\ + MPC_OUT_MUX_REG_LIST_DCN3_0_RI(2),\ + MPC_OUT_MUX_REG_LIST_DCN3_0_RI(3),\ + MPC_RMCM_REG_LIST_DCN42(0),\ + MPC_RMCM_REG_LIST_DCN42(1) + +static const struct dcn60_mpc_shift mpc_shift = { + MPC_COMMON_MASK_SH_LIST_DCN6_0(__SHIFT) +}; + +static const struct dcn60_mpc_mask mpc_mask = { + MPC_COMMON_MASK_SH_LIST_DCN6_0(_MASK) +}; + +#define optc_regs_init(id)\ + OPTC_COMMON_REG_LIST_DCN60_RI(id) + +static struct dcn_optc_registers optc_regs[4]; + +static const struct dcn_optc_shift optc_shift = { + OPTC_COMMON_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct dcn_optc_mask optc_mask = { + OPTC_COMMON_MASK_SH_LIST_DCN60(_MASK) +}; + +static struct dcn_hubp2_registers hubp_regs[4]; + +#define hubp_regs_init(id)\ + HUBP_REG_LIST_DCN60_RI(id) + +static const struct dcn_hubp2_shift hubp_shift = { + HUBP_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct dcn_hubp2_mask hubp_mask = { + HUBP_MASK_SH_LIST_DCN60(_MASK) +}; + +static struct dcn_hubbub_registers hubbub_reg; +#define hubbub_reg_init()\ + HUBBUB_REG_LIST_DCN60_RI(0) + +static const struct dcn_hubbub_shift hubbub_shift = { + HUBBUB_MASK_SH_LIST_DCN6_0(__SHIFT) +}; + +static const struct dcn_hubbub_mask hubbub_mask = { + HUBBUB_MASK_SH_LIST_DCN6_0(_MASK) +}; + +static struct dccg_registers dccg_regs; + +#define dccg_regs_init()\ + DCCG_REG_LIST_DCN60_RI() + +static const struct dccg_shift dccg_shift = { + DCCG_MASK_SH_LIST_DCN60(__SHIFT) +}; + +static const struct dccg_mask dccg_mask = { + DCCG_MASK_SH_LIST_DCN60(_MASK) +}; + +#define SRII2(reg_name_pre, reg_name_post, id)\ + .reg_name_pre ## _ ## reg_name_post[id] = BASE(reg ## reg_name_pre \ + ## id ## _ ## reg_name_post ## _BASE_IDX) + \ + reg ## reg_name_pre ## id ## _ ## reg_name_post + +static struct dce_hwseq_registers hwseq_reg; + +#define hwseq_reg_init()\ + HWSEQ_DCN60_REG_LIST() + +#define HWSEQ_DCN60_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, mask_sh), \ + HWS_SF(, HPO_TOP_CLOCK_CONTROL, HPO_HDMISTREAMCLK_G_GATE_DIS, mask_sh), \ + HWS_SF(, HPO_TOP_HW_CONTROL, HPO_IO_EN, mask_sh), \ + HWS_SF(, ODM_MEM_PWR_CTRL3, ODM_MEM_UNASSIGNED_PWR_MODE, mask_sh), \ + HWS_SF(, ODM_MEM_PWR_CTRL3, ODM_MEM_VBLANK_PWR_MODE, mask_sh), \ + HWS_SF(, HDCP_INTERRUPT_DEST, DOUT_IHC_HDCP0_I2C_XFER_REQ_INTERRUPT_DEST, mask_sh), \ + HWS_SF(, HDCP_INTERRUPT_DEST, DOUT_IHC_HDCP1_I2C_XFER_REQ_INTERRUPT_DEST, mask_sh), \ + HWS_SF(, HDCP_INTERRUPT_DEST, DOUT_IHC_HDCP2_I2C_XFER_REQ_INTERRUPT_DEST, mask_sh), \ + HWS_SF(, HDCP_INTERRUPT_DEST, DOUT_IHC_HDCP3_I2C_XFER_REQ_INTERRUPT_DEST, mask_sh), + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCN60_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCN60_MASK_SH_LIST(_MASK) +}; + +#define vmid_regs_init(id)\ + DCN20_VMID_REG_LIST_RI(id) + +static struct dcn_vmid_registers vmid_regs[16]; + +static const struct dcn20_vmid_shift vmid_shifts = { + DCN20_VMID_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn20_vmid_mask vmid_masks = { + DCN20_VMID_MASK_SH_LIST(_MASK) +}; + +static const struct resource_caps res_cap_dcn6_0 = { + .num_timing_generator = 4, + .num_opp = 4, + .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, + .num_dwb = 0, + .num_ddc = 2, + .num_vmid = 16, + .num_mpc_3dlut = 4, + .num_dsc = 4, + .num_aux = 4, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCN_UNIVERSAL, + .per_pixel_alpha = true, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = true, + .fp16 = true, + .p010 = true, + .ayuv = false, + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 16000, + .fp16 = 16000 + }, + + // 6:1 downscaling ratio: 1000/6 = 166.666 + .max_downscale_factor = { + .argb8888 = 167, + .nv12 = 167, + .fp16 = 167 + }, + 64, + 64 +}; + +static const struct dc_debug_options debug_defaults_drv = { + .disable_dmcu = true, + .force_abm_enable = false, + .clock_trace = true, + .disable_pplib_clock_request = false, + .pipe_split_policy = MPC_SPLIT_AVOID, + .force_single_disp_pipe_split = false, + .disable_dcc = DCC_ENABLE, + .vsr_support = true, + .performance_trace = false, + .max_downscale_src_width = 7680,/*upto 8K*/ + .disable_pplib_wm_range = false, + .scl_reset_length10 = true, + .sanity_checks = false, + .underflow_assert_delay_us = 0xFFFFFFFF, + .dwb_fi_phase = -1, // -1 = disable, + .dmub_command_table = true, + .enable_mem_low_power = { + .bits = { + .vga = false, + .i2c = false, + .dmcu = false, // This is previously known to cause hang on S3 cycles if enabled + .dscl = false, + .cm = false, + .mpc = false, + .optc = true, + } + }, + .use_max_lb = true, + .exit_idle_opt_for_cursor_updates = true, + .using_dml2 = true, + .using_dml21 = true, + .enable_single_display_2to1_odm_policy = true, + + //must match enable_single_display_2to1_odm_policy to support dynamic ODM transitions + .enable_double_buffered_dsc_pg_support = true, + .enable_dp_dig_pixel_rate_div_policy = 1, + .allow_sw_cursor_fallback = false, + .alloc_extra_way_for_cursor = true, + .min_prefetch_in_strobe_ns = 60000, // 60us + .disable_unbounded_requesting = false, + .dcc_meta_propagation_delay_us = 10, + .fams_version = { + .minor = 0, + .major = 3, + }, // v3.0 + .fams2_config = { + .bits = { + .enable = true, + .enable_ppt_check = true, + .enable_offload_flip = true, + .enable_stall_recovery = true, + } + }, + .force_cositing = CHROMA_COSITING_NONE + 1, +}; + +static const struct dc_check_config config_defaults = { + .enable_legacy_fast_update = false, +}; + +static bool is_plane1_enabled(enum surface_pixel_format format) +{ + return (format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA || + format == SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb || + format == SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb); +} + +static void reset_mcache_allocations(struct dml2_hubp_pipe_mcache_regs *pipe_mcache_regs) +{ + // Initialize all entries to special valid MCache ID and special valid split coordinate + pipe_mcache_regs->main.p0.mcache_id_first = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->main.p0.mcache_id_second = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->main.p0.split_location = SPLIT_LOCATION_UNDEFINED; + + pipe_mcache_regs->mall.p0.mcache_id_first = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->mall.p0.mcache_id_second = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->mall.p0.split_location = SPLIT_LOCATION_UNDEFINED; + + pipe_mcache_regs->main.p1.mcache_id_first = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->main.p1.mcache_id_second = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->main.p1.split_location = SPLIT_LOCATION_UNDEFINED; + + pipe_mcache_regs->mall.p1.mcache_id_first = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->mall.p1.mcache_id_second = MCACHE_ID_UNASSIGNED; + pipe_mcache_regs->mall.p1.split_location = SPLIT_LOCATION_UNDEFINED; +} + +static void get_plane_count(struct dc_state *context, unsigned int *plane_count) +{ + int i; + *plane_count = 0; + for (i = 0; i < context->stream_count; i++) { + *plane_count += context->stream_status[i].plane_count; + } +} + +static bool get_plane_index(struct dc_state *context, struct dc_plane_state *plane_state, unsigned int *plane_idx) +{ + int i, j; + *plane_idx = 0; + for (i = 0; i < context->stream_count; i++) { + for (j = 0; j < context->stream_status[i].plane_count; j++) { + if (plane_state == context->stream_status[i].plane_states[j]) { + return true; + } + (*plane_idx)++; + } + } + // No planes have been mapped to this pipe, so just return false + return false; +} + +static void assign_global_mcache_ids(struct dc_state *context, const struct dc_mcache_params *mcache_params, struct dc_mcache_allocations *mcache_allocations) +{ + unsigned int plane_count = 0; + unsigned int i; + unsigned int plane_idx; + uint32_t next_unused_cache_id = 0; + + //Get plane count + get_plane_count(context, &plane_count); + + //Assign global_mcache id + for (plane_idx = 0; plane_idx < plane_count; plane_idx++) { + if (!mcache_params[plane_idx].valid) + continue; + + for (i = 0; i < mcache_params[plane_idx].num_mcaches_plane0; i++) { + mcache_allocations[plane_idx].global_mcache_ids_plane0[i] = next_unused_cache_id++; + } + for (i = 0; i < mcache_params[plane_idx].num_mcaches_plane1; i++) { + mcache_allocations[plane_idx].global_mcache_ids_plane1[i] = next_unused_cache_id++; + } + + // The "psuedo-last" slice is always wrapped around + mcache_allocations[plane_idx].global_mcache_ids_plane0[mcache_params[plane_idx].num_mcaches_plane0] = + mcache_allocations[plane_idx].global_mcache_ids_plane0[0]; + mcache_allocations[plane_idx].global_mcache_ids_plane1[mcache_params[plane_idx].num_mcaches_plane1] = + mcache_allocations[plane_idx].global_mcache_ids_plane1[0]; + + // If we need dedicated caches for mall requesting, then we assign them here. + if (mcache_params[plane_idx].requires_dedicated_mall_mcache) { + for (i = 0; i < mcache_params[plane_idx].num_mcaches_plane0; i++) { + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[i] = next_unused_cache_id++; + } + for (i = 0; i < mcache_params[plane_idx].num_mcaches_plane1; i++) { + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[i] = next_unused_cache_id++; + } + + // The "psuedo-last" slice is always wrapped around + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[mcache_params[plane_idx].num_mcaches_plane0] = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[0]; + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[mcache_params[plane_idx].num_mcaches_plane1] = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[0]; + } + + // If P0 and P1 are sharing caches, then it means the largest mcache IDs for p0 and p1 can be the same + // since mcache IDs are always ascending, then it means the largest mcacheID of p1 should be the + // largest mcacheID of P0 + if (mcache_params[plane_idx].num_mcaches_plane0 > 0 && mcache_params[plane_idx].num_mcaches_plane1 > 0 && + mcache_params[plane_idx].last_slice_sharing.plane0_plane1) { + mcache_allocations[plane_idx].global_mcache_ids_plane1[mcache_params[plane_idx].num_mcaches_plane1 - 1] = + mcache_allocations[plane_idx].global_mcache_ids_plane0[mcache_params[plane_idx].num_mcaches_plane0 - 1]; + } + + // If we need dedicated caches handle last slice sharing + if (mcache_params[plane_idx].requires_dedicated_mall_mcache) { + if (mcache_params[plane_idx].num_mcaches_plane0 > 0 && mcache_params[plane_idx].num_mcaches_plane1 > 0 && + mcache_params[plane_idx].last_slice_sharing.plane0_plane1) { + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[mcache_params[plane_idx].num_mcaches_plane1 - 1] = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[mcache_params[plane_idx].num_mcaches_plane0 - 1]; + } + // If mall_comb_mcache_l is set then it means that largest mcache ID for MALL p0 can be same as regular read p0 + if (mcache_params[plane_idx].num_mcaches_plane0 > 0 && mcache_params[plane_idx].last_slice_sharing.mall_comb_mcache_p0) { + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[mcache_params[plane_idx].num_mcaches_plane0 - 1] = + mcache_allocations[plane_idx].global_mcache_ids_plane0[mcache_params[plane_idx].num_mcaches_plane0 - 1]; + } + // If mall_comb_mcache_c is set then it means that largest mcache ID for MALL p1 can be same as regular + // read p1 (which can be same as regular read p0 if plane0_plane1 is also set) + if (mcache_params[plane_idx].num_mcaches_plane1 > 0 && mcache_params[plane_idx].last_slice_sharing.mall_comb_mcache_p1) { + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[mcache_params[plane_idx].num_mcaches_plane1 - 1] = + mcache_allocations[plane_idx].global_mcache_ids_plane1[mcache_params[plane_idx].num_mcaches_plane1 - 1]; + } + } + + // If you don't need dedicated mall mcaches, the mall mcache assignments are identical to the normal requesting + if (!mcache_params[plane_idx].requires_dedicated_mall_mcache) { + memcpy(mcache_allocations[plane_idx].global_mcache_ids_mall_plane0, mcache_allocations[plane_idx].global_mcache_ids_plane0, + sizeof(mcache_allocations[plane_idx].global_mcache_ids_mall_plane0)); + memcpy(mcache_allocations[plane_idx].global_mcache_ids_mall_plane1, mcache_allocations[plane_idx].global_mcache_ids_plane1, + sizeof(mcache_allocations[plane_idx].global_mcache_ids_mall_plane1)); + } + } +} + +static bool dc_calculate_first_second_splitting(const int *mcache_boundaries, int num_boundaries, int shift, + int pipe_h_vp_start, int pipe_h_vp_end, int *first_offset, int *second_offset) +{ + const int MAX_VP = 0xFFFFFF; + int left_cache_id; + int right_cache_id; + int range_start; + int range_end; + bool success = false; + + if (num_boundaries <= 1) { + if (first_offset && second_offset) { + *first_offset = 0; + *second_offset = -1; + } + success = true; + return success; + } else { + range_start = 0; + for (left_cache_id = 0; left_cache_id < num_boundaries; left_cache_id++) { + range_end = mcache_boundaries[left_cache_id] - shift - 1; + + if (range_start <= pipe_h_vp_start && pipe_h_vp_start <= range_end) + break; + + range_start = range_end + 1; + } + + range_end = MAX_VP; + for (right_cache_id = num_boundaries - 1; right_cache_id >= -1; right_cache_id--) { + if (right_cache_id >= 0) + range_start = mcache_boundaries[right_cache_id] - shift; + else + range_start = 0; + + if (range_start <= pipe_h_vp_end && pipe_h_vp_end <= range_end) { + break; + } + range_end = range_start - 1; + } + right_cache_id = (right_cache_id + 1) % num_boundaries; + + if (right_cache_id == left_cache_id) { + if (first_offset && second_offset) { + *first_offset = left_cache_id; + *second_offset = -1; + } + success = true; + } else if (right_cache_id == (left_cache_id + 1) % num_boundaries) { + if (first_offset && second_offset) { + *first_offset = left_cache_id; + *second_offset = right_cache_id; + } + success = true; + } + } + + return success; +} + +static bool build_mcache_pipe_regs_config(struct dc_state *context, const struct dc_mcache_params *mcache_params, struct dc_mcache_allocations *mcache_allocations) +{ + bool success = true; + int first_offset, second_offset; + unsigned int plane_idx; + int pipe_idx; + + for (pipe_idx = 0; pipe_idx < MAX_PIPES; pipe_idx++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx]; + if (get_plane_index(context, pipe_ctx->plane_state, &plane_idx) && pipe_ctx->plane_res.hubp) { + reset_mcache_allocations(&pipe_ctx->mcache_regs); + if (pipe_ctx->plane_state->dcc.enable) { + // P0 always enabled + if (!dc_calculate_first_second_splitting(mcache_params[plane_idx].mcache_x_offsets_plane0, + mcache_params[plane_idx].num_mcaches_plane0, + 0, + pipe_ctx->plane_res.scl_data.viewport.x, + pipe_ctx->plane_res.scl_data.viewport.x + + pipe_ctx->plane_res.scl_data.viewport.width - 1, + &first_offset, &second_offset)) { + success = false; + break; + } + + pipe_ctx->mcache_regs.main.p0.mcache_id_first = + mcache_allocations[plane_idx].global_mcache_ids_plane0[first_offset]; + + pipe_ctx->mcache_regs.mall.p0.mcache_id_first = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[first_offset]; + + if (second_offset >= 0) { + pipe_ctx->mcache_regs.main.p0.mcache_id_second = + mcache_allocations[plane_idx].global_mcache_ids_plane0[second_offset]; + pipe_ctx->mcache_regs.main.p0.split_location = + mcache_params[plane_idx].mcache_x_offsets_plane0[first_offset] - 1; + + pipe_ctx->mcache_regs.mall.p0.mcache_id_second = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane0[second_offset]; + pipe_ctx->mcache_regs.mall.p0.split_location = + mcache_params[plane_idx].mcache_x_offsets_plane0[first_offset] - 1; + } + + // Populate P1 if enabled + if (is_plane1_enabled(pipe_ctx->plane_state->format)) { + if (!dc_calculate_first_second_splitting(mcache_params[plane_idx].mcache_x_offsets_plane1, + mcache_params[plane_idx].num_mcaches_plane1, + 0, + pipe_ctx->plane_res.scl_data.viewport_c.x, + pipe_ctx->plane_res.scl_data.viewport_c.x + + pipe_ctx->plane_res.scl_data.viewport_c.width - 1, + &first_offset, &second_offset)) { + success = false; + break; + } + + pipe_ctx->mcache_regs.main.p1.mcache_id_first = + mcache_allocations[plane_idx].global_mcache_ids_plane1[first_offset]; + + pipe_ctx->mcache_regs.mall.p1.mcache_id_first = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[first_offset]; + + if (second_offset >= 0) { + pipe_ctx->mcache_regs.main.p1.mcache_id_second = + mcache_allocations[plane_idx].global_mcache_ids_plane1[second_offset]; + pipe_ctx->mcache_regs.main.p1.split_location = + mcache_params[plane_idx].mcache_x_offsets_plane1[first_offset] - 1; + + pipe_ctx->mcache_regs.mall.p1.mcache_id_second = + mcache_allocations[plane_idx].global_mcache_ids_mall_plane1[second_offset]; + pipe_ctx->mcache_regs.mall.p1.split_location = + mcache_params[plane_idx].mcache_x_offsets_plane1[first_offset] - 1; + } + } + } + } + } + return success; +} + +bool dcn60_program_mcache_pipe_config(struct dc_state *context, const struct dc_mcache_params *mcache_params) +{ + struct dc_mcache_allocations mcache_allocations[MAX_PLANES] = {0}; + if (!mcache_params) { + return false; + } + + // Step 1: Assign global mcache IDs for each plane + assign_global_mcache_ids(context, mcache_params, mcache_allocations); + + // Step 2: Build the mcache pipe configuration + if (!build_mcache_pipe_regs_config(context, mcache_params, mcache_allocations)) { + return false; + } + + return true; +} + +bool dcn50_program_mcache_pipe_config(struct dc_state *context, const struct dc_mcache_params *mcache_params) +{ + int pipe_idx; + + if (!mcache_params) { + return false; + } + + for (pipe_idx = 0; pipe_idx < MAX_PIPES; pipe_idx++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx]; + reset_mcache_allocations(&pipe_ctx->mcache_regs); + if (pipe_ctx->plane_state && pipe_ctx->plane_state->dcc.enable) { + pipe_ctx->mcache_regs.main.p0.mcache_id_first = pipe_ctx->pipe_idx; + pipe_ctx->mcache_regs.mall.p0.mcache_id_first = pipe_ctx->pipe_idx; + // Populate P1 if enabled + if (is_plane1_enabled(pipe_ctx->plane_state->format)) { + pipe_ctx->mcache_regs.main.p1.mcache_id_first = pipe_ctx->pipe_idx; + pipe_ctx->mcache_regs.mall.p1.mcache_id_first = pipe_ctx->pipe_idx; + } + } + } + return true; +} + +static struct dce_aux *dcn60_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT aux_engine_regs + aux_engine_regs_init(0), + aux_engine_regs_init(1), + aux_engine_regs_init(2), + aux_engine_regs_init(3); + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs_init(id)\ + I2C_HW_ENGINE_COMMON_REG_LIST_DCN30_RI(id) + +static struct dce_i2c_registers i2c_hw_regs[2]; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCN401(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCN401(_MASK) +}; + +static struct dce_i2c_hw *dcn60_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT i2c_hw_regs + i2c_inst_regs_init(1), + i2c_inst_regs_init(2); + + dcn2_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} + +static struct clock_source *dcn60_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dcn50_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static struct hubbub *dcn60_hubbub_create(struct dc_context *ctx) +{ + int i; + + struct dcn20_hubbub *hubbub2 = kzalloc(sizeof(struct dcn20_hubbub), + GFP_KERNEL); + + if (!hubbub2) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT hubbub_reg + hubbub_reg_init(); + +#undef REG_STRUCT +#define REG_STRUCT vmid_regs + vmid_regs_init(0), + vmid_regs_init(1), + vmid_regs_init(2), + vmid_regs_init(3), + vmid_regs_init(4), + vmid_regs_init(5), + vmid_regs_init(6), + vmid_regs_init(7), + vmid_regs_init(8), + vmid_regs_init(9), + vmid_regs_init(10), + vmid_regs_init(11), + vmid_regs_init(12), + vmid_regs_init(13), + vmid_regs_init(14), + vmid_regs_init(15); + + hubbub60_construct(hubbub2, ctx, + &hubbub_reg, + &hubbub_shift, + &hubbub_mask, + DCN6_0_DEFAULT_DET_SIZE, //nominal (default) detile buffer size in kbytes, + 8, //dml2 ip_params_st.pixel_chunk_size_kbytes + DCN6_0_CRB_SIZE_KB); //dml2 ip_params_st.config_return_buffer_size_in_kbytes + + for (i = 0; i < res_cap_dcn6_0.num_vmid; i++) { + struct dcn20_vmid *vmid = &hubbub2->vmid[i]; + + vmid->ctx = ctx; + + vmid->regs = &vmid_regs[i]; + vmid->shifts = &vmid_shifts; + vmid->masks = &vmid_masks; + } + + return &hubbub2->base; +} + +static struct hubp *dcn60_hubp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dcn20_hubp *hubp2 = + kzalloc(sizeof(struct dcn20_hubp), GFP_KERNEL); + + if (!hubp2) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT hubp_regs + hubp_regs_init(0), + hubp_regs_init(1), + hubp_regs_init(2), + hubp_regs_init(3); + + if (hubp60_construct(hubp2, ctx, inst, + &hubp_regs[inst], &hubp_shift, &hubp_mask)) + return &hubp2->base; + + BREAK_TO_DEBUGGER(); + kfree(hubp2); + return NULL; +} + +static void dcn60_dpp_destroy(struct dpp **dpp) +{ + kfree(TO_DCN60_DPP(*dpp)); + *dpp = NULL; +} + +static struct dpp *dcn60_dpp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dcn60_dpp *dpp60 = + kzalloc(sizeof(struct dcn60_dpp), GFP_KERNEL); + + if (!dpp60) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dpp_regs + dpp_regs_init(0), + dpp_regs_init(1), + dpp_regs_init(2), + dpp_regs_init(3); + + if (dpp60_construct(dpp60, ctx, inst, + &dpp_regs[inst], &tf_shift, &tf_mask)) + return &dpp60->base; + + BREAK_TO_DEBUGGER(); + kfree(dpp60); + return NULL; +} + +static struct mpc *dcn60_mpc_create( + struct dc_context *ctx, + int num_mpcc, + int num_rmu) +{ + struct dcn60_mpc *mpc60 = kzalloc(sizeof(struct dcn60_mpc), + GFP_KERNEL); + + if (!mpc60) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT mpc_regs + dcn_mpc_regs_init(); + + dcn60_mpc_construct(mpc60, ctx, + &mpc_regs, + &mpc_shift, + &mpc_mask, + num_mpcc, + num_rmu); + + return &mpc60->base; +} + +static struct output_pixel_processor *dcn60_opp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dcn20_opp *opp2 = + kzalloc(sizeof(struct dcn20_opp), GFP_KERNEL); + + if (!opp2) { + BREAK_TO_DEBUGGER(); + return NULL; + } + +#undef REG_STRUCT +#define REG_STRUCT opp_regs + opp_regs_init(0), + opp_regs_init(1), + opp_regs_init(2), + opp_regs_init(3); + + dcn60_opp_construct(opp2, ctx, inst, + &opp_regs[inst], &opp_shift, &opp_mask); + return &opp2->base; +} + +static struct timing_generator *dcn60_timing_generator_create( + struct dc_context *ctx, + uint32_t instance) +{ + struct optc *tgn10 = + kzalloc(sizeof(struct optc), GFP_KERNEL); + + if (!tgn10) + return NULL; +#undef REG_STRUCT +#define REG_STRUCT optc_regs + optc_regs_init(0), + optc_regs_init(1), + optc_regs_init(2), + optc_regs_init(3); + + tgn10->base.inst = instance; + tgn10->base.ctx = ctx; + + tgn10->tg_regs = &optc_regs[instance]; + tgn10->tg_shift = &optc_shift; + tgn10->tg_mask = &optc_mask; + + dcn60_timing_generator_init(tgn10); + + return &tgn10->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .hdmi_ycbcr420_supported = true, + .dp_ycbcr420_supported = true, + .fec_supported = true, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true +}; + +static struct link_encoder *dcn60_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dcn20_link_encoder *enc20 = + kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); + + if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) { + kfree(enc20); + return NULL; + } + +#undef REG_STRUCT +#define REG_STRUCT link_enc_aux_regs + aux_regs_init(0, 1), + aux_regs_init(1, 2), + aux_regs_init(2, 3), + aux_regs_init(3, 4); + +#undef REG_STRUCT +#define REG_STRUCT link_enc_hpd_regs + hpd_regs_init(0), + hpd_regs_init(1), + hpd_regs_init(2), + hpd_regs_init(3); +#undef REG_STRUCT +#define REG_STRUCT link_enc_regs + link_regs_init(0, A), + link_regs_init(1, B), + link_regs_init(2, C), + link_regs_init(3, D); + + dcn60_link_encoder_construct(enc20, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source], + &le_shift, + &le_mask); + return &enc20->enc10.base; +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + generic_reg_get(ctx, regDC_PINSTRAPS + BASE(regDC_PINSTRAPS_BASE_IDX), + FN(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO), &straps->dc_pinstraps_audio); + +} + +static struct audio *dcn60_create_audio( + struct dc_context *ctx, unsigned int inst) +{ + +#undef REG_STRUCT +#define REG_STRUCT audio_regs + audio_regs_init(0), + audio_regs_init(1), + audio_regs_init(2), + audio_regs_init(3), + audio_regs_init(4); + + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct vpg *dcn60_vpg_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dcn31_vpg *vpg6 = kzalloc(sizeof(struct dcn31_vpg), GFP_KERNEL); + + if (!vpg6) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT vpg_regs + vpg_regs_init(0), + vpg_regs_init(1), + vpg_regs_init(2), + vpg_regs_init(3), + vpg_regs_init(4), + vpg_regs_init(5), + vpg_regs_init(6), + vpg_regs_init(7), + vpg_regs_init(8); + + vpg31_construct(vpg6, ctx, inst, + &vpg_regs[inst], + &vpg_shift, + &vpg_mask); + + return &vpg6->base; +} + +static struct apg *dcn60_apg_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dcn31_apg *apg60 = kzalloc(sizeof(struct dcn31_apg), GFP_KERNEL); + + if (!apg60) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT apg_regs + apg_regs_init(0), + apg_regs_init(1), + apg_regs_init(2), + apg_regs_init(3); + + apg31_construct(apg60, ctx, inst, + &apg_regs[inst], + &apg_shift, + &apg_mask); + + return &apg60->base; +} + +static struct stream_encoder *dcn60_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn10_stream_encoder *enc1; + struct vpg *vpg; + struct apg *apg; + int vpg_inst; + uint32_t apg_inst; + + /* Mapping of VPG, APG, DME register blocks to DIO block instance */ + if (eng_id <= ENGINE_ID_DIGF) { + vpg_inst = eng_id; + apg_inst = eng_id; + } else + return NULL; + + enc1 = kzalloc(sizeof(struct dcn10_stream_encoder), GFP_KERNEL); + vpg = dcn60_vpg_create(ctx, vpg_inst); + apg = dcn60_apg_create(ctx, apg_inst); + + if (!enc1 || !vpg || !apg || eng_id >= ARRAY_SIZE(stream_enc_regs)) { + kfree(enc1); + kfree(vpg); + kfree(apg); + return NULL; + } +#undef REG_STRUCT +#define REG_STRUCT stream_enc_regs + stream_enc_regs_init(0), + stream_enc_regs_init(1), + stream_enc_regs_init(2), + stream_enc_regs_init(3); + + dcn60_dio_stream_encoder_construct(enc1, ctx, ctx->dc_bios, + eng_id, vpg, apg, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc1->base; +} + +static struct hpo_frl_stream_encoder *dcn60_hpo_frl_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn401_hpo_frl_stream_encoder *hpo_enc60; + struct vpg *vpg; + struct apg *apg; + int vpg_inst; + int 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(4); + + /* Mapping of VPG, APG, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 4; + apg_inst = 4; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG, APG sub-blocks */ + hpo_enc60 = kzalloc(sizeof(struct dcn401_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn60_vpg_create(ctx, vpg_inst); + apg = dcn60_apg_create(ctx, apg_inst); + + if (!hpo_enc60 || !vpg || !apg) { + kfree(hpo_enc60); + kfree(vpg); + kfree(apg); + return NULL; + } + + dcn60_hpo_frl_stream_encoder_construct(hpo_enc60, 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_enc60->base; +} + +static struct hpo_frl_link_encoder *dcn60_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_encoder60_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 *dcn60_hpo_dp_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn31_hpo_dp_stream_encoder *hpo_dp_enc60; + struct vpg *vpg; + struct apg *apg; + uint32_t hpo_dp_inst; + uint32_t vpg_inst; + uint32_t apg_inst; + + ASSERT((eng_id >= ENGINE_ID_HPO_DP_0) && (eng_id <= ENGINE_ID_HPO_DP_3)); + hpo_dp_inst = eng_id - ENGINE_ID_HPO_DP_0; + + /* Mapping of VPG register blocks to HPO DP block instance: + * VPG[6] -> HPO_DP[0] + * VPG[7] -> HPO_DP[1] + * VPG[8] -> HPO_DP[2] + * VPG[9] -> HPO_DP[3] + */ + vpg_inst = hpo_dp_inst + 5; + + /* Mapping of APG register blocks to HPO DP block instance: + * APG[0] -> HPO_DP[0] + * APG[1] -> HPO_DP[1] + * APG[2] -> HPO_DP[2] + * APG[3] -> HPO_DP[3] + */ + apg_inst = hpo_dp_inst; + + /* allocate HPO stream encoder and create VPG sub-block */ + hpo_dp_enc60 = kzalloc(sizeof(struct dcn31_hpo_dp_stream_encoder), GFP_KERNEL); + vpg = dcn60_vpg_create(ctx, vpg_inst); + apg = dcn60_apg_create(ctx, apg_inst); + + if (!hpo_dp_enc60 || !vpg || !apg) { + kfree(hpo_dp_enc60); + kfree(vpg); + kfree(apg); + return NULL; + } + +#undef REG_STRUCT +#define REG_STRUCT hpo_dp_stream_enc_regs + hpo_dp_stream_encoder_reg_init(0), + hpo_dp_stream_encoder_reg_init(1), + hpo_dp_stream_encoder_reg_init(2), + hpo_dp_stream_encoder_reg_init(3); + + dcn31_hpo_dp_stream_encoder_construct(hpo_dp_enc60, ctx, ctx->dc_bios, + hpo_dp_inst, eng_id, vpg, apg, + &hpo_dp_stream_enc_regs[hpo_dp_inst], + &hpo_dp_se_shift, &hpo_dp_se_mask); + + return &hpo_dp_enc60->base; +} + +static struct hpo_dp_link_encoder *dcn60_hpo_dp_link_encoder_create( + uint8_t inst, + struct dc_context *ctx) +{ + struct dcn31_hpo_dp_link_encoder *hpo_dp_enc60; + + /* allocate HPO link encoder */ + hpo_dp_enc60 = kzalloc(sizeof(struct dcn31_hpo_dp_link_encoder), GFP_KERNEL); + if (!hpo_dp_enc60) + return NULL; /* out of memory */ + +#undef REG_STRUCT +#define REG_STRUCT hpo_dp_link_enc_regs + hpo_dp_link_encoder_reg_init(0), + hpo_dp_link_encoder_reg_init(1), + hpo_dp_link_encoder_reg_init(2), + hpo_dp_link_encoder_reg_init(3); + + hpo_dp_link_encoder32_construct(hpo_dp_enc60, ctx, inst, + &hpo_dp_link_enc_regs[inst], + &hpo_dp_le_shift, &hpo_dp_le_mask); + + return &hpo_dp_enc60->base; +} + +static struct dce_hwseq *dcn60_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + +#undef REG_STRUCT +#define REG_STRUCT hwseq_reg + hwseq_reg_init(); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + + return hws; +} +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = dcn60_create_audio, + .create_stream_encoder = dcn60_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn60_hpo_frl_stream_encoder_create, + .create_hpo_dp_stream_encoder = dcn60_hpo_dp_stream_encoder_create, + .create_hpo_dp_link_encoder = dcn60_hpo_dp_link_encoder_create, + .create_hwseq = dcn60_hwseq_create, +}; + +static void dcn60_dsc_destroy(struct display_stream_compressor **dsc) +{ + kfree(container_of(*dsc, struct dcn60_dsc, base)); + *dsc = NULL; +} + +static void dcn60_resource_destruct(struct dcn60_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) { + if (pool->base.stream_enc[i]->vpg != NULL) { + kfree(DCN31_VPG_FROM_VPG(pool->base.stream_enc[i]->vpg)); + pool->base.stream_enc[i]->vpg = NULL; + } + if (pool->base.stream_enc[i]->apg != NULL) { + kfree(DCN31_APG_FROM_APG(pool->base.stream_enc[i]->apg)); + pool->base.stream_enc[i]->apg = NULL; + } + kfree(DCN10STRENC_FROM_STRENC(pool->base.stream_enc[i])); + pool->base.stream_enc[i] = NULL; + } + } + + 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) { + kfree(DCN31_VPG_FROM_VPG(pool->base.hpo_dp_stream_enc[i]->vpg)); + pool->base.hpo_dp_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_dp_stream_enc[i]->apg != NULL) { + kfree(DCN31_APG_FROM_APG(pool->base.hpo_dp_stream_enc[i]->apg)); + pool->base.hpo_dp_stream_enc[i]->apg = NULL; + } + kfree(DCN3_1_HPO_DP_STREAM_ENC_FROM_HPO_STREAM_ENC(pool->base.hpo_dp_stream_enc[i])); + pool->base.hpo_dp_stream_enc[i] = NULL; + } + } + + for (i = 0; i < pool->base.hpo_dp_link_enc_count; i++) { + if (pool->base.hpo_dp_link_enc[i] != NULL) { + kfree(DCN3_1_HPO_DP_LINK_ENC_FROM_HPO_LINK_ENC(pool->base.hpo_dp_link_enc[i])); + pool->base.hpo_dp_link_enc[i] = NULL; + } + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_dsc; i++) { + if (pool->base.dscs[i] != NULL) + dcn60_dsc_destroy(&pool->base.dscs[i]); + } + + if (pool->base.mpc != NULL) { + kfree(TO_DCN20_MPC(pool->base.mpc)); + pool->base.mpc = NULL; + } + if (pool->base.hubbub != NULL) { + kfree(TO_DCN20_HUBBUB(pool->base.hubbub)); + pool->base.hubbub = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.dpps[i] != NULL) + dcn60_dpp_destroy(&pool->base.dpps[i]); + + if (pool->base.ipps[i] != NULL) + pool->base.ipps[i]->funcs->ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.hubps[i] != NULL) { + kfree(TO_DCN20_HUBP(pool->base.hubps[i])); + pool->base.hubps[i] = NULL; + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_aux; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_ddc; i++) { + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_opp; i++) { + if (pool->base.opps[i] != NULL) + pool->base.opps[i]->funcs->opp_destroy(&pool->base.opps[i]); + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_timing_generator; i++) { + if (pool->base.timing_generators[i] != NULL) { + kfree(DCN10TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i]) + dce_aud_destroy(&pool->base.audios[i]); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dcn20_clock_source_destroy(&pool->base.clock_sources[i]); + pool->base.clock_sources[i] = NULL; + } + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_mpc_3dlut; i++) { + if (pool->base.mpc_lut[i] != NULL) { + dc_3dlut_func_release(pool->base.mpc_lut[i]); + pool->base.mpc_lut[i] = NULL; + } + if (pool->base.mpc_shaper[i] != NULL) { + dc_transfer_func_release(pool->base.mpc_shaper[i]); + pool->base.mpc_shaper[i] = NULL; + } + } + + if (pool->base.dp_clock_source != NULL) { + dcn20_clock_source_destroy(&pool->base.dp_clock_source); + pool->base.dp_clock_source = NULL; + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_timing_generator; i++) { + if (pool->base.multiple_abms[i] != NULL) + dce_abm_destroy(&pool->base.multiple_abms[i]); + } + + if (pool->base.psr != NULL) + dmub_psr_destroy(&pool->base.psr); + + if (pool->base.dccg != NULL) + dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.oem_device != NULL) { + struct dc *dc = pool->base.oem_device->ctx->dc; + + dc->link_srv->destroy_ddc_service(&pool->base.oem_device); + } +} + +static struct display_stream_compressor *dcn60_dsc_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dcn60_dsc *dsc = + kzalloc(sizeof(struct dcn60_dsc), GFP_KERNEL); + + if (!dsc) { + BREAK_TO_DEBUGGER(); + return NULL; + } + +#undef REG_STRUCT +#define REG_STRUCT dsc_regs + dsc_regs_init(0), + dsc_regs_init(1), + dsc_regs_init(2), + dsc_regs_init(3); + + dsc60_construct(dsc, ctx, inst, &dsc_regs[inst], &dsc_shift, &dsc_mask); + + //dsc->max_image_width = 6016; + dsc->max_image_width = 5760; + + return &dsc->base; +} + +static void dcn60_destroy_resource_pool(struct resource_pool **pool) +{ + struct dcn60_resource_pool *dcn60_pool = TO_DCN60_RES_POOL(*pool); + + dcn60_resource_destruct(dcn60_pool); + kfree(dcn60_pool); + *pool = NULL; +} + +static struct dc_cap_funcs cap_funcs = { + .get_dcc_compression_cap = dcn20_get_dcc_compression_cap, +}; + +static void dcn60_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) +{ + (void)bw_params; + dc_assert_fp_enabled(); + + if (dc->debug.using_dml2 && dc->current_state && dc->current_state->bw_ctx.dml2) + dml2_reinit(dc, &dc->dml2_options, &dc->current_state->bw_ctx.dml2); + + if (dc->debug.using_dml2 && dc->current_state && dc->current_state->bw_ctx.dml2_dc_power_source) + dml2_reinit(dc, &dc->dml2_dc_power_options, &dc->current_state->bw_ctx.dml2_dc_power_source); +} + +static void dcn60_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn60_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} + +static void dcn60_build_pipe_pix_clk_params(struct pipe_ctx *pipe_ctx) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct link_encoder *link_enc = NULL; + struct pixel_clk_params *pixel_clk_params = &pipe_ctx->stream_res.pix_clk_params; + + pixel_clk_params->requested_pix_clk_100hz = stream->timing.pix_clk_100hz; + + link_enc = link_enc_cfg_get_link_enc(link); + if (link_enc) + pixel_clk_params->encoder_object_id = link_enc->id; + + pixel_clk_params->signal_type = pipe_ctx->stream->signal; + pixel_clk_params->controller_id = pipe_ctx->stream_res.tg->inst + 1; + /* TODO: un-hardcode*/ + + /* TODO - DP2.0 HW: calculate requested_sym_clk for UHBR rates */ + + pixel_clk_params->requested_sym_clk = LINK_RATE_LOW * + LINK_RATE_REF_FREQ_IN_KHZ; + pixel_clk_params->flags.ENABLE_SS = 0; + pixel_clk_params->color_depth = + stream->timing.display_color_depth; + pixel_clk_params->flags.DISPLAY_BLANKED = 1; + pixel_clk_params->pixel_encoding = stream->timing.pixel_encoding; + + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) + pixel_clk_params->color_depth = COLOR_DEPTH_888; + + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + pixel_clk_params->requested_pix_clk_100hz *= 2; + if (dc_is_tmds_signal(stream->signal) && + stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + pixel_clk_params->requested_pix_clk_100hz /= 2; + + pipe_ctx->clock_source->funcs->get_pix_clk_dividers( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + &pipe_ctx->pll_settings); + + pixel_clk_params->dio_se_pix_per_cycle = 1; + if (dc_is_tmds_signal(stream->signal) && + stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) { + pixel_clk_params->dio_se_pix_per_cycle = 2; + } else if (dc_is_dp_signal(stream->signal)) { + /* round up to nearest power of 2, or max at 8 pixels per cycle */ + if (pixel_clk_params->requested_pix_clk_100hz > (uint32_t)(4 * stream->ctx->dc->clk_mgr->dprefclk_khz * 10)) { + pixel_clk_params->dio_se_pix_per_cycle = 8; + } else if (pixel_clk_params->requested_pix_clk_100hz > (uint32_t)(2 * stream->ctx->dc->clk_mgr->dprefclk_khz * 10)) { + pixel_clk_params->dio_se_pix_per_cycle = 4; + } else if (pixel_clk_params->requested_pix_clk_100hz > (uint32_t)(stream->ctx->dc->clk_mgr->dprefclk_khz * 10)) { + pixel_clk_params->dio_se_pix_per_cycle = 2; + } else { + pixel_clk_params->dio_se_pix_per_cycle = 1; + } + } +} + +static int dcn60_get_power_profile(const struct dc_state *context) +{ + unsigned int uclk_mhz = context->bw_ctx.bw.dcn.clk.dramclk_khz / 1000; + int dpm_level = 0; + + for (unsigned int i = 0; i < context->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels; i++) { + if (context->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz == 0 || + uclk_mhz < context->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz) + break; + if (uclk_mhz > context->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz) + dpm_level++; + } + + return dpm_level; +} + +static struct resource_funcs dcn60_res_pool_funcs = { + .destroy = dcn60_destroy_resource_pool, + .link_enc_create = dcn60_link_encoder_create, + .link_enc_create_minimal = NULL, + .hpo_frl_link_enc_create = dcn60_hpo_frl_link_encoder_create, + .panel_cntl_create = dcn32_panel_cntl_create, + .validate_bandwidth = dcn401_validate_bandwidth, + .calculate_wm_and_dlg = NULL, + .populate_dml_pipes = NULL, + .acquire_free_pipe_as_secondary_dpp_pipe = dcn32_acquire_free_pipe_as_secondary_dpp_pipe, + .acquire_free_pipe_as_secondary_opp_head = dcn32_acquire_free_pipe_as_secondary_opp_head, + .release_pipe = dcn20_release_pipe, + .add_stream_to_ctx = dcn30_add_stream_to_ctx, + .add_dsc_to_stream_resource = dcn20_add_dsc_to_stream_resource, + .remove_stream_from_ctx = dcn20_remove_stream_from_ctx, + .populate_dml_writeback_from_context = dcn30_populate_dml_writeback_from_context, + .set_mcif_arb_params = dcn30_set_mcif_arb_params, + .find_first_free_match_stream_enc_for_link = dcn10_find_first_free_match_stream_enc_for_link, + .acquire_post_bldn_3dlut = dcn32_acquire_post_bldn_3dlut, + .release_post_bldn_3dlut = dcn32_release_post_bldn_3dlut, + .update_bw_bounding_box = dcn60_update_bw_bounding_box, + .patch_unknown_plane_state = dcn401_patch_unknown_plane_state, + .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, + .add_phantom_pipes = dcn32_add_phantom_pipes, + .build_pipe_pix_clk_params = dcn60_build_pipe_pix_clk_params, + .get_power_profile = dcn60_get_power_profile, + .program_mcache_pipe_config = dcn60_program_mcache_pipe_config, + .get_default_tiling_info = dcn401_get_default_tiling_info +}; + +static uint32_t read_pipe_fuses(struct dc_context *ctx) +{ + (void)ctx; + uint32_t value = 0; // CC_DC_PIPE_DIS in DCN6 only indicates DCN IP full fuse, not per pipe fuse + return value; +} + +static bool dcn60_resource_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dcn60_resource_pool *pool) +{ + int i; + unsigned int j; + struct dc_context *ctx = dc->ctx; + struct irq_service_init_data init_data; + struct ddc_service_init_data ddc_init_data = {0}; + uint32_t pipe_fuses = 0; + uint32_t num_pipes = 4; + +#undef REG_STRUCT +#define REG_STRUCT bios_regs + bios_regs_init(); + +#undef REG_STRUCT +#define REG_STRUCT clk_src_regs + clk_src_regs_init(0, A), + clk_src_regs_init(1, B), + clk_src_regs_init(2, C), + clk_src_regs_init(3, D); + +#undef REG_STRUCT +#define REG_STRUCT abm_regs + abm_regs_init(0), + abm_regs_init(1), + abm_regs_init(2), + abm_regs_init(3); + +#undef REG_STRUCT +#define REG_STRUCT dccg_regs + dccg_regs_init(); + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_dcn6_0; + + /* max number of pipes for ASIC before checking for pipe fuses */ + num_pipes = pool->base.res_cap->num_timing_generator; + pipe_fuses = read_pipe_fuses(ctx); + + for (i = 0; i < pool->base.res_cap->num_timing_generator; i++) + if (pipe_fuses & 1 << i) + num_pipes--; + + if (pipe_fuses & 1) + ASSERT(0); //Unexpected - Pipe 0 should always be fully functional! + + if (pipe_fuses & CC_DC_PIPE_DIS__DC_FULL_DIS_MASK) + ASSERT(0); //Entire DCN is harvested! + + pool->base.funcs = &dcn60_res_pool_funcs; + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; + pool->base.timing_generator_count = num_pipes; + pool->base.pipe_count = num_pipes; + pool->base.mpcc_count = num_pipes; + dc->caps.max_downscale_ratio = 600; + dc->caps.i2c_speed_in_khz = 95; + dc->caps.i2c_speed_in_khz_hdcp = 95; /*1.4 w/a applied by default*/ + /* TODO: Bring max cursor size back to 256 after subvp cursor corruption is fixed*/ + dc->caps.max_cursor_size = 64; + dc->caps.cursor_not_scaled = true; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.dmdata_alloc_size = 2048; + dc->caps.cursor_cache_size = dc->caps.max_cursor_size * dc->caps.max_cursor_size * 8; + dc->caps.cache_line_size = 64; + dc->caps.cache_num_ways = 16; + + dc->caps.max_slave_planes = 2; + dc->caps.max_slave_yuv_planes = 2; + 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->caps.dp_hpo = true; + dc->caps.dp_hdmi21_pcon_support = true; + dc->caps.edp_dsc_support = true; + dc->caps.extended_aux_timeout_support = true; + dc->caps.dmcub_support = true; + dc->caps.max_v_total = (1 << 15) - 1; + + if (ASICREV_IS_GC_12_0_1_A0(dc->ctx->asic_id.hw_internal_rev)) + dc->caps.dcc_plane_width_limit = 7680; + + /* Color pipeline capabilities */ + dc->caps.color.dpp.dcn_arch = 1; + dc->caps.color.dpp.input_lut_shared = 0; + dc->caps.color.dpp.icsc = 1; + dc->caps.color.dpp.dgam_ram = 0; // must use gamma_corr + dc->caps.color.dpp.dgam_rom_caps.srgb = 1; + dc->caps.color.dpp.dgam_rom_caps.bt2020 = 1; + dc->caps.color.dpp.dgam_rom_caps.gamma2_2 = 1; + dc->caps.color.dpp.dgam_rom_caps.pq = 1; + dc->caps.color.dpp.dgam_rom_caps.hlg = 1; + dc->caps.color.dpp.post_csc = 1; + dc->caps.color.dpp.gamma_corr = 1; + dc->caps.color.dpp.dgam_rom_for_yuv = 0; + dc->caps.color.dpp.upsp_pre_scaler = 1; + + dc->caps.color.dpp.hw_3d_lut = 0; + dc->caps.color.dpp.ogam_ram = 0; + // no OGAM ROM on DCN2 and later ASICs + dc->caps.color.dpp.ogam_rom_caps.srgb = 0; + dc->caps.color.dpp.ogam_rom_caps.bt2020 = 0; + dc->caps.color.dpp.ogam_rom_caps.gamma2_2 = 0; + dc->caps.color.dpp.ogam_rom_caps.pq = 0; + dc->caps.color.dpp.ogam_rom_caps.hlg = 0; + dc->caps.color.dpp.ocsc = 0; + + dc->caps.color.mpc.gamut_remap = 1; + dc->caps.color.mpc.num_3dluts = (uint16_t)pool->base.res_cap->num_mpc_3dlut; //4, configurable to be before or after BLND in MPCC + dc->caps.color.mpc.ogam_ram = 1; + dc->caps.color.mpc.ogam_rom_caps.srgb = 0; + dc->caps.color.mpc.ogam_rom_caps.bt2020 = 0; + dc->caps.color.mpc.ogam_rom_caps.gamma2_2 = 0; + dc->caps.color.mpc.ogam_rom_caps.pq = 0; + 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; + + dc->config.dcn_sharpness_range.sdr_rgb_min = 0; + dc->config.dcn_sharpness_range.sdr_rgb_max = 1750; + dc->config.dcn_sharpness_range.sdr_rgb_mid = 750; + dc->config.dcn_sharpness_range.sdr_yuv_min = 0; + dc->config.dcn_sharpness_range.sdr_yuv_max = 3500; + dc->config.dcn_sharpness_range.sdr_yuv_mid = 1500; + dc->config.dcn_sharpness_range.hdr_rgb_min = 0; + dc->config.dcn_sharpness_range.hdr_rgb_max = 2750; + dc->config.dcn_sharpness_range.hdr_rgb_mid = 1500; + + dc->config.dcn_override_sharpness_range.sdr_rgb_min = 0; + dc->config.dcn_override_sharpness_range.sdr_rgb_max = 3250; + dc->config.dcn_override_sharpness_range.sdr_rgb_mid = 1250; + dc->config.dcn_override_sharpness_range.sdr_yuv_min = 0; + dc->config.dcn_override_sharpness_range.sdr_yuv_max = 3500; + dc->config.dcn_override_sharpness_range.sdr_yuv_mid = 1500; + dc->config.dcn_override_sharpness_range.hdr_rgb_min = 0; + dc->config.dcn_override_sharpness_range.hdr_rgb_max = 2750; + dc->config.dcn_override_sharpness_range.hdr_rgb_mid = 1500; + + dc->config.enable_cursor_offload = true; + dc->config.dc_mode_clk_limit_support = true; + dc->config.enable_windowed_mpo_odm = true; + dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */ + dc->config.dp_connector_no_native_i2c = true; + /* read VBIOS LTTPR caps */ + { + if (ctx->dc_bios->funcs->get_lttpr_caps) { + enum bp_result bp_query_result; + uint8_t is_vbios_lttpr_enable = 0; + + bp_query_result = ctx->dc_bios->funcs->get_lttpr_caps(ctx->dc_bios, &is_vbios_lttpr_enable); + dc->caps.vbios_lttpr_enable = (bp_query_result == BP_RESULT_OK) && !!is_vbios_lttpr_enable; + } + + /* interop bit is implicit */ + { + dc->caps.vbios_lttpr_aware = true; + } + } + dc->check_config = config_defaults; + + if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV) + dc->debug = debug_defaults_drv; + + // Init the vm_helper + if (dc->vm_helper) + vm_helper_init(dc->vm_helper, 16); + + /************************************************* + * Create resources * + *************************************************/ + + /* Clock Sources for Pixel Clock*/ + pool->base.clock_sources[DCN60_CLK_SRC_PLL0] = + dcn60_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCN60_CLK_SRC_PLL1] = + dcn60_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCN60_CLK_SRC_PLL2] = + dcn60_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCN60_CLK_SRC_PLL3] = + dcn60_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + // pool->base.clock_sources[DCN401_CLK_SRC_PLL4] = + // dcn60_clock_source_create(ctx, ctx->dc_bios, + // CLOCK_SOURCE_COMBO_PHY_PLL4, + // &clk_src_regs[4], false); + + pool->base.clk_src_count = DCN60_CLK_SRC_TOTAL; + + /* todo: not reuse phy_pll registers */ + pool->base.dp_clock_source = + dcn60_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, + &clk_src_regs[0], true); + + for (i = 0; i < (int)pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto create_fail; + } + } + + /* DCCG */ + pool->base.dccg = dccg60_create(ctx, &dccg_regs, &dccg_shift, &dccg_mask); + if (pool->base.dccg == NULL) { + dm_error("DC: failed to create dccg!\n"); + BREAK_TO_DEBUGGER(); + goto create_fail; + } + + /* IRQ Service */ + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dcn60_create(&init_data); + if (!pool->base.irqs) + goto create_fail; + + /* HUBBUB */ + pool->base.hubbub = dcn60_hubbub_create(ctx); + if (pool->base.hubbub == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create hubbub!\n"); + goto create_fail; + } + + /* HUBPs, DPPs, OPPs, TGs, ABMs */ + for (i = 0, j = 0; i < (int)pool->base.res_cap->num_timing_generator; i++) { + + /* if pipe is disabled, skip instance of HW pipe, + * i.e, skip ASIC register instance + */ + if (pipe_fuses & 1 << i) + continue; + pool->base.hubps[j] = dcn60_hubp_create(ctx, i); + if (pool->base.hubps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create hubps!\n"); + goto create_fail; + } + + pool->base.dpps[j] = dcn60_dpp_create(ctx, i); + if (pool->base.dpps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create dpps!\n"); + goto create_fail; + } + + pool->base.opps[j] = dcn60_opp_create(ctx, i); + if (pool->base.opps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto create_fail; + } + + pool->base.timing_generators[j] = dcn60_timing_generator_create( + ctx, i); + if (pool->base.timing_generators[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto create_fail; + } + + pool->base.multiple_abms[j] = dmub_abm_create(ctx, + &abm_regs[i], + &abm_shift, + &abm_mask); + if (pool->base.multiple_abms[j] == NULL) { + dm_error("DC: failed to create abm for pipe %d!\n", i); + BREAK_TO_DEBUGGER(); + goto create_fail; + } + + /* index for resource pool arrays for next valid pipe */ + j++; + } + + /* PSR */ + pool->base.psr = dmub_psr_create(ctx); + if (pool->base.psr == NULL) { + dm_error("DC: failed to create psr obj!\n"); + BREAK_TO_DEBUGGER(); + goto create_fail; + } + + /* MPCCs */ + pool->base.mpc = dcn60_mpc_create(ctx, pool->base.res_cap->num_timing_generator, + pool->base.res_cap->num_mpc_3dlut); + if (pool->base.mpc == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create mpc!\n"); + goto create_fail; + } + + /* DSCs */ + for (i = 0; i < pool->base.res_cap->num_dsc; i++) { + pool->base.dscs[i] = dcn60_dsc_create(ctx, i); + if (pool->base.dscs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create display stream compressor %d!\n", i); + goto create_fail; + } + } + + /* AUX */ + for (i = 0; i < pool->base.res_cap->num_aux; i++) { + pool->base.engines[i] = dcn60_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto create_fail; + } + } + + /* I2C */ + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.hw_i2cs[i] = dcn60_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create hw i2c!!\n"); + goto create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + + /* Audio, HWSeq, Stream Encoders including HPO and virtual, MPC 3D LUTs */ + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto create_fail; + + /* HW Sequencer init functions and Plane caps */ + dcn60_hw_sequencer_init_functions(dc); + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < (int)dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.max_odm_combine_factor = 4; + + dc->cap_funcs = cap_funcs; + + if (dc->ctx->dc_bios->fw_info.oem_i2c_present) { + ddc_init_data.ctx = dc->ctx; + ddc_init_data.link = NULL; + ddc_init_data.id.id = dc->ctx->dc_bios->fw_info.oem_i2c_obj_id; + ddc_init_data.id.enum_id = 0; + ddc_init_data.id.type = OBJECT_TYPE_GENERIC; + pool->base.oem_device = dc->link_srv->create_ddc_service(&ddc_init_data); + } else { + pool->base.oem_device = NULL; + } + + //For now enable SDPIF_REQUEST_RATE_LIMIT on DCN4_01 when vram_info.num_chans provided + if (dc->config.sdpif_request_limit_words_per_umc == 0) + dc->config.sdpif_request_limit_words_per_umc = 16; + + dc->dml2_options.dcn_pipe_count = pool->base.pipe_count; + dc->dml2_options.use_native_soc_bb_construction = true; + dc->dml2_options.minimize_dispclk_using_odm = true; + dc->dml2_options.map_dc_pipes_with_callbacks = true; + dc->dml2_options.force_tdlut_enable = true; + + resource_init_common_dml2_callbacks(dc, &dc->dml2_options); + dc->dml2_options.callbacks.can_support_mclk_switch_using_fw_based_vblank_stretch = + &dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch; + + dc->dml2_options.max_segments_per_hubp = 20; + dc->dml2_options.det_segment_size = DCN6_0_CRB_SEGMENT_SIZE_KB; + + /* SPL */ + dc->caps.scl_caps.sharpener_support = true; + + /* init DC limited DML2 options */ + memcpy(&dc->dml2_dc_power_options, &dc->dml2_options, sizeof(struct dml2_configuration_options)); + dc->dml2_dc_power_options.use_clock_dc_limits = true; + + return true; + +create_fail: + + dcn60_resource_destruct(pool); + + return false; +} + +struct resource_pool *dcn60_create_resource_pool( + const struct dc_init_data *init_data, + struct dc *dc) +{ + struct dcn60_resource_pool *pool = + kzalloc(sizeof(struct dcn60_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dcn60_resource_construct((uint8_t)init_data->num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + kfree(pool); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.h new file mode 100644 index 000000000000..c4b6df4838c6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.h @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef _DCN60_RESOURCE_H_ +#define _DCN60_RESOURCE_H_ + +#include "core_types.h" +#include "dcn42/dcn42_mpc.h" + +#define TO_DCN60_RES_POOL(pool)\ + container_of(pool, struct dcn60_resource_pool, base) + +struct dcn60_resource_pool { + struct resource_pool base; +}; + +struct resource_pool *dcn60_create_resource_pool( + const struct dc_init_data *init_data, + struct dc *dc); + +bool dcn60_program_mcache_pipe_config(struct dc_state *context, + const struct dc_mcache_params *mcache_params); + +bool dcn50_program_mcache_pipe_config(struct dc_state *context, + const struct dc_mcache_params *mcache_params); + +/* HWSS */ + +#define HWSEQ_DCN60_REG_LIST() \ + SR(DCHUBBUB_GLOBAL_TIMER_CNTL), \ + SR(DIO_MEM_PWR_CTRL), \ + SR(ODM_MEM_PWR_CTRL3), \ + SR(MMHUBBUB_MEM_PWR_CNTL), \ + SR(DCCG_GATE_DISABLE_CNTL), \ + SR(DCCG_GATE_DISABLE_CNTL2), \ + SR(DCFCLK_CNTL),\ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL), \ + SRII(PIXEL_RATE_CNTL, OTG, 0), \ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 3),\ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(MPC_CRC_CTRL), \ + SR(AZALIA_AUDIO_DTO), \ + SR(HPO_TOP_HW_CONTROL),\ + SR(AZALIA_CONTROLLER_CLOCK_GATING), \ + SR(HDCP_INTERRUPT_DEST) + +/* HUBBUB */ +#define HUBBUB_REG_LIST_DCN60_RI(id) \ + SR(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A), \ + SR(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B), \ + SR(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL), \ + SR(DCHUBBUB_ARB_DRAM_STATE_CNTL), \ + SR(DCHUBBUB_ARB_SAT_LEVEL), \ + SR(DCHUBBUB_ARB_DF_REQ_OUTSTAND), \ + SR(DCHUBBUB_GLOBAL_TIMER_CNTL), \ + SR(DCHUBBUB_TEST_DEBUG_INDEX), \ + SR(DCHUBBUB_TEST_DEBUG_DATA), \ + SR(DCHUBBUB_SOFT_RESET), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DCN_VM_FB_LOCATION_BASE), \ + SR(DCN_VM_FB_LOCATION_TOP), \ + SR(DCN_VM_FB_OFFSET), \ + SR(DCN_VM_AGP_BOT), \ + SR(DCN_VM_AGP_TOP), \ + SR(DCN_VM_AGP_BASE), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A), \ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B), \ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B), \ + SR(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A), \ + SR(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B), \ + SR(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A), \ + SR(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B), \ + SR(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A), \ + SR(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B), \ + SR(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A), \ + SR(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B), \ + SR(DCHUBBUB_DET0_CTRL), \ + SR(DCHUBBUB_DET1_CTRL), \ + SR(DCHUBBUB_DET2_CTRL), \ + SR(DCHUBBUB_DET3_CTRL), \ + SR(DCHUBBUB_COMPBUF_CTRL), \ + SR(COMPBUF_RESERVED_SPACE), \ + SR(DCHUBBUB_DEBUG_CTRL_0), \ + SR(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A), \ + SR(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B), \ + SR(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A), \ + SR(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B), \ + SR(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A), \ + SR(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B), \ + SR(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A), \ + SR(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B), \ + SR(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A), \ + SR(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B), \ + SR(DCN_VM_FAULT_ADDR_MSB), SR(DCN_VM_FAULT_ADDR_LSB), \ + SR(DCN_VM_FAULT_CNTL), \ + SR(DCN_VM_FAULT_STATUS), \ + SR(SDPIF_REQUEST_RATE_LIMIT), \ + SR(DCHUBBUB_CLOCK_CNTL), \ + SR(DCHUBBUB_SDPIF_CFG0), \ + SR(DCHUBBUB_SDPIF_CFG1), \ + SR(DCHUBBUB_MEM_PWR_MODE_CTRL), \ + SR(DCHUBBUB_TIMEOUT_DETECTION_CTRL1), \ + SR(DCHUBBUB_TIMEOUT_DETECTION_CTRL2), \ + SR(DCHUBBUB_CTRL_STATUS), \ + SR(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL), \ + SR(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2), \ + SR(DC_PERFMON5_PERFCOUNTER_CNTL), \ + SR(DC_PERFMON5_PERFCOUNTER_CNTL2), \ + SR(DC_PERFMON5_PERFCOUNTER_STATE), \ + SR(DC_PERFMON5_PERFMON_CNTL), \ + SR(DC_PERFMON5_PERFMON_CNTL2), \ + SR(DC_PERFMON5_PERFMON_CVALUE_INT_MISC), \ + SR(DC_PERFMON5_PERFMON_CVALUE_LOW), \ + SR(DC_PERFMON5_PERFMON_HI), \ + SR(DC_PERFMON5_PERFMON_LOW), \ + SR(FMON_CTRL), \ + SR(DCHUBBUB_ARB_QOS_FORCE) + +/* HUBP */ +#define HUBP_REG_LIST_DCN60_RI(id) \ + SRI_ARR(NOM_PARAMETERS_0, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_1, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_2, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_3, HUBPREQ, id), \ + SRI_ARR(DCN_VM_MX_L1_TLB_CNTL, HUBPREQ, id), \ + SRI_ARR(DCHUBP_CNTL, HUBP, id), \ + SRI_ARR(HUBPREQ_DEBUG_DB, HUBP, id), \ + SRI_ARR(HUBPREQ_DEBUG, HUBP, id), \ + SRI_ARR(DCSURF_ADDR_CONFIG, HUBP, id), \ + SRI_ARR(DCSURF_LEGACY_ADDR_CONFIG, HUBP, id), \ + SRI_ARR(DCSURF_TILING_CONFIG, HUBP, id), \ + SRI_ARR(DCSURF_SURFACE_PITCH, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_PITCH_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_CONFIG, HUBP, id), \ + SRI_ARR(DCSURF_FLIP_CONTROL, HUBPREQ, id), \ + SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION, HUBP, id), \ + SRI_ARR(DCSURF_PRI_VIEWPORT_START, HUBP, id), \ + SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION, HUBP, id), \ + SRI_ARR(DCSURF_SEC_VIEWPORT_START, HUBP, id), \ + SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION_C, HUBP, id), \ + SRI_ARR(DCSURF_PRI_VIEWPORT_START_C, HUBP, id), \ + SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION_C, HUBP, id), \ + SRI_ARR(DCSURF_SEC_VIEWPORT_START_C, HUBP, id), \ + SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \ + SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS, HUBPREQ, id), \ + SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \ + SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS, HUBPREQ, id), \ + SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_INUSE, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_INUSE_HIGH, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_INUSE_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_INUSE_HIGH_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_CONTROL, HUBPREQ, id), \ + SRI_ARR(DCSURF_SURFACE_FLIP_INTERRUPT, HUBPREQ, id), \ + SRI_ARR(HUBPRET_CONTROL, HUBPRET, id), \ + SRI_ARR(HUBPRET_READ_LINE_STATUS, HUBPRET, id), \ + SRI_ARR(DCN_EXPANSION_MODE, HUBPREQ, id), \ + SRI_ARR(DCHUBP_REQ_SIZE_CONFIG, HUBP, id), \ + SRI_ARR(DCHUBP_REQ_SIZE_CONFIG_C, HUBP, id), \ + SRI_ARR(BLANK_OFFSET_0, HUBPREQ, id), \ + SRI_ARR(BLANK_OFFSET_1, HUBPREQ, id), \ + SRI_ARR(DST_DIMENSIONS, HUBPREQ, id), \ + SRI_ARR(DST_AFTER_SCALER, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_0, HUBPREQ, id), \ + SRI_ARR(REF_FREQ_TO_PIX_FREQ, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_1, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_3, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_4, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_5, HUBPREQ, id), \ + SRI_ARR(PER_LINE_DELIVERY_PRE, HUBPREQ, id), \ + SRI_ARR(PER_LINE_DELIVERY, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_2, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_4, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_6, HUBPREQ, id), \ + SRI_ARR(NOM_PARAMETERS_7, HUBPREQ, id), \ + SRI_ARR(DCN_GLOBAL_TTU_CNTL, HUBPREQ, id), \ + SRI_ARR(DCN_SURF0_TTU_CNTL0, HUBPREQ, id), \ + SRI_ARR(DCN_SURF0_TTU_CNTL1, HUBPREQ, id), \ + SRI_ARR(DCN_SURF1_TTU_CNTL0, HUBPREQ, id), \ + SRI_ARR(DCN_SURF1_TTU_CNTL1, HUBPREQ, id), \ + SRI_ARR(DCN_CUR0_TTU_CNTL0, HUBPREQ, id), \ + SRI_ARR(DCN_CUR0_TTU_CNTL1, HUBPREQ, id), \ + SRI_ARR(HUBP_CLK_CNTL, HUBP, id), \ + SRI_ARR(PREFETCH_SETTINGS, HUBPREQ, id), \ + SRI_ARR(PREFETCH_SETTINGS_C, HUBPREQ, id), \ + SRI_ARR(DCN_VM_SYSTEM_APERTURE_LOW_ADDR, HUBPREQ, id), \ + SRI_ARR(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR, HUBPREQ, id), \ + SRI_ARR(CURSOR_SETTINGS, HUBPREQ, id), \ + SRI_ARR(CURSOR_SURFACE_ADDRESS_HIGH, CURSOR0_, id), \ + SRI_ARR(CURSOR_SURFACE_ADDRESS, CURSOR0_, id), \ + SRI_ARR(CURSOR_SIZE, CURSOR0_, id), \ + SRI_ARR(CURSOR_CONTROL, CURSOR0_, id), \ + SRI_ARR(CURSOR_POSITION, CURSOR0_, id), \ + SRI_ARR(CURSOR_HOT_SPOT, CURSOR0_, id), \ + SRI_ARR(CURSOR_DST_OFFSET, CURSOR0_, id), \ + SRI_ARR(DMDATA_ADDRESS_HIGH, CURSOR0_, id), \ + SRI_ARR(DMDATA_ADDRESS_LOW, CURSOR0_, id), \ + SRI_ARR(DMDATA_CNTL, CURSOR0_, id), \ + SRI_ARR(DMDATA_SW_CNTL, CURSOR0_, id), \ + SRI_ARR(DMDATA_QOS_CNTL, CURSOR0_, id), \ + SRI_ARR(DMDATA_SW_DATA, CURSOR0_, id), \ + SRI_ARR(DMDATA_STATUS, CURSOR0_, id), \ + SRI_ARR(FLIP_PARAMETERS_0, HUBPREQ, id), \ + SRI_ARR(FLIP_PARAMETERS_1, HUBPREQ, id), \ + SRI_ARR(FLIP_PARAMETERS_2, HUBPREQ, id), \ + SRI_ARR(DCN_CUR1_TTU_CNTL0, HUBPREQ, id), \ + SRI_ARR(DCN_CUR1_TTU_CNTL1, HUBPREQ, id), \ + SRI_ARR(DCSURF_FLIP_CONTROL2, HUBPREQ, id), \ + SRI_ARR(VMID_SETTINGS_0, HUBPREQ, id), \ + SRI_ARR(FLIP_PARAMETERS_3, HUBPREQ, id), \ + SRI_ARR(FLIP_PARAMETERS_4, HUBPREQ, id), \ + SRI_ARR(FLIP_PARAMETERS_5, HUBPREQ, id), \ + SRI_ARR(FLIP_PARAMETERS_6, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_5, HUBPREQ, id), \ + SRI_ARR(VBLANK_PARAMETERS_6, HUBPREQ, id), \ + SRI_ARR(DCN_DMDATA_VM_CNTL, HUBPREQ, id), \ + SRI_ARR(DCHUBP_VMPG_CONFIG, HUBP, id), \ + SRI_ARR(UCLK_PSTATE_FORCE, HUBPREQ, id), \ + HUBP_3DLUT_FL_REG_LIST_DCN401(id), \ + SRI_ARR(DCSURF_VIEWPORT_MCACHE_SPLIT_COORDINATE, HUBP, id), \ + SRI_ARR(DCHUBP_MCACHEID_CONFIG, HUBP, id), \ + SRI_ARR(HUBPRET_READ_LINE_VALUE, HUBPRET, id), \ + SRI_ARR(DST_Y_DELTA_DRQ_LIMIT, HUBPREQ, id), \ + SRI_ARR(DST_Y_ALT_CH_DRQ_LIMIT, HUBPREQ, id) + +/* DPP */ +#define DPP_REG_LIST_DCN60_RI(id) \ + SRI_ARR(CM_DEALPHA, CM, id), \ + SRI_ARR(CM_MEM_PWR_STATUS, CM, id), \ + SRI_ARR(CM_BIAS_CR_R, CM, id), \ + SRI_ARR(CM_BIAS_Y_G_CB_B, CM, id), \ + SRI_ARR(CM_GAMCOR_CONTROL, CM, id), \ + SRI_ARR(CM_GAMCOR_LUT_CONTROL, CM, id), \ + SRI_ARR(CM_GAMCOR_LUT_INDEX, CM, id), \ + SRI_ARR(CM_GAMCOR_LUT_INDEX, CM, id), \ + SRI_ARR(CM_GAMCOR_LUT_DATA, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_CNTL_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_CNTL_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_CNTL_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_SLOPE_CNTL_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_SLOPE_CNTL_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_SLOPE_CNTL_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_END_CNTL1_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_END_CNTL2_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_END_CNTL1_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_END_CNTL2_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_END_CNTL1_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_END_CNTL2_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_REGION_0_1, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_REGION_32_33, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_OFFSET_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_OFFSET_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_OFFSET_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_BASE_CNTL_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_BASE_CNTL_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMB_START_BASE_CNTL_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_CNTL_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_CNTL_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_CNTL_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_SLOPE_CNTL_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_SLOPE_CNTL_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_SLOPE_CNTL_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_END_CNTL1_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_END_CNTL2_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_END_CNTL1_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_END_CNTL2_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_END_CNTL1_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_END_CNTL2_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_REGION_0_1, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_REGION_32_33, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_OFFSET_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_OFFSET_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_OFFSET_R, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_BASE_CNTL_B, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_BASE_CNTL_G, CM, id), \ + SRI_ARR(CM_GAMCOR_RAMA_START_BASE_CNTL_R, CM, id), \ + SRI_ARR(CM_HIST_CNTL, CM, id), \ + SRI_ARR(CM_HIST_LOCK, CM, id), \ + SRI_ARR(CM_HIST_INDEX, CM, id), \ + SRI_ARR(CM_HIST_DATA, CM, id), \ + SRI_ARR(CM_HIST_STATUS, CM, id), \ + SRI_ARR(CM_HIST_SCALE_SRC1, CM, id), \ + SRI_ARR(CM_HIST_COEFA_SRC2, CM, id), \ + SRI_ARR(CM_HIST_COEFB_SRC2, CM, id), \ + SRI_ARR(CM_HIST_COEFC_SRC2, CM, id), \ + SRI_ARR(CM_HIST_SCALE_SRC3, CM, id), \ + SRI_ARR(CM_HIST_BIAS_SRC1, CM, id), \ + SRI_ARR(CM_HIST_BIAS_SRC2, CM, id), \ + SRI_ARR(CM_HIST_BIAS_SRC3, CM, id), \ + SRI_ARR(DSCL_EXT_OVERSCAN_LEFT_RIGHT, DSCL, id), \ + SRI_ARR(DSCL_EXT_OVERSCAN_TOP_BOTTOM, DSCL, id), \ + SRI_ARR(OTG_H_BLANK, DSCL, id), \ + SRI_ARR(OTG_V_BLANK, DSCL, id), \ + SRI_ARR(SCL_MODE, DSCL, id), \ + SRI_ARR(LB_DATA_FORMAT, DSCL, id), \ + SRI_ARR(LB_MEMORY_CTRL, DSCL, id), \ + SRI_ARR(DSCL_AUTOCAL, DSCL, id), \ + SRI_ARR(SCL_TAP_CONTROL, DSCL, id), \ + SRI_ARR(SCL_COEF_RAM_TAP_SELECT, DSCL, id), \ + SRI_ARR(SCL_COEF_RAM_TAP_DATA, DSCL, id), \ + SRI_ARR(DSCL_2TAP_CONTROL, DSCL, id), \ + SRI_ARR(MPC_SIZE, DSCL, id), \ + SRI_ARR(SCL_BLACK_COLOR, DSCL, id), \ + SRI_ARR(SCL_HORZ_FILTER_SCALE_RATIO, DSCL, id), \ + SRI_ARR(SCL_VERT_FILTER_SCALE_RATIO, DSCL, id), \ + SRI_ARR(SCL_HORZ_FILTER_SCALE_RATIO_C, DSCL, id), \ + SRI_ARR(SCL_VERT_FILTER_SCALE_RATIO_C, DSCL, id), \ + SRI_ARR(SCL_HORZ_FILTER_INIT, DSCL, id), \ + SRI_ARR(SCL_HORZ_FILTER_INIT_C, DSCL, id), \ + SRI_ARR(SCL_VERT_FILTER_INIT, DSCL, id), \ + SRI_ARR(SCL_VERT_FILTER_INIT_C, DSCL, id), \ + SRI_ARR(RECOUT_START, DSCL, id), \ + SRI_ARR(RECOUT_SIZE, DSCL, id), \ + SRI_ARR(PRE_DEALPHA, CNVC_CFG, id), \ + SRI_ARR(PRE_REALPHA, CNVC_CFG, id), \ + SRI_ARR(PRE_GAM, CNVC_CFG, id), \ + SRI_ARR(PRE_CSC_MODE, CNVC_CFG, id), \ + SRI_ARR(PRE_CSC_C11_C12, CNVC_CFG, id), \ + SRI_ARR(PRE_CSC_C33_C34, CNVC_CFG, id), \ + SRI_ARR(PRE_CSC_B_C11_C12, CNVC_CFG, id), \ + SRI_ARR(PRE_CSC_B_C33_C34, CNVC_CFG, id), \ + SRI_ARR(CM_POST_CSC_CONTROL, CM, id), \ + SRI_ARR(CM_POST_CSC_C11_C12, CM, id), \ + SRI_ARR(CM_POST_CSC_C33_C34, CM, id), \ + SRI_ARR(CM_POST_CSC_B_C11_C12, CM, id), \ + SRI_ARR(CM_POST_CSC_B_C33_C34, CM, id), \ + SRI_ARR(CM_MEM_PWR_CTRL, CM, id), \ + SRI_ARR(CM_CONTROL, CM, id), \ + SRI_ARR(CM_TEST_DEBUG_INDEX, CM, id), \ + SRI_ARR(CM_TEST_DEBUG_DATA, CM, id), \ + SRI_ARR(FORMAT_CONTROL, CNVC_CFG, id), \ + SRI_ARR(CNVC_SURFACE_PIXEL_FORMAT, CNVC_CFG, id), \ + SRI_ARR(CURSOR0_CONTROL, CM_CUR, id), \ + SRI_ARR(CURSOR0_COLOR0, CM_CUR, id), \ + SRI_ARR(CURSOR0_COLOR1, CM_CUR, id), \ + SRI_ARR(CURSOR0_FP_SCALE_BIAS_G_Y, CM_CUR, id), \ + SRI_ARR(CURSOR0_FP_SCALE_BIAS_RB_CRCB, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_MODE, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C11_C12_A, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C13_C14_A, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C21_C22_A, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C23_C24_A, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C31_C32_A, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C33_C34_A, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C11_C12_B, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C13_C14_B, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C21_C22_B, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C23_C24_B, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C31_C32_B, CM_CUR, id), \ + SRI_ARR(CUR0_MATRIX_C33_C34_B, CM_CUR, id), \ + SRI_ARR(DPP_CONTROL, DPP_TOP, id), \ + SRI_ARR(CM_HDR_MULT_COEF, CM, id), \ + SRI_ARR(CURSOR_CONTROL, CURSOR0_, id), \ + SRI_ARR(ALPHA_2BIT_LUT01, CNVC_CFG, id), \ + SRI_ARR(ALPHA_2BIT_LUT23, CNVC_CFG, id), \ + SRI_ARR(FCNV_FP_BIAS_R, CNVC_CFG, id), \ + SRI_ARR(FCNV_FP_BIAS_G, CNVC_CFG, id), \ + SRI_ARR(FCNV_FP_BIAS_B, CNVC_CFG, id), \ + SRI_ARR(FCNV_FP_SCALE_R, CNVC_CFG, id), \ + SRI_ARR(FCNV_FP_SCALE_G, CNVC_CFG, id), \ + SRI_ARR(FCNV_FP_SCALE_B, CNVC_CFG, id), \ + SRI_ARR(COLOR_KEYER_CONTROL, CNVC_CFG, id), \ + SRI_ARR(COLOR_KEYER_ALPHA, CNVC_CFG, id), \ + SRI_ARR(COLOR_KEYER_RED, CNVC_CFG, id), \ + SRI_ARR(COLOR_KEYER_GREEN, CNVC_CFG, id), \ + SRI_ARR(COLOR_KEYER_BLUE, CNVC_CFG, id), \ + SRI_ARR(OBUF_MEM_PWR_CTRL, DSCL, id), \ + SRI_ARR(DSCL_MEM_PWR_STATUS, DSCL, id), \ + SRI_ARR(DSCL_MEM_PWR_CTRL, DSCL, id), \ + SRI_ARR(DSCL_CONTROL, DSCL, id), \ + SRI_ARR(DSCL_SC_MODE, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_MODE, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF_CNTL, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_RINGEST_EVENTAP_REDUCE, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_RINGEST_EVENTAP_GAIN, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF_FINAL_MAX_MIN, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG0, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG1, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG2, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG3, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG4, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG5, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG6, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF1_PWL_SEG7, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF3_PWL_SEG0, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF3_PWL_SEG1, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF3_PWL_SEG2, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF3_PWL_SEG3, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF3_PWL_SEG4, DSCL, id), \ + SRI_ARR(DSCL_EASF_H_BF3_PWL_SEG5, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_MODE, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF_CNTL, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_RINGEST_3TAP_CNTL1, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_RINGEST_3TAP_CNTL2, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_RINGEST_3TAP_CNTL3, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_RINGEST_EVENTAP_REDUCE, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_RINGEST_EVENTAP_GAIN, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF_FINAL_MAX_MIN, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG0, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG1, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG2, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG3, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG4, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG5, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG6, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF1_PWL_SEG7, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF3_PWL_SEG0, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF3_PWL_SEG1, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF3_PWL_SEG2, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF3_PWL_SEG3, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF3_PWL_SEG4, DSCL, id), \ + SRI_ARR(DSCL_EASF_V_BF3_PWL_SEG5, DSCL, id), \ + SRI_ARR(DSCL_SC_MATRIX_C0C1, DSCL, id), \ + SRI_ARR(DSCL_SC_MATRIX_C2C3, DSCL, id), \ + SRI_ARR(ISHARP_MODE, DSCL, id), \ + SRI_ARR(ISHARP_NOISEDET_THRESHOLD, DSCL, id), \ + SRI_ARR(ISHARP_NOISE_GAIN_PWL, DSCL, id), \ + SRI_ARR(ISHARP_LBA_PWL_SEG0, DSCL, id), \ + SRI_ARR(ISHARP_LBA_PWL_SEG1, DSCL, id), \ + SRI_ARR(ISHARP_LBA_PWL_SEG2, DSCL, id), \ + SRI_ARR(ISHARP_LBA_PWL_SEG3, DSCL, id), \ + SRI_ARR(ISHARP_LBA_PWL_SEG4, DSCL, id), \ + SRI_ARR(ISHARP_LBA_PWL_SEG5, DSCL, id), \ + SRI_ARR(ISHARP_DELTA_CTRL, DSCL, id), \ + SRI_ARR(ISHARP_DELTA_DATA, DSCL, id), \ + SRI_ARR(ISHARP_DELTA_INDEX, DSCL, id), \ + SRI_ARR(ISHARP_DELTA_LUT_MEM_PWR_CTRL, DSCL, id), \ + SRI_ARR(ISHARP_NLDELTA_SOFT_CLIP, DSCL, id) + +/* Stream encoder */ +#define SE_REG_LIST_DCN60_RI(id) \ + SRI_ARR(HDMI_CONTROL, DIG, id), SRI_ARR(HDMI_DB_CONTROL, DIG, id), \ + SRI_ARR(HDMI_GC, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL0, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL1, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL2, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL3, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL4, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL5, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL6, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL7, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL8, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL9, DIG, id), \ + SRI_ARR(HDMI_GENERIC_PACKET_CONTROL10, DIG, id), \ + SRI_ARR(HDMI_INFOFRAME_CONTROL0, DIG, id), \ + SRI_ARR(HDMI_VBI_PACKET_CONTROL, DIG, id), \ + SRI_ARR(HDMI_AUDIO_PACKET_CONTROL, DIG, id), \ + SRI_ARR(HDMI_ACR_PACKET_CONTROL, DIG, id), \ + SRI_ARR(HDMI_ACR_32_0, DIG, id), SRI_ARR(HDMI_ACR_32_1, DIG, id), \ + SRI_ARR(HDMI_ACR_44_0, DIG, id), SRI_ARR(HDMI_ACR_44_1, DIG, id), \ + SRI_ARR(HDMI_ACR_48_0, DIG, id), SRI_ARR(HDMI_ACR_48_1, DIG, id), \ + SRI_ARR(DP_DB_CNTL, DP, id), SRI_ARR(DP_MSA_MISC, DP, id), \ + SRI_ARR(DP_MSA_VBID_MISC, DP, id), SRI_ARR(DP_MSA_COLORIMETRY, DP, id), \ + SRI_ARR(DP_MSA_TIMING_PARAM1, DP, id), \ + SRI_ARR(DP_MSA_TIMING_PARAM2, DP, id), \ + SRI_ARR(DP_MSA_TIMING_PARAM3, DP, id), \ + SRI_ARR(DP_MSA_TIMING_PARAM4, DP, id), \ + SRI_ARR(DP_MSE_RATE_CNTL, DP, id), SRI_ARR(DP_MSE_RATE_UPDATE, DP, id), \ + SRI_ARR(DP_PIXEL_FORMAT, DP, id), SRI_ARR(DP_SEC_CNTL, DP, id), \ + SRI_ARR(DP_SEC_CNTL1, DP, id), SRI_ARR(DP_SEC_CNTL2, DP, id), \ + SRI_ARR(DP_SEC_CNTL5, DP, id), SRI_ARR(DP_SEC_CNTL6, DP, id), \ + SRI_ARR(DP_STEER_FIFO, DP, id), SRI_ARR(DP_VID_M, DP, id), \ + SRI_ARR(DP_VID_N, DP, id), SRI_ARR(DP_VID_STREAM_CNTL, DP, id), \ + SRI_ARR(DP_VID_TIMING, DP, id), SRI_ARR(DP_SEC_AUD_N, DP, id), \ + SRI_ARR(DP_SEC_TIMESTAMP, DP, id), \ + SRI_ARR(DP_SEC_METADATA_TRANSMISSION, DP, id), \ + SRI_ARR(HDMI_METADATA_PACKET_CONTROL, DIG, id), \ + SRI_ARR(DP_SEC_FRAMING4, DP, id), SRI_ARR(DP_GSP11_CNTL, DP, id), \ + SRI_ARR(DME_CONTROL, DME, id), \ + SRI_ARR(DP_SEC_METADATA_TRANSMISSION, DP, id), \ + SRI_ARR(HDMI_METADATA_PACKET_CONTROL, DIG, id), \ + SRI_ARR(DIG_FE_CNTL, DIG, id), \ + SRI_ARR(DIG_FE_EN_CNTL, DIG, id), \ + SRI_ARR(DIG_FE_CLK_CNTL, DIG, id), \ + SRI_ARR(DIG_CLOCK_PATTERN, DIG, id), \ + SRI_ARR(DIG_FIFO_CTRL0, DIG, id), \ + SRI_ARR(STREAM_MAPPER_CONTROL, DIG, id), \ + SRI_ARR(DIG_FE_AUDIO_CNTL, DIG, id) + +/* DCCG */ +#define DCCG_REG_LIST_DCN60_RI() \ + SR(DPPCLK_DTO_CTRL), DCCG_SRII(DTO_PARAM, DPPCLK, 0), \ + DCCG_SRII(DTO_PARAM, DPPCLK, 1), DCCG_SRII(DTO_PARAM, DPPCLK, 2), \ + DCCG_SRII(DTO_PARAM, DPPCLK, 3), DCCG_SRII(CLOCK_CNTL, HDMICHARCLK, 0), \ + SR(PHYASYMCLK_CLOCK_CNTL), SR(PHYBSYMCLK_CLOCK_CNTL), \ + SR(PHYCSYMCLK_CLOCK_CNTL), SR(PHYDSYMCLK_CLOCK_CNTL), \ + SR(DPSTREAMCLK_CNTL), SR(HDMISTREAMCLK_CNTL), \ + SR(SYMCLK32_SE_CNTL), SR(SYMCLK32_LE_CNTL), \ + DCCG_SRII(PIXEL_RATE_CNTL, OTG, 0), DCCG_SRII(PIXEL_RATE_CNTL, OTG, 1), \ + DCCG_SRII(PIXEL_RATE_CNTL, OTG, 2), DCCG_SRII(PIXEL_RATE_CNTL, OTG, 3), \ + SR(OTG_PIXEL_RATE_DIV), SR(DTBCLK_P_CNTL), \ + SR(DCCG_AUDIO_DTO_SOURCE), \ + SR(DPPCLK_CTRL), \ + DCCG_SRII(MODULO, DP_DTO, 0), DCCG_SRII(MODULO, DP_DTO, 1), \ + DCCG_SRII(MODULO, DP_DTO, 2), DCCG_SRII(MODULO, DP_DTO, 3), \ + DCCG_SRII(PHASE, DP_DTO, 0), DCCG_SRII(PHASE, DP_DTO, 1), \ + DCCG_SRII(PHASE, DP_DTO, 2), DCCG_SRII(PHASE, DP_DTO, 3), \ + SR(OTG_ADD_DROP_PIXEL_CNTL), \ + SR(DSCCLK0_DTO_PARAM), \ + SR(DSCCLK1_DTO_PARAM), \ + SR(DSCCLK2_DTO_PARAM), \ + SR(DSCCLK3_DTO_PARAM), \ + SR(DSCCLK_DTO_CTRL), \ + SR(DCCG_GATE_DISABLE_CNTL), \ + SR(DCCG_GATE_DISABLE_CNTL2), \ + SR(DCCG_GATE_DISABLE_CNTL3), \ + SR(DCCG_GATE_DISABLE_CNTL4), \ + SR(DCCG_GATE_DISABLE_CNTL5), \ + SR(DCCG_GATE_DISABLE_CNTL6), \ + SR(SYMCLKA_CLOCK_ENABLE), \ + SR(SYMCLKB_CLOCK_ENABLE), \ + SR(SYMCLKC_CLOCK_ENABLE), \ + SR(SYMCLKD_CLOCK_ENABLE), \ + SR(DSCCLK_SRC_SEL) + +/* HPO FRL stream encoder */ +#define DCN60_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) \ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id), \ + SR_ARR(HDMI_STREAM_ENC_AUDIO_CONTROL, id) + +#define DCN60_AUX_REG_LIST_RI(id, ddc_id) \ + DCN2_AUX_REG_LIST_RI(id), \ + SRI_ARR_DDC(DC_GPIO_DDC, MASK, ddc_id) + +/* HPD */ +#define HPD_REG_LIST_DCN60_RI(id) \ + HPD_REG_LIST_RI(id), \ + SR_ARR(HPD_CTRL, id) + +/* DSC */ +#define DSC_REG_LIST_DCN60_RI(id) \ + SRI_ARR(DSC_TOP_CONTROL, DSC_TOP, id), \ + SRI_ARR(DSC_DEBUG_CONTROL, DSC_TOP, id), \ + SRI_ARR(DSCC_CONFIG0, DSCC, id), SRI_ARR(DSCC_CONFIG1, DSCC, id), \ + SRI_ARR(DSCC_STATUS, DSCC, id), \ + SRI_ARR(DSCC_INTERRUPT_CONTROL0, DSCC, id), \ + SRI_ARR(DSCC_INTERRUPT_CONTROL1, DSCC, id), \ + SRI_ARR(DSCC_INTERRUPT_STATUS0, DSCC, id), \ + SRI_ARR(DSCC_INTERRUPT_STATUS1, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG0, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG1, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG2, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG3, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG4, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG5, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG6, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG7, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG8, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG9, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG10, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG11, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG12, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG13, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG14, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG15, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG16, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG17, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG18, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG19, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG20, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG21, DSCC, id), \ + SRI_ARR(DSCC_PPS_CONFIG22, DSCC, id), \ + SRI_ARR(DSCC_MEM_POWER_CONTROL0, DSCC, id), \ + SRI_ARR(DSCC_MEM_POWER_CONTROL1, DSCC, id), \ + SRI_ARR(DSCC_TEST_DEBUG_BUS_ROTATE, DSCC, id), \ + SRI_ARR(DSCRM_DSC_FORWARD_CONFIG, DSCRM, id) + +/* OPTC */ +#define OPTC_COMMON_REG_LIST_DCN60_RI(inst) \ + SRI_ARR(OTG_VSTARTUP_PARAM, OTG, inst), \ + SRI_ARR(OTG_VUPDATE_PARAM, OTG, inst), \ + SRI_ARR(OTG_VREADY_PARAM, OTG, inst), \ + SRI_ARR(OTG_MASTER_UPDATE_LOCK, OTG, inst), \ + SRI_ARR(OTG_GLOBAL_CONTROL0, OTG, inst), \ + SRI_ARR(OTG_GLOBAL_CONTROL1, OTG, inst), \ + SRI_ARR(OTG_GLOBAL_CONTROL2, OTG, inst), \ + SRI_ARR(OTG_GLOBAL_CONTROL4, OTG, inst), \ + SRI_ARR(OTG_DOUBLE_BUFFER_CONTROL, OTG, inst), \ + SRI_ARR(OTG_H_TOTAL, OTG, inst), \ + SRI_ARR(OTG_H_BLANK_START_END, OTG, inst), \ + SRI_ARR(OTG_H_SYNC_A, OTG, inst), SRI_ARR(OTG_H_SYNC_A_CNTL, OTG, inst), \ + SRI_ARR(OTG_H_TIMING_CNTL, OTG, inst), SRI_ARR(OTG_V_TOTAL, OTG, inst), \ + SRI_ARR(OTG_V_BLANK_START_END, OTG, inst), \ + SRI_ARR(OTG_V_SYNC_A, OTG, inst), SRI_ARR(OTG_V_SYNC_A_CNTL, OTG, inst), \ + SRI_ARR(OTG_CONTROL, OTG, inst), SRI_ARR(OTG_STEREO_CONTROL, OTG, inst), \ + SRI_ARR(OTG_3D_STRUCTURE_CONTROL, OTG, inst), \ + SRI_ARR(OTG_STEREO_STATUS, OTG, inst), \ + SRI_ARR(OTG_V_TOTAL_MAX, OTG, inst), \ + SRI_ARR(OTG_V_TOTAL_MIN, OTG, inst), \ + SRI_ARR(OTG_V_TOTAL_CONTROL, OTG, inst), \ + SRI_ARR(OTG_TRIGA_CNTL, OTG, inst), \ + SRI_ARR(OTG_FORCE_COUNT_NOW_CNTL, OTG, inst), \ + SRI_ARR(OTG_STATIC_SCREEN_CONTROL, OTG, inst), \ + SRI_ARR(OTG_STATUS_FRAME_COUNT, OTG, inst), \ + SRI_ARR(OTG_STATUS, OTG, inst), SRI_ARR(OTG_STATUS_POSITION, OTG, inst), \ + SRI_ARR(OTG_NOM_VERT_POSITION, OTG, inst), \ + SRI_ARR(OTG_M_CONST_DTO0, OTG, inst), \ + SRI_ARR(OTG_M_CONST_DTO1, OTG, inst), \ + SRI_ARR(OTG_CLOCK_CONTROL, OTG, inst), \ + SRI_ARR(OTG_VERTICAL_INTERRUPT0_CONTROL, OTG, inst), \ + SRI_ARR(OTG_VERTICAL_INTERRUPT0_POSITION, OTG, inst), \ + SRI_ARR(OTG_VERTICAL_INTERRUPT1_CONTROL, OTG, inst), \ + SRI_ARR(OTG_VERTICAL_INTERRUPT1_POSITION, OTG, inst), \ + SRI_ARR(OTG_VERTICAL_INTERRUPT2_CONTROL, OTG, inst), \ + SRI_ARR(OTG_VERTICAL_INTERRUPT2_POSITION, OTG, inst), \ + SRI_ARR(OPTC_INPUT_CLOCK_CONTROL, ODM, inst), \ + SRI_ARR(OPTC_DATA_SOURCE_SELECT, ODM, inst), \ + SRI_ARR(OPTC_INPUT_GLOBAL_CONTROL, ODM, inst), \ + SRI_ARR(CONTROL, VTG, inst), SRI_ARR(OTG_VERT_SYNC_CONTROL, OTG, inst), \ + SRI_ARR(OTG_GSL_CONTROL, OTG, inst), SRI_ARR(OTG_CRC_CNTL, OTG, inst), \ + SRI_ARR(OTG_CRC0_DATA_R, OTG, inst), \ + SRI_ARR(OTG_CRC0_DATA_G, OTG, inst), \ + SRI_ARR(OTG_CRC0_DATA_B, OTG, inst), \ + SRI_ARR(OTG_CRC1_DATA_R, OTG, inst), \ + SRI_ARR(OTG_CRC1_DATA_G, OTG, inst), \ + SRI_ARR(OTG_CRC1_DATA_B, OTG, inst), \ + SRI_ARR(OTG_CRC0_WINDOWA_X_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC0_WINDOWA_Y_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC0_WINDOWB_X_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC0_WINDOWB_Y_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC1_WINDOWA_X_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC1_WINDOWA_Y_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC1_WINDOWB_X_CONTROL, OTG, inst), \ + SRI_ARR(OTG_CRC1_WINDOWB_Y_CONTROL, OTG, inst), \ + SRI_ARR(OTG_TRIGA_MANUAL_TRIG, OTG, inst), \ + SRI_ARR(OTG_GLOBAL_CONTROL1, OTG, inst), \ + SRI_ARR(OTG_GLOBAL_CONTROL2, OTG, inst), \ + SRI_ARR(OTG_GSL_WINDOW_X, OTG, inst), \ + SRI_ARR(OTG_GSL_WINDOW_Y, OTG, inst), \ + SRI_ARR(OTG_VUPDATE_KEEPOUT, OTG, inst), \ + SRI_ARR(OTG_DRR_TRIGGER_WINDOW, OTG, inst), \ + SRI_ARR(OTG_DRR_V_TOTAL_CHANGE, OTG, inst), \ + SRI_ARR(OPTC_DATA_FORMAT_CONTROL, ODM, inst), \ + SRI_ARR(OPTC_BYTES_PER_PIXEL, ODM, inst), \ + SRI_ARR(OPTC_WIDTH_CONTROL, ODM, inst), \ + SRI_ARR(OPTC_WIDTH_CONTROL2, ODM, inst), \ + SRI_ARR(OPTC_MEMORY_CONFIG, ODM, inst), \ + SRI_ARR(OTG_DRR_CONTROL, OTG, inst), \ + SRI_ARR(OTG_PSTATE_REGISTER, OTG, inst), \ + SRI_ARR(OTG_PIPE_UPDATE_STATUS, OTG, inst),\ + SRI_ARR(INTERRUPT_DEST, OTG, inst) + +#endif /* _DCN60_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c new file mode 100644 index 000000000000..c26ad0903975 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dcn60_soc_and_ip_translator.h" +#include "soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h" +#include "bounding_boxes/dcn6_soc_bb.h" + +/* soc_and_ip_translator component used to get up-to-date values for bounding box. + * Bounding box values are stored in several locations and locations can vary with DCN revision. + * This component provides an interface to get DCN-specific bounding box values. + */ +static void dcn60_update_soc_bb_with_values_from_dmub(struct dml2_soc_bb *soc_bb, const struct dml2_configuration_options *config) +{ + const struct dmub_soc_bb_params *dmub_bb_params = + (const struct dmub_soc_bb_params *)config->bb_from_dmub; + int i; + unsigned int min_alt_ch_carveout_size_mb = 0; + + if (dmub_bb_params == NULL) + return; + + //only update if value is provided + if (dmub_bb_params->dram_clk_change_blackout_ns > 0) + soc_bb->power_management_parameters.dram_clk_change_blackout_us = + (double) dmub_bb_params->dram_clk_change_blackout_ns / 1000.0; + if (dmub_bb_params->dram_clk_change_read_only_ns > 0) + soc_bb->power_management_parameters.dram_clk_change_read_only_us = + (double) dmub_bb_params->dram_clk_change_read_only_ns / 1000.0; + if (dmub_bb_params->dram_clk_change_write_only_ns > 0) + soc_bb->power_management_parameters.dram_clk_change_write_only_us = + (double) dmub_bb_params->dram_clk_change_write_only_ns / 1000.0; + if (dmub_bb_params->fclk_change_blackout_ns > 0) + soc_bb->power_management_parameters.fclk_change_blackout_us = + (double) dmub_bb_params->fclk_change_blackout_ns / 1000.0; + if (dmub_bb_params->g7_ppt_blackout_ns > 0) + soc_bb->power_management_parameters.g7_ppt_blackout_us = + (double) dmub_bb_params->g7_ppt_blackout_ns / 1000.0; + if (dmub_bb_params->stutter_enter_plus_exit_latency_ns > 0) + soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us = + (double) dmub_bb_params->stutter_enter_plus_exit_latency_ns / 1000.0; + if (dmub_bb_params->stutter_exit_latency_ns > 0) + soc_bb->power_management_parameters.stutter_exit_latency_us = + (double) dmub_bb_params->stutter_exit_latency_ns / 1000.0; + if (dmub_bb_params->z8_stutter_enter_plus_exit_latency_ns > 0) + soc_bb->power_management_parameters.z8_stutter_enter_plus_exit_latency_us = + (double) dmub_bb_params->z8_stutter_enter_plus_exit_latency_ns / 1000.0; + if (dmub_bb_params->z8_stutter_exit_latency_ns > 0) + soc_bb->power_management_parameters.z8_stutter_exit_latency_us = + (double) dmub_bb_params->z8_stutter_exit_latency_ns / 1000.0; + if (dmub_bb_params->z8_min_idle_time_ns > 0) + soc_bb->power_management_parameters.z8_min_idle_time = + (double) dmub_bb_params->z8_min_idle_time_ns / 1000.0; + if (dmub_bb_params->type_b_dram_clk_change_blackout_ns > 0) + soc_bb->power_management_parameters.type_b_dram_clk_change_blackout_us = + (double) dmub_bb_params->type_b_dram_clk_change_blackout_ns / 1000.0; + if (dmub_bb_params->type_b_ppt_blackout_ns > 0) + soc_bb->power_management_parameters.type_b_ppt_blackout_us = + (double) dmub_bb_params->type_b_ppt_blackout_ns / 1000.0; + if (dmub_bb_params->vmin_limit_dispclk_khz > 0) + soc_bb->vmin_limit.dispclk_khz = dmub_bb_params->vmin_limit_dispclk_khz; + if (dmub_bb_params->vmin_limit_dcfclk_khz > 0) + soc_bb->vmin_limit.dcfclk_khz = dmub_bb_params->vmin_limit_dcfclk_khz; + if (dmub_bb_params->g7_temperature_read_blackout_ns > 0) + soc_bb->power_management_parameters.g7_temperature_read_blackout_us = + (double) dmub_bb_params->g7_temperature_read_blackout_ns / 1000.0; + + /* populate alt-channel info */ + for (i = 0; i < 2; i++) { + /* find the minimum carveout size (expected to be the same for all) */ + if (min_alt_ch_carveout_size_mb > config->alt_ch_cfg.region_size_bytes[i] >> 20) { + min_alt_ch_carveout_size_mb = (config->alt_ch_cfg.region_size_bytes[i] >> 20); + } + } + + if (min_alt_ch_carveout_size_mb > 0) + soc_bb->power_management_parameters.alternate_dram_carveout_size_mb = + min_alt_ch_carveout_size_mb; +} + +static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +{ + /* Individual modification can be overwritten even if it was obtained by a previous function. + * Modifications are acquired in order of priority (lowest to highest). + */ + dc_assert_fp_enabled(); + + dcn60_update_soc_bb_with_values_from_dmub(soc_bb, config); + dcn401_update_soc_bb_with_values_from_clk_mgr(soc_bb, dc, config); + dcn401_update_soc_bb_with_values_from_vbios(soc_bb, dc); + dcn401_update_soc_bb_with_values_from_software_policy(soc_bb, dc); +} + +static void dcn60_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +{ + //get default soc_bb with static values + dcn6_test_initialize_soc_bb(soc_bb); + //get default soc_bb with static values + apply_soc_bb_updates(soc_bb, dc, config); + +} + +static void dcn60_get_ip_caps(struct dml2_ip_capabilities *ip_caps) +{ + dcn6_test_initialize_ip_caps(ip_caps); +} + +static struct soc_and_ip_translator_funcs dcn60_translator_funcs = { + .get_soc_bb = dcn60_get_soc_bb, + .get_ip_caps = dcn60_get_ip_caps, +}; + +void dcn60_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator) +{ + soc_and_ip_translator->translator_funcs = &dcn60_translator_funcs; +} + diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.h new file mode 100644 index 000000000000..5ee7ad021598 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef _DCN60_SOC_AND_IP_TRANSLATOR_H_ +#define _DCN60_SOC_AND_IP_TRANSLATOR_H_ + +#include "core_types.h" +#include "dc.h" +#include "clk_mgr.h" +#include "dml_top_soc_parameter_types.h" +#include "soc_and_ip_translator.h" + +void dcn60_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator); + +#endif /* _DCN60_SOC_AND_IP_TRANSLATOR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.c new file mode 100644 index 000000000000..5c524f8d3fdf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: MIT + +/* Copyright 2025 Advanced Micro Devices, Inc. */ + +#include "../dmub_srv.h" +#include "dmub_reg.h" +#include "dmub_dcn60.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" +#include "mmhub/mmhub_5_0_1_offset.h" +#include "mmhub/mmhub_5_0_1_sh_mask.h" + +#define DCN_BASE__INST0_SEG2 0x000034C0 +#define MMHUB_BASE__INST0_SEG1 0x0001A000 + +#define BASE_INNER(seg) DCN_BASE__INST0_SEG##seg +#define CTX dmub +#define REGS dmub->regs_dcn60 +#define REG_OFFSET_EXP(reg_name) (BASE(reg##reg_name##_BASE_IDX) + reg##reg_name) +#define EXT_REG_READ(addr) dmub->funcs.reg_read(dmub->user_ctx, addr); +#define DAGB0_WRCLI_OSD_PENDING 0x1A083 + +#define MMHUB_BASE_INNER(seg) MMHUB_BASE__INST0_SEG##seg +#define MMHUB_BASE(seg) MMHUB_BASE_INNER(seg) +#define MMHUB_REG_OFFSET(reg_name) (MMHUB_BASE(reg##reg_name##_BASE_IDX) + reg##reg_name) + +const struct dmub_srv_dcn60_regs dmub_srv_dcn60_regs = { +#define DMUB_SR(reg) .reg = REG_OFFSET_EXP(reg), +#define MMHUB_SR(reg) .reg = MMHUB_REG_OFFSET(reg), + .offset = { + DMUB_DCN60_REGS() + DMCUB_INTERNAL_REGS() + DCN60_EXTERNAL_MMHUB_REGS() + }, +#undef MMHUB_SR +#undef DMUB_SR + +#define DMUB_SF(reg, field) .reg##__##field = FD_MASK(reg, field), + .mask = { + DMUB_DCN60_FIELDS() + DCN60_EXTERNAL_MMHUB_FIELDS() + }, +#undef DMUB_SF + +#define DMUB_SF(reg, field) .reg##__##field = FD_SHIFT(reg, field), + .shift = { + DMUB_DCN60_FIELDS() + DCN60_EXTERNAL_MMHUB_FIELDS() + }, +#undef DMUB_SF +}; + +static void dmub_dcn60_get_fb_base_offset(struct dmub_srv *dmub, + uint64_t *fb_base, + uint64_t *fb_offset) +{ + uint32_t tmp_fb_base, tmp_fb_offset; + + /* + * Favor override values from DM. + * This can handle multi device systems with shared memory space. + */ + if (dmub->soc_fb_info.fb_base || dmub->soc_fb_info.fb_offset) { + *fb_base = dmub->soc_fb_info.fb_base; + *fb_offset = dmub->soc_fb_info.fb_offset; + return; + } + + /* + * For DCN only environments prefer DCN local registers first. + * These should be a copy of the MMHUB registers below for single device systems. + */ + REG_GET(DCN_VM_FB_LOCATION_BASE, FB_BASE, &tmp_fb_base); + REG_GET(DCN_VM_FB_OFFSET, FB_OFFSET, &tmp_fb_offset); + + if (!dmub->no_ext_reg_access && tmp_fb_base == 0 && tmp_fb_offset == 0) { + /* + * Memory translation is handled by MMHUB for DMU. + * Use these as a fallback if DCN is not configured. + */ + REG_GET(MMMC_VM_FB_LOCATION_BASE, FB_BASE, &tmp_fb_base); + REG_GET(MMMC_VM_FB_OFFSET, FB_OFFSET, &tmp_fb_offset); + } + + *fb_base = (uint64_t)tmp_fb_base << 24; + *fb_offset = (uint64_t)tmp_fb_offset << 24; +} + +static inline void dmub_dcn60_translate_addr(const union dmub_addr *addr_in, + uint64_t fb_base, + uint64_t fb_offset, + union dmub_addr *addr_out) +{ + addr_out->quad_part = addr_in->quad_part - fb_base + fb_offset; +} + +void dmub_dcn60_reset(struct dmub_srv *dmub) +{ + union dmub_gpint_data_register cmd; + const uint32_t timeout_us = 1 * 1000 * 1000; //1s + const uint32_t poll_delay_us = 1; //1us + uint32_t i = 0; + uint32_t enabled, in_reset, scratch, pwait_mode, outstanding_req;; + + REG_GET(DMCUB_CNTL, + DMCUB_ENABLE, &enabled); + REG_GET(DMCUB_CNTL2, + DMCUB_SOFT_RESET, &in_reset); + + if (enabled && in_reset == 0) { + cmd.bits.status = 1; + cmd.bits.command_code = DMUB_GPINT__STOP_FW; + cmd.bits.param = 0; + + dmub->hw_funcs.set_gpint(dmub, cmd); + + for (; i < timeout_us; i++) { + scratch = REG_READ(DMCUB_SCRATCH7); + if (scratch == DMUB_GPINT__STOP_FW_RESPONSE) + break; + + udelay(poll_delay_us); + } + + for (; i < timeout_us; i++) { + REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &pwait_mode); + if (pwait_mode & (1 << 0)) + break; + + udelay(poll_delay_us); + } + + if (!dmub->no_ext_reg_access) { + for (; i < timeout_us; i++) { + outstanding_req = EXT_REG_READ(DAGB0_WRCLI_OSD_PENDING); + if (!(outstanding_req & 0x10)) + break; + + udelay(poll_delay_us); + } + } + } + + if (enabled) { + REG_UPDATE(DMCUB_CNTL2, DMCUB_SOFT_RESET, 1); + udelay(1); + REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0); + } + + if (i >= timeout_us) { + /* timeout should never occur */ + BREAK_TO_DEBUGGER(); + } + + REG_UPDATE(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_ENABLE, 0); + REG_UPDATE(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_ENABLE, 0); + REG_UPDATE(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_ENABLE, 0); + REG_UPDATE(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_ENABLE, 0); + REG_UPDATE(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, 0); + REG_UPDATE(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_ENABLE, 0); + + REG_WRITE(DMCUB_INBOX1_RPTR, 0); + REG_WRITE(DMCUB_INBOX1_WPTR, 0); + REG_WRITE(DMCUB_OUTBOX1_RPTR, 0); + REG_WRITE(DMCUB_OUTBOX1_WPTR, 0); + REG_WRITE(DMCUB_OUTBOX0_RPTR, 0); + REG_WRITE(DMCUB_OUTBOX0_WPTR, 0); + REG_WRITE(DMCUB_SCRATCH0, 0); + + /* Clear the GPINT command manually so we don't reset again. */ + cmd.all = 0; + dmub->hw_funcs.set_gpint(dmub, cmd); +} + +void dmub_dcn60_reset_release(struct dmub_srv *dmub) +{ + REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 0); + REG_WRITE(DMCUB_SCRATCH15, dmub->psp_version & 0x001100FF); + REG_UPDATE_2(DMCUB_CNTL, DMCUB_ENABLE, 1, DMCUB_TRACEPORT_EN, 1); + REG_UPDATE(DMCUB_CNTL2, DMCUB_SOFT_RESET, 0); +} + +void dmub_dcn60_backdoor_load(struct dmub_srv *dmub, + const struct dmub_window *cw0, + const struct dmub_window *cw1) +{ + union dmub_addr offset; + uint64_t fb_base, fb_offset; + + dmub_dcn60_get_fb_base_offset(dmub, &fb_base, &fb_offset); + + /* reset and disable DMCUB and MMHUBBUB DMUIF */ + REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1); + REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0); + + dmub_dcn60_translate_addr(&cw0->offset, fb_base, fb_offset, &offset); + + REG_WRITE(DMCUB_REGION3_CW0_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW0_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW0_BASE_ADDRESS, cw0->region.base); + REG_SET_2(DMCUB_REGION3_CW0_TOP_ADDRESS, 0, + DMCUB_REGION3_CW0_TOP_ADDRESS, cw0->region.top, + DMCUB_REGION3_CW0_ENABLE, 1); + + dmub_dcn60_translate_addr(&cw1->offset, fb_base, fb_offset, &offset); + + REG_WRITE(DMCUB_REGION3_CW1_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW1_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW1_BASE_ADDRESS, cw1->region.base); + REG_SET_2(DMCUB_REGION3_CW1_TOP_ADDRESS, 0, + DMCUB_REGION3_CW1_TOP_ADDRESS, cw1->region.top, + DMCUB_REGION3_CW1_ENABLE, 1); + + /* release DMCUB reset only to prevent premature execution */ + REG_UPDATE_3(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, + DMCUB_MEM_SEC_LVL, 0x2, + DMCUB_MEM_UNIT_ID, 0x20); +} + +void dmub_dcn60_backdoor_load_zfb_mode(struct dmub_srv *dmub, + const struct dmub_window *cw0, + const struct dmub_window *cw1) +{ + union dmub_addr offset; + + /* reset and disable DMCUB and MMHUBBUB DMUIF */ + REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1); + REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0); + + offset = cw0->offset; + + REG_WRITE(DMCUB_REGION3_CW0_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW0_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW0_BASE_ADDRESS, cw0->region.base); + REG_SET_2(DMCUB_REGION3_CW0_TOP_ADDRESS, 0, + DMCUB_REGION3_CW0_TOP_ADDRESS, cw0->region.top, + DMCUB_REGION3_CW0_ENABLE, 1); + + offset = cw1->offset; + + REG_WRITE(DMCUB_REGION3_CW1_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW1_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW1_BASE_ADDRESS, cw1->region.base); + REG_SET_2(DMCUB_REGION3_CW1_TOP_ADDRESS, 0, + DMCUB_REGION3_CW1_TOP_ADDRESS, cw1->region.top, + DMCUB_REGION3_CW1_ENABLE, 1); + + /* release DMCUB reset only to prevent premature execution */ + REG_UPDATE_3(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, + DMCUB_MEM_SEC_LVL, 0x2, + DMCUB_MEM_UNIT_ID, 0x20); +} + +void dmub_dcn60_setup_windows(struct dmub_srv *dmub, + const struct dmub_window *cw2, + const struct dmub_window *cw3, + const struct dmub_window *cw4, + const struct dmub_window *cw5, + const struct dmub_window *cw6, + const struct dmub_window *region6) +{ + (void)cw2; + union dmub_addr offset; + + offset = cw3->offset; + + REG_WRITE(DMCUB_REGION3_CW3_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW3_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW3_BASE_ADDRESS, cw3->region.base); + REG_SET_2(DMCUB_REGION3_CW3_TOP_ADDRESS, 0, + DMCUB_REGION3_CW3_TOP_ADDRESS, cw3->region.top, + DMCUB_REGION3_CW3_ENABLE, 1); + + offset = cw4->offset; + + REG_WRITE(DMCUB_REGION3_CW4_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW4_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW4_BASE_ADDRESS, cw4->region.base); + REG_SET_2(DMCUB_REGION3_CW4_TOP_ADDRESS, 0, + DMCUB_REGION3_CW4_TOP_ADDRESS, cw4->region.top, + DMCUB_REGION3_CW4_ENABLE, 1); + + offset = cw5->offset; + + REG_WRITE(DMCUB_REGION3_CW5_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW5_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW5_BASE_ADDRESS, cw5->region.base); + REG_SET_2(DMCUB_REGION3_CW5_TOP_ADDRESS, 0, + DMCUB_REGION3_CW5_TOP_ADDRESS, cw5->region.top, + DMCUB_REGION3_CW5_ENABLE, 1); + + REG_WRITE(DMCUB_REGION5_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION5_OFFSET_HIGH, offset.u.high_part); + REG_SET_2(DMCUB_REGION5_TOP_ADDRESS, 0, + DMCUB_REGION5_TOP_ADDRESS, + cw5->region.top - cw5->region.base - 1, + DMCUB_REGION5_ENABLE, 1); + + offset = cw6->offset; + + REG_WRITE(DMCUB_REGION3_CW6_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION3_CW6_OFFSET_HIGH, offset.u.high_part); + REG_WRITE(DMCUB_REGION3_CW6_BASE_ADDRESS, cw6->region.base); + REG_SET_2(DMCUB_REGION3_CW6_TOP_ADDRESS, 0, + DMCUB_REGION3_CW6_TOP_ADDRESS, cw6->region.top, + DMCUB_REGION3_CW6_ENABLE, 1); + + offset = region6->offset; + + REG_WRITE(DMCUB_REGION6_OFFSET, offset.u.low_part); + REG_WRITE(DMCUB_REGION6_OFFSET_HIGH, offset.u.high_part); + REG_SET_2(DMCUB_REGION6_TOP_ADDRESS, 0, + DMCUB_REGION6_TOP_ADDRESS, + region6->region.top - region6->region.base - 1, + DMCUB_REGION6_ENABLE, 1); +} + +void dmub_dcn60_setup_mailbox(struct dmub_srv *dmub, + const struct dmub_region *inbox1) +{ + REG_WRITE(DMCUB_INBOX1_BASE_ADDRESS, inbox1->base); + REG_WRITE(DMCUB_INBOX1_SIZE, inbox1->top - inbox1->base); +} + +uint32_t dmub_dcn60_get_inbox1_wptr(struct dmub_srv *dmub) +{ + return REG_READ(DMCUB_INBOX1_WPTR); +} + +uint32_t dmub_dcn60_get_inbox1_rptr(struct dmub_srv *dmub) +{ + return REG_READ(DMCUB_INBOX1_RPTR); +} + +void dmub_dcn60_set_inbox1_wptr(struct dmub_srv *dmub, uint32_t wptr_offset) +{ + REG_WRITE(DMCUB_INBOX1_WPTR, wptr_offset); +} + +void dmub_dcn60_setup_out_mailbox(struct dmub_srv *dmub, + const struct dmub_region *outbox1) +{ + REG_WRITE(DMCUB_OUTBOX1_BASE_ADDRESS, outbox1->base); + REG_WRITE(DMCUB_OUTBOX1_SIZE, outbox1->top - outbox1->base); +} + +uint32_t dmub_dcn60_get_outbox1_wptr(struct dmub_srv *dmub) +{ + /** + * outbox1 wptr register is accessed without locks (dal & dc) + * and to be called only by dmub_srv_stat_get_notification() + */ + return REG_READ(DMCUB_OUTBOX1_WPTR); +} + +void dmub_dcn60_set_outbox1_rptr(struct dmub_srv *dmub, uint32_t rptr_offset) +{ + /** + * outbox1 rptr register is accessed without locks (dal & dc) + * and to be called only by dmub_srv_stat_get_notification() + */ + REG_WRITE(DMCUB_OUTBOX1_RPTR, rptr_offset); +} + +bool dmub_dcn60_is_hw_init(struct dmub_srv *dmub) +{ + union dmub_fw_boot_status status; + uint32_t is_hw_init; + + status.all = REG_READ(DMCUB_SCRATCH0); + REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_hw_init); + + return is_hw_init != 0 && status.bits.dal_fw; +} + +bool dmub_dcn60_is_supported(struct dmub_srv *dmub) +{ + (void)dmub; + // TODO: CC_DC_PIPE_DIS::DC_DMCUB_ENABLE no longer exists + //REG_GET(CC_DC_PIPE_DIS, DC_DMCUB_ENABLE, &supported); + return 1; +} + +void dmub_dcn60_set_gpint(struct dmub_srv *dmub, + union dmub_gpint_data_register reg) +{ + REG_WRITE(DMCUB_GPINT_DATAIN1, reg.all); +} + +bool dmub_dcn60_is_gpint_acked(struct dmub_srv *dmub, + union dmub_gpint_data_register reg) +{ + union dmub_gpint_data_register test; + + reg.bits.status = 0; + test.all = REG_READ(DMCUB_GPINT_DATAIN1); + + return test.all == reg.all; +} + +uint32_t dmub_dcn60_get_gpint_response(struct dmub_srv *dmub) +{ + return REG_READ(DMCUB_SCRATCH7); +} + +uint32_t dmub_dcn60_get_gpint_dataout(struct dmub_srv *dmub) +{ + uint32_t dataout = REG_READ(DMCUB_GPINT_DATAOUT); + + REG_UPDATE(DMCUB_INTERRUPT_ENABLE, DMCUB_GPINT_IH_INT_EN, 0); + + REG_WRITE(DMCUB_GPINT_DATAOUT, 0); + REG_UPDATE(DMCUB_INTERRUPT_ACK, DMCUB_GPINT_IH_INT_ACK, 1); + REG_UPDATE(DMCUB_INTERRUPT_ACK, DMCUB_GPINT_IH_INT_ACK, 0); + + REG_UPDATE(DMCUB_INTERRUPT_ENABLE, DMCUB_GPINT_IH_INT_EN, 1); + + return dataout; +} + +union dmub_fw_boot_status dmub_dcn60_get_fw_boot_status(struct dmub_srv *dmub) +{ + union dmub_fw_boot_status status; + + status.all = REG_READ(DMCUB_SCRATCH0); + return status; +} + +void dmub_dcn60_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmub_srv_hw_params *params) +{ + union dmub_fw_boot_options boot_options = {0}; + + boot_options.bits.z10_disable = params->disable_z10; + + boot_options.bits.skip_phy_access = params->disallow_phy_access; + + REG_WRITE(DMCUB_SCRATCH14, boot_options.all); +} + +void dmub_dcn60_skip_dmub_panel_power_sequence(struct dmub_srv *dmub, bool skip) +{ + union dmub_fw_boot_options boot_options; + boot_options.all = REG_READ(DMCUB_SCRATCH14); + boot_options.bits.skip_phy_init_panel_sequence = skip; + REG_WRITE(DMCUB_SCRATCH14, boot_options.all); +} + +void dmub_dcn60_setup_outbox0(struct dmub_srv *dmub, + const struct dmub_region *outbox0) +{ + REG_WRITE(DMCUB_OUTBOX0_BASE_ADDRESS, outbox0->base); + + REG_WRITE(DMCUB_OUTBOX0_SIZE, outbox0->top - outbox0->base); +} + +uint32_t dmub_dcn60_get_outbox0_wptr(struct dmub_srv *dmub) +{ + return REG_READ(DMCUB_OUTBOX0_WPTR); +} + +void dmub_dcn60_set_outbox0_rptr(struct dmub_srv *dmub, uint32_t rptr_offset) +{ + REG_WRITE(DMCUB_OUTBOX0_RPTR, rptr_offset); +} + +uint32_t dmub_dcn60_get_current_time(struct dmub_srv *dmub) +{ + return REG_READ(DMCUB_TIMER_CURRENT); +} + +void dmub_dcn60_get_diagnostic_data(struct dmub_srv *dmub) +{ + uint32_t is_dmub_enabled, is_soft_reset, is_sec_reset, is_pwait; + uint32_t is_traceport_enabled, is_cw0_enabled, is_cw6_enabled; + struct dmub_timeout_info timeout = {0}; + + if (!dmub) + return; + + /* timeout data filled externally, cache before resetting memory */ + timeout = dmub->debug.timeout_info; + memset(&dmub->debug, 0, sizeof(dmub->debug)); + dmub->debug.timeout_info = timeout; + + dmub->debug.dmcub_version = dmub->fw_version; + + dmub->debug.scratch[0] = REG_READ(DMCUB_SCRATCH0); + dmub->debug.scratch[1] = REG_READ(DMCUB_SCRATCH1); + dmub->debug.scratch[2] = REG_READ(DMCUB_SCRATCH2); + dmub->debug.scratch[3] = REG_READ(DMCUB_SCRATCH3); + dmub->debug.scratch[4] = REG_READ(DMCUB_SCRATCH4); + dmub->debug.scratch[5] = REG_READ(DMCUB_SCRATCH5); + dmub->debug.scratch[6] = REG_READ(DMCUB_SCRATCH6); + dmub->debug.scratch[7] = REG_READ(DMCUB_SCRATCH7); + dmub->debug.scratch[8] = REG_READ(DMCUB_SCRATCH8); + dmub->debug.scratch[9] = REG_READ(DMCUB_SCRATCH9); + dmub->debug.scratch[10] = REG_READ(DMCUB_SCRATCH10); + dmub->debug.scratch[11] = REG_READ(DMCUB_SCRATCH11); + dmub->debug.scratch[12] = REG_READ(DMCUB_SCRATCH12); + dmub->debug.scratch[13] = REG_READ(DMCUB_SCRATCH13); + dmub->debug.scratch[14] = REG_READ(DMCUB_SCRATCH14); + dmub->debug.scratch[15] = REG_READ(DMCUB_SCRATCH15); + dmub->debug.scratch[16] = REG_READ(DMCUB_SCRATCH16); + + dmub->debug.undefined_address_fault_addr = REG_READ(DMCUB_UNDEFINED_ADDRESS_FAULT_ADDR); + dmub->debug.inst_fetch_fault_addr = REG_READ(DMCUB_INST_FETCH_FAULT_ADDR); + dmub->debug.data_write_fault_addr = REG_READ(DMCUB_DATA_WRITE_FAULT_ADDR); + + dmub->debug.inbox1_rptr = REG_READ(DMCUB_INBOX1_RPTR); + dmub->debug.inbox1_wptr = REG_READ(DMCUB_INBOX1_WPTR); + dmub->debug.inbox1_size = REG_READ(DMCUB_INBOX1_SIZE); + + dmub->debug.inbox0_rptr = REG_READ(DMCUB_INBOX0_RPTR); + dmub->debug.inbox0_wptr = REG_READ(DMCUB_INBOX0_WPTR); + dmub->debug.inbox0_size = REG_READ(DMCUB_INBOX0_SIZE); + + dmub->debug.outbox1_rptr = REG_READ(DMCUB_OUTBOX1_RPTR); + dmub->debug.outbox1_wptr = REG_READ(DMCUB_OUTBOX1_WPTR); + dmub->debug.outbox1_size = REG_READ(DMCUB_OUTBOX1_SIZE); + + REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; + + REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &is_pwait); + ASSERT(is_pwait <= 0xFF); + dmub->debug.is_pwait = (uint8_t)is_pwait; + + REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &is_soft_reset); + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; + + REG_GET(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS, &is_sec_reset); + ASSERT(is_sec_reset <= 0xFF); + dmub->debug.is_dmcub_secure_reset = (uint8_t)is_sec_reset; + + REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; + + REG_GET(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE, &is_cw0_enabled); + ASSERT(is_cw0_enabled <= 0xFF); + dmub->debug.is_cw0_enabled = (uint8_t)is_cw0_enabled; + + REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; + + dmub->debug.gpint_datain0 = REG_READ(DMCUB_GPINT_DATAIN0); +} +void dmub_dcn60_configure_dmub_in_system_memory(struct dmub_srv *dmub) +{ + /* DMCUB_REGION3_TMR_AXI_SPACE values: + * 0b011 (0x3) - FB physical address + * 0b100 (0x4) - GPU virtual address + * + * Default value is 0x3 (FB Physical address for TMR). When programming + * DMUB to be in system memory, change to 0x4. The system memory allocated + * is accessible by both GPU and CPU, so we use GPU virtual address. + */ + REG_WRITE(DMCUB_REGION3_TMR_AXI_SPACE, 0x4); +} + +void dmub_dcn60_send_inbox0_cmd(struct dmub_srv *dmub, union dmub_inbox0_data_register data) +{ + REG_WRITE(DMCUB_INBOX0_WPTR, data.inbox0_cmd_common.all); +} + +void dmub_dcn60_clear_inbox0_ack_register(struct dmub_srv *dmub) +{ + REG_WRITE(DMCUB_SCRATCH17, 0); +} + +uint32_t dmub_dcn60_read_inbox0_ack_register(struct dmub_srv *dmub) +{ + return REG_READ(DMCUB_SCRATCH17); +} + +void dmub_dcn60_send_reg_inbox0_cmd_msg(struct dmub_srv *dmub, + union dmub_rb_cmd *cmd) +{ + uint32_t *dwords = (uint32_t *)cmd; + uint32_t payload_size_bytes = cmd->cmd_common.header.payload_bytes; + uint32_t msg_index; + static_assert(sizeof(*cmd) == 64, "DMUB command size mismatch"); + + /* read remaining data based on payload size */ + for (msg_index = 0; msg_index < 15; msg_index++) { + if (payload_size_bytes <= msg_index * 4) { + break; + } + + switch (msg_index) { + case 0: + REG_WRITE(DMCUB_REG_INBOX0_MSG0, dwords[msg_index + 1]); + break; + case 1: + REG_WRITE(DMCUB_REG_INBOX0_MSG1, dwords[msg_index + 1]); + break; + case 2: + REG_WRITE(DMCUB_REG_INBOX0_MSG2, dwords[msg_index + 1]); + break; + case 3: + REG_WRITE(DMCUB_REG_INBOX0_MSG3, dwords[msg_index + 1]); + break; + case 4: + REG_WRITE(DMCUB_REG_INBOX0_MSG4, dwords[msg_index + 1]); + break; + case 5: + REG_WRITE(DMCUB_REG_INBOX0_MSG5, dwords[msg_index + 1]); + break; + case 6: + REG_WRITE(DMCUB_REG_INBOX0_MSG6, dwords[msg_index + 1]); + break; + case 7: + REG_WRITE(DMCUB_REG_INBOX0_MSG7, dwords[msg_index + 1]); + break; + case 8: + REG_WRITE(DMCUB_REG_INBOX0_MSG8, dwords[msg_index + 1]); + break; + case 9: + REG_WRITE(DMCUB_REG_INBOX0_MSG9, dwords[msg_index + 1]); + break; + case 10: + REG_WRITE(DMCUB_REG_INBOX0_MSG10, dwords[msg_index + 1]); + break; + case 11: + REG_WRITE(DMCUB_REG_INBOX0_MSG11, dwords[msg_index + 1]); + break; + case 12: + REG_WRITE(DMCUB_REG_INBOX0_MSG12, dwords[msg_index + 1]); + break; + case 13: + REG_WRITE(DMCUB_REG_INBOX0_MSG13, dwords[msg_index + 1]); + break; + case 14: + REG_WRITE(DMCUB_REG_INBOX0_MSG14, dwords[msg_index + 1]); + break; + } + } + + /* writing to INBOX RDY register will trigger DMUB REG INBOX0 RDY + * interrupt. + */ + REG_WRITE(DMCUB_REG_INBOX0_RDY, dwords[0]); +} + +uint32_t dmub_dcn60_read_reg_inbox0_rsp_int_status(struct dmub_srv *dmub) +{ + uint32_t status; + + REG_GET(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_STAT, &status); + return status; +} + +void dmub_dcn60_read_reg_inbox0_cmd_rsp(struct dmub_srv *dmub, + union dmub_rb_cmd *cmd) +{ + uint32_t *dwords = (uint32_t *)cmd; + + static_assert(sizeof(*cmd) == 64, "DMUB command size mismatch"); + + dwords[0] = REG_READ(DMCUB_REG_INBOX0_RSP); + dwords[1] = REG_READ(DMCUB_REG_INBOX0_MSG0); + dwords[2] = REG_READ(DMCUB_REG_INBOX0_MSG1); + dwords[3] = REG_READ(DMCUB_REG_INBOX0_MSG2); + dwords[4] = REG_READ(DMCUB_REG_INBOX0_MSG3); + dwords[5] = REG_READ(DMCUB_REG_INBOX0_MSG4); + dwords[6] = REG_READ(DMCUB_REG_INBOX0_MSG5); + dwords[7] = REG_READ(DMCUB_REG_INBOX0_MSG6); + dwords[8] = REG_READ(DMCUB_REG_INBOX0_MSG7); + dwords[9] = REG_READ(DMCUB_REG_INBOX0_MSG8); + dwords[10] = REG_READ(DMCUB_REG_INBOX0_MSG9); + dwords[11] = REG_READ(DMCUB_REG_INBOX0_MSG10); + dwords[12] = REG_READ(DMCUB_REG_INBOX0_MSG11); + dwords[13] = REG_READ(DMCUB_REG_INBOX0_MSG12); + dwords[14] = REG_READ(DMCUB_REG_INBOX0_MSG13); + dwords[15] = REG_READ(DMCUB_REG_INBOX0_MSG14); +} + +void dmub_dcn60_write_reg_inbox0_rsp_int_ack(struct dmub_srv *dmub) +{ + REG_UPDATE(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_ACK, 1); +} + +void dmub_dcn60_clear_reg_inbox0_rsp_int_ack(struct dmub_srv *dmub) +{ + REG_UPDATE(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_ACK, 0); +} + +void dmub_dcn60_enable_reg_inbox0_rsp_int(struct dmub_srv *dmub, bool enable) +{ + REG_UPDATE(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_EN, enable ? 1:0); +} + +void dmub_dcn60_write_reg_outbox0_rdy_int_ack(struct dmub_srv *dmub) +{ + REG_UPDATE(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_ACK, 1); + REG_UPDATE(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_ACK, 0); +} + +void dmub_dcn60_read_reg_outbox0_msg(struct dmub_srv *dmub, uint32_t *msg) +{ + *msg = REG_READ(DMCUB_REG_OUTBOX0_MSG0); +} + +void dmub_dcn60_write_reg_outbox0_rsp(struct dmub_srv *dmub, uint32_t *rsp) +{ + REG_WRITE(DMCUB_REG_OUTBOX0_RSP, *rsp); +} + +uint32_t dmub_dcn60_read_reg_outbox0_rsp_int_status(struct dmub_srv *dmub) +{ + uint32_t status; + + REG_GET(DMCUB_INTERRUPT_STATUS, DMCUB_REG_OUTBOX0_RSP_INT_STAT, &status); + return status; +} + +void dmub_dcn60_enable_reg_outbox0_rdy_int(struct dmub_srv *dmub, bool enable) +{ + REG_UPDATE(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_EN, enable ? 1:0); +} + +uint32_t dmub_dcn60_read_reg_outbox0_rdy_int_status(struct dmub_srv *dmub) +{ + uint32_t status; + + REG_GET(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_STAT, &status); + return status; +} diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.h new file mode 100644 index 000000000000..5007ed1c19d7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn60.h @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: MIT */ + +/* Copyright 2025 Advanced Micro Devices, Inc. */ + +#ifndef _DMUB_DCN60_H_ +#define _DMUB_DCN60_H_ + +#include "dmub_dcn31.h" + +struct dmub_srv; + +/* DCN60 register definitions. */ + +#define DMUB_DCN60_REGS() \ + DMUB_SR(DMCUB_CNTL) \ + DMUB_SR(DMCUB_CNTL2) \ + DMUB_SR(DMCUB_SEC_CNTL) \ + DMUB_SR(DMCUB_INBOX0_SIZE) \ + DMUB_SR(DMCUB_INBOX0_RPTR) \ + DMUB_SR(DMCUB_INBOX0_WPTR) \ + DMUB_SR(DMCUB_INBOX1_BASE_ADDRESS) \ + DMUB_SR(DMCUB_INBOX1_SIZE) \ + DMUB_SR(DMCUB_INBOX1_RPTR) \ + DMUB_SR(DMCUB_INBOX1_WPTR) \ + DMUB_SR(DMCUB_OUTBOX0_BASE_ADDRESS) \ + DMUB_SR(DMCUB_OUTBOX0_SIZE) \ + DMUB_SR(DMCUB_OUTBOX0_RPTR) \ + DMUB_SR(DMCUB_OUTBOX0_WPTR) \ + DMUB_SR(DMCUB_OUTBOX1_BASE_ADDRESS) \ + DMUB_SR(DMCUB_OUTBOX1_SIZE) \ + DMUB_SR(DMCUB_OUTBOX1_RPTR) \ + DMUB_SR(DMCUB_OUTBOX1_WPTR) \ + DMUB_SR(DMCUB_REGION3_CW0_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW1_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW2_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW3_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW4_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW5_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW6_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW7_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW0_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW1_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW2_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW3_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW4_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW5_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW6_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW7_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW0_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW1_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW2_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW3_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW4_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW5_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW6_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW7_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW0_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW1_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW2_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW3_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW4_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW5_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW6_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW7_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION4_OFFSET) \ + DMUB_SR(DMCUB_REGION4_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION4_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION5_OFFSET) \ + DMUB_SR(DMCUB_REGION5_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION5_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION6_OFFSET) \ + DMUB_SR(DMCUB_REGION6_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION6_TOP_ADDRESS) \ + DMUB_SR(DMCUB_SCRATCH0) \ + DMUB_SR(DMCUB_SCRATCH1) \ + DMUB_SR(DMCUB_SCRATCH2) \ + DMUB_SR(DMCUB_SCRATCH3) \ + DMUB_SR(DMCUB_SCRATCH4) \ + DMUB_SR(DMCUB_SCRATCH5) \ + DMUB_SR(DMCUB_SCRATCH6) \ + DMUB_SR(DMCUB_SCRATCH7) \ + DMUB_SR(DMCUB_SCRATCH8) \ + DMUB_SR(DMCUB_SCRATCH9) \ + DMUB_SR(DMCUB_SCRATCH10) \ + DMUB_SR(DMCUB_SCRATCH11) \ + DMUB_SR(DMCUB_SCRATCH12) \ + DMUB_SR(DMCUB_SCRATCH13) \ + DMUB_SR(DMCUB_SCRATCH14) \ + DMUB_SR(DMCUB_SCRATCH15) \ + DMUB_SR(DMCUB_SCRATCH16) \ + DMUB_SR(DMCUB_SCRATCH17) \ + DMUB_SR(DMCUB_GPINT_DATAIN0) \ + DMUB_SR(DMCUB_GPINT_DATAIN1) \ + DMUB_SR(DMCUB_GPINT_DATAOUT) \ + DMUB_SR(CC_DC_PIPE_DIS) \ + DMUB_SR(MMHUBBUB_SOFT_RESET) \ + DMUB_SR(DCN_VM_FB_LOCATION_BASE) \ + DMUB_SR(DCN_VM_FB_OFFSET) \ + DMUB_SR(DMCUB_TIMER_CURRENT) \ + DMUB_SR(DMCUB_INST_FETCH_FAULT_ADDR) \ + DMUB_SR(DMCUB_UNDEFINED_ADDRESS_FAULT_ADDR) \ + DMUB_SR(DMCUB_DATA_WRITE_FAULT_ADDR) \ + DMUB_SR(DMCUB_REGION3_TMR_AXI_SPACE) \ + DMUB_SR(DMCUB_INTERRUPT_ENABLE) \ + DMUB_SR(DMCUB_INTERRUPT_ACK) \ + DMUB_SR(DMCUB_INTERRUPT_STATUS) \ + DMUB_SR(DMCUB_REG_INBOX0_RDY) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG0) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG1) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG2) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG3) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG4) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG5) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG6) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG7) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG8) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG9) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG10) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG11) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG12) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG13) \ + DMUB_SR(DMCUB_REG_INBOX0_MSG14) \ + DMUB_SR(DMCUB_REG_INBOX0_RSP) \ + DMUB_SR(DMCUB_REG_OUTBOX0_RDY) \ + DMUB_SR(DMCUB_REG_OUTBOX0_MSG0) \ + DMUB_SR(DMCUB_REG_OUTBOX0_RSP) \ + DMUB_SR(HOST_INTERRUPT_CSR) + +#define DMUB_DCN60_FIELDS() \ + DMUB_SF(DMCUB_CNTL, DMCUB_ENABLE) \ + DMUB_SF(DMCUB_CNTL, DMCUB_TRACEPORT_EN) \ + DMUB_SF(DMCUB_CNTL2, DMCUB_SOFT_RESET) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_SEC_RESET) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_MEM_SEC_LVL) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_MEM_UNIT_ID) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS) \ + DMUB_SF(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW1_TOP_ADDRESS, DMCUB_REGION3_CW1_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW1_TOP_ADDRESS, DMCUB_REGION3_CW1_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_ENABLE) \ + DMUB_SF(DMCUB_REGION4_TOP_ADDRESS, DMCUB_REGION4_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION4_TOP_ADDRESS, DMCUB_REGION4_ENABLE) \ + DMUB_SF(DMCUB_REGION5_TOP_ADDRESS, DMCUB_REGION5_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION5_TOP_ADDRESS, DMCUB_REGION5_ENABLE) \ + DMUB_SF(DMCUB_REGION6_TOP_ADDRESS, DMCUB_REGION6_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION6_TOP_ADDRESS, DMCUB_REGION6_ENABLE) \ + DMUB_SF(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET) \ + DMUB_SF(DCN_VM_FB_LOCATION_BASE, FB_BASE) \ + DMUB_SF(DCN_VM_FB_OFFSET, FB_OFFSET) \ + DMUB_SF(DMCUB_INBOX0_WPTR, DMCUB_INBOX0_WPTR) \ + DMUB_SF(DMCUB_REGION3_TMR_AXI_SPACE, DMCUB_REGION3_TMR_AXI_SPACE) \ + DMUB_SF(DMCUB_INTERRUPT_ENABLE, DMCUB_GPINT_IH_INT_EN) \ + DMUB_SF(DMCUB_INTERRUPT_ACK, DMCUB_GPINT_IH_INT_ACK) \ + DMUB_SF(DMCUB_INTERRUPT_STATUS, DMCUB_REG_OUTBOX0_RSP_INT_STAT) \ + DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_ACK) \ + DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_STAT) \ + DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_EN) \ + DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_ACK) \ + DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_STAT) \ + DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_EN) \ + DMUB_SF(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS) + +#define DCN60_EXTERNAL_MMHUB_REGS() \ + MMHUB_SR(MMMC_VM_FB_LOCATION_BASE) \ + MMHUB_SR(MMMC_VM_FB_OFFSET) + +#define DCN60_EXTERNAL_MMHUB_FIELDS() \ + DMUB_SF(MMMC_VM_FB_LOCATION_BASE, FB_BASE) \ + DMUB_SF(MMMC_VM_FB_OFFSET, FB_OFFSET) + +struct dmub_srv_dcn60_reg_offset { +#define DMUB_SR(reg) uint32_t reg; +#define MMHUB_SR(reg) uint32_t reg; + DMUB_DCN60_REGS() + DMCUB_INTERNAL_REGS() + DCN60_EXTERNAL_MMHUB_REGS() +#undef MMHUB_SR +#undef DMUB_SR +}; + +struct dmub_srv_dcn60_reg_shift { +#define DMUB_SF(reg, field) uint8_t reg##__##field; + DMUB_DCN60_FIELDS() + DCN60_EXTERNAL_MMHUB_FIELDS() +#undef DMUB_SF +}; + +struct dmub_srv_dcn60_reg_mask { +#define DMUB_SF(reg, field) uint32_t reg##__##field; + DMUB_DCN60_FIELDS() + DCN60_EXTERNAL_MMHUB_FIELDS() +#undef DMUB_SF +}; + +struct dmub_srv_dcn60_regs { + const struct dmub_srv_dcn60_reg_offset offset; + const struct dmub_srv_dcn60_reg_mask mask; + const struct dmub_srv_dcn60_reg_shift shift; +}; + +extern const struct dmub_srv_dcn60_regs dmub_srv_dcn60_regs; + +void dmub_dcn60_reset(struct dmub_srv *dmub); + +void dmub_dcn60_reset_release(struct dmub_srv *dmub); + +void dmub_dcn60_backdoor_load(struct dmub_srv *dmub, + const struct dmub_window *cw0, + const struct dmub_window *cw1); + +void dmub_dcn60_backdoor_load_zfb_mode(struct dmub_srv *dmub, + const struct dmub_window *cw0, + const struct dmub_window *cw1); + +void dmub_dcn60_setup_windows(struct dmub_srv *dmub, + const struct dmub_window *cw2, + const struct dmub_window *cw3, + const struct dmub_window *cw4, + const struct dmub_window *cw5, + const struct dmub_window *cw6, + const struct dmub_window *region6); + +void dmub_dcn60_setup_mailbox(struct dmub_srv *dmub, + const struct dmub_region *inbox1); + +uint32_t dmub_dcn60_get_inbox1_wptr(struct dmub_srv *dmub); + +uint32_t dmub_dcn60_get_inbox1_rptr(struct dmub_srv *dmub); + +void dmub_dcn60_set_inbox1_wptr(struct dmub_srv *dmub, uint32_t wptr_offset); + +void dmub_dcn60_setup_out_mailbox(struct dmub_srv *dmub, + const struct dmub_region *outbox1); + +uint32_t dmub_dcn60_get_outbox1_wptr(struct dmub_srv *dmub); + +void dmub_dcn60_set_outbox1_rptr(struct dmub_srv *dmub, uint32_t rptr_offset); + +bool dmub_dcn60_is_hw_init(struct dmub_srv *dmub); + +bool dmub_dcn60_is_supported(struct dmub_srv *dmub); + +void dmub_dcn60_set_gpint(struct dmub_srv *dmub, + union dmub_gpint_data_register reg); + +bool dmub_dcn60_is_gpint_acked(struct dmub_srv *dmub, + union dmub_gpint_data_register reg); + +uint32_t dmub_dcn60_get_gpint_response(struct dmub_srv *dmub); + +uint32_t dmub_dcn60_get_gpint_dataout(struct dmub_srv *dmub); + +void dmub_dcn60_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmub_srv_hw_params *params); + +void dmub_dcn60_skip_dmub_panel_power_sequence(struct dmub_srv *dmub, bool skip); + +union dmub_fw_boot_status dmub_dcn60_get_fw_boot_status(struct dmub_srv *dmub); + +void dmub_dcn60_setup_outbox0(struct dmub_srv *dmub, + const struct dmub_region *outbox0); + +uint32_t dmub_dcn60_get_outbox0_wptr(struct dmub_srv *dmub); + +void dmub_dcn60_set_outbox0_rptr(struct dmub_srv *dmub, uint32_t rptr_offset); + +uint32_t dmub_dcn60_get_current_time(struct dmub_srv *dmub); + +void dmub_dcn60_get_diagnostic_data(struct dmub_srv *dmub); + +void dmub_dcn60_configure_dmub_in_system_memory(struct dmub_srv *dmub); +void dmub_dcn60_send_inbox0_cmd(struct dmub_srv *dmub, union dmub_inbox0_data_register data); +void dmub_dcn60_clear_inbox0_ack_register(struct dmub_srv *dmub); +uint32_t dmub_dcn60_read_inbox0_ack_register(struct dmub_srv *dmub); + +void dmub_dcn60_send_reg_inbox0_cmd_msg(struct dmub_srv *dmub, + union dmub_rb_cmd *cmd); +uint32_t dmub_dcn60_read_reg_inbox0_rsp_int_status(struct dmub_srv *dmub); +void dmub_dcn60_read_reg_inbox0_cmd_rsp(struct dmub_srv *dmub, + union dmub_rb_cmd *cmd); +void dmub_dcn60_write_reg_inbox0_rsp_int_ack(struct dmub_srv *dmub); +void dmub_dcn60_clear_reg_inbox0_rsp_int_ack(struct dmub_srv *dmub); +void dmub_dcn60_enable_reg_inbox0_rsp_int(struct dmub_srv *dmub, bool enable); + +void dmub_dcn60_write_reg_outbox0_rdy_int_ack(struct dmub_srv *dmub); +void dmub_dcn60_read_reg_outbox0_msg(struct dmub_srv *dmub, uint32_t *msg); +void dmub_dcn60_write_reg_outbox0_rsp(struct dmub_srv *dmub, uint32_t *msg); +uint32_t dmub_dcn60_read_reg_outbox0_rsp_int_status(struct dmub_srv *dmub); +void dmub_dcn60_enable_reg_outbox0_rdy_int(struct dmub_srv *dmub, bool enable); +uint32_t dmub_dcn60_read_reg_outbox0_rdy_int_status(struct dmub_srv *dmub); + +#endif /* _DMUB_DCN60_H_ */