diff options
Diffstat (limited to 'common/recipes-kernel/linux/files/0510-drm-amd-dal-Add-dal-display-driver.patch')
-rw-r--r-- | common/recipes-kernel/linux/files/0510-drm-amd-dal-Add-dal-display-driver.patch | 90113 |
1 files changed, 90113 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0510-drm-amd-dal-Add-dal-display-driver.patch b/common/recipes-kernel/linux/files/0510-drm-amd-dal-Add-dal-display-driver.patch new file mode 100644 index 00000000..1606a1aa --- /dev/null +++ b/common/recipes-kernel/linux/files/0510-drm-amd-dal-Add-dal-display-driver.patch @@ -0,0 +1,90113 @@ +From 35eea4f1b20ded08fc0d65891163d03238e3adf6 Mon Sep 17 00:00:00 2001 +From: Harry Wentland <harry.wentland@amd.com> +Date: Wed, 25 Nov 2015 14:45:50 -0500 +Subject: [PATCH 0510/1110] drm/amd/dal: Add dal display driver + +Signed-off-by: Harry Wentland <harry.wentland@amd.com> +Acked-by: Alex Deucher <alexander.deucher@amd.com> +--- + drivers/gpu/drm/amd/dal/Kconfig | 39 + + drivers/gpu/drm/amd/dal/Makefile | 19 + + .../gpu/drm/amd/dal/dal_power_interface_types.h | 76 + + drivers/gpu/drm/amd/dal/dal_services.h | 266 ++ + drivers/gpu/drm/amd/dal/dal_services_types.h | 62 + + drivers/gpu/drm/amd/dal/dc/Makefile | 24 + + drivers/gpu/drm/amd/dal/dc/adapter/Makefile | 18 + + .../gpu/drm/amd/dal/dc/adapter/adapter_service.c | 2037 +++++++++ + .../gpu/drm/amd/dal/dc/adapter/adapter_service.h | 67 + + .../adapter/dce110/hw_ctx_adapter_service_dce110.c | 303 ++ + .../adapter/dce110/hw_ctx_adapter_service_dce110.h | 40 + + .../amd/dal/dc/adapter/hw_ctx_adapter_service.c | 164 + + .../amd/dal/dc/adapter/hw_ctx_adapter_service.h | 86 + + .../drm/amd/dal/dc/adapter/wireless_data_source.c | 209 + + .../drm/amd/dal/dc/adapter/wireless_data_source.h | 80 + + .../gpu/drm/amd/dal/dc/asic_capability/Makefile | 23 + + .../amd/dal/dc/asic_capability/asic_capability.c | 178 + + .../dc/asic_capability/carrizo_asic_capability.c | 146 + + .../dc/asic_capability/carrizo_asic_capability.h | 36 + + drivers/gpu/drm/amd/dal/dc/audio/Makefile | 22 + + drivers/gpu/drm/amd/dal/dc/audio/audio.h | 195 + + drivers/gpu/drm/amd/dal/dc/audio/audio_base.c | 463 ++ + .../gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c | 452 ++ + .../gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h | 42 + + .../amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c | 1929 ++++++++ + .../amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h | 47 + + drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c | 771 ++++ + drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h | 285 ++ + drivers/gpu/drm/amd/dal/dc/basics/Makefile | 10 + + drivers/gpu/drm/amd/dal/dc/basics/conversion.c | 223 + + drivers/gpu/drm/amd/dal/dc/basics/conversion.h | 49 + + drivers/gpu/drm/amd/dal/dc/basics/fixpt31_32.c | 692 +++ + drivers/gpu/drm/amd/dal/dc/basics/fixpt32_32.c | 223 + + drivers/gpu/drm/amd/dal/dc/basics/grph_object_id.c | 135 + + drivers/gpu/drm/amd/dal/dc/basics/logger.c | 947 ++++ + drivers/gpu/drm/amd/dal/dc/basics/logger.h | 64 + + .../gpu/drm/amd/dal/dc/basics/register_logger.c | 197 + + drivers/gpu/drm/amd/dal/dc/basics/signal_types.c | 116 + + drivers/gpu/drm/amd/dal/dc/basics/vector.c | 309 ++ + drivers/gpu/drm/amd/dal/dc/bios/Makefile | 27 + + drivers/gpu/drm/amd/dal/dc/bios/bios_parser.c | 4758 ++++++++++++++++++++ + drivers/gpu/drm/amd/dal/dc/bios/bios_parser.h | 78 + + .../gpu/drm/amd/dal/dc/bios/bios_parser_helper.c | 193 + + .../gpu/drm/amd/dal/dc/bios/bios_parser_helper.h | 108 + + drivers/gpu/drm/amd/dal/dc/bios/command_table.c | 2616 +++++++++++ + drivers/gpu/drm/amd/dal/dc/bios/command_table.h | 117 + + .../gpu/drm/amd/dal/dc/bios/command_table_helper.c | 315 ++ + .../gpu/drm/amd/dal/dc/bios/command_table_helper.h | 87 + + .../dal/dc/bios/dce110/bios_parser_helper_dce110.c | 484 ++ + .../dal/dc/bios/dce110/bios_parser_helper_dce110.h | 34 + + .../dc/bios/dce110/command_table_helper_dce110.c | 369 ++ + .../dc/bios/dce110/command_table_helper_dce110.h | 34 + + drivers/gpu/drm/amd/dal/dc/calcs/Makefile | 10 + + drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c | 3478 ++++++++++++++ + drivers/gpu/drm/amd/dal/dc/calcs/bw_fixed.c | 278 ++ + drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.c | 1992 ++++++++ + drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.h | 74 + + drivers/gpu/drm/amd/dal/dc/connector/Makefile | 10 + + drivers/gpu/drm/amd/dal/dc/connector/connector.h | 39 + + .../gpu/drm/amd/dal/dc/connector/connector_base.c | 421 ++ + .../drm/amd/dal/dc/connector/connector_signals.c | 204 + + drivers/gpu/drm/amd/dal/dc/core/dc.c | 849 ++++ + drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c | 49 + + drivers/gpu/drm/amd/dal/dc/core/dc_link.c | 1081 +++++ + drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c | 1689 +++++++ + drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c | 188 + + drivers/gpu/drm/amd/dal/dc/core/dc_resource.c | 378 ++ + drivers/gpu/drm/amd/dal/dc/core/dc_sink.c | 118 + + drivers/gpu/drm/amd/dal/dc/core/dc_stream.c | 172 + + drivers/gpu/drm/amd/dal/dc/core/dc_surface.c | 124 + + drivers/gpu/drm/amd/dal/dc/core/dc_target.c | 473 ++ + drivers/gpu/drm/amd/dal/dc/dc.h | 440 ++ + drivers/gpu/drm/amd/dal/dc/dc_helpers.h | 75 + + drivers/gpu/drm/amd/dal/dc/dc_services.h | 174 + + drivers/gpu/drm/amd/dal/dc/dc_temp.h | 508 +++ + drivers/gpu/drm/amd/dal/dc/dc_types.h | 677 +++ + drivers/gpu/drm/amd/dal/dc/dce110/Makefile | 33 + + .../gpu/drm/amd/dal/dc/dce110/dce110_compressor.c | 886 ++++ + .../gpu/drm/amd/dal/dc/dce110/dce110_compressor.h | 84 + + .../drm/amd/dal/dc/dce110/dce110_hw_sequencer.c | 1825 ++++++++ + .../drm/amd/dal/dc/dce110/dce110_hw_sequencer.h | 36 + + drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.c | 85 + + drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.h | 90 + + .../gpu/drm/amd/dal/dc/dce110/dce110_ipp_cursor.c | 256 ++ + .../gpu/drm/amd/dal/dc/dce110/dce110_ipp_gamma.c | 877 ++++ + .../drm/amd/dal/dc/dce110/dce110_link_encoder.c | 2049 +++++++++ + .../drm/amd/dal/dc/dce110/dce110_link_encoder.h | 91 + + .../gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c | 969 ++++ + .../gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h | 88 + + drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c | 296 ++ + drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h | 140 + + drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c | 904 ++++ + .../drm/amd/dal/dc/dce110/dce110_opp_formatter.c | 610 +++ + .../gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c | 2473 ++++++++++ + .../gpu/drm/amd/dal/dc/dce110/dce110_resource.c | 1276 ++++++ + .../gpu/drm/amd/dal/dc/dce110/dce110_resource.h | 55 + + .../drm/amd/dal/dc/dce110/dce110_stream_encoder.c | 1168 +++++ + .../drm/amd/dal/dc/dce110/dce110_stream_encoder.h | 64 + + .../amd/dal/dc/dce110/dce110_timing_generator.c | 1878 ++++++++ + .../amd/dal/dc/dce110/dce110_timing_generator.h | 178 + + .../gpu/drm/amd/dal/dc/dce110/dce110_transform.c | 116 + + .../gpu/drm/amd/dal/dc/dce110/dce110_transform.h | 91 + + .../amd/dal/dc/dce110/dce110_transform_bit_depth.c | 840 ++++ + .../amd/dal/dc/dce110/dce110_transform_bit_depth.h | 51 + + .../drm/amd/dal/dc/dce110/dce110_transform_gamut.c | 297 ++ + .../drm/amd/dal/dc/dce110/dce110_transform_scl.c | 818 ++++ + .../drm/amd/dal/dc/dce110/dce110_transform_sclv.c | 531 +++ + drivers/gpu/drm/amd/dal/dc/dcs/Makefile | 10 + + drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.c | 159 + + drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.h | 60 + + drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.c | 1034 +++++ + drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.h | 38 + + drivers/gpu/drm/amd/dal/dc/gpio/Makefile | 24 + + .../gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.c | 883 ++++ + .../gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.h | 46 + + .../drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.c | 84 + + .../drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.h | 32 + + .../gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.c | 367 ++ + .../gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.h | 47 + + .../amd/dal/dc/gpio/dce110/hw_translate_dce110.c | 440 ++ + .../amd/dal/dc/gpio/dce110/hw_translate_dce110.h | 34 + + drivers/gpu/drm/amd/dal/dc/gpio/ddc.c | 290 ++ + drivers/gpu/drm/amd/dal/dc/gpio/ddc.h | 45 + + drivers/gpu/drm/amd/dal/dc/gpio/dvo.c | 138 + + drivers/gpu/drm/amd/dal/dc/gpio/dvo.h | 42 + + drivers/gpu/drm/amd/dal/dc/gpio/gpio.h | 48 + + drivers/gpu/drm/amd/dal/dc/gpio/gpio_base.c | 279 ++ + drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.c | 470 ++ + drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.h | 57 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.c | 105 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.h | 60 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.c | 318 ++ + drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.h | 89 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c | 80 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.h | 74 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.c | 408 ++ + drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.h | 129 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.c | 93 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.h | 47 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.c | 86 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.h | 79 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.c | 88 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.h | 45 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c | 67 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.h | 49 + + drivers/gpu/drm/amd/dal/dc/gpio/irq.c | 181 + + drivers/gpu/drm/amd/dal/dc/gpio/irq.h | 42 + + drivers/gpu/drm/amd/dal/dc/gpu/Makefile | 26 + + .../gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.c | 407 ++ + .../gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.h | 79 + + drivers/gpu/drm/amd/dal/dc/gpu/clock_source.c | 649 +++ + drivers/gpu/drm/amd/dal/dc/gpu/clock_source.h | 136 + + .../gpu/drm/amd/dal/dc/gpu/dc_clock_generator.c | 92 + + .../gpu/drm/amd/dal/dc/gpu/dc_clock_generator.h | 63 + + .../amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.c | 90 + + .../amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.h | 33 + + .../amd/dal/dc/gpu/dce110/display_clock_dce110.c | 958 ++++ + .../amd/dal/dc/gpu/dce110/display_clock_dce110.h | 53 + + .../dal/dc/gpu/dce110/ext_clock_source_dce110.c | 383 ++ + .../dal/dc/gpu/dce110/ext_clock_source_dce110.h | 38 + + .../dal/dc/gpu/dce110/pll_clock_source_dce110.c | 718 +++ + .../dal/dc/gpu/dce110/pll_clock_source_dce110.h | 55 + + .../dal/dc/gpu/dce110/vce_clock_source_dce110.c | 193 + + .../dal/dc/gpu/dce110/vce_clock_source_dce110.h | 32 + + drivers/gpu/drm/amd/dal/dc/gpu/display_clock.c | 204 + + drivers/gpu/drm/amd/dal/dc/gpu/display_clock.h | 82 + + drivers/gpu/drm/amd/dal/dc/gpu/divider_range.c | 127 + + drivers/gpu/drm/amd/dal/dc/gpu/divider_range.h | 63 + + drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.c | 119 + + drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.h | 47 + + drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.c | 141 + + drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.h | 52 + + drivers/gpu/drm/amd/dal/dc/i2caux/Makefile | 23 + + drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.c | 568 +++ + drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.h | 119 + + .../amd/dal/dc/i2caux/dce110/aux_engine_dce110.c | 789 ++++ + .../amd/dal/dc/i2caux/dce110/aux_engine_dce110.h | 56 + + .../i2caux/dce110/i2c_generic_hw_engine_dce110.h | 25 + + .../dal/dc/i2caux/dce110/i2c_hw_engine_dce110.c | 954 ++++ + .../dal/dc/i2caux/dce110/i2c_hw_engine_dce110.h | 58 + + .../dal/dc/i2caux/dce110/i2c_sw_engine_dce110.c | 172 + + .../dal/dc/i2caux/dce110/i2c_sw_engine_dce110.h | 43 + + .../drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.c | 260 ++ + .../drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.h | 39 + + drivers/gpu/drm/amd/dal/dc/i2caux/engine.h | 129 + + drivers/gpu/drm/amd/dal/dc/i2caux/engine_base.c | 68 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.c | 122 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.h | 113 + + .../drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.c | 287 ++ + .../drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.h | 77 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.c | 247 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.h | 80 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.c | 615 +++ + drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.h | 81 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c | 519 +++ + drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.h | 123 + + drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h | 463 ++ + drivers/gpu/drm/amd/dal/dc/inc/bw_fixed.h | 60 + + drivers/gpu/drm/amd/dal/dc/inc/compressor.h | 140 + + drivers/gpu/drm/amd/dal/dc/inc/core_dc.h | 39 + + drivers/gpu/drm/amd/dal/dc/inc/core_status.h | 46 + + drivers/gpu/drm/amd/dal/dc/inc/core_types.h | 308 ++ + drivers/gpu/drm/amd/dal/dc/inc/dc_link_dp.h | 51 + + drivers/gpu/drm/amd/dal/dc/inc/hw_sequencer.h | 170 + + drivers/gpu/drm/amd/dal/dc/inc/ipp.h | 66 + + drivers/gpu/drm/amd/dal/dc/inc/link_hwss.h | 67 + + drivers/gpu/drm/amd/dal/dc/inc/mem_input.h | 55 + + drivers/gpu/drm/amd/dal/dc/inc/opp.h | 206 + + drivers/gpu/drm/amd/dal/dc/inc/resource.h | 61 + + drivers/gpu/drm/amd/dal/dc/inc/transform.h | 81 + + drivers/gpu/drm/amd/dal/dc/irq/Makefile | 21 + + .../drm/amd/dal/dc/irq/dce110/irq_service_dce110.c | 389 ++ + .../drm/amd/dal/dc/irq/dce110/irq_service_dce110.h | 34 + + drivers/gpu/drm/amd/dal/dc/irq/irq_service.c | 173 + + drivers/gpu/drm/amd/dal/dc/irq/irq_service.h | 85 + + drivers/gpu/drm/amd/dal/dc/irq_types.h | 199 + + .../amd/dal/include/adapter_service_interface.h | 628 +++ + .../drm/amd/dal/include/adapter_service_types.h | 70 + + .../gpu/drm/amd/dal/include/adjustment_interface.h | 230 + + drivers/gpu/drm/amd/dal/include/adjustment_types.h | 420 ++ + .../amd/dal/include/asic_capability_interface.h | 58 + + .../drm/amd/dal/include/asic_capability_types.h | 134 + + drivers/gpu/drm/amd/dal/include/audio_interface.h | 184 + + drivers/gpu/drm/amd/dal/include/audio_types.h | 275 ++ + .../drm/amd/dal/include/bios_parser_interface.h | 294 ++ + .../gpu/drm/amd/dal/include/bios_parser_types.h | 305 ++ + drivers/gpu/drm/amd/dal/include/bit_set.h | 61 + + .../drm/amd/dal/include/clock_source_interface.h | 89 + + .../gpu/drm/amd/dal/include/clock_source_types.h | 118 + + .../gpu/drm/amd/dal/include/connector_interface.h | 82 + + drivers/gpu/drm/amd/dal/include/dal_asic_id.h | 106 + + .../gpu/drm/amd/dal/include/dal_register_logger.h | 43 + + drivers/gpu/drm/amd/dal/include/dal_types.h | 292 ++ + .../amd/dal/include/dc_clock_generator_interface.h | 77 + + drivers/gpu/drm/amd/dal/include/dcs_interface.h | 351 ++ + drivers/gpu/drm/amd/dal/include/dcs_types.h | 742 +++ + drivers/gpu/drm/amd/dal/include/ddc_interface.h | 74 + + .../drm/amd/dal/include/ddc_service_interface.h | 100 + + .../gpu/drm/amd/dal/include/ddc_service_types.h | 220 + + .../amd/dal/include/default_mode_list_interface.h | 37 + + .../drm/amd/dal/include/display_clock_interface.h | 189 + + .../drm/amd/dal/include/display_path_interface.h | 436 ++ + .../gpu/drm/amd/dal/include/display_path_types.h | 132 + + .../amd/dal/include/display_service_interface.h | 165 + + .../drm/amd/dal/include/display_service_types.h | 167 + + drivers/gpu/drm/amd/dal/include/dmcu_interface.h | 87 + + drivers/gpu/drm/amd/dal/include/dmcu_types.h | 199 + + .../dal/include/dpcd_access_service_interface.h | 65 + + drivers/gpu/drm/amd/dal/include/dpcd_defs.h | 869 ++++ + drivers/gpu/drm/amd/dal/include/dvo_interface.h | 48 + + .../gpu/drm/amd/dal/include/encoder_interface.h | 278 ++ + drivers/gpu/drm/amd/dal/include/encoder_types.h | 216 + + drivers/gpu/drm/amd/dal/include/fixed31_32.h | 389 ++ + drivers/gpu/drm/amd/dal/include/fixed32_32.h | 80 + + drivers/gpu/drm/amd/dal/include/gpio_interface.h | 93 + + .../drm/amd/dal/include/gpio_service_interface.h | 94 + + drivers/gpu/drm/amd/dal/include/gpio_types.h | 393 ++ + drivers/gpu/drm/amd/dal/include/gpu_clock_info.h | 43 + + drivers/gpu/drm/amd/dal/include/gpu_interface.h | 91 + + drivers/gpu/drm/amd/dal/include/grph_csc_types.h | 98 + + .../drm/amd/dal/include/grph_object_ctrl_defs.h | 598 +++ + drivers/gpu/drm/amd/dal/include/grph_object_defs.h | 328 ++ + drivers/gpu/drm/amd/dal/include/grph_object_id.h | 285 ++ + .../gpu/drm/amd/dal/include/hw_adjustment_set.h | 50 + + .../gpu/drm/amd/dal/include/hw_adjustment_types.h | 205 + + .../amd/dal/include/hw_path_mode_set_interface.h | 48 + + .../drm/amd/dal/include/hw_sequencer_interface.h | 388 ++ + .../gpu/drm/amd/dal/include/hw_sequencer_types.h | 305 ++ + drivers/gpu/drm/amd/dal/include/i2caux_interface.h | 127 + + drivers/gpu/drm/amd/dal/include/irq_interface.h | 53 + + .../drm/amd/dal/include/irq_service_interface.h | 55 + + drivers/gpu/drm/amd/dal/include/isr_config_types.h | 157 + + .../gpu/drm/amd/dal/include/link_encoder_types.h | 32 + + .../drm/amd/dal/include/link_service_interface.h | 202 + + .../gpu/drm/amd/dal/include/link_service_types.h | 428 ++ + drivers/gpu/drm/amd/dal/include/logger_interface.h | 153 + + drivers/gpu/drm/amd/dal/include/logger_types.h | 356 ++ + .../gpu/drm/amd/dal/include/mode_manager_types.h | 71 + + .../gpu/drm/amd/dal/include/mode_query_interface.h | 93 + + .../amd/dal/include/mode_timing_list_interface.h | 51 + + .../gpu/drm/amd/dal/include/overlay_interface.h | 137 + + drivers/gpu/drm/amd/dal/include/overlay_types.h | 164 + + .../drm/amd/dal/include/path_mode_set_interface.h | 107 + + drivers/gpu/drm/amd/dal/include/plane_types.h | 309 ++ + drivers/gpu/drm/amd/dal/include/scaler_types.h | 196 + + .../amd/dal/include/set_mode_params_interface.h | 101 + + drivers/gpu/drm/amd/dal/include/set_mode_types.h | 285 ++ + drivers/gpu/drm/amd/dal/include/signal_types.h | 58 + + .../gpu/drm/amd/dal/include/stream_encoder_types.h | 16 + + .../drm/amd/dal/include/timing_generator_types.h | 150 + + .../amd/dal/include/timing_list_query_interface.h | 69 + + drivers/gpu/drm/amd/dal/include/vector.h | 150 + + drivers/gpu/drm/amd/dal/include/video_csc_types.h | 135 + + .../gpu/drm/amd/dal/include/video_gamma_types.h | 56 + + 294 files changed, 87748 insertions(+) + create mode 100644 drivers/gpu/drm/amd/dal/Kconfig + create mode 100644 drivers/gpu/drm/amd/dal/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dal_power_interface_types.h + create mode 100644 drivers/gpu/drm/amd/dal/dal_services.h + create mode 100644 drivers/gpu/drm/amd/dal/dal_services_types.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/asic_capability.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/audio.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/audio_base.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/conversion.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/conversion.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/fixpt31_32.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/fixpt32_32.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/grph_object_id.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/logger.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/logger.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/register_logger.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/signal_types.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/basics/vector.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/bios_parser.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/bios_parser.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/command_table.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/command_table.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/calcs/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/calcs/bw_fixed.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/connector/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/connector/connector.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/connector/connector_base.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/connector/connector_signals.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_resource.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_sink.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_stream.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_surface.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_target.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dc.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dc_helpers.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dc_services.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dc_temp.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dc_types.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_cursor.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_gamma.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dcs/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/ddc.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/ddc.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dvo.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dvo.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/gpio.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/gpio_base.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/irq.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/irq.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/clock_source.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/clock_source.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/display_clock.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/display_clock.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/divider_range.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/divider_range.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_generic_hw_engine_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/engine.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/engine_base.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/bw_fixed.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/compressor.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/core_dc.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/core_status.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/core_types.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/dc_link_dp.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/hw_sequencer.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/ipp.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/link_hwss.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/mem_input.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/opp.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/resource.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/inc/transform.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/irq/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/irq/irq_service.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/irq/irq_service.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/irq_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/adapter_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/adapter_service_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/adjustment_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/adjustment_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/asic_capability_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/asic_capability_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/audio_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/audio_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/bios_parser_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/bios_parser_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/bit_set.h + create mode 100644 drivers/gpu/drm/amd/dal/include/clock_source_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/clock_source_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/connector_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dal_asic_id.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dal_register_logger.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dal_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dc_clock_generator_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dcs_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dcs_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/ddc_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/ddc_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/ddc_service_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/display_clock_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/display_path_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/display_path_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/display_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/display_service_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dmcu_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dmcu_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dpcd_access_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dpcd_defs.h + create mode 100644 drivers/gpu/drm/amd/dal/include/dvo_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/encoder_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/encoder_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/fixed31_32.h + create mode 100644 drivers/gpu/drm/amd/dal/include/fixed32_32.h + create mode 100644 drivers/gpu/drm/amd/dal/include/gpio_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/gpio_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/gpio_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/gpu_clock_info.h + create mode 100644 drivers/gpu/drm/amd/dal/include/gpu_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/grph_csc_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/grph_object_ctrl_defs.h + create mode 100644 drivers/gpu/drm/amd/dal/include/grph_object_defs.h + create mode 100644 drivers/gpu/drm/amd/dal/include/grph_object_id.h + create mode 100644 drivers/gpu/drm/amd/dal/include/hw_adjustment_set.h + create mode 100644 drivers/gpu/drm/amd/dal/include/hw_adjustment_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/hw_path_mode_set_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/hw_sequencer_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/hw_sequencer_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/i2caux_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/irq_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/irq_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/isr_config_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/link_encoder_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/link_service_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/link_service_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/logger_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/logger_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/mode_manager_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/mode_query_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/mode_timing_list_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/overlay_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/overlay_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/path_mode_set_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/plane_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/scaler_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/set_mode_params_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/set_mode_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/signal_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/stream_encoder_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/timing_generator_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/timing_list_query_interface.h + create mode 100644 drivers/gpu/drm/amd/dal/include/vector.h + create mode 100644 drivers/gpu/drm/amd/dal/include/video_csc_types.h + create mode 100644 drivers/gpu/drm/amd/dal/include/video_gamma_types.h + +diff --git a/drivers/gpu/drm/amd/dal/Kconfig b/drivers/gpu/drm/amd/dal/Kconfig +new file mode 100644 +index 0000000..14df02e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/Kconfig +@@ -0,0 +1,39 @@ ++menu "Display Engine Configuration" ++ depends on DRM && (DRM_AMDSOC || DRM_AMDGPU) ++ ++config DRM_AMD_DAL ++ bool "AMD DAL - Enable new display engine (will be deprecated when the development is done)" ++ help ++ Choose this option if you want to use the new display engine ++ support for AMD SOC. ++ ++ Will be deprecated when the DAL component becomes stable and ++ AMDSOC will fully switch to it. ++ ++config DRM_AMD_DAL_VBIOS_PRESENT ++ bool "Video Bios available on board" ++ depends on DRM_AMD_DAL ++ help ++ This option is needed to allow a full range of feature ++ support when working on ++ x86 platforms and there is a VBIOS ++ present in the system ++ ++config DRM_AMD_DAL_DCE11_0 ++ bool "Carrizo family" ++ depends on DRM_AMD_DAL ++ help ++ Choose this option ++ if you want to have ++ CZ family ++ for display engine ++ ++config DEBUG_KERNEL_DAL ++ bool "Enable kgdb break in DAL" ++ depends on DRM_AMD_DAL ++ help ++ Choose this option ++ if you want to hit ++ kdgb_break in assert. ++ ++endmenu +diff --git a/drivers/gpu/drm/amd/dal/Makefile b/drivers/gpu/drm/amd/dal/Makefile +new file mode 100644 +index 0000000..bdf5d18 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/Makefile +@@ -0,0 +1,19 @@ ++# ++# Makefile for the DAL (Display Abstract Layer), which is a sub-component ++# of the AMDGPU drm driver. ++# It provides the HW control for display related functionalities. ++ ++AMDDALPATH = $(RELATIVE_AMD_DAL_PATH) ++ ++subdir-ccflags-y += -I$(AMDDALPATH)/ -I$(AMDDALPATH)/include -DDAL_CZ_BRINGUP ++ ++subdir-ccflags-y += -I$(FULL_AMD_DAL_PATH)/dc/inc/ ++ ++#TODO: remove when Timing Sync feature is complete ++subdir-ccflags-y += -DBUILD_FEATURE_TIMING_SYNC=0 ++ ++DAL_LIBS = amdgpu_dm dc ++ ++AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/,$(DAL_LIBS))) ++ ++include $(AMD_DAL) +diff --git a/drivers/gpu/drm/amd/dal/dal_power_interface_types.h b/drivers/gpu/drm/amd/dal/dal_power_interface_types.h +new file mode 100644 +index 0000000..82e8ca2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dal_power_interface_types.h +@@ -0,0 +1,76 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_POWER_INTERFACE_TYPES_H__ ++#define __DAL_POWER_INTERFACE_TYPES_H__ ++ ++enum dal_to_power_clocks_state { ++ PP_CLOCKS_STATE_INVALID, ++ PP_CLOCKS_STATE_ULTRA_LOW, ++ PP_CLOCKS_STATE_LOW, ++ PP_CLOCKS_STATE_NOMINAL, ++ PP_CLOCKS_STATE_PERFORMANCE ++}; ++ ++/* clocks in khz */ ++struct dal_to_power_info { ++ enum dal_to_power_clocks_state required_clock; ++ uint32_t min_sclk; ++ uint32_t min_mclk; ++ uint32_t min_deep_sleep_sclk; ++}; ++ ++/* clocks in khz */ ++struct power_to_dal_info { ++ uint32_t min_sclk; ++ uint32_t max_sclk; ++ uint32_t min_mclk; ++ uint32_t max_mclk; ++}; ++ ++/* clocks in khz */ ++struct dal_system_clock_range { ++ uint32_t min_sclk; ++ uint32_t max_sclk; ++ ++ uint32_t min_mclk; ++ uint32_t max_mclk; ++ ++ uint32_t min_dclk; ++ uint32_t max_dclk; ++ ++ /* Wireless Display */ ++ uint32_t min_eclk; ++ uint32_t max_eclk; ++}; ++ ++/* clocks in khz */ ++struct dal_to_power_dclk { ++ uint32_t optimal; /* input: best optimizes for stutter efficiency */ ++ uint32_t minimal; /* input: the lowest clk that DAL can support */ ++ uint32_t established; /* output: the actually set one */ ++}; ++ ++#endif /* __DAL_POWER_INTERFACE_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dal_services.h b/drivers/gpu/drm/amd/dal/dal_services.h +new file mode 100644 +index 0000000..398e4e5 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dal_services.h +@@ -0,0 +1,266 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_SERVICES_H__ ++#define __DAL_SERVICES_H__ ++ ++/* DC headers*/ ++#include "dc/dc_services.h" ++ ++#include "dal_power_interface_types.h" ++ ++#include "irq_types.h" ++#include "include/dal_types.h" ++ ++/* TODO: investigate if it can be removed. */ ++/* Undefine DEPRECATED because it conflicts with printk.h */ ++#undef DEPRECATED ++ ++/* ++ * ++ * interrupt services to register and unregister handlers ++ * ++ */ ++ ++/* the timer "interrupt" current implementation supports only ++'one-shot' type, and LOW level (asynchronous) context */ ++void dal_register_timer_interrupt( ++ struct dc_context *ctx, ++ struct dc_timer_interrupt_params *int_params, ++ interrupt_handler ih, ++ void *handler_args); ++ ++/* ++ * ++ * kernel memory manipulation ++ * ++ */ ++ ++/* Reallocate memory. The contents will remain unchanged.*/ ++void *dc_service_realloc(struct dc_context *ctx, const void *ptr, uint32_t size); ++ ++void dc_service_memmove(void *dst, const void *src, uint32_t size); ++ ++void dc_service_memset(void *p, int32_t c, uint32_t count); ++ ++int32_t dal_memcmp(const void *p1, const void *p2, uint32_t count); ++ ++int32_t dal_strncmp(const int8_t *p1, const int8_t *p2, uint32_t count); ++ ++/* ++ * ++ * GPU registers access ++ * ++ */ ++static inline uint32_t dal_read_reg( ++ const struct dc_context *ctx, ++ uint32_t address) ++{ ++ uint32_t value = cgs_read_register(ctx->cgs_device, address); ++ ++#if defined(__DAL_REGISTER_LOGGER__) ++ if (true == dal_reg_logger_should_dump_register()) { ++ dal_reg_logger_rw_count_increment(); ++ DRM_INFO("%s 0x%x 0x%x\n", __func__, address, value); ++ } ++#endif ++ return value; ++} ++ ++static inline uint32_t get_reg_field_value_ex( ++ uint32_t reg_value, ++ uint32_t mask, ++ uint8_t shift) ++{ ++ return (mask & reg_value) >> shift; ++} ++ ++#define get_reg_field_value(reg_value, reg_name, reg_field)\ ++ get_reg_field_value_ex(\ ++ (reg_value),\ ++ reg_name ## __ ## reg_field ## _MASK,\ ++ reg_name ## __ ## reg_field ## __SHIFT) ++ ++static inline uint32_t set_reg_field_value_ex( ++ uint32_t reg_value, ++ uint32_t value, ++ uint32_t mask, ++ uint8_t shift) ++{ ++ return (reg_value & ~mask) | (mask & (value << shift)); ++} ++ ++#define set_reg_field_value(reg_value, value, reg_name, reg_field)\ ++ (reg_value) = set_reg_field_value_ex(\ ++ (reg_value),\ ++ (value),\ ++ reg_name ## __ ## reg_field ## _MASK,\ ++ reg_name ## __ ## reg_field ## __SHIFT) ++ ++static inline void dal_write_reg( ++ const struct dc_context *ctx, ++ uint32_t address, ++ uint32_t value) ++{ ++#if defined(__DAL_REGISTER_LOGGER__) ++ if (true == dal_reg_logger_should_dump_register()) { ++ dal_reg_logger_rw_count_increment(); ++ DRM_INFO("%s 0x%x 0x%x\n", __func__, address, value); ++ } ++#endif ++ cgs_write_register(ctx->cgs_device, address, value); ++} ++ ++static inline uint32_t dal_read_index_reg( ++ const struct dc_context *ctx, ++ enum cgs_ind_reg addr_space, ++ uint32_t index) ++{ ++ return cgs_read_ind_register(ctx->cgs_device,addr_space,index); ++} ++ ++static inline void dal_write_index_reg( ++ const struct dc_context *ctx, ++ enum cgs_ind_reg addr_space, ++ uint32_t index, ++ uint32_t value) ++{ ++ cgs_write_ind_register(ctx->cgs_device,addr_space,index,value); ++} ++ ++enum platform_method { ++ PM_GET_AVAILABLE_METHODS = 1 << 0, ++ PM_GET_LID_STATE = 1 << 1, ++ PM_GET_EXTENDED_BRIGHNESS_CAPS = 1 << 2 ++}; ++ ++struct platform_info_params { ++ enum platform_method method; ++ void *data; ++}; ++ ++struct platform_info_brightness_caps { ++ uint8_t ac_level_percentage; ++ uint8_t dc_level_percentage; ++}; ++ ++struct platform_info_ext_brightness_caps { ++ struct platform_info_brightness_caps basic_caps; ++ struct data_point { ++ uint8_t luminance; ++ uint8_t signal_level; ++ } data_points[99]; ++ ++ uint8_t data_points_num; ++ uint8_t min_input_signal; ++ uint8_t max_input_signal; ++}; ++ ++bool dal_get_platform_info( ++ struct dc_context *ctx, ++ struct platform_info_params *params); ++ ++ ++static inline uint32_t dal_bios_cmd_table_para_revision( ++ struct dc_context *ctx, ++ uint32_t index) ++{ ++ uint8_t frev; ++ uint8_t crev; ++ ++ if (cgs_atom_get_cmd_table_revs( ++ ctx->cgs_device, ++ index, ++ &frev, ++ &crev) != 0) ++ return 0; ++ ++ return crev; ++} ++ ++/* Calls to notification */ ++ ++/* Notify display manager for hotplug event */ ++void dal_notify_hotplug( ++ struct dc_context *ctx, ++ uint32_t display_index, ++ bool is_connected); ++ ++ ++void dal_notify_setmode_complete( ++ struct dc_context *ctx, ++ uint32_t h_total, ++ uint32_t v_total, ++ uint32_t h_active, ++ uint32_t v_active, ++ uint32_t pix_clk_in_khz); ++ ++/* End of notification calls */ ++ ++/* ++ * ++ * Delay functions. ++ * ++ * ++ */ ++ ++/* Following the guidance: ++ * https://www.kernel.org/doc/Documentation/timers/timers-howto.txt ++ * ++ * This is a busy wait for nano seconds and should be used only for ++ * extremely short ranges ++ */ ++void dal_delay_in_nanoseconds(uint32_t nanoseconds); ++ ++ ++/* ++ * ++ * atombios services ++ * ++ */ ++ ++bool dal_exec_bios_cmd_table( ++ struct dc_context *ctx, ++ uint32_t index, ++ void *params); ++ ++/* ++ * ++ * print-out services ++ * ++ */ ++#define dal_log_to_buffer(buffer, size, fmt, args)\ ++ vsnprintf(buffer, size, fmt, args) ++ ++long dal_get_pid(void); ++long dal_get_tgid(void); ++ ++/* ++ * ++ * general debug capabilities ++ * ++ */ ++ ++#endif /* __DAL_SERVICES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dal_services_types.h b/drivers/gpu/drm/amd/dal/dal_services_types.h +new file mode 100644 +index 0000000..89c73c6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dal_services_types.h +@@ -0,0 +1,62 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_SERVICES_TYPES_H__ ++#define __DAL_SERVICES_TYPES_H__ ++ ++#define INVALID_DISPLAY_INDEX 0xffffffff ++ ++#if defined __KERNEL__ ++ ++#include <asm/byteorder.h> ++#include <linux/types.h> ++#include <drm/drmP.h> ++ ++#include "cgs_linux.h" ++ ++#if defined(__BIG_ENDIAN) && !defined(BIGENDIAN_CPU) ++#define BIGENDIAN_CPU ++#elif defined(__LITTLE_ENDIAN) && !defined(LITTLEENDIAN_CPU) ++#define LITTLEENDIAN_CPU ++#endif ++ ++#undef READ ++#undef WRITE ++#undef FRAME_SIZE ++ ++#define dal_output_to_console(fmt, ...) DRM_INFO(fmt, ##__VA_ARGS__) ++ ++#define dal_error(fmt, ...) DRM_ERROR(fmt, ##__VA_ARGS__) ++ ++#define dal_debug(fmt, ...) DRM_DEBUG_KMS(fmt, ##__VA_ARGS__) ++ ++#define dal_vlog(fmt, args) vprintk(fmt, args) ++ ++#define dal_min(x, y) min(x, y) ++#define dal_max(x, y) max(x, y) ++ ++#endif ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/Makefile b/drivers/gpu/drm/amd/dal/dc/Makefile +new file mode 100644 +index 0000000..6926356 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/Makefile +@@ -0,0 +1,24 @@ ++# ++# Makefile for Display Core (dc) component. ++# ++ ++DC_LIBS = adapter asic_capability audio basics bios calcs connector \ ++dcs gpio gpu i2caux irq ++ ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++DC_LIBS += dce110 ++endif ++ ++AMD_DC = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/dc/,$(DC_LIBS))) ++ ++include $(AMD_DC) ++ ++DISPLAY_CORE = dc.o dc_link.o dc_resource.o dc_target.o dc_sink.o dc_stream.o \ ++dc_hw_sequencer.o dc_surface.o dc_link_hwss.o dc_link_dp.o ++ ++AMD_DISPLAY_CORE = $(addprefix $(AMDDALPATH)/dc/core/,$(DISPLAY_CORE)) ++ ++AMD_DAL_FILES += $(AMD_DISPLAY_CORE) ++ ++ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/Makefile b/drivers/gpu/drm/amd/dal/dc/adapter/Makefile +new file mode 100644 +index 0000000..8ede504 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/Makefile +@@ -0,0 +1,18 @@ ++# ++# Makefile for the 'adapter' sub-component of DAL. ++# It provides the control and status of HW adapter. ++ ++ADAPTER = adapter_service.o hw_ctx_adapter_service.o wireless_data_source.o ++ ++AMD_DAL_ADAPTER = $(addprefix $(AMDDALPATH)/dc/adapter/,$(ADAPTER)) ++ ++AMD_DAL_FILES += $(AMD_DAL_ADAPTER) ++ ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++AMD_DAL_FILES += $(AMDDALPATH)/dc/adapter/dce110/hw_ctx_adapter_service_dce110.o ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c +new file mode 100644 +index 0000000..4f9a637 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c +@@ -0,0 +1,2037 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++ ++#include "dal_services.h" ++ ++#include "include/adapter_service_interface.h" ++#include "include/i2caux_interface.h" ++#include "include/asic_capability_types.h" ++#include "include/bios_parser_interface.h" ++#include "include/gpio_service_interface.h" ++#include "include/asic_capability_interface.h" ++#include "include/logger_interface.h" ++ ++#include "adapter_service.h" ++#include "hw_ctx_adapter_service.h" ++#include "wireless_data_source.h" ++ ++#include "atom.h" ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/hw_ctx_adapter_service_dce110.h" ++#endif ++ ++/* ++ * Adapter service feature entry table. ++ * ++ * This is an array of features that is used to generate feature set. Each ++ * entry consists three element: ++ * ++ * Feature name, default value, and if this feature is a boolean type. A ++ * feature can only be a boolean or int type. ++ * ++ * Example 1: a boolean type feature ++ * FEATURE_ENABLE_HW_EDID_POLLING, false, true ++ * ++ * First element is feature name: EATURE_ENABLE_HW_EDID_POLLING, it has a ++ * default value 0, and it is a boolean feature. ++ * ++ * Example 2: an int type feature ++ * FEATURE_DCP_PROGRAMMING_WA, 0x1FF7, false ++ * ++ * In this case, the default value is 0x1FF7 and not a boolean type, which ++ * makes it an int type. ++ */ ++ ++static ++#if !defined(DAL_CZ_BRINGUP) ++const ++#endif ++struct feature_source_entry feature_entry_table[] = { ++ /* Feature name | default value | is boolean type */ ++ {FEATURE_ENABLE_HW_EDID_POLLING, false, true}, ++ {FEATURE_DP_SINK_DETECT_POLL_DATA_PIN, false, true}, ++ {FEATURE_UNDERFLOW_INTERRUPT, false, true}, ++ {FEATURE_ALLOW_WATERMARK_ADJUSTMENT, false, true}, ++ {FEATURE_LIGHT_SLEEP, false, true}, ++ {FEATURE_DCP_DITHER_FRAME_RANDOM_ENABLE, false, true}, ++ {FEATURE_DCP_DITHER_RGB_RANDOM_ENABLE, false, true}, ++ {FEATURE_DCP_DITHER_HIGH_PASS_RANDOM_ENABLE, false, true}, ++ {FEATURE_LINE_BUFFER_ENHANCED_PIXEL_DEPTH, false, true}, ++ {FEATURE_MAXIMIZE_URGENCY_WATERMARKS, false, true}, ++ {FEATURE_MAXIMIZE_STUTTER_MARKS, false, true}, ++ {FEATURE_MAXIMIZE_NBP_MARKS, false, true}, ++ /* ++ * We meet HW I2C issue when test S3 resume on KB. ++ * An EPR is created for debug the issue. ++ * Make Test has already been implemented ++ * with HW I2C. The work load for revert back to SW I2C in make test ++ * is big. Below is workaround for this issue. ++ * Driver uses SW I2C. ++ * Make Test uses HW I2C. ++ */ ++#if defined(DAL_CZ_BRINGUP) ++ {FEATURE_RESTORE_USAGE_I2C_SW_ENGINE, true, true}, ++#else ++ {FEATURE_RESTORE_USAGE_I2C_SW_ENGINE, false, true}, ++#endif ++ {FEATURE_USE_MAX_DISPLAY_CLK, false, true}, ++ {FEATURE_ALLOW_EDP_RESOURCE_SHARING, false, true}, ++ {FEATURE_SUPPORT_DP_YUV, false, true}, ++ {FEATURE_SUPPORT_DP_Y_ONLY, false, true}, ++ {FEATURE_DISABLE_DP_GTC_SYNC, true, true}, ++ {FEATURE_MODIFY_TIMINGS_FOR_WIRELESS, false, true}, ++ {FEATURE_DCP_BIT_DEPTH_REDUCTION_MODE, 0, false}, ++ {FEATURE_DCP_DITHER_MODE, 0, false}, ++ {FEATURE_DCP_PROGRAMMING_WA, 0, false}, ++ {FEATURE_NO_HPD_LOW_POLLING_VCC_OFF, false, true}, ++ {FEATURE_ENABLE_DFS_BYPASS, false, true}, ++ {FEATURE_WIRELESS_FULL_TIMING_ADJUSTMENT, false, true}, ++ {FEATURE_MAX_COFUNC_NON_DP_DISPLAYS, 2, false}, ++ {FEATURE_WIRELESS_LIMIT_720P, false, true}, ++ {FEATURE_MODIFY_TIMINGS_FOR_WIRELESS, false, true}, ++ {FEATURE_SUPPORTED_HDMI_CONNECTION_NUM, 0, false}, ++ {FEATURE_DETECT_REQUIRE_HPD_HIGH, false, true}, ++ {FEATURE_NO_HPD_LOW_POLLING_VCC_OFF, false, true}, ++ {FEATURE_LB_HIGH_RESOLUTION, false, true}, ++ {FEATURE_MAX_CONTROLLER_NUM, 0, false}, ++ {FEATURE_DRR_SUPPORT, AS_DRR_SUPPORT_ENABLED, false}, ++ {FEATURE_STUTTER_MODE, 15, false}, ++ {FEATURE_DP_DISPLAY_FORCE_SS_ENABLE, false, true}, ++ {FEATURE_REPORT_CE_MODE_ONLY, false, true}, ++ {FEATURE_ALLOW_OPTIMIZED_MODE_AS_DEFAULT, false, true}, ++ {FEATURE_DDC_READ_FORCE_REPEATED_START, false, true}, ++ {FEATURE_FORCE_TIMING_RESYNC, false, true}, ++ {FEATURE_TMDS_DISABLE_DITHERING, false, true}, ++ {FEATURE_HDMI_DISABLE_DITHERING, false, true}, ++ {FEATURE_DP_DISABLE_DITHERING, false, true}, ++ {FEATURE_EMBEDDED_DISABLE_DITHERING, true, true}, ++ {FEATURE_ALLOW_SELF_REFRESH, false, true}, ++ {FEATURE_ALLOW_DYNAMIC_PIXEL_ENCODING_CHANGE, false, true}, ++ {FEATURE_ALLOW_HSYNC_VSYNC_ADJUSTMENT, false, true}, ++ {FEATURE_FORCE_PSR, false, true}, ++ {FEATURE_PSR_SETUP_TIME_TEST, 0, false}, ++ {FEATURE_POWER_GATING_PIPE_IN_TILE, true, true}, ++ {FEATURE_POWER_GATING_LB_PORTION, true, true}, ++ {FEATURE_PREFER_3D_TIMING, false, true}, ++ {FEATURE_VARI_BRIGHT_ENABLE, true, true}, ++ {FEATURE_PSR_ENABLE, false, true}, ++ {FEATURE_WIRELESS_ENABLE_COMPRESSED_AUDIO, false, true}, ++ {FEATURE_WIRELESS_INCLUDE_UNVERIFIED_TIMINGS, true, true}, ++ {FEATURE_EDID_STRESS_READ, false, true}, ++ {FEATURE_DP_FRAME_PACK_STEREO3D, false, true}, ++ {FEATURE_DISPLAY_PREFERRED_VIEW, 0, false}, ++ {FEATURE_ALLOW_HDMI_WITHOUT_AUDIO, false, true}, ++ {FEATURE_ABM_2_0, false, true}, ++ {FEATURE_SUPPORT_MIRABILIS, false, true}, ++ {FEATURE_OPTIMIZATION, 0xFFFF, false}, ++ {FEATURE_PERF_MEASURE, 0, false}, ++ {FEATURE_MIN_BACKLIGHT_LEVEL, 0, false}, ++ {FEATURE_MAX_BACKLIGHT_LEVEL, 255, false}, ++ {FEATURE_LOAD_DMCU_FIRMWARE, true, true}, ++ {FEATURE_DISABLE_AZ_CLOCK_GATING, false, true}, ++ {FEATURE_ENABLE_GPU_SCALING, false, true}, ++ {FEATURE_DONGLE_SINK_COUNT_CHECK, true, true}, ++ {FEATURE_INSTANT_UP_SCALE_DOWN_SCALE, false, true}, ++ {FEATURE_TILED_DISPLAY, false, true}, ++ {FEATURE_PREFERRED_ABM_CONFIG_SET, 0, false}, ++ {FEATURE_CHANGE_SW_I2C_SPEED, 50, false}, ++ {FEATURE_CHANGE_HW_I2C_SPEED, 50, false}, ++ {FEATURE_CHANGE_I2C_SPEED_CONTROL, false, true}, ++ {FEATURE_DEFAULT_PSR_LEVEL, 0, false}, ++ {FEATURE_MAX_CLOCK_SOURCE_NUM, 0, false}, ++ {FEATURE_REPORT_SINGLE_SELECTED_TIMING, false, true}, ++ {FEATURE_ALLOW_HDMI_HIGH_CLK_DP_DONGLE, true, true}, ++ {FEATURE_SUPPORT_EXTERNAL_PANEL_DRR, false, true}, ++ {FEATURE_LVDS_SAFE_PIXEL_CLOCK_RANGE, 0, false}, ++ {FEATURE_ABM_CONFIG, 0, false}, ++ {FEATURE_WIRELESS_ENABLE, false, true}, ++ {FEATURE_ALLOW_DIRECT_MEMORY_ACCESS_TRIG, false, true}, ++ {FEATURE_FORCE_STATIC_SCREEN_EVENT_TRIGGERS, 0, false}, ++ {FEATURE_USE_PPLIB, true, true}, ++ {FEATURE_DISABLE_LPT_SUPPORT, false, true}, ++ {FEATURE_DUMMY_FBC_BACKEND, false, true}, ++ {FEATURE_DPMS_AUDIO_ENDPOINT_CONTROL, true, true}, ++ {FEATURE_DISABLE_FBC_COMP_CLK_GATE, false, true}, ++ {FEATURE_PIXEL_PERFECT_OUTPUT, false, true}, ++ {FEATURE_8BPP_SUPPORTED, false, true} ++}; ++ ++ ++/* Stores entire ASIC features by sets */ ++uint32_t adapter_feature_set[FEATURE_MAXIMUM/32]; ++ ++enum { ++ LEGACY_MAX_NUM_OF_CONTROLLERS = 2, ++ DEFAULT_NUM_COFUNC_NON_DP_DISPLAYS = 2 ++}; ++ ++/* ++ * get_feature_entries_num ++ * ++ * Get number of feature entries ++ */ ++static inline uint32_t get_feature_entries_num(void) ++{ ++ return ARRAY_SIZE(feature_entry_table); ++} ++ ++static void get_platform_info_methods( ++ struct adapter_service *as) ++{ ++ struct platform_info_params params; ++ uint32_t mask = 0; ++ ++ params.data = &mask; ++ params.method = PM_GET_AVAILABLE_METHODS; ++ ++ if (dal_get_platform_info(as->ctx, ¶ms)) ++ as->platform_methods_mask = mask; ++ ++ ++} ++ ++static void initialize_backlight_caps( ++ struct adapter_service *as) ++{ ++ struct firmware_info fw_info; ++ struct embedded_panel_info panel_info; ++ struct platform_info_ext_brightness_caps caps; ++ struct platform_info_params params; ++ bool custom_curve_present = false; ++ bool custom_min_max_present = false; ++ ++ if (!(PM_GET_EXTENDED_BRIGHNESS_CAPS & as->platform_methods_mask)) { ++ dal_logger_write(as->ctx->logger, ++ LOG_MAJOR_BACKLIGHT, ++ LOG_MINOR_BACKLIGHT_BRIGHTESS_CAPS, ++ "This method is not supported\n"); ++ return; ++ } ++ ++ if (dal_bios_parser_get_firmware_info ++ (as->bios_parser, &fw_info) != BP_RESULT_OK || ++ dal_bios_parser_get_embedded_panel_info ++ (as->bios_parser, &panel_info) != BP_RESULT_OK) ++ return; ++ ++ params.data = ∩︀ ++ params.method = PM_GET_EXTENDED_BRIGHNESS_CAPS; ++ ++ if (dal_get_platform_info(as->ctx, ¶ms)) { ++ as->ac_level_percentage = caps.basic_caps.ac_level_percentage; ++ as->dc_level_percentage = caps.basic_caps.dc_level_percentage; ++ custom_curve_present = (caps.data_points_num > 0); ++ custom_min_max_present = true; ++ } else ++ return; ++ /* Choose minimum backlight level base on priority: ++ * extended caps,VBIOS,default */ ++ if (custom_min_max_present) ++ as->backlight_8bit_lut[0] = caps.min_input_signal; ++ ++ else if (fw_info.min_allowed_bl_level > 0) ++ as->backlight_8bit_lut[0] = fw_info.min_allowed_bl_level; ++ ++ else ++ as->backlight_8bit_lut[0] = DEFAULT_MIN_BACKLIGHT; ++ ++ /* Choose maximum backlight level base on priority: ++ * extended caps,default */ ++ if (custom_min_max_present) ++ as->backlight_8bit_lut[100] = caps.max_input_signal; ++ ++ else ++ as->backlight_8bit_lut[100] = DEFAULT_MAX_BACKLIGHT; ++ ++ if (as->backlight_8bit_lut[100] > ABSOLUTE_BACKLIGHT_MAX) ++ as->backlight_8bit_lut[100] = ABSOLUTE_BACKLIGHT_MAX; ++ ++ if (as->backlight_8bit_lut[0] > as->backlight_8bit_lut[100]) ++ as->backlight_8bit_lut[0] = as->backlight_8bit_lut[100]; ++ ++ if (custom_curve_present) { ++ uint16_t index = 1; ++ uint16_t i; ++ uint16_t num_of_data_points = (caps.data_points_num <= 99 ? ++ caps.data_points_num : 99); ++ /* Filling translation table from data points - ++ * between every two provided data points we ++ * lineary interpolate missing values ++ */ ++ for (i = 0 ; i < num_of_data_points; i++) { ++ uint16_t luminance = caps.data_points[i].luminance; ++ uint16_t signal_level = ++ caps.data_points[i].signal_level; ++ ++ if (signal_level < as->backlight_8bit_lut[0]) ++ signal_level = as->backlight_8bit_lut[0]; ++ ++ if (signal_level > as->backlight_8bit_lut[100]) ++ signal_level = as->backlight_8bit_lut[100]; ++ ++ /* Lineary interpolate missing values */ ++ if (index < luminance) { ++ uint16_t base_value = ++ as->backlight_8bit_lut[index-1]; ++ uint16_t delta_signal = ++ signal_level - base_value; ++ uint16_t delta_luma = luminance - index + 1; ++ uint16_t step = delta_signal; ++ ++ for (; index < luminance ; index++) { ++ as->backlight_8bit_lut[index] = ++ base_value + ++ (step / delta_luma); ++ step += delta_signal; ++ } ++ } ++ /* Now [index == luminance], so we can add ++ * data point to the translation table */ ++ as->backlight_8bit_lut[index++] = signal_level; ++ } ++ /* Complete the final segment of interpolation - ++ * between last datapoint and maximum value */ ++ if (index < 100) { ++ uint16_t base_value = as->backlight_8bit_lut[index-1]; ++ uint16_t delta_signal = ++ as->backlight_8bit_lut[100]-base_value; ++ uint16_t delta_luma = 100 - index + 1; ++ uint16_t step = delta_signal; ++ ++ for (; index < 100 ; index++) { ++ as->backlight_8bit_lut[index] = base_value + ++ (step / delta_luma); ++ step += delta_signal; ++ } ++ } ++ } ++ /* build backlight translation table based on default curve */ ++ else { ++ /* Default backlight curve can be defined by ++ * polinomial F(x) = A(x*x) + Bx + C. ++ * Backlight curve should always satisfy ++ * F(0) = min, F(100) = max, so polinomial coefficients are: ++ * A is 0.0255 - B/100 - min/10000 - ++ * (255-max)/10000 = (max - min)/10000 - B/100 ++ * B is adjustable factor to modify the curve. ++ * Bigger B results in less concave curve. ++ * B range is [0..(max-min)/100] ++ * C is backlight minimum ++ */ ++ uint16_t delta = as->backlight_8bit_lut[100] - ++ as->backlight_8bit_lut[0]; ++ uint16_t coeffc = as->backlight_8bit_lut[0]; ++ uint16_t coeffb = (BACKLIGHT_CURVE_COEFFB < delta ? ++ BACKLIGHT_CURVE_COEFFB : delta); ++ uint16_t coeffa = delta - coeffb; ++ uint16_t i; ++ uint32_t temp; ++ ++ for (i = 1; i < 100 ; i++) { ++ temp = (coeffa * i * i) / BACKLIGHT_CURVE_COEFFA_FACTOR; ++ as->backlight_8bit_lut[i] = temp + (coeffb * i) / ++ BACKLIGHT_CURVE_COEFFB_FACTOR + coeffc; ++ } ++ } ++ as->backlight_caps_initialized = true; ++} ++ ++static void log_overriden_features( ++ struct adapter_service *as, ++ const char *feature_name, ++ enum adapter_feature_id id, ++ bool bool_feature, ++ uint32_t value) ++{ ++ if (bool_feature) ++ dal_logger_write(as->ctx->logger, ++ LOG_MAJOR_FEATURE_OVERRIDE, ++ LOG_MINOR_FEATURE_OVERRIDE, ++ "Overridden %s is %s now\n", ++ feature_name, ++ (value == 0) ? "disabled" : "enabled"); ++ else ++ dal_logger_write(as->ctx->logger, ++ LOG_MAJOR_FEATURE_OVERRIDE, ++ LOG_MINOR_FEATURE_OVERRIDE, ++ "Overridden %s new value: %d\n", ++ feature_name, ++ value); ++} ++ ++/************************************* ++ * Local static functions definition * ++ *************************************/ ++ ++#define check_bool_feature(feature) \ ++case FEATURE_ ## feature: \ ++ if (param->bool_param_enable_mask & \ ++ (1 << DAL_PARAM_ ## feature)) { \ ++ *data = param->bool_param_values & \ ++ (1 << DAL_PARAM_ ## feature); \ ++ ret = true; \ ++ feature_name = "FEATURE_" #feature; \ ++ } \ ++ break ++ ++#define check_int_feature(feature) \ ++case FEATURE_ ## feature: \ ++ if (param->int_param_values[DAL_PARAM_ ## feature] != \ ++ DAL_PARAM_INVALID_INT) { \ ++ *data = param->int_param_values[DAL_PARAM_ ## feature];\ ++ ret = true;\ ++ bool_feature = false;\ ++ feature_name = "FEATURE_" #feature;\ ++ } \ ++ break ++ ++/* ++ * override_default_parameters ++ * ++ * Override features (from runtime parameter) ++ * corresponding to Adapter Service Feature ID ++ */ ++static bool override_default_parameters( ++ struct adapter_service *as, ++ const struct dal_override_parameters *param, ++ const uint32_t idx, ++ uint32_t *data) ++{ ++ bool ret = false; ++ bool bool_feature = true; ++ char *feature_name; ++ ++ if (idx >= get_feature_entries_num()) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ switch (feature_entry_table[idx].feature_id) { ++ check_int_feature(MAX_COFUNC_NON_DP_DISPLAYS); ++ check_int_feature(DRR_SUPPORT); ++ check_bool_feature(LIGHT_SLEEP); ++ check_bool_feature(MAXIMIZE_STUTTER_MARKS); ++ check_bool_feature(MAXIMIZE_URGENCY_WATERMARKS); ++ check_bool_feature(USE_MAX_DISPLAY_CLK); ++ check_bool_feature(ENABLE_DFS_BYPASS); ++ check_bool_feature(POWER_GATING_PIPE_IN_TILE); ++ check_bool_feature(POWER_GATING_LB_PORTION); ++ check_bool_feature(PSR_ENABLE); ++ check_bool_feature(VARI_BRIGHT_ENABLE); ++ check_bool_feature(USE_PPLIB); ++ check_bool_feature(DISABLE_LPT_SUPPORT); ++ check_bool_feature(DUMMY_FBC_BACKEND); ++ check_bool_feature(ENABLE_GPU_SCALING); ++ default: ++ return false; ++ } ++ if (ret) ++ log_overriden_features( ++ as, ++ feature_name, ++ feature_entry_table[idx].feature_id, ++ bool_feature, ++ *data); ++ ++ return ret; ++} ++ ++/* ++ * get_feature_value_from_data_sources ++ * ++ * For a given feature, determine its value from ASIC cap and wireless ++ * data source. ++ * idx : index of feature_entry_table for the feature id. ++ */ ++static bool get_feature_value_from_data_sources( ++ const struct adapter_service *as, ++ const uint32_t idx, ++ uint32_t *data) ++{ ++ if (idx >= get_feature_entries_num()) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ switch (feature_entry_table[idx].feature_id) { ++ case FEATURE_MAX_COFUNC_NON_DP_DISPLAYS: ++ *data = as->asic_cap->data[ASIC_DATA_MAX_COFUNC_NONDP_DISPLAYS]; ++ break; ++ ++ case FEATURE_WIRELESS_LIMIT_720P: ++ *data = as->asic_cap->caps.WIRELESS_LIMIT_TO_720P; ++ break; ++ ++ case FEATURE_WIRELESS_FULL_TIMING_ADJUSTMENT: ++ *data = as->asic_cap->caps.WIRELESS_FULL_TIMING_ADJUSTMENT; ++ break; ++ ++ case FEATURE_MODIFY_TIMINGS_FOR_WIRELESS: ++ *data = as->asic_cap->caps.WIRELESS_TIMING_ADJUSTMENT; ++ break; ++ ++ case FEATURE_SUPPORTED_HDMI_CONNECTION_NUM: ++ *data = ++ as->asic_cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM]; ++ break; ++ ++ case FEATURE_DETECT_REQUIRE_HPD_HIGH: ++ *data = as->asic_cap->caps.HPD_CHECK_FOR_EDID; ++ break; ++ ++ case FEATURE_NO_HPD_LOW_POLLING_VCC_OFF: ++ *data = as->asic_cap->caps.NO_VCC_OFF_HPD_POLLING; ++ break; ++ ++ case FEATURE_STUTTER_MODE: ++ *data = as->asic_cap->data[ASIC_DATA_STUTTERMODE]; ++ break; ++ ++ case FEATURE_WIRELESS_ENABLE: ++ *data = as->wireless_data.wireless_enable; ++ break; ++ ++ case FEATURE_8BPP_SUPPORTED: ++ *data = as->asic_cap->caps.SUPPORT_8BPP; ++ break; ++ ++ default: ++ return false; ++ } ++ ++ return true; ++} ++ ++/* get_bool_value ++ * ++ * Get the boolean value of a given feature ++ */ ++static bool get_bool_value( ++ const uint32_t set, ++ const uint32_t idx) ++{ ++ if (idx >= 32) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ return ((set & (1 << idx)) != 0); ++} ++ ++/* ++ * get_hpd_info ++ * ++ * Get HPD information from BIOS ++ */ ++static bool get_hpd_info(struct adapter_service *as, ++ struct graphics_object_id id, ++ struct graphics_object_hpd_info *info) ++{ ++ return BP_RESULT_OK == ++ dal_bios_parser_get_hpd_info(as->bios_parser, id, info); ++} ++ ++/* ++ * lookup_feature_entry ++ * ++ * Find the entry index of a given feature in feature table ++ */ ++static uint32_t lookup_feature_entry( ++ enum adapter_feature_id feature_id) ++{ ++ uint32_t entries_num = get_feature_entries_num(); ++ uint32_t i = 0; ++ ++ while (i != entries_num) { ++ if (feature_entry_table[i].feature_id == feature_id) ++ break; ++ ++ ++i; ++ } ++ ++ return i; ++} ++ ++/* ++ * set_bool_value ++ * ++ * Set the boolean value of a given feature ++ */ ++static void set_bool_value( ++ uint32_t *set, ++ const uint32_t idx, ++ bool value) ++{ ++ if (idx >= 32) { ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ if (value) ++ *set |= (1 << idx); ++ else ++ *set &= ~(1 << idx); ++} ++ ++/* ++ * generate_feature_set ++ * ++ * Generate the internal feature set from multiple data sources ++ */ ++static bool generate_feature_set( ++ struct adapter_service *as, ++ const struct dal_override_parameters *param) ++{ ++ uint32_t i = 0; ++ uint32_t value = 0; ++ uint32_t set_idx = 0; ++ uint32_t internal_idx = 0; ++ uint32_t entry_num = 0; ++ const struct feature_source_entry *entry = NULL; ++ ++ dc_service_memset(adapter_feature_set, 0, sizeof(adapter_feature_set)); ++ entry_num = get_feature_entries_num(); ++ ++ ++ while (i != entry_num) { ++ entry = &feature_entry_table[i]; ++ ++ if (entry->feature_id <= FEATURE_UNKNOWN || ++ entry->feature_id >= FEATURE_MAXIMUM) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ set_idx = (uint32_t)((entry->feature_id - 1) / 32); ++ internal_idx = (uint32_t)((entry->feature_id - 1) % 32); ++ ++ /* TODO: wireless, runtime parameter, vbios */ ++ if (!override_default_parameters(as, param, i, &value)) { ++ if (!get_feature_value_from_data_sources( ++ as, i, &value)) { ++ /* ++ * Can't find feature values from ++ * above data sources ++ * Assign default value ++ */ ++ value = entry->default_value; ++ } ++ } ++ ++ if (entry->is_boolean_type) ++ set_bool_value(&adapter_feature_set[set_idx], ++ internal_idx, ++ value != 0); ++ else ++ adapter_feature_set[set_idx] = value; ++ ++ i++; ++ } ++ ++ return true; ++} ++ ++ ++/* ++ * create_hw_ctx ++ * ++ * Create HW context for adapter service. This is DCE specific. ++ */ ++static struct hw_ctx_adapter_service *create_hw_ctx( ++ enum dce_version dce_version, ++ struct dc_context *ctx) ++{ ++ switch (dce_version) { ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ return dal_adapter_service_create_hw_ctx_dce110(ctx); ++#endif ++ default: ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++} ++ ++/* ++ * adapter_service_destruct ++ * ++ * Release memory of objects in adapter service ++ */ ++static void adapter_service_destruct( ++ struct adapter_service *as) ++{ ++ dal_adapter_service_destroy_hw_ctx(&as->hw_ctx); ++ dal_i2caux_destroy(&as->i2caux); ++ dal_bios_parser_destroy(&as->bios_parser); ++ dal_gpio_service_destroy(&as->gpio_service); ++ dal_asic_capability_destroy(&as->asic_cap); ++ dal_bios_parser_destroy_integrated_info(as->ctx, &as->integrated_info); ++} ++ ++/* ++ * adapter_service_construct ++ * ++ * Construct the derived type of adapter service ++ */ ++static bool adapter_service_construct( ++ struct adapter_service *as, ++ struct as_init_data *init_data) ++{ ++ if (!init_data) ++ return false; ++ ++ /* Create ASIC capability */ ++ as->ctx = init_data->ctx; ++ as->asic_cap = dal_asic_capability_create( ++ &init_data->hw_init_data, as->ctx); ++ ++ if (!as->asic_cap) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++#if defined(DAL_CZ_BRINGUP) ++ if (dal_adapter_service_get_dce_version(as) == DCE_VERSION_11_0) { ++ uint32_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(feature_entry_table); i++) { ++ enum adapter_feature_id id = ++ feature_entry_table[i].feature_id; ++ if (id == FEATURE_MAXIMIZE_URGENCY_WATERMARKS || ++ id == FEATURE_MAXIMIZE_STUTTER_MARKS || ++ id == FEATURE_MAXIMIZE_NBP_MARKS) ++ feature_entry_table[i].default_value = true; ++ } ++ } ++#endif ++ ++ /* Generate feature set table */ ++ if (!generate_feature_set(as, init_data->display_param)) { ++ ASSERT_CRITICAL(false); ++ goto failed_to_generate_features; ++ } ++ ++ /* Create BIOS parser */ ++ init_data->bp_init_data.ctx = init_data->ctx; ++ as->bios_parser = ++ dal_bios_parser_create(&init_data->bp_init_data, as); ++ ++ if (!as->bios_parser) { ++ ASSERT_CRITICAL(false); ++ goto failed_to_create_bios_parser; ++ } ++ ++ /* Create GPIO service */ ++ as->gpio_service = ++ dal_gpio_service_create( ++ dal_adapter_service_get_dce_version(as), ++ as->ctx); ++ ++ if (!as->gpio_service) { ++ ASSERT_CRITICAL(false); ++ goto failed_to_create_gpio_service; ++ } ++ ++ /* Create I2C AUX */ ++ as->i2caux = dal_i2caux_create(as, as->ctx); ++ ++ if (!as->i2caux) { ++ ASSERT_CRITICAL(false); ++ goto failed_to_create_i2caux; ++ } ++ ++ /* Create Adapter Service HW Context*/ ++ as->hw_ctx = create_hw_ctx( ++ dal_adapter_service_get_dce_version(as), ++ as->ctx); ++ ++ if (!as->hw_ctx) { ++ ASSERT_CRITICAL(false); ++ goto failed_to_create_hw_ctx; ++ } ++ ++ /* Avoid wireless encoder creation in upstream branch. */ ++ ++ /* Integrated info is not provided on discrete ASIC. NULL is allowed */ ++ as->integrated_info = dal_bios_parser_create_integrated_info( ++ as->bios_parser); ++ ++ dal_bios_parser_post_init(as->bios_parser); ++ ++ /* Generate backlight translation table and initializes ++ other brightness properties */ ++ as->backlight_caps_initialized = false; ++ ++ get_platform_info_methods(as); ++ ++ initialize_backlight_caps(as); ++ ++ return true; ++ ++failed_to_generate_features: ++ dal_adapter_service_destroy_hw_ctx(&as->hw_ctx); ++ ++failed_to_create_hw_ctx: ++ dal_i2caux_destroy(&as->i2caux); ++ ++failed_to_create_i2caux: ++ dal_gpio_service_destroy(&as->gpio_service); ++ ++failed_to_create_gpio_service: ++ dal_bios_parser_destroy(&as->bios_parser); ++ ++failed_to_create_bios_parser: ++ dal_asic_capability_destroy(&as->asic_cap); ++ ++ return false; ++} ++ ++/* ++ * Global function definition ++ */ ++ ++/* ++ * dal_adapter_service_create ++ * ++ * Create adapter service ++ */ ++struct adapter_service *dal_adapter_service_create( ++ struct as_init_data *init_data) ++{ ++ struct adapter_service *as; ++ ++ as = dc_service_alloc(init_data->ctx, sizeof(struct adapter_service)); ++ ++ if (!as) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (adapter_service_construct(as, init_data)) ++ return as; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(init_data->ctx, as); ++ ++ return NULL; ++} ++ ++/* ++ * dal_adapter_service_destroy ++ * ++ * Destroy adapter service and objects it contains ++ */ ++void dal_adapter_service_destroy( ++ struct adapter_service **as) ++{ ++ if (!as) { ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ if (!*as) { ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ adapter_service_destruct(*as); ++ ++ dc_service_free((*as)->ctx, *as); ++ ++ *as = NULL; ++} ++ ++/* ++ * dal_adapter_service_get_dce_version ++ * ++ * Get the DCE version of current ASIC ++ */ ++enum dce_version dal_adapter_service_get_dce_version( ++ const struct adapter_service *as) ++{ ++ uint32_t version = as->asic_cap->data[ASIC_DATA_DCE_VERSION]; ++ ++ switch (version) { ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case 0x110: ++ return DCE_VERSION_11_0; ++#endif ++ default: ++ ASSERT_CRITICAL(false); ++ return DCE_VERSION_UNKNOWN; ++ } ++} ++ ++/* ++ * dal_adapter_service_get_controllers_num ++ * ++ * Get number of controllers ++ */ ++uint8_t dal_adapter_service_get_controllers_num( ++ struct adapter_service *as) ++{ ++ uint32_t result = as->asic_cap->data[ASIC_DATA_CONTROLLERS_NUM]; ++ ++ /* Check the "max num of controllers" feature, ++ * use it for debugging purposes only */ ++ /* TODO implement ++ * dal_adapter_service_get_feature_value(as, ) */ ++ ++ return result; ++} ++ ++ ++/** Get total number of connectors. ++ * ++ * \param as Adapter Service ++ * ++ * \return Total number of connectors. It is up-to-the caller to decide ++ * if the number is valid. ++ */ ++uint8_t dal_adapter_service_get_connectors_num( ++ struct adapter_service *as) ++{ ++ uint8_t vbios_connectors_num = 0; ++ uint8_t wireless_connectors_num = 0; ++ ++ vbios_connectors_num = dal_bios_parser_get_connectors_number( ++ as->bios_parser); ++ wireless_connectors_num = wireless_get_connectors_num(as); ++ ++ return vbios_connectors_num + wireless_connectors_num; ++} ++ ++static bool is_wireless_object(struct graphics_object_id id) ++{ ++ if ((id.type == OBJECT_TYPE_ENCODER && ++ id.id == ENCODER_ID_INTERNAL_WIRELESS) || ++ (id.type == OBJECT_TYPE_CONNECTOR && id.id == ++ CONNECTOR_ID_WIRELESS) || ++ (id.type == OBJECT_TYPE_CONNECTOR && id.id == ++ CONNECTOR_ID_MIRACAST)) ++ return true; ++ return false; ++} ++ ++/** ++ * Get the number of source objects of an object ++ * ++ * \param [in] as: Adapter Service ++ * ++ * \param [in] id: The graphics object id ++ * ++ * \return ++ * The number of the source objects of an object ++ */ ++uint32_t dal_adapter_service_get_src_num( ++ struct adapter_service *as, struct graphics_object_id id) ++{ ++ if (is_wireless_object(id)) ++ return wireless_get_srcs_num(as, id); ++ else ++ return dal_bios_parser_get_src_number(as->bios_parser, id); ++} ++ ++/** ++ * Get the source objects of an object ++ * ++ * \param [in] id The graphics object id ++ * \param [in] index Enumerating index which starts at 0 ++ * ++ * \return If enumerating successfully, return the VALID source object id, ++ * otherwise, returns "zeroed out" object id. ++ * Client should call dal_graphics_object_id_is_valid() to check ++ * weather the id is valid. ++ */ ++struct graphics_object_id dal_adapter_service_get_src_obj( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ uint32_t index) ++{ ++ struct graphics_object_id src_object_id; ++ ++ if (is_wireless_object(id)) ++ src_object_id = wireless_get_src_obj_id(as, id, index); ++ else { ++ if (BP_RESULT_OK != ++ dal_bios_parser_get_src_obj( ++ as->bios_parser, id, index, &src_object_id)) ++ src_object_id = ++ dal_graphics_object_id_init( ++ 0, ++ ENUM_ID_UNKNOWN, ++ OBJECT_TYPE_UNKNOWN); ++ } ++ ++ return src_object_id; ++} ++ ++/** Get connector object id associated with a connector index. ++ * ++ * \param as Adapter Service ++ * ++ * \param connector_index Index of connector between zero and total number ++ * returned by dal_adapter_service_get_connectors_num() ++ * ++ * \return graphics object id corresponding to the connector_index. ++ */ ++struct graphics_object_id dal_adapter_service_get_connector_obj_id( ++ struct adapter_service *as, ++ uint8_t connector_index) ++{ ++ uint8_t bios_connectors_num = ++ dal_bios_parser_get_connectors_number(as->bios_parser); ++ ++ if (connector_index >= bios_connectors_num) ++ return wireless_get_connector_id( ++ as, ++ connector_index); ++ else ++ return dal_bios_parser_get_connector_id( ++ as->bios_parser, ++ connector_index); ++} ++ ++bool dal_adapter_service_get_device_tag( ++ struct adapter_service *as, ++ struct graphics_object_id connector_object_id, ++ uint32_t device_tag_index, ++ struct connector_device_tag_info *info) ++{ ++ if (BP_RESULT_OK == dal_bios_parser_get_device_tag(as->bios_parser, ++ connector_object_id, device_tag_index, info)) ++ return true; ++ else ++ return false; ++} ++ ++/* Check if DeviceId is supported by ATOM_OBJECT_HEADER support info */ ++bool dal_adapter_service_is_device_id_supported(struct adapter_service *as, ++ struct device_id id) ++{ ++ return dal_bios_parser_is_device_id_supported(as->bios_parser, id); ++} ++ ++bool dal_adapter_service_is_meet_underscan_req(struct adapter_service *as) ++{ ++ struct firmware_info fw_info; ++ enum bp_result bp_result = dal_adapter_service_get_firmware_info( ++ as, &fw_info); ++ uint32_t disp_clk_limit = ++ as->asic_cap->data[ASIC_DATA_MIN_DISPCLK_FOR_UNDERSCAN]; ++ if (BP_RESULT_OK == bp_result) { ++ dal_logger_write(as->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_ADAPTER_SERVICE, ++ "Read firmware is NULL"); ++ return false; ++ } ++ if (fw_info.default_display_engine_pll_frequency < disp_clk_limit) ++ return false; ++ return true; ++} ++ ++bool dal_adapter_service_underscan_for_hdmi_only(struct adapter_service *as) ++{ ++ return as->asic_cap->caps.UNDERSCAN_FOR_HDMI_ONLY; ++} ++/* ++ * dal_adapter_service_get_clock_sources_num ++ * ++ * Get number of clock sources ++ */ ++uint8_t dal_adapter_service_get_clock_sources_num( ++ struct adapter_service *as) ++{ ++ struct firmware_info fw_info; ++ uint32_t max_clk_src = 0; ++ uint32_t num = as->asic_cap->data[ASIC_DATA_CLOCKSOURCES_NUM]; ++ ++ /* ++ * Check is system supports the use of the External clock source ++ * as a clock source for DP ++ */ ++ enum bp_result bp_result = ++ dal_bios_parser_get_firmware_info(as->bios_parser, ++ &fw_info); ++ ++ if (BP_RESULT_OK == bp_result && ++ fw_info.external_clock_source_frequency_for_dp != 0) ++ ++num; ++ ++ /* ++ * Add clock source for wireless if supported ++ */ ++ num += (uint32_t)wireless_get_clocks_num(as); ++ ++ /* Check the "max number of clock sources" feature */ ++ if (dal_adapter_service_get_feature_value( ++ FEATURE_MAX_CLOCK_SOURCE_NUM, ++ &max_clk_src, ++ sizeof(uint32_t))) ++ if ((max_clk_src != 0) && (max_clk_src < num)) ++ num = max_clk_src; ++ ++ return num; ++} ++ ++/* ++ * dal_adapter_service_get_func_controllers_num ++ * ++ * Get number of controllers ++ */ ++uint8_t dal_adapter_service_get_func_controllers_num( ++ struct adapter_service *as) ++{ ++ uint32_t result = ++ as->asic_cap->data[ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM]; ++ ++ /* Check the "max num of controllers" feature, ++ * use it for debugging purposes only */ ++ ++ /* Limit number of controllers by OS */ ++ ++ struct asic_feature_flags flags; ++ ++ flags.raw = as->asic_cap->data[ASIC_DATA_FEATURE_FLAGS]; ++ ++ if (flags.bits.LEGACY_CLIENT && ++ (result > LEGACY_MAX_NUM_OF_CONTROLLERS)) ++ result = LEGACY_MAX_NUM_OF_CONTROLLERS; ++ ++ return result; ++} ++ ++/* ++ * dal_adapter_service_is_feature_supported ++ * ++ * Return if a given feature is supported by the ASIC. The feature has to be ++ * a boolean type. ++ */ ++bool dal_adapter_service_is_feature_supported( ++ enum adapter_feature_id feature_id) ++{ ++ bool data = 0; ++ ++ dal_adapter_service_get_feature_value(feature_id, &data, sizeof(bool)); ++ ++ return data; ++} ++ ++/** ++ * Reports maximum number of confunctional non-DP displays. ++ * Value can be overriden if FEATURE_REPORT_SINGLE_SELECTED_TIMING feature is ++ * enabled. ++ * ++ * \return ++ * Maximum number of confunctional non-DP displays ++ */ ++uint32_t dal_adapter_service_get_max_cofunc_non_dp_displays(void) ++{ ++ uint32_t non_dp_displays = DEFAULT_NUM_COFUNC_NON_DP_DISPLAYS; ++ ++ if (true == dal_adapter_service_get_feature_value( ++ FEATURE_MAX_COFUNC_NON_DP_DISPLAYS, ++ &non_dp_displays, ++ sizeof(non_dp_displays))) { ++ /* the cached value exist */ ++ /* TODO: add more logic as per-DAL2 */ ++ } ++ ++ return non_dp_displays; ++} ++ ++uint32_t dal_adapter_service_get_single_selected_timing_signals(void) ++{ ++ uint32_t signals_bitmap = 0; ++ ++ if (dal_adapter_service_is_feature_supported( ++ FEATURE_REPORT_SINGLE_SELECTED_TIMING)) { ++ /* the cached value exist */ ++ /* TODO: add more logic as per-DAL2 */ ++ signals_bitmap = 0; ++ } ++ ++ return signals_bitmap; ++} ++ ++/* ++ * dal_adapter_service_get_i2c_info ++ * ++ * Get I2C information from BIOS ++ */ ++bool dal_adapter_service_get_i2c_info( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ struct graphics_object_i2c_info *i2c_info) ++{ ++ if (!i2c_info) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ return BP_RESULT_OK == ++ dal_bios_parser_get_i2c_info(as->bios_parser, id, i2c_info); ++} ++ ++/* ++ * dal_adapter_service_obtain_ddc ++ * ++ * Obtain DDC ++ */ ++struct ddc *dal_adapter_service_obtain_ddc( ++ struct adapter_service *as, ++ struct graphics_object_id id) ++{ ++ struct graphics_object_i2c_info i2c_info; ++ struct gpio_ddc_hw_info hw_info; ++ ++ ++ if (!dal_adapter_service_get_i2c_info(as, id, &i2c_info)) ++ return NULL; ++ ++ hw_info.ddc_channel = i2c_info.i2c_line; ++ hw_info.hw_supported = i2c_info.i2c_hw_assist; ++ ++ return dal_gpio_service_create_ddc( ++ as->gpio_service, ++ i2c_info.gpio_info.clk_a_register_index, ++ 1 << i2c_info.gpio_info.clk_a_shift, ++ &hw_info); ++} ++ ++/* ++ * dal_adapter_service_release_ddc ++ * ++ * Release DDC ++ */ ++void dal_adapter_service_release_ddc( ++ struct adapter_service *as, ++ struct ddc *ddc) ++{ ++ dal_gpio_service_destroy_ddc(&ddc); ++} ++ ++/* ++ * dal_adapter_service_obtain_hpd_irq ++ * ++ * Obtain HPD interrupt request ++ */ ++struct irq *dal_adapter_service_obtain_hpd_irq( ++ struct adapter_service *as, ++ struct graphics_object_id id) ++{ ++ enum bp_result bp_result; ++ ++ struct graphics_object_hpd_info hpd_info; ++ struct gpio_pin_info pin_info; ++ ++ if (!get_hpd_info(as, id, &hpd_info)) ++ return NULL; ++ ++ bp_result = dal_bios_parser_get_gpio_pin_info(as->bios_parser, ++ hpd_info.hpd_int_gpio_uid, &pin_info); ++ ++ if (bp_result != BP_RESULT_OK) { ++ ASSERT(bp_result == BP_RESULT_NORECORD); ++ return NULL; ++ } ++ ++ return dal_gpio_service_create_irq( ++ as->gpio_service, ++ pin_info.offset, ++ pin_info.mask); ++} ++ ++/* ++ * dal_adapter_service_release_irq ++ * ++ * Release interrupt request ++ */ ++void dal_adapter_service_release_irq( ++ struct adapter_service *as, ++ struct irq *irq) ++{ ++ dal_gpio_service_destroy_irq(&irq); ++} ++ ++/* ++ * dal_adapter_service_get_ss_info_num ++ * ++ * Get number of spread spectrum entries from BIOS ++ */ ++uint32_t dal_adapter_service_get_ss_info_num( ++ struct adapter_service *as, ++ enum as_signal_type signal) ++{ ++ return dal_bios_parser_get_ss_entry_number(as->bios_parser, signal); ++} ++ ++/* ++ * dal_adapter_service_get_ss_info ++ * ++ * Get spread spectrum info from BIOS ++ */ ++bool dal_adapter_service_get_ss_info( ++ struct adapter_service *as, ++ enum as_signal_type signal, ++ uint32_t idx, ++ struct spread_spectrum_info *info) ++{ ++ enum bp_result bp_result = ++ dal_bios_parser_get_spread_spectrum_info( ++ as->bios_parser, signal, idx, info); ++ ++ return BP_RESULT_OK == bp_result; ++} ++ ++/* ++ * dal_adapter_service_get_integrated_info ++ * ++ * Get integrated information on BIOS ++ */ ++bool dal_adapter_service_get_integrated_info( ++ struct adapter_service *as, ++ struct integrated_info *info) ++{ ++ if (info == NULL || as->integrated_info == NULL) ++ return false; ++ ++ dc_service_memmove(info, as->integrated_info, sizeof(struct integrated_info)); ++ ++ return true; ++} ++ ++/* ++ * dal_adapter_service_is_dfs_bypass_enabled ++ * ++ * Check if DFS bypass is enabled ++ */ ++bool dal_adapter_service_is_dfs_bypass_enabled( ++ struct adapter_service *as) ++{ ++ if (as->integrated_info == NULL) ++ return false; ++ if ((as->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) && ++ dal_adapter_service_is_feature_supported( ++ FEATURE_ENABLE_DFS_BYPASS)) ++ return true; ++ else ++ return false; ++} ++ ++/* ++ * dal_adapter_service_get_sw_i2c_speed ++ * ++ * Get SW I2C speed ++ */ ++uint32_t dal_adapter_service_get_sw_i2c_speed( ++ struct adapter_service *as) ++{ ++ /* TODO: only from ASIC caps. Feature key is not implemented*/ ++ return as->asic_cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ]; ++} ++ ++/* ++ * dal_adapter_service_get_hw_i2c_speed ++ * ++ * Get HW I2C speed ++ */ ++uint32_t dal_adapter_service_get_hw_i2c_speed( ++ struct adapter_service *as) ++{ ++ /* TODO: only from ASIC caps. Feature key is not implemented*/ ++ return as->asic_cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ]; ++} ++ ++/* ++ * dal_adapter_service_get_mc_latency ++ * ++ * Get memory controller latency ++ */ ++uint32_t dal_adapter_service_get_mc_latency( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_MC_LATENCY]; ++} ++ ++/* ++ * dal_adapter_service_get_asic_vram_bit_width ++ * ++ * Get the video RAM bit width set on the ASIC ++ */ ++uint32_t dal_adapter_service_get_asic_vram_bit_width( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_VRAM_BITWIDTH]; ++} ++ ++/* ++ * dal_adapter_service_get_asic_bugs ++ * ++ * Get the bug flags set on this ASIC ++ */ ++struct asic_bugs dal_adapter_service_get_asic_bugs( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->bugs; ++} ++ ++ ++struct dal_asic_runtime_flags dal_adapter_service_get_asic_runtime_flags( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->runtime_flags; ++} ++ ++/* ++ * dal_adapter_service_get_line_buffer_size ++ * ++ * Get line buffer size ++ */ ++uint32_t dal_adapter_service_get_line_buffer_size( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_LINEBUFFER_SIZE]; ++} ++ ++/* ++ * dal_adapter_service_get_bandwidth_tuning_params ++ * ++ * Get parameters for bandwidth tuning ++ */ ++bool dal_adapter_service_get_bandwidth_tuning_params( ++ struct adapter_service *as, ++ union bandwidth_tuning_params *params) ++{ ++ /* TODO: add implementation */ ++ /* note: data comes from runtime parameters */ ++ return false; ++} ++ ++/* ++ * dal_adapter_service_get_feature_flags ++ * ++ * Get a copy of ASIC feature flags ++ */ ++struct asic_feature_flags dal_adapter_service_get_feature_flags( ++ struct adapter_service *as) ++{ ++ struct asic_feature_flags result = { { 0 } }; ++ ++ if (!as) { ++ ASSERT_CRITICAL(false); ++ return result; ++ } ++ ++ result.raw = as->asic_cap->data[ASIC_DATA_FEATURE_FLAGS]; ++ ++ return result; ++} ++ ++/* ++ * dal_adapter_service_get_dram_bandwidth_efficiency ++ * ++ * Get efficiency of DRAM ++ */ ++uint32_t dal_adapter_service_get_dram_bandwidth_efficiency( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_DRAM_BANDWIDTH_EFFICIENCY]; ++} ++ ++/* ++ * dal_adapter_service_obtain_gpio ++ * ++ * Obtain GPIO ++ */ ++struct gpio *dal_adapter_service_obtain_gpio( ++ struct adapter_service *as, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ return dal_gpio_service_create_gpio_ex( ++ as->gpio_service, id, en, ++ GPIO_PIN_OUTPUT_STATE_DEFAULT); ++} ++ ++/* ++ * dal_adapter_service_obtain_stereo_gpio ++ * ++ * Obtain GPIO for stereo3D ++ */ ++struct gpio *dal_adapter_service_obtain_stereo_gpio( ++ struct adapter_service *as) ++{ ++ const bool have_param_stereo_gpio = false; ++ ++ struct asic_feature_flags result; ++ ++ result.raw = as->asic_cap->data[ASIC_DATA_FEATURE_FLAGS]; ++ ++ /* Case 1 : Workstation stereo */ ++ if (result.bits.WORKSTATION_STEREO) ++ /* "active low" <--> "default 3d right eye polarity" = false */ ++ return dal_gpio_service_create_gpio_ex( ++ as->gpio_service, GPIO_ID_GENERIC, GPIO_GENERIC_A, ++ GPIO_PIN_OUTPUT_STATE_ACTIVE_LOW); ++ /* Case 2 : runtime parameter override for sideband stereo */ ++ else if (have_param_stereo_gpio) { ++ /* TODO implement */ ++ return NULL; ++ /* Case 3 : VBIOS gives us GPIO for sideband stereo */ ++ } else { ++ const struct graphics_object_id id = ++ dal_graphics_object_id_init( ++ GENERIC_ID_STEREO, ++ ENUM_ID_1, ++ OBJECT_TYPE_GENERIC); ++ ++ struct bp_gpio_cntl_info cntl_info; ++ struct gpio_pin_info pin_info; ++ ++ /* Get GPIO record for this object. ++ * Stereo GPIO record should have exactly one entry ++ * where active state defines stereosync polarity */ ++ if (1 != dal_bios_parser_get_gpio_record( ++ as->bios_parser, id, &cntl_info, 1)) { ++ return NULL; ++ } else if (BP_RESULT_OK != dal_bios_parser_get_gpio_pin_info( ++ as->bios_parser, cntl_info.id, &pin_info)) { ++ /*ASSERT_CRITICAL(false);*/ ++ return NULL; ++ } else ++ return dal_gpio_service_create_gpio_ex( ++ as->gpio_service, ++ pin_info.offset, pin_info.mask, ++ cntl_info.state); ++ } ++} ++ ++/* ++ * dal_adapter_service_release_gpio ++ * ++ * Release GPIO ++ */ ++void dal_adapter_service_release_gpio( ++ struct adapter_service *as, ++ struct gpio *gpio) ++{ ++ dal_gpio_service_destroy_gpio(&gpio); ++} ++ ++/* ++ * dal_adapter_service_get_firmware_info ++ * ++ * Get firmware information from BIOS ++ */ ++bool dal_adapter_service_get_firmware_info( ++ struct adapter_service *as, ++ struct firmware_info *info) ++{ ++ return dal_bios_parser_get_firmware_info(as->bios_parser, info) == ++ BP_RESULT_OK; ++} ++ ++/* ++ * dal_adapter_service_get_audio_support ++ * ++ * Get information on audio support ++ */ ++union audio_support dal_adapter_service_get_audio_support( ++ struct adapter_service *as) ++{ ++ return dal_adapter_service_hw_ctx_get_audio_support(as->hw_ctx); ++} ++ ++/* ++ * dal_adapter_service_get_stream_engines_num ++ * ++ * Get number of stream engines ++ */ ++uint8_t dal_adapter_service_get_stream_engines_num( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_DIGFE_NUM]; ++} ++ ++/* ++ * dal_adapter_service_get_feature_value ++ * ++ * Get the cached value of a given feature. This value can be a boolean, int, ++ * or characters. ++ */ ++bool dal_adapter_service_get_feature_value( ++ const enum adapter_feature_id feature_id, ++ void *data, ++ uint32_t size) ++{ ++ uint32_t entry_idx = 0; ++ uint32_t set_idx = 0; ++ uint32_t set_internal_idx = 0; ++ ++ if (feature_id >= FEATURE_MAXIMUM || feature_id <= FEATURE_UNKNOWN) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if (data == NULL) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ entry_idx = lookup_feature_entry(feature_id); ++ set_idx = (uint32_t)((feature_id - 1)/32); ++ set_internal_idx = (uint32_t)((feature_id - 1) % 32); ++ ++ if (entry_idx >= get_feature_entries_num()) { ++ /* Cannot find this entry */ ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if (feature_entry_table[entry_idx].is_boolean_type) { ++ if (size != sizeof(bool)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ *(bool *)data = get_bool_value(adapter_feature_set[set_idx], ++ set_internal_idx); ++ } else { ++ if (size != sizeof(uint32_t)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ *(uint32_t *)data = adapter_feature_set[set_idx]; ++ } ++ ++ return true; ++} ++ ++/* ++ * dal_adapter_service_get_memory_type_multiplier ++ * ++ * Get multiplier for the memory type ++ */ ++uint32_t dal_adapter_service_get_memory_type_multiplier( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_MEMORYTYPE_MULTIPLIER]; ++} ++ ++/* ++ * dal_adapter_service_get_bios_parser ++ * ++ * Get BIOS parser handler ++ */ ++struct bios_parser *dal_adapter_service_get_bios_parser( ++ struct adapter_service *as) ++{ ++ return as->bios_parser; ++} ++ ++/* ++ * dal_adapter_service_get_i2caux ++ * ++ * Get i2c aux handler ++ */ ++struct i2caux *dal_adapter_service_get_i2caux( ++ struct adapter_service *as) ++{ ++ return as->i2caux; ++} ++ ++bool dal_adapter_service_initialize_hw_data( ++ struct adapter_service *as) ++{ ++ return as->hw_ctx->funcs->power_up(as->hw_ctx); ++} ++ ++struct graphics_object_id dal_adapter_service_enum_fake_path_resource( ++ struct adapter_service *as, ++ uint32_t index) ++{ ++ return as->hw_ctx->funcs->enum_fake_path_resource(as->hw_ctx, index); ++} ++ ++struct graphics_object_id dal_adapter_service_enum_stereo_sync_object( ++ struct adapter_service *as, ++ uint32_t index) ++{ ++ return as->hw_ctx->funcs->enum_stereo_sync_object(as->hw_ctx, index); ++} ++ ++struct graphics_object_id dal_adapter_service_enum_sync_output_object( ++ struct adapter_service *as, ++ uint32_t index) ++{ ++ return as->hw_ctx->funcs->enum_sync_output_object(as->hw_ctx, index); ++} ++ ++struct graphics_object_id dal_adapter_service_enum_audio_object( ++ struct adapter_service *as, ++ uint32_t index) ++{ ++ return as->hw_ctx->funcs->enum_audio_object(as->hw_ctx, index); ++} ++ ++ ++void dal_adapter_service_update_audio_connectivity( ++ struct adapter_service *as, ++ uint32_t number_of_audio_capable_display_path) ++{ ++ as->hw_ctx->funcs->update_audio_connectivity( ++ as->hw_ctx, ++ number_of_audio_capable_display_path, ++ dal_adapter_service_get_controllers_num(as)); ++} ++ ++bool dal_adapter_service_has_embedded_display_connector( ++ struct adapter_service *as) ++{ ++ uint8_t index; ++ uint8_t num_connectors = dal_adapter_service_get_connectors_num(as); ++ ++ if (num_connectors == 0 || num_connectors > ENUM_ID_COUNT) ++ return false; ++ ++ for (index = 0; index < num_connectors; index++) { ++ struct graphics_object_id obj_id = ++ dal_adapter_service_get_connector_obj_id(as, index); ++ enum connector_id connector_id = ++ dal_graphics_object_id_get_connector_id(obj_id); ++ ++ if ((connector_id == CONNECTOR_ID_LVDS) || ++ (connector_id == CONNECTOR_ID_EDP)) ++ return true; ++ } ++ ++ return false; ++} ++ ++bool dal_adapter_service_get_embedded_panel_info( ++ struct adapter_service *as, ++ struct embedded_panel_info *info) ++{ ++ enum bp_result result; ++ ++ if (info == NULL) ++ /*TODO: add DALASSERT_MSG here*/ ++ return false; ++ ++ result = dal_bios_parser_get_embedded_panel_info( ++ as->bios_parser, info); ++ ++ return result == BP_RESULT_OK; ++} ++ ++bool dal_adapter_service_enum_embedded_panel_patch_mode( ++ struct adapter_service *as, ++ uint32_t index, ++ struct embedded_panel_patch_mode *mode) ++{ ++ enum bp_result result; ++ ++ if (mode == NULL) ++ /*TODO: add DALASSERT_MSG here*/ ++ return false; ++ ++ result = dal_bios_parser_enum_embedded_panel_patch_mode( ++ as->bios_parser, index, mode); ++ ++ return result == BP_RESULT_OK; ++} ++ ++bool dal_adapter_service_get_faked_edid_len( ++ struct adapter_service *as, ++ uint32_t *len) ++{ ++ enum bp_result result; ++ ++ result = dal_bios_parser_get_faked_edid_len( ++ as->bios_parser, ++ len); ++ return result == BP_RESULT_OK; ++} ++ ++bool dal_adapter_service_get_faked_edid_buf( ++ struct adapter_service *as, ++ uint8_t *buf, ++ uint32_t len) ++{ ++ enum bp_result result; ++ ++ result = dal_bios_parser_get_faked_edid_buf( ++ as->bios_parser, ++ buf, ++ len); ++ return result == BP_RESULT_OK; ++ ++} ++ ++/* ++ * dal_adapter_service_is_fusion ++ * ++ * Is this Fusion ASIC ++ */ ++bool dal_adapter_service_is_fusion(struct adapter_service *as) ++{ ++ return as->asic_cap->caps.IS_FUSION; ++} ++ ++/* ++ * dal_adapter_service_is_dfsbyass_dynamic ++ * ++ * ++ **/ ++bool dal_adapter_service_is_dfsbyass_dynamic(struct adapter_service *as) ++{ ++ return as->asic_cap->caps.DFSBYPASS_DYNAMIC_SUPPORT; ++} ++ ++/* ++ * dal_adapter_service_should_optimize ++ * ++ * @brief Reports whether driver settings allow requested optimization ++ * ++ * @param ++ * as: adapter service handler ++ * feature: for which optimization is validated ++ * ++ * @return ++ * true if requested feature can be optimized ++ */ ++bool dal_adapter_service_should_optimize( ++ struct adapter_service *as, enum optimization_feature feature) ++{ ++ uint32_t supported_optimization = 0; ++ struct dal_asic_runtime_flags flags; ++ ++ if (!dal_adapter_service_get_feature_value(FEATURE_OPTIMIZATION, ++ &supported_optimization, sizeof(uint32_t))) ++ return false; ++ ++ /* Retrieve ASIC runtime flags */ ++ flags = dal_adapter_service_get_asic_runtime_flags(as); ++ ++ /* Check runtime flags against different optimization features */ ++ switch (feature) { ++ case OF_SKIP_HW_PROGRAMMING_ON_ENABLED_EMBEDDED_DISPLAY: ++ if (!flags.flags.bits.OPTIMIZED_DISPLAY_PROGRAMMING_ON_BOOT) ++ return false; ++ break; ++ ++ case OF_SKIP_RESET_OF_ALL_HW_ON_S3RESUME: ++ if (as->integrated_info == NULL || ++ !flags.flags.bits.SKIP_POWER_DOWN_ON_RESUME) ++ return false; ++ break; ++ case OF_SKIP_POWER_DOWN_INACTIVE_ENCODER: ++ if (!dal_adapter_service_get_asic_runtime_flags(as).flags.bits. ++ SKIP_POWER_DOWN_ON_RESUME) ++ return false; ++ break; ++ default: ++ break; ++ } ++ ++ return (supported_optimization & feature) != 0; ++} ++ ++/* ++ * dal_adapter_service_is_in_accelerated_mode ++ * ++ * @brief Determine if driver is in accelerated mode ++ * ++ * @param ++ * as: Adapter Service handler ++ * ++ * @out ++ * True if driver is in accelerated mode, false otherwise. ++ */ ++bool dal_adapter_service_is_in_accelerated_mode(struct adapter_service *as) ++{ ++ return dal_bios_parser_is_accelerated_mode(as->bios_parser); ++} ++ ++struct ddc *dal_adapter_service_obtain_ddc_from_i2c_info( ++ struct adapter_service *as, ++ struct graphics_object_i2c_info *info) ++{ ++ struct gpio_ddc_hw_info hw_info = { ++ info->i2c_hw_assist, ++ info->i2c_line }; ++ return dal_gpio_service_create_ddc(as->gpio_service, ++ info->gpio_info.clk_a_register_index, ++ (1 << info->gpio_info.clk_a_shift), &hw_info); ++} ++ ++struct bdf_info dal_adapter_service_get_adapter_info(struct adapter_service *as) ++{ ++ return as->bdf_info; ++} ++ ++/* ++ * dal_adapter_service_should_psr_skip_wait_for_pll_lock ++ * ++ * @brief Determine if this ASIC needs to wait on PLL lock bit ++ * ++ * @param ++ * as: Adapter Service handle ++ * ++ * @out ++ * True if ASIC does not need to wait for PLL lock bit, i.e. skip the wait. ++ */ ++bool dal_adapter_service_should_psr_skip_wait_for_pll_lock( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->caps.SKIP_PSR_WAIT_FOR_PLL_LOCK_BIT; ++} ++ ++bool dal_adapter_service_is_lid_open(struct adapter_service *as) ++{ ++ bool is_lid_open = false; ++ struct platform_info_params params; ++ ++ params.data = &is_lid_open; ++ params.method = PM_GET_LID_STATE; ++ ++ if ((PM_GET_LID_STATE & as->platform_methods_mask) && ++ dal_get_platform_info(as->ctx, ¶ms)) ++ return is_lid_open; ++ ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ return dal_bios_parser_is_lid_open(as->bios_parser); ++#else ++ return false; ++#endif ++} ++ ++bool dal_adapter_service_get_panel_backlight_default_levels( ++ struct adapter_service *as, ++ struct panel_backlight_levels *levels) ++{ ++ if (!as->backlight_caps_initialized) ++ return false; ++ ++ levels->ac_level_percentage = as->ac_level_percentage; ++ levels->dc_level_percentage = as->dc_level_percentage; ++ return true; ++} ++ ++bool dal_adapter_service_get_panel_backlight_boundaries( ++ struct adapter_service *as, ++ struct panel_backlight_boundaries *boundaries) ++{ ++ if (!as->backlight_caps_initialized) ++ return false; ++ if (boundaries != NULL) { ++ boundaries->min_signal_level = as->backlight_8bit_lut[0]; ++ boundaries->max_signal_level = ++ as->backlight_8bit_lut[SIZEOF_BACKLIGHT_LUT - 1]; ++ return true; ++ } ++ return false; ++} ++ ++ ++uint32_t dal_adapter_service_get_view_port_pixel_granularity( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY]; ++} ++ ++/** ++ * Get number of paths per DP 1.2 connector from the runtime parameter if it ++ * exists. ++ * A check to see if MST is supported for the generation of ASIC is done ++ * ++ * \return ++ * Number of paths per DP 1.2 connector is exists in runtime parameters ++ * or ASIC cap ++ */ ++uint32_t dal_adapter_service_get_num_of_path_per_dp_mst_connector( ++ struct adapter_service *as) ++{ ++ if (as->asic_cap->caps.DP_MST_SUPPORTED == 0) { ++ /* ASIC doesn't support DP MST at all */ ++ return 0; ++ } ++ ++ return as->asic_cap->data[ASIC_DATA_PATH_NUM_PER_DPMST_CONNECTOR]; ++} ++ ++uint32_t dal_adapter_service_get_num_of_underlays( ++ struct adapter_service *as) ++{ ++ return as->asic_cap->data[ASIC_DATA_NUM_OF_VIDEO_PLANES]; ++} ++ ++bool dal_adapter_service_get_encoder_cap_info( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ struct graphics_object_encoder_cap_info *info) ++{ ++ struct bp_encoder_cap_info bp_cap_info = {0}; ++ enum bp_result result; ++ ++ if (NULL == info) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ /* ++ * Retrieve Encoder Capability Information from VBIOS and store the ++ * call result (success or fail) ++ * Info from VBIOS about HBR2 has two fields: ++ * ++ * - dpHbr2Cap: indicates supported/not supported by HW Encoder ++ * - dpHbr2En : indicates DP spec compliant/not compliant ++ */ ++ result = dal_bios_parser_get_encoder_cap_info( ++ as->bios_parser, ++ id, ++ &bp_cap_info); ++ ++ /* Set dp_hbr2_validated flag (it's equal to Enable) */ ++ info->dp_hbr2_validated = bp_cap_info.DP_HBR2_EN; ++ ++ if (result == BP_RESULT_OK) { ++ info->dp_hbr2_cap = bp_cap_info.DP_HBR2_CAP; ++ return true; ++ } ++ ++ return false; ++} ++ ++bool dal_adapter_service_is_mc_tuning_req(struct adapter_service *as) ++{ ++ return as->asic_cap->caps.NEED_MC_TUNING ? true : false; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.h b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.h +new file mode 100644 +index 0000000..25ac648 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ADAPTER_SERVICE_H__ ++#define __DAL_ADAPTER_SERVICE_H__ ++ ++/* Include */ ++#include "include/adapter_service_interface.h" ++#include "wireless_data_source.h" ++ ++/* ++ * Forward declaration ++ */ ++struct gpio_service; ++struct asic_cap; ++ ++/* Adapter service */ ++struct adapter_service { ++ struct dc_context *ctx; ++ struct asic_capability *asic_cap; ++ struct bios_parser *bios_parser; ++ struct gpio_service *gpio_service; ++ struct i2caux *i2caux; ++ struct wireless_data wireless_data; ++ struct hw_ctx_adapter_service *hw_ctx; ++ struct integrated_info *integrated_info; ++ struct bdf_info bdf_info; ++ uint32_t platform_methods_mask; ++ uint32_t ac_level_percentage; ++ uint32_t dc_level_percentage; ++ uint32_t backlight_caps_initialized; ++ uint32_t backlight_8bit_lut[SIZEOF_BACKLIGHT_LUT]; ++}; ++ ++/* Type of feature with its runtime parameter and default value */ ++struct feature_source_entry { ++ enum adapter_feature_id feature_id; ++ uint32_t default_value; ++ bool is_boolean_type; ++}; ++ ++/* Stores entire ASIC features by sets */ ++extern uint32_t adapter_feature_set[]; ++ ++#endif /* __DAL_ADAPTER_SERVICE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.c b/drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.c +new file mode 100644 +index 0000000..31c2aab +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.c +@@ -0,0 +1,303 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "../hw_ctx_adapter_service.h" ++ ++#include "hw_ctx_adapter_service_dce110.h" ++ ++#include "include/logger_interface.h" ++#include "include/grph_object_id.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#ifndef mmCC_DC_HDMI_STRAPS ++#define mmCC_DC_HDMI_STRAPS 0x4819 ++#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 ++#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 ++#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 ++#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 ++#endif ++ ++static const struct graphics_object_id invalid_go = { ++ 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN, 0 ++}; ++ ++/* Macro */ ++#define AUDIO_STRAPS_HDMI_ENABLE 0x2 ++ ++#define FROM_HW_CTX(ptr) \ ++ container_of((ptr), struct hw_ctx_adapter_service_dce110, base) ++ ++static const uint32_t audio_index_reg_offset[] = { ++ /*CZ has 3 DIGs but 4 audio endpoints*/ ++ mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, ++ mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_INDEX, ++ mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_INDEX, ++ mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_INDEX ++}; ++ ++static const uint32_t audio_data_reg_offset[] = { ++ mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA, ++ mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_DATA, ++ mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_DATA, ++ mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_DATA, ++}; ++ ++enum { ++ MAX_NUMBER_OF_AUDIO_PINS = 4 ++}; ++ ++static void destruct( ++ struct hw_ctx_adapter_service_dce110 *hw_ctx) ++{ ++ /* There is nothing to destruct at the moment */ ++ dal_adapter_service_destruct_hw_ctx(&hw_ctx->base); ++} ++ ++static void destroy( ++ struct hw_ctx_adapter_service *ptr) ++{ ++ struct hw_ctx_adapter_service_dce110 *hw_ctx = ++ FROM_HW_CTX(ptr); ++ ++ destruct(hw_ctx); ++ ++ dc_service_free(ptr->ctx, hw_ctx); ++} ++ ++/* ++ * enum_audio_object ++ * ++ * @brief enumerate audio object ++ * ++ * @param ++ * const struct hw_ctx_adapter_service *hw_ctx - [in] provides num of endpoints ++ * uint32_t index - [in] audio index ++ * ++ * @return ++ * grphic object id ++ */ ++static struct graphics_object_id enum_audio_object( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index) ++{ ++ uint32_t number_of_connected_audio_endpoints = ++ FROM_HW_CTX(hw_ctx)->number_of_connected_audio_endpoints; ++ ++ if (index >= number_of_connected_audio_endpoints || ++ number_of_connected_audio_endpoints == 0) ++ return invalid_go; ++ else ++ return dal_graphics_object_id_init( ++ AUDIO_ID_INTERNAL_AZALIA, ++ (enum object_enum_id)(index + 1), ++ OBJECT_TYPE_AUDIO); ++} ++ ++static uint32_t get_number_of_connected_audio_endpoints_multistream( ++ struct dc_context *ctx) ++{ ++ uint32_t num_connected_audio_endpoints = 0; ++ uint32_t i; ++ uint32_t default_config = ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT; ++ ++ /* find the total number of streams available via the ++ * AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT ++ * registers (one for each pin) starting from pin 1 ++ * up to the max number of audio pins. ++ * We stop on the first pin where ++ * PORT_CONNECTIVITY == 1 (as instructed by HW team). ++ */ ++ for (i = 0; i < MAX_NUMBER_OF_AUDIO_PINS; i++) { ++ uint32_t value = 0; ++ ++ set_reg_field_value(value, ++ default_config, ++ AZALIA_F0_CODEC_ENDPOINT_INDEX, ++ AZALIA_ENDPOINT_REG_INDEX); ++ ++ dal_write_reg(ctx, audio_index_reg_offset[i], value); ++ ++ value = 0; ++ value = dal_read_reg(ctx, audio_data_reg_offset[i]); ++ ++ /* 1 means not supported*/ ++ if (get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT, ++ PORT_CONNECTIVITY) == 1) ++ break; ++ ++ num_connected_audio_endpoints++; ++ } ++ ++ return num_connected_audio_endpoints; ++ ++} ++ ++/* ++ * get_number_of_connected_audio_endpoints ++ */ ++static uint32_t get_number_of_connected_audio_endpoints( ++ struct hw_ctx_adapter_service *hw_ctx) ++{ ++ uint32_t addr = mmCC_DC_HDMI_STRAPS; ++ uint32_t value = 0; ++ uint32_t field = 0; ++ ++ if (hw_ctx->cached_audio_straps == AUDIO_STRAPS_NOT_ALLOWED) ++ /* audio straps indicate no audio supported */ ++ return 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ field = get_reg_field_value( ++ value, CC_DC_HDMI_STRAPS, AUDIO_STREAM_NUMBER); ++ if (field == 1) ++ /* multi streams not supported */ ++ return 1; ++ else if (field == 0) ++ /* multi streams supported */ ++ return get_number_of_connected_audio_endpoints_multistream( ++ hw_ctx->ctx); ++ ++ /* unexpected value */ ++ ASSERT_CRITICAL(false); ++ return field; ++} ++ ++ ++/* ++ * power_up ++ * ++ * @brief ++ * Determine and cache audio support from register. ++ * ++ * @param ++ * struct hw_ctx_adapter_service *hw_ctx - [in] adapter service hw context ++ * ++ * @return ++ * true if succeed, false otherwise ++ */ ++static bool power_up( ++ struct hw_ctx_adapter_service *hw_ctx) ++{ ++ struct hw_ctx_adapter_service_dce110 *hw_ctx_dce11 = ++ FROM_HW_CTX(hw_ctx); ++ /* Allow DP audio all the time ++ * without additional pinstrap check on Fusion */ ++ ++ ++ { ++ uint32_t value = 0; ++ uint32_t field = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, mmCC_DC_HDMI_STRAPS); ++ field = get_reg_field_value( ++ value, CC_DC_HDMI_STRAPS, HDMI_DISABLE); ++ ++ if (field == 0) { ++ hw_ctx->cached_audio_straps = AUDIO_STRAPS_DP_HDMI_AUDIO; ++ } else { ++ value = dal_read_reg( ++ hw_ctx->ctx, mmDC_PINSTRAPS); ++ field = get_reg_field_value( ++ value, ++ DC_PINSTRAPS, ++ DC_PINSTRAPS_AUDIO); ++ ++ if (field & AUDIO_STRAPS_HDMI_ENABLE) ++ hw_ctx->cached_audio_straps = ++ AUDIO_STRAPS_DP_HDMI_AUDIO_ON_DONGLE; ++ else ++ hw_ctx->cached_audio_straps = ++ AUDIO_STRAPS_DP_AUDIO_ALLOWED; ++ } ++ ++ } ++ ++ /* get the number of connected audio endpoints */ ++ hw_ctx_dce11->number_of_connected_audio_endpoints = ++ get_number_of_connected_audio_endpoints(hw_ctx); ++ ++ return true; ++} ++ ++static void update_audio_connectivity( ++ struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t number_of_audio_capable_display_path, ++ uint32_t number_of_controllers) ++{ ++ /* this one should be empty on DCE110 */ ++} ++ ++static const struct hw_ctx_adapter_service_funcs funcs = { ++ .destroy = destroy, ++ .power_up = power_up, ++ .enum_fake_path_resource = NULL, ++ .enum_stereo_sync_object = NULL, ++ .enum_sync_output_object = NULL, ++ .enum_audio_object = enum_audio_object, ++ .update_audio_connectivity = update_audio_connectivity ++}; ++ ++static bool construct( ++ struct hw_ctx_adapter_service_dce110 *hw_ctx, ++ struct dc_context *ctx) ++{ ++ if (!dal_adapter_service_construct_hw_ctx(&hw_ctx->base, ctx)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ hw_ctx->base.funcs = &funcs; ++ hw_ctx->number_of_connected_audio_endpoints = 0; ++ ++ return true; ++} ++ ++struct hw_ctx_adapter_service * ++ dal_adapter_service_create_hw_ctx_dce110( ++ struct dc_context *ctx) ++{ ++ struct hw_ctx_adapter_service_dce110 *hw_ctx = ++ dc_service_alloc(ctx, sizeof(struct hw_ctx_adapter_service_dce110)); ++ ++ if (!hw_ctx) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(hw_ctx, ctx)) ++ return &hw_ctx->base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(ctx, hw_ctx); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.h b/drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.h +new file mode 100644 +index 0000000..092b671 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/dce110/hw_ctx_adapter_service_dce110.h +@@ -0,0 +1,40 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_CTX_ADAPTER_SERVICE_DCE110_H__ ++#define __DAL_HW_CTX_ADAPTER_SERVICE_DCE110_H__ ++ ++struct hw_ctx_adapter_service_dce110 { ++ struct hw_ctx_adapter_service base; ++ uint32_t number_of_connected_audio_endpoints; ++}; ++ ++struct hw_ctx_adapter_service * ++ dal_adapter_service_create_hw_ctx_dce110( ++ struct dc_context *ctx); ++ ++#endif /* __DAL_HW_CTX_ADAPTER_SERVICE_DCE110_H__ */ ++ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.c b/drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.c +new file mode 100644 +index 0000000..5fa886f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.c +@@ -0,0 +1,164 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/adapter_service_types.h" ++#include "include/grph_object_id.h" ++#include "hw_ctx_adapter_service.h" ++ ++static const struct graphics_object_id invalid_go = { ++ 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN ++}; ++ ++static void destroy( ++ struct hw_ctx_adapter_service *hw_ctx) ++{ ++ /* Attention! ++ * You must override impl method in derived class */ ++ BREAK_TO_DEBUGGER(); ++} ++ ++static bool power_up( ++ struct hw_ctx_adapter_service *hw_ctx) ++{ ++ /* Attention! ++ * You must override impl method in derived class */ ++ BREAK_TO_DEBUGGER(); ++ ++ return false; ++} ++ ++static struct graphics_object_id enum_fake_path_resource( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index) ++{ ++ return invalid_go; ++} ++ ++static struct graphics_object_id enum_stereo_sync_object( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index) ++{ ++ return invalid_go; ++} ++ ++static struct graphics_object_id enum_sync_output_object( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index) ++{ ++ return invalid_go; ++} ++ ++static struct graphics_object_id enum_audio_object( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index) ++{ ++ /* by default, we only allow one audio */ ++ ++ if (index > 0) ++ return invalid_go; ++ else if (hw_ctx->cached_audio_straps == AUDIO_STRAPS_NOT_ALLOWED) ++ return invalid_go; ++ else ++ return dal_graphics_object_id_init( ++ AUDIO_ID_INTERNAL_AZALIA, ++ ENUM_ID_1, ++ OBJECT_TYPE_AUDIO); ++} ++ ++static void update_audio_connectivity( ++ struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t number_of_audio_capable_display_path, ++ uint32_t number_of_controllers) ++{ ++ /* Attention! ++ * You must override impl method in derived class */ ++ BREAK_TO_DEBUGGER(); ++} ++ ++static const struct hw_ctx_adapter_service_funcs funcs = { ++ destroy, ++ power_up, ++ enum_fake_path_resource, ++ enum_stereo_sync_object, ++ enum_sync_output_object, ++ enum_audio_object, ++ update_audio_connectivity ++}; ++ ++bool dal_adapter_service_construct_hw_ctx( ++ struct hw_ctx_adapter_service *hw_ctx, ++ struct dc_context *ctx) ++{ ++ ++ hw_ctx->ctx = ctx; ++ hw_ctx->funcs = &funcs; ++ hw_ctx->cached_audio_straps = AUDIO_STRAPS_NOT_ALLOWED; ++ ++ return true; ++} ++ ++union audio_support dal_adapter_service_hw_ctx_get_audio_support( ++ const struct hw_ctx_adapter_service *hw_ctx) ++{ ++ union audio_support result; ++ ++ result.raw = 0; ++ ++ switch (hw_ctx->cached_audio_straps) { ++ case AUDIO_STRAPS_DP_HDMI_AUDIO: ++ result.bits.HDMI_AUDIO_NATIVE = true; ++ /* do not break ! */ ++ case AUDIO_STRAPS_DP_HDMI_AUDIO_ON_DONGLE: ++ result.bits.HDMI_AUDIO_ON_DONGLE = true; ++ /* do not break ! */ ++ case AUDIO_STRAPS_DP_AUDIO_ALLOWED: ++ result.bits.DP_AUDIO = true; ++ break; ++ default: ++ break; ++ } ++ ++ return result; ++} ++ ++void dal_adapter_service_destruct_hw_ctx( ++ struct hw_ctx_adapter_service *hw_ctx) ++{ ++ /* There is nothing to destruct at the moment */ ++} ++ ++void dal_adapter_service_destroy_hw_ctx( ++ struct hw_ctx_adapter_service **ptr) ++{ ++ if (!ptr || !*ptr) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ (*ptr)->funcs->destroy(*ptr); ++ ++ *ptr = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.h b/drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.h +new file mode 100644 +index 0000000..f98c2d4 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/hw_ctx_adapter_service.h +@@ -0,0 +1,86 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_CTX_ADAPTER_SERVICE_H__ ++#define __DAL_HW_CTX_ADAPTER_SERVICE_H__ ++ ++enum audio_straps { ++ AUDIO_STRAPS_NOT_ALLOWED = 0, ++ AUDIO_STRAPS_DP_AUDIO_ALLOWED, ++ AUDIO_STRAPS_DP_HDMI_AUDIO_ON_DONGLE, ++ AUDIO_STRAPS_DP_HDMI_AUDIO ++}; ++ ++struct hw_ctx_adapter_service; ++ ++struct hw_ctx_adapter_service_funcs { ++ void (*destroy)( ++ struct hw_ctx_adapter_service *hw_ctx); ++ /* Initializes relevant HW registers ++ * and caches relevant data from HW registers */ ++ bool (*power_up)( ++ struct hw_ctx_adapter_service *hw_ctx); ++ /* Enumerate fake path resources */ ++ struct graphics_object_id (*enum_fake_path_resource)( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index); ++ /* Enumerate stereo sync objects */ ++ struct graphics_object_id (*enum_stereo_sync_object)( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index); ++ /* Enumerate (H/V) sync output objects */ ++ struct graphics_object_id (*enum_sync_output_object)( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index); ++ /* Enumerate audio objects */ ++ struct graphics_object_id (*enum_audio_object)( ++ const struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t index); ++ void (*update_audio_connectivity)( ++ struct hw_ctx_adapter_service *hw_ctx, ++ uint32_t number_of_audio_capable_display_path, ++ uint32_t number_of_controllers); ++}; ++ ++struct hw_ctx_adapter_service { ++ struct dc_context *ctx; ++ const struct hw_ctx_adapter_service_funcs *funcs; ++ enum audio_straps cached_audio_straps; ++}; ++ ++bool dal_adapter_service_construct_hw_ctx( ++ struct hw_ctx_adapter_service *hw_ctx, ++ struct dc_context *ctx); ++ ++union audio_support dal_adapter_service_hw_ctx_get_audio_support( ++ const struct hw_ctx_adapter_service *hw_ctx); ++ ++void dal_adapter_service_destruct_hw_ctx( ++ struct hw_ctx_adapter_service *hw_ctx); ++ ++void dal_adapter_service_destroy_hw_ctx( ++ struct hw_ctx_adapter_service **ptr); ++ ++#endif /* __DAL_HW_CTX_ADAPTER_SERVICE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.c b/drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.c +new file mode 100644 +index 0000000..dcb885d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.c +@@ -0,0 +1,209 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++ ++#include "dal_services.h" ++#include "adapter_service.h" ++#include "wireless_data_source.h" ++ ++#include "atom.h" ++ ++/*construct wireless data*/ ++bool wireless_data_init(struct wireless_data *data, ++ struct bios_parser *bp, ++ struct wireless_init_data *init_data) ++{ ++ struct firmware_info info; ++ ++ if (data == NULL || bp == NULL || init_data == NULL) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ data->miracast_connector_enable = false; ++ data->wireless_disp_path_enable = false; ++ data->wireless_enable = false; ++ ++ /* Wireless it not supported if VCE is not supported */ ++ if (!init_data->vce_supported) ++ return true; ++ ++ if (init_data->miracast_target_required) ++ data->miracast_connector_enable = true; ++ ++ /* ++ * If override is in place for platform support, we will both ++ * enable wireless display as a feature (i.e. CCC aspect) and ++ * enable the wireless display path without any further checks. ++ */ ++ if (init_data->platform_override) { ++ data->wireless_enable = true; ++ data->wireless_disp_path_enable = true; ++ } else { ++ /* ++ * Check if SBIOS sets remote display enable, exposed ++ * through VBIOS. This is only valid for APU, not dGPU ++ */ ++ dal_bios_parser_get_firmware_info(bp, &info); ++ ++ if ((REMOTE_DISPLAY_ENABLE == ++ info.remote_display_config) && ++ init_data->fusion) { ++ data->wireless_enable = true; ++ data->wireless_disp_path_enable = true; ++ } ++ } ++ ++ /* ++ * If remote display path override is enabled, we enable just the ++ * remote display path. This is mainly used for testing purposes ++ */ ++ if (init_data->remote_disp_path_override) ++ data->wireless_disp_path_enable = true; ++ ++ return true; ++} ++ ++uint8_t wireless_get_clocks_num( ++ struct adapter_service *as) ++{ ++ if (as->wireless_data.wireless_enable || ++ as->wireless_data.wireless_disp_path_enable) ++ return 1; ++ else ++ return 0; ++} ++ ++static uint8_t wireless_get_encoders_num( ++ struct adapter_service *as) ++{ ++ if (as->wireless_data.wireless_enable || ++ as->wireless_data.wireless_disp_path_enable) ++ return 1; ++ else ++ return 0; ++} ++ ++uint8_t wireless_get_connectors_num( ++ struct adapter_service *as) ++{ ++ uint8_t wireless_connectors_num = 0; ++ ++ if (as->wireless_data.wireless_enable && ++ as->wireless_data.miracast_connector_enable) ++ wireless_connectors_num++; ++ ++ if (as->wireless_data.wireless_disp_path_enable) ++ wireless_connectors_num++; ++ ++ return wireless_connectors_num; ++} ++ ++struct graphics_object_id wireless_get_connector_id( ++ struct adapter_service *as, ++ uint8_t index) ++{ ++ struct graphics_object_id unknown_object_id = ++ dal_graphics_object_id_init( ++ 0, ++ ENUM_ID_UNKNOWN, ++ OBJECT_TYPE_UNKNOWN); ++ ++ if (!as->wireless_data.wireless_enable && ++ !as->wireless_data.wireless_disp_path_enable) ++ return unknown_object_id; ++ ++ else if (!as->wireless_data.miracast_connector_enable) ++ return dal_graphics_object_id_init( ++ CONNECTOR_ID_WIRELESS, ++ ENUM_ID_1, ++ OBJECT_TYPE_CONNECTOR); ++ ++ switch (index) { ++ case 0: ++ return dal_graphics_object_id_init( ++ CONNECTOR_ID_WIRELESS, ++ ENUM_ID_1, ++ OBJECT_TYPE_CONNECTOR); ++ break; ++ case 1: ++ return dal_graphics_object_id_init( ++ CONNECTOR_ID_MIRACAST, ++ ENUM_ID_1, ++ OBJECT_TYPE_CONNECTOR); ++ break; ++ default: ++ return unknown_object_id; ++ } ++} ++ ++uint8_t wireless_get_srcs_num( ++ struct adapter_service *as, ++ struct graphics_object_id id) ++{ ++ switch (id.type) { ++ case OBJECT_TYPE_CONNECTOR: ++ return wireless_get_encoders_num(as); ++ case OBJECT_TYPE_ENCODER: ++ return 1; ++ ++ default: ++ ASSERT_CRITICAL(false); ++ break; ++ } ++ ++ return 0; ++} ++ ++struct graphics_object_id wireless_get_src_obj_id( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ uint8_t index) ++{ ++ if (index < wireless_get_srcs_num(as, id)) { ++ switch (id.type) { ++ case OBJECT_TYPE_CONNECTOR: ++ return dal_graphics_object_id_init( ++ ENCODER_ID_INTERNAL_WIRELESS, ++ ENUM_ID_1, ++ OBJECT_TYPE_ENCODER); ++ break; ++ case OBJECT_TYPE_ENCODER: ++ return dal_graphics_object_id_init( ++ 0, ++ ENUM_ID_1, ++ OBJECT_TYPE_GPU); ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ break; ++ } ++ } ++ ++ return dal_graphics_object_id_init( ++ 0, ++ ENUM_ID_UNKNOWN, ++ OBJECT_TYPE_UNKNOWN); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.h b/drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.h +new file mode 100644 +index 0000000..54b140a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/wireless_data_source.h +@@ -0,0 +1,80 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_WIRELESS_DATA_SOURCE_H__ ++#define __DAL_WIRELESS_DATA_SOURCE_H__ ++ ++/* Include */ ++#include "include/grph_object_id.h" ++ ++/* ++ * Forward declaration ++ */ ++struct adapter_service; ++struct bios_parser; ++ ++/* Wireless data init structure */ ++struct wireless_init_data { ++ bool fusion; /* Fusion flag */ ++ bool platform_override; /* Override for platform BIOS option */ ++ bool remote_disp_path_override; /* Override enabling wireless path */ ++ bool vce_supported; /* Existence of VCE block on this DCE */ ++ bool miracast_target_required; /* OS requires Miracast target */ ++}; ++ ++/* Wireless data */ ++struct wireless_data { ++ bool wireless_enable; ++ bool wireless_disp_path_enable; ++ bool miracast_connector_enable; ++}; ++ ++ ++/*construct wireless data*/ ++bool wireless_data_init( ++ struct wireless_data *data, ++ struct bios_parser *bp, ++ struct wireless_init_data *init_data); ++ ++uint8_t wireless_get_clocks_num( ++ struct adapter_service *as); ++ ++uint8_t wireless_get_connectors_num( ++ struct adapter_service *as); ++ ++struct graphics_object_id wireless_get_connector_id( ++ struct adapter_service *as, ++ uint8_t connector_index); ++ ++uint8_t wireless_get_srcs_num( ++ struct adapter_service *as, ++ struct graphics_object_id id); ++ ++struct graphics_object_id wireless_get_src_obj_id( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ uint8_t index); ++ ++#endif /* __DAL_WIRELESS_DATA_SOURCE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile b/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile +new file mode 100644 +index 0000000..5e01a86 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile +@@ -0,0 +1,23 @@ ++# ++# Makefile for the 'asic_capability' sub-component of DAL. ++# ++ ++ASIC_CAPABILITY = asic_capability.o ++ ++AMD_DAL_ASIC_CAPABILITY = \ ++ $(addprefix $(AMDDALPATH)/dc/asic_capability/,$(ASIC_CAPABILITY)) ++ ++AMD_DAL_FILES += $(AMD_DAL_ASIC_CAPABILITY) ++ ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++ASIC_CAPABILITY_DCE11 = carrizo_asic_capability.o ++ ++AMD_DAL_ASIC_CAPABILITY_DCE11 = \ ++ $(addprefix $(AMDDALPATH)/dc/asic_capability/,$(ASIC_CAPABILITY_DCE11)) ++ ++AMD_DAL_FILES += $(AMD_DAL_ASIC_CAPABILITY_DCE11) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/asic_capability.c b/drivers/gpu/drm/amd/dal/dc/asic_capability/asic_capability.c +new file mode 100644 +index 0000000..a532e2f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/asic_capability.c +@@ -0,0 +1,178 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/logger_interface.h" ++ ++#include "include/asic_capability_interface.h" ++#include "include/asic_capability_types.h" ++#include "include/dal_types.h" ++#include "include/dal_asic_id.h" ++ ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "carrizo_asic_capability.h" ++#endif ++ ++/* ++ * Initializes asic_capability instance. ++ */ ++static bool construct( ++ struct asic_capability *cap, ++ struct hw_asic_id *init, ++ struct dc_context *ctx) ++{ ++ bool asic_supported = false; ++ ++ cap->ctx = ctx; ++ dc_service_memset(cap->data, 0, sizeof(cap->data)); ++ ++ /* ASIC data */ ++ cap->data[ASIC_DATA_VRAM_TYPE] = init->vram_type; ++ cap->data[ASIC_DATA_VRAM_BITWIDTH] = init->vram_width; ++ cap->data[ASIC_DATA_FEATURE_FLAGS] = init->feature_flags; ++ cap->runtime_flags = init->runtime_flags; ++ cap->data[ASIC_DATA_REVISION_ID] = init->hw_internal_rev; ++ cap->data[ASIC_DATA_MAX_UNDERSCAN_PERCENTAGE] = 10; ++ cap->data[ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY] = 4; ++ cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM] = 1; ++ cap->data[ASIC_DATA_NUM_OF_VIDEO_PLANES] = 0; ++ cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ] = 25; ++ ++ /* ASIC basic capability */ ++ cap->caps.UNDERSCAN_FOR_HDMI_ONLY = true; ++ cap->caps.SUPPORT_CEA861E_FINAL = true; ++ cap->caps.MIRABILIS_SUPPORTED = false; ++ cap->caps.MIRABILIS_ENABLED_BY_DEFAULT = false; ++ cap->caps.WIRELESS_LIMIT_TO_720P = false; ++ cap->caps.WIRELESS_FULL_TIMING_ADJUSTMENT = false; ++ cap->caps.WIRELESS_TIMING_ADJUSTMENT = true; ++ cap->caps.WIRELESS_COMPRESSED_AUDIO = false; ++ cap->caps.VCE_SUPPORTED = false; ++ cap->caps.HPD_CHECK_FOR_EDID = false; ++ cap->caps.NO_VCC_OFF_HPD_POLLING = false; ++ cap->caps.NEED_MC_TUNING = false; ++ cap->caps.SUPPORT_8BPP = true; ++ ++ /* ASIC stereo 3D capability */ ++ cap->stereo_3d_caps.SUPPORTED = true; ++ ++ switch (init->chip_family) { ++ case FAMILY_CI: ++ break; ++ ++ case FAMILY_KV: ++ if (ASIC_REV_IS_KALINDI(init->hw_internal_rev) || ++ ASIC_REV_IS_BHAVANI(init->hw_internal_rev)) { ++ } else { ++ } ++ break; ++ ++ case FAMILY_CZ: ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ carrizo_asic_capability_create(cap, init); ++ asic_supported = true; ++#endif ++ break; ++ ++ default: ++ /* unsupported "chip_family" */ ++ break; ++ } ++ ++ if (false == asic_supported) { ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_MASK_ALL, ++ "%s: ASIC not supported!\n", __func__); ++ } ++ ++ return asic_supported; ++} ++ ++static void destruct( ++ struct asic_capability *cap) ++{ ++ /* nothing to do (yet?) */ ++} ++ ++/* ++ * dal_asic_capability_create ++ * ++ * Creates asic capability based on DCE version. ++ */ ++struct asic_capability *dal_asic_capability_create( ++ struct hw_asic_id *init, ++ struct dc_context *ctx) ++{ ++ struct asic_capability *cap; ++ ++ if (!init) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ cap = dc_service_alloc(ctx, sizeof(struct asic_capability)); ++ ++ if (!cap) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ if (construct(cap, init, ctx)) ++ return cap; ++ ++ BREAK_TO_DEBUGGER(); ++ ++ dc_service_free(ctx, cap); ++ ++ return NULL; ++} ++ ++/* ++ * dal_asic_capability_destroy ++ * ++ * Destroy allocated memory. ++ */ ++void dal_asic_capability_destroy( ++ struct asic_capability **cap) ++{ ++ if (!cap) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ if (!*cap) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ destruct(*cap); ++ ++ dc_service_free((*cap)->ctx, *cap); ++ ++ *cap = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.c b/drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.c +new file mode 100644 +index 0000000..f57d3f7 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.c +@@ -0,0 +1,146 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/asic_capability_interface.h" ++#include "include/asic_capability_types.h" ++ ++#include "carrizo_asic_capability.h" ++ ++#include "atom.h" ++#include "dce/dce_11_0_d.h" ++#include "smu/smu_8_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "dal_asic_id.h" ++ ++#define ixVCE_HARVEST_FUSE_MACRO__ADDRESS 0xC0014074 ++ ++/* ++ * carrizo_asic_capability_create ++ * ++ * Create and initiate Carrizo capability. ++ */ ++void carrizo_asic_capability_create(struct asic_capability *cap, ++ struct hw_asic_id *init) ++{ ++ uint32_t e_fuse_setting; ++ /* ASIC data */ ++ cap->data[ASIC_DATA_CONTROLLERS_NUM] = 3; ++ cap->data[ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM] = 3; ++ cap->data[ASIC_DATA_LINEBUFFER_NUM] = 3; ++ cap->data[ASIC_DATA_PATH_NUM_PER_DPMST_CONNECTOR] = 4; ++ cap->data[ASIC_DATA_DCE_VERSION] = 0x110; /* DCE 11 */ ++ cap->data[ASIC_DATA_LINEBUFFER_SIZE] = 1712 * 144; ++ cap->data[ASIC_DATA_DRAM_BANDWIDTH_EFFICIENCY] = 45; ++ cap->data[ASIC_DATA_CLOCKSOURCES_NUM] = 2; ++ cap->data[ASIC_DATA_MC_LATENCY] = 5000; ++ cap->data[ASIC_DATA_STUTTERMODE] = 0x200A; ++ cap->data[ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY] = 2; ++ cap->data[ASIC_DATA_MAX_COFUNC_NONDP_DISPLAYS] = 2; ++ cap->data[ASIC_DATA_MEMORYTYPE_MULTIPLIER] = 2; ++ cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ] = 100; ++ cap->data[ASIC_DATA_NUM_OF_VIDEO_PLANES] = 1; ++ cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM] = 3; ++ ++ /* ASIC basic capability */ ++ cap->caps.IS_FUSION = true; ++ cap->caps.DP_MST_SUPPORTED = true; ++ cap->caps.PANEL_SELF_REFRESH_SUPPORTED = true; ++ cap->caps.MIRABILIS_SUPPORTED = true; ++ cap->caps.NO_VCC_OFF_HPD_POLLING = true; ++ cap->caps.VCE_SUPPORTED = true; ++ cap->caps.HPD_CHECK_FOR_EDID = true; ++ cap->caps.DFSBYPASS_DYNAMIC_SUPPORT = true; ++ cap->caps.SUPPORT_8BPP = false; ++ ++ /* ASIC stereo 3d capability */ ++ cap->stereo_3d_caps.DISPLAY_BASED_ON_WS = true; ++ cap->stereo_3d_caps.HDMI_FRAME_PACK = true; ++ cap->stereo_3d_caps.INTERLACE_FRAME_PACK = true; ++ cap->stereo_3d_caps.DISPLAYPORT_FRAME_PACK = true; ++ cap->stereo_3d_caps.DISPLAYPORT_FRAME_ALT = true; ++ cap->stereo_3d_caps.INTERLEAVE = true; ++ ++ e_fuse_setting = dal_read_index_reg(cap->ctx,CGS_IND_REG__SMC,ixVCE_HARVEST_FUSE_MACRO__ADDRESS); ++ ++ /* Bits [28:27]*/ ++ switch ((e_fuse_setting >> 27) & 0x3) { ++ case 0: ++ /*both VCE engine are working*/ ++ cap->caps.VCE_SUPPORTED = true; ++ cap->caps.WIRELESS_TIMING_ADJUSTMENT = false; ++ /*TODO: ++ cap->caps.wirelessLowVCEPerformance = false; ++ m_AsicCaps.vceInstance0Enabled = true; ++ m_AsicCaps.vceInstance1Enabled = true;*/ ++ cap->caps.NEED_MC_TUNING = true; ++ break; ++ ++ case 1: ++ cap->caps.VCE_SUPPORTED = true; ++ cap->caps.WIRELESS_TIMING_ADJUSTMENT = true; ++ /*TODO: ++ m_AsicCaps.wirelessLowVCEPerformance = false; ++ m_AsicCaps.vceInstance1Enabled = true;*/ ++ cap->caps.NEED_MC_TUNING = true; ++ break; ++ ++ case 2: ++ cap->caps.VCE_SUPPORTED = true; ++ cap->caps.WIRELESS_TIMING_ADJUSTMENT = true; ++ /*TODO: ++ m_AsicCaps.wirelessLowVCEPerformance = false; ++ m_AsicCaps.vceInstance0Enabled = true;*/ ++ cap->caps.NEED_MC_TUNING = true; ++ break; ++ ++ case 3: ++ /* VCE_DISABLE = 0x3 - both VCE ++ * instances are in harvesting, ++ * no VCE supported any more. ++ */ ++ cap->caps.VCE_SUPPORTED = false; ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (ASIC_REV_IS_STONEY(init->hw_internal_rev)) ++ { ++ /* Stoney is the same DCE11, but only two pipes, three digs. ++ * and HW added 64bit back for non SG */ ++ cap->data[ASIC_DATA_CONTROLLERS_NUM] = 2; ++ cap->data[ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM] = 2; ++ cap->data[ASIC_DATA_LINEBUFFER_NUM] = 2; ++ /*3 DP MST per connector, limited by number of pipe and number ++ * of Dig.*/ ++ cap->data[ASIC_DATA_PATH_NUM_PER_DPMST_CONNECTOR] = 2; ++ ++ } ++ ++ ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.h b/drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.h +new file mode 100644 +index 0000000..d1e9b83 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/carrizo_asic_capability.h +@@ -0,0 +1,36 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CARRIZO_ASIC_CAPABILITY_H__ ++#define __DAL_CARRIZO_ASIC_CAPABILITY_H__ ++ ++/* Forward declaration */ ++struct asic_capability; ++ ++/* Create and initialize Carrizo data */ ++void carrizo_asic_capability_create(struct asic_capability *cap, ++ struct hw_asic_id *init); ++ ++#endif /* __DAL_CARRIZO_ASIC_CAPABILITY_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/Makefile b/drivers/gpu/drm/amd/dal/dc/audio/Makefile +new file mode 100644 +index 0000000..0999372 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/Makefile +@@ -0,0 +1,22 @@ ++# ++# Makefile for the 'audio' sub-component of DAL. ++# It provides the control and status of HW adapter resources, ++# that are global for the ASIC and sharable between pipes. ++ ++AUDIO = audio_base.o hw_ctx_audio.o ++ ++AMD_DAL_AUDIO = $(addprefix $(AMDDALPATH)/dc/audio/,$(AUDIO)) ++ ++AMD_DAL_FILES += $(AMD_DAL_AUDIO) ++ ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++AUDIO_DCE11 = audio_dce110.o hw_ctx_audio_dce110.o ++ ++AMD_DAL_AUDIO_DCE11 = $(addprefix $(AMDDALPATH)/dc/audio/dce110/,$(AUDIO_DCE11)) ++ ++AMD_DAL_FILES += $(AMD_DAL_AUDIO_DCE11) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/audio.h b/drivers/gpu/drm/amd/dal/dc/audio/audio.h +new file mode 100644 +index 0000000..ad2dc18 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/audio.h +@@ -0,0 +1,195 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_AUDIO_H__ ++#define __DAL_AUDIO_H__ ++ ++#include "include/audio_interface.h" ++#include "hw_ctx_audio.h" ++#include "include/link_service_types.h" ++ ++/***** only for hook functions *****/ ++/** ++ *which will be overwritten by derived audio object. ++ *audio hw context object is independent object ++ */ ++ ++struct audio; ++ ++struct audio_funcs { ++ /* ++ *get_object_id ++ *get_object_type ++ *enumerate_input_signals ++ *enumerate_output_signals ++ *is_input_signal_supported ++ *is_output_signal_supported ++ *set_object_properties ++ *get_object_properties ++ */ ++ ++ void (*destroy)(struct audio **audio); ++ /*power_up ++ *power_down ++ *release_hw_base ++ */ ++ ++ /* setup audio */ ++ enum audio_result (*setup)( ++ struct audio *audio, ++ struct audio_output *output, ++ struct audio_info *info); ++ ++ enum audio_result (*enable_output)( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++ enum audio_result (*disable_output)( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++ /*enable_azalia_audio_jack_presence ++ * disable_azalia_audio_jack_presence ++ */ ++ ++ enum audio_result (*unmute)( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++ enum audio_result (*mute)( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++ /* SW initialization that cannot be done in constructor. This will ++ * be done is audio_power_up but is not in audio_interface. It is only ++ * called by power_up ++ */ ++ enum audio_result (*initialize)( ++ struct audio *audio); ++ ++ /* enable channel splitting mapping */ ++ void (*enable_channel_splitting_mapping)( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable); ++ ++ /* get current multi channel split. */ ++ enum audio_result (*get_channel_splitting_mapping)( ++ struct audio *audio, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping); ++ ++ /* set payload value for the unsolicited response */ ++ void (*set_unsolicited_response_payload)( ++ struct audio *audio, ++ enum audio_payload payload); ++ ++ /* Update audio wall clock source */ ++ void (*setup_audio_wall_dto)( ++ struct audio *audio, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info); ++ ++ /* options and features supported by Audio */ ++ struct audio_feature_support (*get_supported_features)( ++ struct audio *audio); ++ ++ /* ++ *check_audio_bandwidth ++ *write_reg ++ *read_reg ++ *enable_gtc_embedding_with_group ++ *disable_gtc_embedding ++ *register_interrupt ++ *unregister_interrupt ++ *process_interrupt ++ *create_hw_ctx ++ *getHwCtx ++ *setHwCtx ++ *handle_interrupt ++ */ ++}; ++ ++struct audio { ++ /* hook functions. they will be overwritten by specific ASIC */ ++ const struct audio_funcs *funcs; ++ /* TODO: static struct audio_funcs funcs;*/ ++ ++ /*external structures - get service from external*/ ++ struct graphics_object_id id; ++ struct adapter_service *adapter_service; ++ /* audio HW context */ ++ struct hw_ctx_audio *hw_ctx; ++ struct dc_context *ctx; ++ /* audio supports input and output signals */ ++ uint32_t input_signals; ++ uint32_t output_signals; ++}; ++ ++/* - functions defined by audio.h will be used by audio component only. ++ * but audio.c also implements some function defined by dal\include ++ */ ++ ++/* graphics_object_base implemention ++ * 1.input_signals and output_signals are moved ++ * into audio object. ++ * ++ * 2.Get the Graphics Object ID ++ * ++ * Outside audio: ++ * use dal_graphics_object_id_get_audio_id ++ * Within audio: ++ * use audio->go_base.id ++ * ++ * 3. Get the Graphics Object Type ++ * ++ * use object_id.type ++ * not function implemented. ++ * 4. Common Graphics Object Properties ++ * use object id ->go_properties.multi_path ++ * not function implemented. ++ */ ++ ++bool dal_audio_construct_base( ++ struct audio *audio, ++ const struct audio_init_data *init_data); ++ ++void dal_audio_destruct_base( ++ struct audio *audio); ++ ++void dal_audio_release_hw_base( ++ struct audio *audio); ++ ++#endif /* __DAL_AUDIO__ */ ++ ++ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c +new file mode 100644 +index 0000000..6bac3ed +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c +@@ -0,0 +1,463 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/logger_interface.h" ++ ++#include "audio.h" ++#include "hw_ctx_audio.h" ++ ++#include "dce110/audio_dce110.h" ++ ++/***** static function : only used within audio.c *****/ ++ ++/* stub for hook functions */ ++static void destroy( ++ struct audio **audio) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++static enum audio_result setup( ++ struct audio *audio, ++ struct audio_output *output, ++ struct audio_info *info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++static enum audio_result enable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++static enum audio_result disable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++static enum audio_result unmute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++static enum audio_result mute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++static enum audio_result initialize( ++ struct audio *audio) ++{ ++ /*DCE specific, must be implemented in derived. Implemeentaion of ++ *initialize will create audio hw context. create_hw_ctx ++ */ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++static void enable_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* get current multi channel split. */ ++static enum audio_result get_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return AUDIO_RESULT_OK; ++} ++ ++/* set payload value for the unsolicited response */ ++static void set_unsolicited_response_payload( ++ struct audio *audio, ++ enum audio_payload payload) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* update audio wall clock source */ ++static void setup_audio_wall_dto( ++ struct audio *audio, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++static struct audio_feature_support get_supported_features(struct audio *audio) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ struct audio_feature_support features; ++ ++ dc_service_memset(&features, 0, sizeof(features)); ++ ++ features.ENGINE_DIGA = 1; ++ features.ENGINE_DIGB = 1; ++ ++ return features; ++} ++ ++static const struct audio_funcs audio_funcs = { ++ .destroy = destroy, ++ .setup = setup, ++ .enable_output = enable_output, ++ .disable_output = disable_output, ++ .unmute = unmute, ++ .mute = mute, ++ .initialize = initialize, ++ .enable_channel_splitting_mapping = ++ enable_channel_splitting_mapping, ++ .get_channel_splitting_mapping = ++ get_channel_splitting_mapping, ++ .set_unsolicited_response_payload = ++ set_unsolicited_response_payload, ++ .setup_audio_wall_dto = setup_audio_wall_dto, ++ .get_supported_features = get_supported_features, ++}; ++ ++/***** SCOPE : declare in audio.h. use within dal-audio. *****/ ++ ++bool dal_audio_construct_base( ++ struct audio *audio, ++ const struct audio_init_data *init_data) ++{ ++ enum signal_type signals = SIGNAL_TYPE_HDMI_TYPE_A; ++ ++ ASSERT(init_data->as != NULL); ++ ++ /* base hook functions */ ++ audio->funcs = &audio_funcs; ++ ++ /*setup pointers to get service from dal service compoenents*/ ++ audio->adapter_service = init_data->as; ++ ++ audio->ctx = init_data->ctx; ++ ++ /* save audio endpoint number to identify object creating */ ++ audio->id = init_data->audio_stream_id; ++ ++ /* Fill supported signals. !!! be aware that android definition is ++ * already shift to vector. ++ */ ++ signals |= SIGNAL_TYPE_DISPLAY_PORT; ++ signals |= SIGNAL_TYPE_DISPLAY_PORT_MST; ++ signals |= SIGNAL_TYPE_EDP; ++ signals |= SIGNAL_TYPE_DISPLAY_PORT; ++ signals |= SIGNAL_TYPE_WIRELESS; ++ ++ /* Audio supports same set for input and output signals */ ++ audio->input_signals = signals; ++ audio->output_signals = signals; ++ ++ return true; ++} ++ ++/* except hw_ctx, no other hw need reset. so do nothing */ ++void dal_audio_destruct_base( ++ struct audio *audio) ++{ ++} ++ ++/* Enumerate Graphics Object supported Input/Output Signal Types */ ++uint32_t dal_audio_enumerate_input_signals( ++ struct audio *audio) ++{ ++ return audio->input_signals; ++} ++ ++uint32_t dal_audio_enumerate_output_signals( ++ struct audio *audio) ++{ ++ return audio->output_signals; ++} ++ ++/* Check if signal supported by GraphicsObject */ ++bool dal_audio_is_input_signal_supported( ++ struct audio *audio, ++ enum signal_type signal) ++{ ++ return (signal & audio->output_signals) != 0; ++} ++ ++bool dal_audio_is_output_signal_supported( ++ struct audio *audio, ++ enum signal_type signal) ++{ ++ return (signal & audio->input_signals) != 0; ++} ++ ++/***** SCOPE : declare in dal\include *****/ ++ ++/* audio object creator triage. memory allocate and release will be ++ * done within dal_audio_create_dcexx ++ */ ++struct audio *dal_audio_create( ++ const struct audio_init_data *init_data) ++{ ++ struct adapter_service *as; ++ ++ if (init_data->as == NULL) ++ return NULL; ++ ++ as = init_data->as; ++ switch (dal_adapter_service_get_dce_version(as)) { ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ return dal_audio_create_dce110(init_data); ++#endif ++ default: ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ return NULL; ++} ++ ++/* audio object creator triage. ++ * memory for "struct audio dal_audio_create_dce8x" allocate ++ * will happens within dal_audio_dce8x. memory allocate is done ++ * with dal_audio_create_dce8x. memory release is initiated by ++ * dal_audio_destroy. It will call hook function which will finially ++ * used destroy() of dal_audio_dce8x. therefore, no memroy allocate ++ *and release happen physcially at audio base object. ++ */ ++void dal_audio_destroy( ++ struct audio **audio) ++{ ++ if (!audio || !*audio) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ (*audio)->funcs->destroy(audio); ++ ++ *audio = NULL; ++} ++ ++const struct graphics_object_id dal_audio_get_graphics_object_id( ++ const struct audio *audio) ++{ ++ return audio->id; ++} ++ ++/* enable azalia audio endpoint. This function call hw_ctx directly ++ *not overwitten at audio level. ++ */ ++enum audio_result dal_audio_enable_azalia_audio_jack_presence( ++ struct audio *audio, ++ enum engine_id engine_id) ++{ ++ audio->hw_ctx->funcs->enable_azalia_audio(audio->hw_ctx, engine_id); ++ return AUDIO_RESULT_OK; ++} ++ ++/* disable azalia audio endpoint. This function call hw_ctx directly ++ *not overwitten at audio level. ++ */ ++enum audio_result dal_audio_disable_azalia_audio_jack_presence( ++ struct audio *audio, ++ enum engine_id engine_id) ++{ ++ audio->hw_ctx->funcs->disable_azalia_audio(audio->hw_ctx, engine_id); ++ return AUDIO_RESULT_OK; ++} ++ ++/* get audio bandwidth information. This function call hw_ctx directly ++ *not overwitten at audio level. ++ */ ++void dal_audio_check_audio_bandwidth( ++ struct audio *audio, ++ const struct audio_crtc_info *info, ++ uint32_t channel_count, ++ enum signal_type signal, ++ union audio_sample_rates *sample_rates) ++{ ++ dal_hw_ctx_audio_check_audio_bandwidth( ++ audio->hw_ctx, info, channel_count, signal, sample_rates); ++} ++ ++/* DP Audio register write access. This function call hw_ctx directly ++ * not overwitten at audio level. ++ */ ++ ++/*assign GTC group and enable GTC value embedding*/ ++void dal_audio_enable_gtc_embedding_with_group( ++ struct audio *audio, ++ uint32_t group_num, ++ uint32_t audio_latency) ++{ ++ audio->hw_ctx->funcs->enable_gtc_embedding_with_group( ++ audio->hw_ctx, group_num, audio_latency); ++} ++ ++/* disable GTC value embedding */ ++void dal_audio_disable_gtc_embedding( ++ struct audio *audio) ++{ ++ audio->hw_ctx->funcs->disable_gtc_embedding(audio->hw_ctx); ++} ++ ++/* perform power up sequence (boot up, resume, recovery) */ ++enum audio_result dal_audio_power_up( ++ struct audio *audio) ++{ ++ return audio->funcs->initialize(audio); ++} ++ ++/* perform power down (shut down, stand by) */ ++enum audio_result dal_audio_power_down( ++ struct audio *audio) ++{ ++ return AUDIO_RESULT_OK; ++} ++ ++/* setup audio */ ++enum audio_result dal_audio_setup( ++ struct audio *audio, ++ struct audio_output *output, ++ struct audio_info *info) ++{ ++ return audio->funcs->setup(audio, output, info); ++} ++ ++/* enable audio */ ++enum audio_result dal_audio_enable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ return audio->funcs->enable_output(audio, engine_id, signal); ++} ++ ++/* disable audio */ ++enum audio_result dal_audio_disable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ return audio->funcs->disable_output(audio, engine_id, signal); ++} ++ ++/* unmute audio */ ++enum audio_result dal_audio_unmute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ return audio->funcs->unmute(audio, engine_id, signal); ++} ++ ++/* mute audio */ ++enum audio_result dal_audio_mute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ return audio->funcs->mute(audio, engine_id, signal); ++} ++ ++/* Enable multi channel split */ ++void dal_audio_enable_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable) ++{ ++ audio->funcs->enable_channel_splitting_mapping( ++ audio, engine_id, signal, audio_mapping, enable); ++} ++ ++/* get current multi channel split. */ ++enum audio_result dal_audio_get_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping) ++{ ++ return audio->funcs->get_channel_splitting_mapping( ++ audio, engine_id, audio_mapping); ++} ++ ++/* set payload value for the unsolicited response */ ++void dal_audio_set_unsolicited_response_payload( ++ struct audio *audio, ++ enum audio_payload payload) ++{ ++ audio->funcs->set_unsolicited_response_payload(audio, payload); ++} ++ ++/* update audio wall clock source */ ++void dal_audio_setup_audio_wall_dto( ++ struct audio *audio, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info) ++{ ++ audio->funcs->setup_audio_wall_dto(audio, signal, crtc_info, pll_info); ++} ++ ++struct audio_feature_support dal_audio_get_supported_features( ++ struct audio *audio) ++{ ++ return audio->funcs->get_supported_features(audio); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c +new file mode 100644 +index 0000000..f284870 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c +@@ -0,0 +1,452 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/logger_interface.h" ++ ++#include "audio_dce110.h" ++ ++/***** static functions *****/ ++ ++static void destruct(struct audio_dce110 *audio) ++{ ++ /*release memory allocated for hw_ctx -- allocated is initiated ++ *by audio_dce110 power_up ++ *audio->base->hw_ctx = NULL is done within hw-ctx->destroy ++ */ ++ if (audio->base.hw_ctx) ++ audio->base.hw_ctx->funcs->destroy(&(audio->base.hw_ctx)); ++ ++ /* reset base_audio_block */ ++ dal_audio_destruct_base(&audio->base); ++} ++ ++static void destroy(struct audio **ptr) ++{ ++ struct audio_dce110 *audio = NULL; ++ ++ audio = container_of(*ptr, struct audio_dce110, base); ++ ++ destruct(audio); ++ ++ /* release memory allocated for audio_dce110*/ ++ dc_service_free((*ptr)->ctx, audio); ++ *ptr = NULL; ++} ++ ++ ++/* The inital call of hook function comes from audio object level. ++ *The passing object handle "struct audio *audio" point to base object ++ *already.There is not need to get base object from audio_dce110. ++ */ ++ ++/** ++* setup ++* ++* @brief ++* setup Audio HW block, to be called by dal_audio_setup ++* ++*/ ++static enum audio_result setup( ++ struct audio *audio, ++ struct audio_output *output, ++ struct audio_info *info) ++{ ++ switch (output->signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ /*setup HDMI audio engine*/ ++ audio->hw_ctx->funcs->enable_afmt_clock( ++ audio->hw_ctx, ++ output->engine_id, ++ true); ++ audio->hw_ctx->funcs->setup_hdmi_audio( ++ audio->hw_ctx, output->engine_id, &output->crtc_info); ++ ++ audio->hw_ctx->funcs->setup_azalia( ++ audio->hw_ctx, ++ output->engine_id, ++ output->signal, ++ &output->crtc_info, ++ &output->pll_info, ++ info); ++ break; ++ ++ case SIGNAL_TYPE_WIRELESS: ++ /* setup Azalia block for Wireless Display - This ++ is different than for wired ++ displays because there is no ++ DIG to program.*/ ++ /*TODO: ++ audio->hw_ctx->funcs->setup_azalia_for_vce( ++ audio->hw_ctx, ++ audio->signal, ++ audio->crtc_info, ++ info); ++ */ ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ /* setup DP audio engine will be done at enable output */ ++ ++ /* setup Azalia block*/ ++ audio->hw_ctx->funcs->setup_azalia( ++ audio->hw_ctx, ++ output->engine_id, ++ output->signal, ++ &output->crtc_info, ++ &output->pll_info, ++ info); ++ ++ break; ++ default: ++ return AUDIO_RESULT_ERROR; ++ } ++ ++ return AUDIO_RESULT_OK; ++} ++ ++/** ++* enable_output ++* ++* @brief ++* enable Audio HW block, to be called by dal_audio_enable_output ++*/ ++static enum audio_result enable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ /* enable audio output */ ++ switch (signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: { ++ /* enable AFMT clock before enable audio*/ ++ audio->hw_ctx->funcs->enable_afmt_clock( ++ audio->hw_ctx, engine_id, true); ++ /* setup DP audio engine */ ++ audio->hw_ctx->funcs->setup_dp_audio( ++ audio->hw_ctx, engine_id); ++ /* enabl DP audio packets will be done at unblank */ ++ audio->hw_ctx->funcs->enable_dp_audio( ++ audio->hw_ctx, engine_id); ++ } ++ break; ++ case SIGNAL_TYPE_WIRELESS: ++ /* route audio to VCE block */ ++ audio->hw_ctx->funcs->setup_vce_audio(audio->hw_ctx); ++ break; ++ default: ++ return AUDIO_RESULT_ERROR; ++ } ++ return AUDIO_RESULT_OK; ++} ++ ++/** ++* disable_output ++* ++* @brief ++* disable Audio HW block, to be called by dal_audio_disable_output ++* ++*/ ++static enum audio_result disable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ case SIGNAL_TYPE_WIRELESS: ++ /* disable HDMI audio */ ++ audio->hw_ctx-> ++ funcs->disable_azalia_audio( ++ audio->hw_ctx, engine_id); ++ audio->hw_ctx-> ++ funcs->enable_afmt_clock( ++ audio->hw_ctx, engine_id, ++ false); ++ ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: { ++ /* disable DP audio */ ++ audio->hw_ctx->funcs->disable_dp_audio( ++ audio->hw_ctx, engine_id); ++ audio->hw_ctx->funcs->disable_azalia_audio( ++ audio->hw_ctx, engine_id); ++ audio->hw_ctx->funcs->enable_afmt_clock( ++ audio->hw_ctx, engine_id, false); ++ } ++ break; ++ default: ++ return AUDIO_RESULT_ERROR; ++ } ++ ++ return AUDIO_RESULT_OK; ++} ++ ++/** ++* unmute ++* ++* @brief ++* unmute audio, to be called by dal_audio_unmute ++* ++*/ ++static enum audio_result unmute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ /* unmute Azalia audio */ ++ audio->hw_ctx->funcs->unmute_azalia_audio( ++ audio->hw_ctx, engine_id); ++ break; ++ case SIGNAL_TYPE_WIRELESS: ++ /*Do nothing for wireless display*/ ++ break; ++ default: ++ return AUDIO_RESULT_ERROR; ++ } ++ return AUDIO_RESULT_OK; ++} ++ ++/** ++* mute ++* ++* @brief ++* mute audio, to be called by dal_audio_nmute ++* ++*/ ++static enum audio_result mute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ /* mute Azalia audio */ ++ audio->hw_ctx->funcs->mute_azalia_audio( ++ audio->hw_ctx, engine_id); ++ break; ++ case SIGNAL_TYPE_WIRELESS: ++ /*Do nothing for wireless display*/ ++ break; ++ default: ++ return AUDIO_RESULT_ERROR; ++ } ++ return AUDIO_RESULT_OK; ++} ++ ++/** ++* initialize ++* ++* @brief ++* Perform SW initialization - create audio hw context. Then do HW ++* initialization. this function is called at dal_audio_power_up. ++* ++*/ ++static enum audio_result initialize( ++ struct audio *audio) ++{ ++ uint8_t audio_endpoint_enum_id = 0; ++ ++ audio_endpoint_enum_id = audio->id.enum_id; ++ ++ /* HW CTX already create*/ ++ if (audio->hw_ctx != NULL) ++ return AUDIO_RESULT_OK; ++ ++ audio->hw_ctx = dal_hw_ctx_audio_dce110_create( ++ audio->ctx, ++ audio_endpoint_enum_id); ++ ++ if (audio->hw_ctx == NULL) ++ return AUDIO_RESULT_ERROR; ++ ++ /* override HW default settings */ ++ audio->hw_ctx->funcs->hw_initialize(audio->hw_ctx); ++ ++ return AUDIO_RESULT_OK; ++} ++ ++/* enable multi channel split */ ++static void enable_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable) ++{ ++ audio->hw_ctx->funcs->setup_channel_splitting_mapping( ++ audio->hw_ctx, ++ engine_id, ++ signal, ++ audio_mapping, enable); ++} ++ ++/* get current multi channel split. */ ++static enum audio_result get_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping) ++{ ++ if (audio->hw_ctx->funcs->get_channel_splitting_mapping( ++ audio->hw_ctx, engine_id, audio_mapping)) { ++ return AUDIO_RESULT_OK; ++ } else { ++ return AUDIO_RESULT_ERROR; ++ } ++} ++ ++/** ++* set_unsolicited_response_payload ++* ++* @brief ++* Set payload value for the unsolicited response ++*/ ++static void set_unsolicited_response_payload( ++ struct audio *audio, ++ enum audio_payload payload) ++{ ++ audio->hw_ctx->funcs->set_unsolicited_response_payload( ++ audio->hw_ctx, payload); ++} ++ ++/** ++* setup_audio_wall_dto ++* ++* @brief ++* Update audio source clock from hardware context. ++* ++*/ ++static void setup_audio_wall_dto( ++ struct audio *audio, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info) ++{ ++ audio->hw_ctx->funcs->setup_audio_wall_dto( ++ audio->hw_ctx, signal, crtc_info, pll_info); ++} ++ ++/** ++* get_supported_features ++* ++* @brief ++* options and features supported by Audio ++* returns supported engines, signals. ++* features are reported for HW audio/Azalia block rather then Audio object ++* itself the difference for DCE6.x is that MultiStream Audio is now supported ++* ++*/ ++static struct audio_feature_support get_supported_features(struct audio *audio) ++{ ++ struct audio_feature_support afs = {0}; ++ ++ afs.ENGINE_DIGA = 1; ++ afs.ENGINE_DIGB = 1; ++ afs.ENGINE_DIGC = 1; ++ afs.MULTISTREAM_AUDIO = 1; ++ ++ return afs; ++} ++ ++static const struct audio_funcs funcs = { ++ .destroy = destroy, ++ .setup = setup, ++ .enable_output = enable_output, ++ .disable_output = disable_output, ++ .unmute = unmute, ++ .mute = mute, ++ .initialize = initialize, ++ .enable_channel_splitting_mapping = ++ enable_channel_splitting_mapping, ++ .get_channel_splitting_mapping = ++ get_channel_splitting_mapping, ++ .set_unsolicited_response_payload = ++ set_unsolicited_response_payload, ++ .setup_audio_wall_dto = setup_audio_wall_dto, ++ .get_supported_features = get_supported_features, ++}; ++ ++static bool construct( ++ struct audio_dce110 *audio, ++ const struct audio_init_data *init_data) ++{ ++ struct audio *base = &audio->base; ++ ++ /* base audio construct*/ ++ if (!dal_audio_construct_base(base, init_data)) ++ return false; ++ ++ /*vtable methods*/ ++ base->funcs = &funcs; ++ return true; ++} ++ ++ ++/* --- audio scope functions --- */ ++ ++struct audio *dal_audio_create_dce110( ++ const struct audio_init_data *init_data) ++{ ++ /*allocate memory for audio_dce110 */ ++ struct audio_dce110 *audio = dc_service_alloc(init_data->ctx, sizeof(*audio)); ++ ++ if (audio == NULL) { ++ ASSERT_CRITICAL(audio); ++ return NULL; ++ } ++ /*pointer to base_audio_block of audio_dce110 ==> audio base object */ ++ if (construct(audio, init_data)) ++ return &audio->base; ++ ++ dal_logger_write( ++ init_data->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_AUDIO, ++ "Failed to create audio object for DCE11\n"); ++ ++ /*release memory allocated if fail */ ++ dc_service_free(init_data->ctx, audio); ++ return NULL; ++} ++ ++/* Do not need expose construct_dce110 and destruct_dce110 becuase there is ++ *derived object after dce110 ++ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h +new file mode 100644 +index 0000000..e5ff823 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_AUDIO_DCE_110_H__ ++#define __DAL_AUDIO_DCE_110_H__ ++ ++#include "audio/audio.h" ++#include "audio/hw_ctx_audio.h" ++#include "audio/dce110/hw_ctx_audio_dce110.h" ++ ++ ++ ++struct audio_dce110 { ++ struct audio base; ++ /* dce-specific members are following */ ++ /* none */ ++}; ++ ++struct audio *dal_audio_create_dce110(const struct audio_init_data *init_data); ++ ++#endif /*__DAL_AUDIO_DCE_110_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c +new file mode 100644 +index 0000000..a13b2ab +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c +@@ -0,0 +1,1929 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/logger_interface.h" ++#include "../hw_ctx_audio.h" ++#include "hw_ctx_audio_dce110.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#define FROM_BASE(ptr) \ ++ container_of((ptr), struct hw_ctx_audio_dce110, base) ++ ++#define DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT 0x8000 ++#define DP_AUDIO_DTO_MODULE_WITHOUT_SS 360 ++#define DP_AUDIO_DTO_PHASE_WITHOUT_SS 24 ++ ++#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUDIO_FRONT_END 0 ++#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC 1 ++#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__REGISTER_PROGRAMMABLE 2 ++ ++#define FIRST_AUDIO_STREAM_ID 1 ++ ++#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_AUDIO, \ ++ "Audio:%s()\n", __func__) ++ ++static const uint32_t engine_offset[] = { ++ 0, ++ mmDIG1_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG2_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG3_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL ++}; ++ ++static void destruct( ++ struct hw_ctx_audio_dce110 *hw_ctx_dce110) ++{ ++ dal_audio_destruct_hw_ctx_audio(&hw_ctx_dce110->base); ++} ++ ++static void destroy( ++ struct hw_ctx_audio **ptr) ++{ ++ struct hw_ctx_audio_dce110 *hw_ctx_dce110; ++ ++ hw_ctx_dce110 = container_of( ++ *ptr, struct hw_ctx_audio_dce110, base); ++ ++ destruct(hw_ctx_dce110); ++ /* release memory allocated for struct hw_ctx_audio_dce110 */ ++ dc_service_free((*ptr)->ctx, hw_ctx_dce110); ++ ++ *ptr = NULL; ++} ++ ++/* --- helpers --- */ ++static void write_indirect_azalia_reg( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t reg_index, ++ uint32_t reg_data) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ ++ { ++ addr = ++ FROM_BASE(hw_ctx)->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_index; ++ ++ set_reg_field_value(value, reg_index, ++ AZALIA_F0_CODEC_ENDPOINT_INDEX, ++ AZALIA_ENDPOINT_REG_INDEX); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ ++ { ++ addr = ++ FROM_BASE(hw_ctx)->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_data; ++ ++ value = 0; ++ set_reg_field_value(value, reg_data, ++ AZALIA_F0_CODEC_ENDPOINT_DATA, ++ AZALIA_ENDPOINT_REG_DATA); ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ dal_logger_write( ++ hw_ctx->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_AUDIO, ++ "AUDIO:write_indirect_azalia_reg: index: %u data: %u\n", ++ reg_index, reg_data); ++} ++ ++static uint32_t read_indirect_azalia_reg( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t reg_index) ++{ ++ uint32_t ret_val = 0; ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ ++ /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ ++ { ++ addr = ++ FROM_BASE(hw_ctx)->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_index; ++ ++ set_reg_field_value(value, reg_index, ++ AZALIA_F0_CODEC_ENDPOINT_INDEX, ++ AZALIA_ENDPOINT_REG_INDEX); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ ++ { ++ addr = ++ FROM_BASE(hw_ctx)->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_data; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ret_val = value; ++ } ++ ++ dal_logger_write( ++ hw_ctx->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_AUDIO, ++ "AUDIO:read_indirect_azalia_reg: index: %u data: %u\n", ++ reg_index, ret_val); ++ ++ return ret_val; ++} ++ ++/* expose/not expose HBR capability to Audio driver */ ++static void set_high_bit_rate_capable( ++ const struct hw_ctx_audio *hw_ctx, ++ bool capable) ++{ ++ uint32_t value = 0; ++ ++ /* set high bit rate audio capable*/ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR); ++ ++ set_reg_field_value(value, capable, ++ AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, ++ HBR_CAPABLE); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, ++ value); ++} ++ ++/* set HBR channnel count * ++static void set_hbr_channel_count( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t hbr_channel_count) ++{ ++ uint32_t value = 0; ++ ++ if (hbr_channel_count > 7) ++ return; ++ ++ value = dal_read_reg(hw_ctx->ctx, ++ mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL); ++ ++ set_reg_field_value(value, hbr_channel_count, ++ AZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, ++ HBR_CHANNEL_COUNT); ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, value); ++ ++} ++ ++*set compressed audio channel count * ++static void set_compressed_audio_channel_count( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t compressed_audio_ch_count) ++{ ++ uint32_t value = 0; ++ if (compressed_audio_ch_count > 7) ++ return; ++ ++ value = dal_read_reg(hw_ctx->ctx, ++ mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL); ++ ++ set_reg_field_value(value, compressed_audio_ch_count, ++ AZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, ++ COMPRESSED_CHANNEL_COUNT); ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, ++ value); ++ ++} ++*/ ++/* set video latency in in ms/2+1 */ ++static void set_video_latency( ++ const struct hw_ctx_audio *hw_ctx, ++ int latency_in_ms) ++{ ++ uint32_t value = 0; ++ ++ if ((latency_in_ms < 0) || (latency_in_ms > 255)) ++ return; ++ ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); ++ ++ set_reg_field_value(value, latency_in_ms, ++ AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, ++ VIDEO_LIPSYNC); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, ++ value); ++ ++} ++ ++/* set audio latency in in ms/2+1 */ ++static void set_audio_latency( ++ const struct hw_ctx_audio *hw_ctx, ++ int latency_in_ms) ++{ ++ uint32_t value = 0; ++ ++ if (latency_in_ms < 0) ++ latency_in_ms = 0; ++ ++ if (latency_in_ms > 255) ++ latency_in_ms = 255; ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); ++ ++ set_reg_field_value(value, latency_in_ms, ++ AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, ++ AUDIO_LIPSYNC); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, ++ value); ++ ++} ++ ++/* enable HW/SW Sync */ ++/*static void enable_hw_sw_sync( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ union AZALIA_CYCLIC_BUFFER_SYNC value; ++ ++ value = dal_read_reg(mmAZALIA_CYCLIC_BUFFER_SYNC); ++ value.bits.CYCLIC_BUFFER_SYNC_ENABLE = 1; ++ dal_write_reg(mmAZALIA_CYCLIC_BUFFER_SYNC, value); ++}*/ ++ ++/* disable HW/SW Sync */ ++/*static void disable_hw_sw_sync( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ union AZALIA_CYCLIC_BUFFER_SYNC value; ++ ++ value = dal_read_reg( ++ mmAZALIA_CYCLIC_BUFFER_SYNC); ++ value.bits.CYCLIC_BUFFER_SYNC_ENABLE = 0; ++ dal_write_reg( ++ mmAZALIA_CYCLIC_BUFFER_SYNC, value); ++}*/ ++ ++/* update hardware with software's current position in cyclic buffer */ ++/*static void update_sw_write_ptr( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t offset) ++{ ++ union AZALIA_APPLICATION_POSITION_IN_CYCLIC_BUFFER value; ++ ++ value = dal_read_reg( ++ mmAZALIA_APPLICATION_POSITION_IN_CYCLIC_BUFFER); ++ value.bits.APPLICATION_POSITION_IN_CYCLIC_BUFFER = offset; ++ dal_write_reg( ++ mmAZALIA_APPLICATION_POSITION_IN_CYCLIC_BUFFER, ++ value); ++}*/ ++ ++/* update Audio/Video association */ ++/*static void update_av_association( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ uint32_t displayId) ++{ ++ ++}*/ ++ ++/* --- hook functions --- */ ++static bool get_azalia_clock_info_hdmi( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t crtc_pixel_clock_in_khz, ++ uint32_t actual_pixel_clock_in_khz, ++ struct azalia_clock_info *azalia_clock_info); ++ ++static bool get_azalia_clock_info_dp( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t requested_pixel_clock_in_khz, ++ const struct audio_pll_info *pll_info, ++ struct azalia_clock_info *azalia_clock_info); ++ ++static void setup_audio_wall_dto( ++ const struct hw_ctx_audio *hw_ctx, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info) ++{ ++ struct azalia_clock_info clock_info = { 0 }; ++ ++ uint32_t value = dal_read_reg(hw_ctx->ctx, mmDCCG_AUDIO_DTO_SOURCE); ++ ++ /* TODO: GraphicsObject\inc\GraphicsObjectDefs.hpp(131): ++ *inline bool isHdmiSignal(SignalType signal) ++ *if (Signals::isHdmiSignal(signal)) ++ */ ++ if (dc_is_hdmi_signal(signal)) { ++ /*DTO0 Programming goal: ++ -generate 24MHz, 128*Fs from 24MHz ++ -use DTO0 when an active HDMI port is connected ++ (optionally a DP is connected) */ ++ ++ /* calculate DTO settings */ ++ get_azalia_clock_info_hdmi( ++ hw_ctx, ++ crtc_info->requested_pixel_clock, ++ crtc_info->calculated_pixel_clock, ++ &clock_info); ++ ++ /* On TN/SI, Program DTO source select and DTO select before ++ programming DTO modulo and DTO phase. These bits must be ++ programmed first, otherwise there will be no HDMI audio at boot ++ up. This is a HW sequence change (different from old ASICs). ++ Caution when changing this programming sequence. ++ ++ HDMI enabled, using DTO0 ++ program master CRTC for DTO0 */ ++ { ++ set_reg_field_value(value, ++ pll_info->dto_source - DTO_SOURCE_ID0, ++ DCCG_AUDIO_DTO_SOURCE, ++ DCCG_AUDIO_DTO0_SOURCE_SEL); ++ ++ set_reg_field_value(value, ++ 0, ++ DCCG_AUDIO_DTO_SOURCE, ++ DCCG_AUDIO_DTO_SEL); ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO_SOURCE, value); ++ } ++ ++ /* module */ ++ { ++ value = dal_read_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO0_MODULE); ++ set_reg_field_value(value, ++ clock_info.audio_dto_module, ++ DCCG_AUDIO_DTO0_MODULE, ++ DCCG_AUDIO_DTO0_MODULE); ++ dal_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO0_MODULE, value); ++ } ++ ++ /* phase */ ++ { ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO0_PHASE); ++ set_reg_field_value(value, ++ clock_info.audio_dto_phase, ++ DCCG_AUDIO_DTO0_PHASE, ++ DCCG_AUDIO_DTO0_PHASE); ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO0_PHASE, value); ++ } ++ ++ } else { ++ /*DTO1 Programming goal: ++ -generate 24MHz, 512*Fs, 128*Fs from 24MHz ++ -default is to used DTO1, and switch to DTO0 when an audio ++ master HDMI port is connected ++ -use as default for DP ++ ++ calculate DTO settings */ ++ get_azalia_clock_info_dp( ++ hw_ctx, ++ crtc_info->requested_pixel_clock, ++ pll_info, ++ &clock_info); ++ ++ /* Program DTO select before programming DTO modulo and DTO ++ phase. default to use DTO1 */ ++ ++ { ++ set_reg_field_value(value, 1, ++ DCCG_AUDIO_DTO_SOURCE, ++ DCCG_AUDIO_DTO_SEL); ++ /*dal_write_reg(mmDCCG_AUDIO_DTO_SOURCE, value)*/ ++ ++ /* Select 512fs for DP TODO: web register definition ++ does not match register header file ++ set_reg_field_value(value, 1, ++ DCCG_AUDIO_DTO_SOURCE, ++ DCCG_AUDIO_DTO2_USE_512FBR_DTO); ++ */ ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO_SOURCE, value); ++ } ++ ++ /* module */ ++ { ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO1_MODULE); ++ ++ set_reg_field_value(value, ++ clock_info.audio_dto_module, ++ DCCG_AUDIO_DTO1_MODULE, ++ DCCG_AUDIO_DTO1_MODULE); ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO1_MODULE, value); ++ } ++ ++ /* phase */ ++ { ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO1_PHASE); ++ ++ set_reg_field_value(value, ++ clock_info.audio_dto_phase, ++ DCCG_AUDIO_DTO1_PHASE, ++ DCCG_AUDIO_DTO1_PHASE); ++ ++ dal_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO1_PHASE, value); ++ } ++ ++ /* DAL2 code separate DCCG_AUDIO_DTO_SEL and ++ DCCG_AUDIO_DTO2_USE_512FBR_DTO programming into two different ++ location. merge together should not hurt */ ++ /*value.bits.DCCG_AUDIO_DTO2_USE_512FBR_DTO = 1; ++ dal_write_reg(mmDCCG_AUDIO_DTO_SOURCE, value);*/ ++ } ++} ++ ++/* setup HDMI audio */ ++static void setup_hdmi_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ const struct audio_crtc_info *crtc_info) ++{ ++ struct audio_clock_info audio_clock_info = {0}; ++ uint32_t max_packets_per_line; ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ /* For now still do calculation, although this field is ignored when ++ above HDMI_PACKET_GEN_VERSION set to 1 */ ++ max_packets_per_line = ++ dal_audio_hw_ctx_calc_max_audio_packets_per_line( ++ hw_ctx, ++ crtc_info); ++ ++ /* HDMI_AUDIO_PACKET_CONTROL */ ++ { ++ addr = ++ mmHDMI_AUDIO_PACKET_CONTROL + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, max_packets_per_line, ++ HDMI_AUDIO_PACKET_CONTROL, ++ HDMI_AUDIO_PACKETS_PER_LINE); ++ /* still apply RS600's default setting which is 1. */ ++ set_reg_field_value(value, 1, ++ HDMI_AUDIO_PACKET_CONTROL, ++ HDMI_AUDIO_DELAY_EN); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_AUDIO_PACKET_CONTROL */ ++ { ++ addr = mmAFMT_AUDIO_PACKET_CONTROL + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 1, ++ AFMT_AUDIO_PACKET_CONTROL, ++ AFMT_60958_CS_UPDATE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_AUDIO_PACKET_CONTROL2 */ ++ { ++ addr = mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 0, ++ AFMT_AUDIO_PACKET_CONTROL2, ++ AFMT_AUDIO_LAYOUT_OVRD); ++ ++ /*Register field changed.*/ ++ set_reg_field_value(value, 0, ++ AFMT_AUDIO_PACKET_CONTROL2, ++ AFMT_60958_OSF_OVRD); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_PACKET_CONTROL */ ++ { ++ addr = mmHDMI_ACR_PACKET_CONTROL + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 1, ++ HDMI_ACR_PACKET_CONTROL, ++ HDMI_ACR_AUTO_SEND); ++ ++ /* Set HDMI_ACR_SOURCE to 0, to use hardwre ++ * computed CTS values.*/ ++ set_reg_field_value(value, 0, ++ HDMI_ACR_PACKET_CONTROL, ++ HDMI_ACR_SOURCE); ++ ++ /* For now clear HDMI_ACR_AUDIO_PRIORITY =>ACR packet has ++ higher priority over Audio Sample */ ++ set_reg_field_value(value, 0, ++ HDMI_ACR_PACKET_CONTROL, ++ HDMI_ACR_AUDIO_PRIORITY); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* Program audio clock sample/regeneration parameters */ ++ if (dal_audio_hw_ctx_get_audio_clock_info( ++ hw_ctx, ++ crtc_info->color_depth, ++ crtc_info->requested_pixel_clock, ++ crtc_info->calculated_pixel_clock, ++ &audio_clock_info)) { ++ ++ /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ ++ { ++ addr = mmHDMI_ACR_32_0 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, audio_clock_info.cts_32khz, ++ HDMI_ACR_32_0, ++ HDMI_ACR_CTS_32); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ ++ { ++ addr = mmHDMI_ACR_32_1 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.n_32khz, ++ HDMI_ACR_32_1, ++ HDMI_ACR_N_32); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ ++ { ++ addr = mmHDMI_ACR_44_0 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.cts_44khz, ++ HDMI_ACR_44_0, ++ HDMI_ACR_CTS_44); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ ++ { ++ addr = mmHDMI_ACR_44_1 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.n_44khz, ++ HDMI_ACR_44_1, ++ HDMI_ACR_N_44); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ ++ { ++ addr = mmHDMI_ACR_48_0 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.cts_48khz, ++ HDMI_ACR_48_0, ++ HDMI_ACR_CTS_48); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ ++ { ++ addr = mmHDMI_ACR_48_1 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.n_48khz, ++ HDMI_ACR_48_1, ++ HDMI_ACR_N_48); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* 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 */ ++ } /* if */ ++ ++ /* AFMT_60958_0__AFMT_60958_CS_CHANNEL_NUMBER_L_MASK & ++ AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ ++ { ++ addr = mmAFMT_60958_0 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 1, ++ AFMT_60958_0, ++ AFMT_60958_CS_CHANNEL_NUMBER_L); ++ ++ /*HW default */ ++ set_reg_field_value(value, 0, ++ AFMT_60958_0, ++ AFMT_60958_CS_CLOCK_ACCURACY); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_60958_1 AFMT_60958_CS_CHALNNEL_NUMBER_R */ ++ { ++ addr = mmAFMT_60958_1 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 2, ++ AFMT_60958_1, ++ AFMT_60958_CS_CHANNEL_NUMBER_R); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /*AFMT_60958_2 now keep this settings until ++ * Programming guide comes out*/ ++ { ++ addr = mmAFMT_60958_2 + engine_offset[engine_id]; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 3, ++ AFMT_60958_2, ++ AFMT_60958_CS_CHANNEL_NUMBER_2); ++ ++ set_reg_field_value(value, 4, ++ AFMT_60958_2, ++ AFMT_60958_CS_CHANNEL_NUMBER_3); ++ ++ set_reg_field_value(value, 5, ++ AFMT_60958_2, ++ AFMT_60958_CS_CHANNEL_NUMBER_4); ++ ++ set_reg_field_value(value, 6, ++ AFMT_60958_2, ++ AFMT_60958_CS_CHANNEL_NUMBER_5); ++ ++ set_reg_field_value(value, 7, ++ AFMT_60958_2, ++ AFMT_60958_CS_CHANNEL_NUMBER_6); ++ ++ set_reg_field_value(value, 8, ++ AFMT_60958_2, ++ AFMT_60958_CS_CHANNEL_NUMBER_7); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++} ++ ++ /* setup DP audio */ ++static void setup_dp_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /* --- DP Audio packet configurations --- */ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ /* ATP Configuration */ ++ { ++ addr = mmDP_SEC_AUD_N + engine_offset[engine_id]; ++ ++ set_reg_field_value(value, ++ DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT, ++ DP_SEC_AUD_N, ++ DP_SEC_AUD_N); ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* Async/auto-calc timestamp mode */ ++ { ++ addr = mmDP_SEC_TIMESTAMP + ++ engine_offset[engine_id]; ++ ++ value = 0; ++ ++ set_reg_field_value(value, ++ DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC, ++ DP_SEC_TIMESTAMP, ++ DP_SEC_TIMESTAMP_MODE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* --- The following are the registers ++ * copied from the SetupHDMI --- */ ++ ++ ++ /* AFMT_AUDIO_PACKET_CONTROL */ ++ { ++ addr = mmAFMT_AUDIO_PACKET_CONTROL + ++ engine_offset[engine_id]; ++ ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, ++ 1, ++ AFMT_AUDIO_PACKET_CONTROL, ++ AFMT_60958_CS_UPDATE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_AUDIO_PACKET_CONTROL2 */ ++ { ++ addr = ++ mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; ++ ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, ++ 0, ++ AFMT_AUDIO_PACKET_CONTROL2, ++ AFMT_AUDIO_LAYOUT_OVRD); ++ ++ set_reg_field_value(value, ++ 0, ++ AFMT_AUDIO_PACKET_CONTROL2, ++ AFMT_60958_OSF_OVRD); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_INFOFRAME_CONTROL0 */ ++ { ++ addr = ++ mmAFMT_INFOFRAME_CONTROL0 + engine_offset[engine_id]; ++ ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, ++ 1, ++ AFMT_INFOFRAME_CONTROL0, ++ AFMT_AUDIO_INFO_UPDATE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ ++ { ++ addr = mmAFMT_60958_0 + engine_offset[engine_id]; ++ ++ value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, ++ 0, ++ AFMT_60958_0, ++ AFMT_60958_CS_CLOCK_ACCURACY); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++} ++ ++ /* setup VCE audio */ ++static void setup_vce_audio( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ struct dc_context *ctx = hw_ctx->ctx; ++ ++ NOT_IMPLEMENTED(); ++ ++ /*TODO: ++ const uint32_t addr = mmDOUT_DCE_VCE_CONTROL; ++ uint32_t value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, ++ addr); ++ ++ set_reg_field_value(value, ++ FROM_BASE(hw_ctx)->azalia_stream_id - 1, ++ DOUT_DCE_VCE_CONTROL, ++ DC_VCE_AUDIO_STREAM_SELECT); ++ ++ dal_write_reg(hw_ctx->ctx, ++ addr, value);*/ ++} ++ ++static void enable_afmt_clock( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ bool enable_flag) ++{ ++ uint32_t engine_offs = engine_offset[engine_id]; ++ uint32_t value; ++ uint32_t count = 0; ++ uint32_t enable = enable_flag ? 1:0; ++ ++ /* Enable Audio packets*/ ++ value = dal_read_reg(hw_ctx->ctx, mmAFMT_CNTL + engine_offs); ++ ++ /*enable AFMT clock*/ ++ set_reg_field_value(value, enable, ++ AFMT_CNTL, AFMT_AUDIO_CLOCK_EN); ++ dal_write_reg(hw_ctx->ctx, mmAFMT_CNTL + engine_offs, value); ++ ++ /*wait for AFMT clock to turn on, ++ * the expectation is that this ++ * should complete in 1-2 reads) ++ */ ++ do { ++ /* Wait for 1us between subsequent register reads.*/ ++ dc_service_delay_in_microseconds(hw_ctx->ctx, 1); ++ value = dal_read_reg(hw_ctx->ctx, ++ mmAFMT_CNTL + engine_offs); ++ } while (get_reg_field_value(value, ++ AFMT_CNTL, AFMT_AUDIO_CLOCK_ON) != ++ enable && count++ < 10); ++} ++ ++/* enable Azalia audio */ ++static void enable_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ uint32_t value; ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); ++ ++ if (get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, ++ AUDIO_ENABLED) != 1) ++ set_reg_field_value(value, 1, ++ AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, ++ AUDIO_ENABLED); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, ++ value); ++} ++ ++/* disable Azalia audio */ ++static void disable_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ uint32_t value; ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); ++ ++ set_reg_field_value(value, 0, ++ AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, ++ AUDIO_ENABLED); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, ++ value); ++} ++ ++/* enable DP audio */ ++static void enable_dp_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ const uint32_t addr = mmDP_SEC_CNTL + engine_offset[engine_id]; ++ ++ uint32_t value; ++ ++ /* Enable Audio packets */ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 1, ++ DP_SEC_CNTL, ++ DP_SEC_ASP_ENABLE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ ++ /* Program the ATP and AIP next */ ++ set_reg_field_value(value, 1, ++ DP_SEC_CNTL, ++ DP_SEC_ATP_ENABLE); ++ ++ set_reg_field_value(value, 1, ++ DP_SEC_CNTL, ++ DP_SEC_AIP_ENABLE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ ++ /* Program STREAM_ENABLE after all the other enables. */ ++ set_reg_field_value(value, 1, ++ DP_SEC_CNTL, ++ DP_SEC_STREAM_ENABLE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++} ++ ++/* disable DP audio */ ++static void disable_dp_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ const uint32_t addr = mmDP_SEC_CNTL + engine_offset[engine_id]; ++ ++ uint32_t value; ++ ++ /* Disable Audio packets */ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 0, ++ DP_SEC_CNTL, ++ DP_SEC_ASP_ENABLE); ++ ++ set_reg_field_value(value, 0, ++ DP_SEC_CNTL, ++ DP_SEC_ATP_ENABLE); ++ ++ set_reg_field_value(value, 0, ++ DP_SEC_CNTL, ++ DP_SEC_AIP_ENABLE); ++ ++ set_reg_field_value(value, 0, ++ DP_SEC_CNTL, ++ DP_SEC_ACM_ENABLE); ++ ++ set_reg_field_value(value, 0, ++ DP_SEC_CNTL, ++ DP_SEC_STREAM_ENABLE); ++ ++ /* This register shared with encoder info frame. Therefore we need to ++ keep master enabled if at least on of the fields is not 0 */ ++ if (value != 0) ++ set_reg_field_value(value, 1, ++ DP_SEC_CNTL, ++ DP_SEC_STREAM_ENABLE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++} ++ ++static void configure_azalia( ++ const struct hw_ctx_audio *hw_ctx, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_info *audio_info) ++{ ++ uint32_t speakers = audio_info->flags.info.ALLSPEAKERS; ++ uint32_t value; ++ uint32_t field = 0; ++ enum audio_format_code audio_format_code; ++ uint32_t format_index; ++ uint32_t index; ++ bool is_ac3_supported = false; ++ bool is_audio_format_supported = false; ++ union audio_sample_rates sample_rate; ++ uint32_t strlen = 0; ++ ++ /* Speaker Allocation */ ++ /* ++ uint32_t value; ++ uint32_t field = 0;*/ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER); ++ ++ set_reg_field_value(value, ++ speakers, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ SPEAKER_ALLOCATION); ++ ++ /* LFE_PLAYBACK_LEVEL = LFEPBL ++ * LFEPBL = 0 : Unknown or refer to other information ++ * LFEPBL = 1 : 0dB playback ++ * LFEPBL = 2 : +10dB playback ++ * LFE_BL = 3 : Reserved ++ */ ++ set_reg_field_value(value, ++ 0, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ LFE_PLAYBACK_LEVEL); ++ ++ set_reg_field_value(value, ++ 0, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ HDMI_CONNECTION); ++ ++ set_reg_field_value(value, ++ 0, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ DP_CONNECTION); ++ ++ field = get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ EXTRA_CONNECTION_INFO); ++ ++ field &= ~0x1; ++ ++ set_reg_field_value(value, ++ field, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ EXTRA_CONNECTION_INFO); ++ ++ /* set audio for output signal */ ++ switch (signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ set_reg_field_value(value, ++ 1, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ HDMI_CONNECTION); ++ ++ break; ++ case SIGNAL_TYPE_WIRELESS: { ++ /*LSB used for "is wireless" flag */ ++ field = 0; ++ field = get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ EXTRA_CONNECTION_INFO); ++ field |= 0x1; ++ set_reg_field_value(value, ++ field, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ EXTRA_CONNECTION_INFO); ++ ++ set_reg_field_value(value, ++ 1, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ HDMI_CONNECTION); ++ ++ } ++ break; ++ case SIGNAL_TYPE_EDP: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ set_reg_field_value(value, ++ 1, ++ AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ DP_CONNECTION); ++ ++ break; ++ default: ++ break; ++ } ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, ++ value); ++ ++ /* Wireless Display identification */ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION); ++ ++ set_reg_field_value(value, ++ signal == SIGNAL_TYPE_WIRELESS ? 1 : 0, ++ AZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION, ++ WIRELESS_DISPLAY_IDENTIFICATION); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION, ++ value); ++ ++ /* Audio Descriptors */ ++ /* pass through all formats */ ++ for (format_index = 0; format_index < AUDIO_FORMAT_CODE_COUNT; ++ format_index++) { ++ audio_format_code = ++ (AUDIO_FORMAT_CODE_FIRST + format_index); ++ ++ /* those are unsupported, skip programming */ ++ if (audio_format_code == AUDIO_FORMAT_CODE_1BITAUDIO || ++ audio_format_code == AUDIO_FORMAT_CODE_DST) ++ continue; ++ ++ value = 0; ++ ++ /* check if supported */ ++ is_audio_format_supported = ++ dal_audio_hw_ctx_is_audio_format_supported( ++ hw_ctx, ++ audio_info, ++ audio_format_code, &index); ++ ++ if (is_audio_format_supported) { ++ const struct audio_mode *audio_mode = ++ &audio_info->modes[index]; ++ union audio_sample_rates sample_rates = ++ audio_mode->sample_rates; ++ uint8_t byte2 = audio_mode->max_bit_rate; ++ ++ /* adjust specific properties */ ++ switch (audio_format_code) { ++ case AUDIO_FORMAT_CODE_LINEARPCM: { ++ dal_hw_ctx_audio_check_audio_bandwidth( ++ hw_ctx, ++ crtc_info, ++ audio_mode->channel_count, ++ signal, ++ &sample_rates); ++ ++ byte2 = audio_mode->sample_size; ++ ++ set_reg_field_value(value, ++ sample_rates.all, ++ AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, ++ SUPPORTED_FREQUENCIES_STEREO); ++ ++ } ++ break; ++ case AUDIO_FORMAT_CODE_AC3: ++ is_ac3_supported = true; ++ break; ++ case AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS: ++ case AUDIO_FORMAT_CODE_DTS_HD: ++ case AUDIO_FORMAT_CODE_MAT_MLP: ++ case AUDIO_FORMAT_CODE_DST: ++ case AUDIO_FORMAT_CODE_WMAPRO: ++ byte2 = audio_mode->vendor_specific; ++ break; ++ default: ++ break; ++ } ++ ++ /* fill audio format data */ ++ set_reg_field_value(value, ++ audio_mode->channel_count - 1, ++ AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, ++ MAX_CHANNELS); ++ ++ set_reg_field_value(value, ++ sample_rates.all, ++ AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, ++ SUPPORTED_FREQUENCIES); ++ ++ set_reg_field_value(value, ++ byte2, ++ AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, ++ DESCRIPTOR_BYTE_2); ++ ++ } /* if */ ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 + ++ format_index, ++ value); ++ } /* for */ ++ ++ if (is_ac3_supported) ++ dal_write_reg(hw_ctx->ctx, ++ mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS, ++ 0x05); ++ ++ /* check for 192khz/8-Ch support for HBR requirements */ ++ sample_rate.all = 0; ++ sample_rate.rate.RATE_192 = 1; ++ dal_hw_ctx_audio_check_audio_bandwidth( ++ hw_ctx, ++ crtc_info, ++ 8, ++ signal, ++ &sample_rate); ++ ++ set_high_bit_rate_capable(hw_ctx, sample_rate.rate.RATE_192); ++ ++ /* Audio and Video Lipsync */ ++ set_video_latency(hw_ctx, audio_info->video_latency); ++ set_audio_latency(hw_ctx, audio_info->audio_latency); ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->manufacture_id, ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, ++ MANUFACTURER_ID); ++ ++ set_reg_field_value(value, audio_info->product_id, ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, ++ PRODUCT_ID); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, ++ value); ++ ++ ++ value = 0; ++ ++ /*get display name string length */ ++ while (audio_info->display_name[strlen++] != '\0') { ++ if (strlen >= ++ MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS) ++ break; ++ } ++ set_reg_field_value(value, strlen, ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, ++ SINK_DESCRIPTION_LEN); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, ++ value); ++ ++ ++ /* ++ *write the port ID: ++ *PORT_ID0 = display index ++ *PORT_ID1 = 16bit BDF ++ *(format MSB->LSB: 8bit Bus, 5bit Device, 3bit Function) ++ */ ++ ++ value = 0; ++ ++ set_reg_field_value(value, audio_info->port_id[0], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, ++ PORT_ID0); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->port_id[1], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, ++ PORT_ID1); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, ++ value); ++ ++ /*write the 18 char monitor string */ ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->display_name[0], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, ++ DESCRIPTION0); ++ ++ set_reg_field_value(value, audio_info->display_name[1], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, ++ DESCRIPTION1); ++ ++ set_reg_field_value(value, audio_info->display_name[2], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, ++ DESCRIPTION2); ++ ++ set_reg_field_value(value, audio_info->display_name[3], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, ++ DESCRIPTION3); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, ++ value); ++ ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->display_name[4], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, ++ DESCRIPTION4); ++ ++ set_reg_field_value(value, audio_info->display_name[5], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, ++ DESCRIPTION5); ++ ++ set_reg_field_value(value, audio_info->display_name[6], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, ++ DESCRIPTION6); ++ ++ set_reg_field_value(value, audio_info->display_name[7], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, ++ DESCRIPTION7); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->display_name[8], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, ++ DESCRIPTION8); ++ ++ set_reg_field_value(value, audio_info->display_name[9], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, ++ DESCRIPTION9); ++ ++ set_reg_field_value(value, audio_info->display_name[10], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, ++ DESCRIPTION10); ++ ++ set_reg_field_value(value, audio_info->display_name[11], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, ++ DESCRIPTION11); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->display_name[12], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, ++ DESCRIPTION12); ++ ++ set_reg_field_value(value, audio_info->display_name[13], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, ++ DESCRIPTION13); ++ ++ set_reg_field_value(value, audio_info->display_name[14], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, ++ DESCRIPTION14); ++ ++ set_reg_field_value(value, audio_info->display_name[15], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, ++ DESCRIPTION15); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, ++ value); ++ ++ ++ value = 0; ++ set_reg_field_value(value, audio_info->display_name[16], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, ++ DESCRIPTION16); ++ ++ set_reg_field_value(value, audio_info->display_name[17], ++ AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, ++ DESCRIPTION17); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, ++ value); ++ ++} ++ ++/* setup Azalia HW block */ ++static void setup_azalia( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info, ++ const struct audio_info *audio_info) ++{ ++ uint32_t speakers = 0; ++ uint32_t channels = 0; ++ ++ if (audio_info == NULL) ++ /* This should not happen.it does so we don't get BSOD*/ ++ return; ++ ++ speakers = audio_info->flags.info.ALLSPEAKERS; ++ channels = dal_audio_hw_ctx_speakers_to_channels( ++ hw_ctx, ++ audio_info->flags.speaker_flags).all; ++ ++ /* setup the audio stream source select (audio -> dig mapping) */ ++ { ++ const uint32_t addr = ++ mmAFMT_AUDIO_SRC_CONTROL + engine_offset[engine_id]; ++ ++ uint32_t value = 0; ++ /*convert one-based index to zero-based */ ++ set_reg_field_value(value, ++ FROM_BASE(hw_ctx)->azalia_stream_id - 1, ++ AFMT_AUDIO_SRC_CONTROL, ++ AFMT_AUDIO_SRC_SELECT); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* Channel allocation */ ++ { ++ const uint32_t addr = ++ mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; ++ uint32_t value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, ++ channels, ++ AFMT_AUDIO_PACKET_CONTROL2, ++ AFMT_AUDIO_CHANNEL_ENABLE); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ configure_azalia(hw_ctx, signal, crtc_info, audio_info); ++} ++ ++/* unmute audio */ ++static void unmute_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ const uint32_t addr = mmAFMT_AUDIO_PACKET_CONTROL + ++ engine_offset[engine_id]; ++ ++ uint32_t value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 1, ++ AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++} ++ ++/* mute audio */ ++static void mute_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ const uint32_t addr = mmAFMT_AUDIO_PACKET_CONTROL + ++ engine_offset[engine_id]; ++ ++ uint32_t value = 0; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 0, ++ AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++} ++ ++/* enable channel splitting mapping */ ++static void setup_channel_splitting_mapping( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable) ++{ ++ uint32_t value = 0; ++ ++ if ((audio_mapping == NULL || audio_mapping->u32all == 0) && enable) ++ return; ++ ++ ++ value = audio_mapping->u32all; ++ ++ if (enable == false) ++ /*0xFFFFFFFF;*/ ++ value = MULTI_CHANNEL_SPLIT_NO_ASSO_INFO; ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_ASSOCIATION_INFO, ++ value); ++} ++ ++/* get current channel spliting */ ++static bool get_channel_splitting_mapping( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping) ++{ ++ uint32_t value = 0; ++ ++ if (audio_mapping == NULL) ++ return false; ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_ASSOCIATION_INFO); ++ ++ /*0xFFFFFFFF*/ ++ if (get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_ASSOCIATION_INFO, ++ ASSOCIATION_INFO) != ++ MULTI_CHANNEL_SPLIT_NO_ASSO_INFO) { ++ uint32_t multi_channel01_enable = 0; ++ uint32_t multi_channel23_enable = 0; ++ uint32_t multi_channel45_enable = 0; ++ uint32_t multi_channel67_enable = 0; ++ /* get the one we set.*/ ++ audio_mapping->u32all = value; ++ ++ /* check each enable status*/ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE); ++ ++ multi_channel01_enable = get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, ++ MULTICHANNEL01_ENABLE); ++ ++ multi_channel23_enable = get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, ++ MULTICHANNEL23_ENABLE); ++ ++ multi_channel45_enable = get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, ++ MULTICHANNEL45_ENABLE); ++ ++ multi_channel67_enable = get_reg_field_value(value, ++ AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, ++ MULTICHANNEL67_ENABLE); ++ ++ if (multi_channel01_enable == 0 && ++ multi_channel23_enable == 0 && ++ multi_channel45_enable == 0 && ++ multi_channel67_enable == 0) ++ dal_logger_write(hw_ctx->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_COMPONENT_AUDIO, ++ "Audio driver did not enable multi-channel\n"); ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++/* set the payload value for the unsolicited response */ ++static void set_unsolicited_response_payload( ++ const struct hw_ctx_audio *hw_ctx, ++ enum audio_payload payload) ++{ ++ /* set the payload value for the unsolicited response ++ Jack presence is not required to be enabled */ ++ uint32_t value = 0; ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE); ++ ++ set_reg_field_value(value, payload, ++ AZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE, ++ UNSOLICITED_RESPONSE_PAYLOAD); ++ ++ set_reg_field_value(value, 1, ++ AZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE, ++ UNSOLICITED_RESPONSE_FORCE); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE, ++ value); ++} ++ ++/* initialize HW state */ ++static void hw_initialize( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ uint32_t stream_id = FROM_BASE(hw_ctx)->azalia_stream_id; ++ uint32_t addr; ++ ++ /* we only need to program the following registers once, so we only do ++ it for the first audio stream.*/ ++ if (stream_id != FIRST_AUDIO_STREAM_ID) ++ return; ++ ++ /* Suport R5 - 32khz ++ * Suport R6 - 44.1khz ++ * Suport R7 - 48khz ++ */ ++ addr = mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES; ++ { ++ uint32_t value; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 0x70, ++ AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, ++ AUDIO_RATE_CAPABILITIES); ++ ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /*Keep alive bit to verify HW block in BU. */ ++ addr = mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES; ++ { ++ uint32_t value; ++ ++ value = dal_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 1, ++ AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, ++ CLKSTOP); ++ ++ set_reg_field_value(value, 1, ++ AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, ++ EPSS); ++ dal_write_reg(hw_ctx->ctx, addr, value); ++ } ++} ++ ++/* Assign GTC group and enable GTC value embedding */ ++static void enable_gtc_embedding_with_group( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t group_num, ++ uint32_t audio_latency) ++{ ++ /*need to replace the static number with variable */ ++ if (group_num <= 6) { ++ uint32_t value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING); ++ ++ set_reg_field_value( ++ value, ++ group_num, ++ AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, ++ PRESENTATION_TIME_EMBEDDING_GROUP); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, ++ PRESENTATION_TIME_EMBEDDING_ENABLE); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, ++ value); ++ ++ /*update audio latency to LIPSYNC*/ ++ set_audio_latency(hw_ctx, audio_latency); ++ } else { ++ dal_logger_write( ++ hw_ctx->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_COMPONENT_AUDIO, ++ "GTC group number %d is too big", ++ group_num); ++ } ++} ++ ++ /* Disable GTC value embedding */ ++static void disable_gtc_embedding( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ uint32_t value = 0; ++ ++ value = read_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING); ++ ++ set_reg_field_value(value, 0, ++ AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, ++ PRESENTATION_TIME_EMBEDDING_ENABLE); ++ ++ set_reg_field_value(value, 0, ++ AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, ++ PRESENTATION_TIME_EMBEDDING_GROUP); ++ ++ write_indirect_azalia_reg( ++ hw_ctx, ++ ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, ++ value); ++} ++ ++/* search pixel clock value for Azalia HDMI Audio */ ++static bool get_azalia_clock_info_hdmi( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t crtc_pixel_clock_in_khz, ++ uint32_t actual_pixel_clock_in_khz, ++ struct azalia_clock_info *azalia_clock_info) ++{ ++ if (azalia_clock_info == NULL) ++ return false; ++ ++ /* audio_dto_phase= 24 * 10,000; ++ * 24MHz in [100Hz] units */ ++ azalia_clock_info->audio_dto_phase = ++ 24 * 10000; ++ ++ /* audio_dto_module = PCLKFrequency * 10,000; ++ * [khz] -> [100Hz] */ ++ azalia_clock_info->audio_dto_module = ++ actual_pixel_clock_in_khz * 10; ++ ++ return true; ++} ++ ++/* search pixel clock value for Azalia DP Audio */ ++static bool get_azalia_clock_info_dp( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t requested_pixel_clock_in_khz, ++ const struct audio_pll_info *pll_info, ++ struct azalia_clock_info *azalia_clock_info) ++{ ++ if (pll_info == NULL || azalia_clock_info == NULL) ++ return false; ++ ++ /* Reported dpDtoSourceClockInkhz value for ++ * DCE8 already adjusted for SS, do not need any ++ * adjustment here anymore ++ */ ++ ++ /*audio_dto_phase = 24 * 10,000; ++ * 24MHz in [100Hz] units */ ++ azalia_clock_info->audio_dto_phase = 24 * 10000; ++ ++ /*audio_dto_module = dpDtoSourceClockInkhz * 10,000; ++ * [khz] ->[100Hz] */ ++ azalia_clock_info->audio_dto_module = ++ pll_info->dp_dto_source_clock_in_khz * 10; ++ ++ return true; ++} ++ ++static const struct hw_ctx_audio_funcs funcs = { ++ .destroy = destroy, ++ .setup_audio_wall_dto = ++ setup_audio_wall_dto, ++ .setup_hdmi_audio = ++ setup_hdmi_audio, ++ .setup_dp_audio = setup_dp_audio, ++ .setup_vce_audio = setup_vce_audio, ++ .enable_azalia_audio = ++ enable_azalia_audio, ++ .disable_azalia_audio = ++ disable_azalia_audio, ++ .enable_dp_audio = ++ enable_dp_audio, ++ .disable_dp_audio = ++ disable_dp_audio, ++ .setup_azalia = ++ setup_azalia, ++ .disable_az_clock_gating = NULL, ++ .unmute_azalia_audio = ++ unmute_azalia_audio, ++ .mute_azalia_audio = ++ mute_azalia_audio, ++ .setup_channel_splitting_mapping = ++ setup_channel_splitting_mapping, ++ .get_channel_splitting_mapping = ++ get_channel_splitting_mapping, ++ .set_unsolicited_response_payload = ++ set_unsolicited_response_payload, ++ .hw_initialize = ++ hw_initialize, ++ .enable_gtc_embedding_with_group = ++ enable_gtc_embedding_with_group, ++ .disable_gtc_embedding = ++ disable_gtc_embedding, ++ .get_azalia_clock_info_hdmi = ++ get_azalia_clock_info_hdmi, ++ .get_azalia_clock_info_dp = ++ get_azalia_clock_info_dp, ++ .enable_afmt_clock = enable_afmt_clock ++}; ++ ++static bool construct( ++ struct hw_ctx_audio_dce110 *hw_ctx, ++ uint8_t azalia_stream_id, ++ struct dc_context *ctx) ++{ ++ struct hw_ctx_audio *base = &hw_ctx->base; ++ ++ if (!dal_audio_construct_hw_ctx_audio(base)) ++ return false; ++ ++ base->funcs = &funcs; ++ ++ /* save audio endpoint or dig front for current dce110 audio object */ ++ hw_ctx->azalia_stream_id = azalia_stream_id; ++ hw_ctx->base.ctx = ctx; ++ ++ /* azalia audio endpoints register offsets. azalia is associated with ++ DIG front. save AUDIO register offset */ ++ switch (azalia_stream_id) { ++ case 1: { ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_index = ++ mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX; ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_data = ++ mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA; ++ } ++ break; ++ case 2: { ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_index = ++ mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_INDEX; ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_data = ++ mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_DATA; ++ } ++ break; ++ case 3: { ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_index = ++ mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_INDEX; ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_data = ++ mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_DATA; ++ } ++ break; ++ case 4: { ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_index = ++ mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_INDEX; ++ hw_ctx->az_mm_reg_offsets. ++ azf0endpointx_azalia_f0_codec_endpoint_data = ++ mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_DATA; ++ } ++ break; ++ default: ++ dal_logger_write( ++ hw_ctx->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_AUDIO, ++ "Invalid Azalia stream ID!"); ++ break; ++ } ++ ++ return true; ++} ++ ++/* audio_dce110 is derived from audio directly, not via dce80 */ ++struct hw_ctx_audio *dal_hw_ctx_audio_dce110_create( ++ struct dc_context *ctx, ++ uint32_t azalia_stream_id) ++{ ++ /* allocate memory for struc hw_ctx_audio_dce110 */ ++ struct hw_ctx_audio_dce110 *hw_ctx_dce110 = ++ dc_service_alloc(ctx, sizeof(struct hw_ctx_audio_dce110)); ++ ++ if (!hw_ctx_dce110) { ++ ASSERT_CRITICAL(hw_ctx_dce110); ++ return NULL; ++ } ++ ++ /*return pointer to hw_ctx_audio back to caller -- audio object */ ++ if (construct( ++ hw_ctx_dce110, azalia_stream_id, ctx)) ++ return &hw_ctx_dce110->base; ++ ++ dal_logger_write( ++ ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_AUDIO, ++ "Failed to create hw_ctx_audio for DCE11\n"); ++ ++ ++ dc_service_free(ctx, hw_ctx_dce110); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h +new file mode 100644 +index 0000000..1ad3826 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h +@@ -0,0 +1,47 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_CTX_AUDIO_DCE110_H__ ++#define __DAL_HW_CTX_AUDIO_DCE110_H__ ++ ++#include "audio/hw_ctx_audio.h" ++ ++struct hw_ctx_audio_dce110 { ++ struct hw_ctx_audio base; ++ ++ /* azalia stream id 1 based indexing, corresponding to audio GO enumId*/ ++ uint32_t azalia_stream_id; ++ ++ /* azalia stream endpoint register offsets */ ++ struct azalia_reg_offsets az_mm_reg_offsets; ++ ++ /* audio encoder block MM register offset -- associate with DIG FRONT */ ++}; ++ ++struct hw_ctx_audio *dal_hw_ctx_audio_dce110_create( ++ struct dc_context *ctx, ++ uint32_t azalia_stream_id); ++ ++#endif /* __DAL_HW_CTX_AUDIO_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c +new file mode 100644 +index 0000000..f1f1298 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c +@@ -0,0 +1,771 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "hw_ctx_audio.h" ++ ++/* 25.2MHz/1.001*/ ++/* 25.2MHz/1.001*/ ++/* 25.2MHz*/ ++/* 27MHz */ ++/* 27MHz*1.001*/ ++/* 27MHz*1.001*/ ++/* 54MHz*/ ++/* 54MHz*1.001*/ ++/* 74.25MHz/1.001*/ ++/* 74.25MHz*/ ++/* 148.5MHz/1.001*/ ++/* 148.5MHz*/ ++ ++static const struct audio_clock_info audio_clock_info_table[12] = { ++ {2517, 4576, 28125, 7007, 31250, 6864, 28125}, ++ {2518, 4576, 28125, 7007, 31250, 6864, 28125}, ++ {2520, 4096, 25200, 6272, 28000, 6144, 25200}, ++ {2700, 4096, 27000, 6272, 30000, 6144, 27000}, ++ {2702, 4096, 27027, 6272, 30030, 6144, 27027}, ++ {2703, 4096, 27027, 6272, 30030, 6144, 27027}, ++ {5400, 4096, 54000, 6272, 60000, 6144, 54000}, ++ {5405, 4096, 54054, 6272, 60060, 6144, 54054}, ++ {7417, 11648, 210937, 17836, 234375, 11648, 140625}, ++ {7425, 4096, 74250, 6272, 82500, 6144, 74250}, ++ {14835, 11648, 421875, 8918, 234375, 5824, 140625}, ++ {14850, 4096, 148500, 6272, 165000, 6144, 148500} ++}; ++ ++static const struct audio_clock_info audio_clock_info_table_36bpc[12] = { ++ {2517, 9152, 84375, 7007, 48875, 9152, 56250}, ++ {2518, 9152, 84375, 7007, 48875, 9152, 56250}, ++ {2520, 4096, 37800, 6272, 42000, 6144, 37800}, ++ {2700, 4096, 40500, 6272, 45000, 6144, 40500}, ++ {2702, 8192, 81081, 6272, 45045, 8192, 54054}, ++ {2703, 8192, 81081, 6272, 45045, 8192, 54054}, ++ {5400, 4096, 81000, 6272, 90000, 6144, 81000}, ++ {5405, 4096, 81081, 6272, 90090, 6144, 81081}, ++ {7417, 11648, 316406, 17836, 351562, 11648, 210937}, ++ {7425, 4096, 111375, 6272, 123750, 6144, 111375}, ++ {14835, 11648, 632812, 17836, 703125, 11648, 421875}, ++ {14850, 4096, 222750, 6272, 247500, 6144, 222750} ++}; ++ ++static const struct audio_clock_info audio_clock_info_table_48bpc[12] = { ++ {2517, 4576, 56250, 7007, 62500, 6864, 56250}, ++ {2518, 4576, 56250, 7007, 62500, 6864, 56250}, ++ {2520, 4096, 50400, 6272, 56000, 6144, 50400}, ++ {2700, 4096, 54000, 6272, 60000, 6144, 54000}, ++ {2702, 4096, 54054, 6267, 60060, 8192, 54054}, ++ {2703, 4096, 54054, 6272, 60060, 8192, 54054}, ++ {5400, 4096, 108000, 6272, 120000, 6144, 108000}, ++ {5405, 4096, 108108, 6272, 120120, 6144, 108108}, ++ {7417, 11648, 421875, 17836, 468750, 11648, 281250}, ++ {7425, 4096, 148500, 6272, 165000, 6144, 148500}, ++ {14835, 11648, 843750, 8918, 468750, 11648, 281250}, ++ {14850, 4096, 297000, 6272, 330000, 6144, 297000} ++}; ++ ++ ++/***** static function *****/ ++ ++/* ++ * except of HW context create function, caller will access other functions of ++ * hw ctx via handle hw_ctx. Memory allocation for struct hw_ctx_audio_dce8x ++ * will happen in hw_ctx_audio_dce8x. Memory allocation is done with ++ * dal_audio_create_hw_ctx_audio_dce8x. Memory release is done by caller ++ * via hw_ctx->functions.destroy(). It will finally use destroy() of ++ * hw_ctx_audio_dce8x. Therefore, no memory allocate and release happen ++ * physically at hw ctx base object. ++ */ ++static void destroy( ++ struct hw_ctx_audio **ptr) ++{ ++ /* Attention! ++ * You must override this method in derived class */ ++} ++ ++static void setup_audio_wall_dto( ++ const struct hw_ctx_audio *hw_ctx, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* setup HDMI audio */ ++static void setup_hdmi_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ const struct audio_crtc_info *crtc_info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++ /* setup DP audio */ ++static void setup_dp_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++ /* setup VCE audio */ ++static void setup_vce_audio( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* enable Azalia audio */ ++static void enable_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* disable Azalia audio */ ++static void disable_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* enable DP audio */ ++static void enable_dp_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* disable DP audio */ ++static void disable_dp_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* setup Azalia HW block */ ++static void setup_azalia( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info, ++ const struct audio_info *audio_info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* unmute audio */ ++static void unmute_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* mute audio */ ++static void mute_azalia_audio( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* enable channel splitting mapping */ ++static void setup_channel_splitting_mapping( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* get current channel spliting */ ++static bool get_channel_splitting_mapping( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return false; ++} ++ ++/* set the payload value for the unsolicited response */ ++static void set_unsolicited_response_payload( ++ const struct hw_ctx_audio *hw_ctx, ++ enum audio_payload payload) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* initialize HW state */ ++static void hw_initialize( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* Assign GTC group and enable GTC value embedding */ ++static void enable_gtc_embedding_with_group( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t groupNum, ++ uint32_t audioLatency) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* Disable GTC value embedding */ ++static void disable_gtc_embedding( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* Disable Azalia Clock Gating Feature */ ++static void disable_az_clock_gating( ++ const struct hw_ctx_audio *hw_ctx) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++} ++ ++/* search pixel clock value for Azalia HDMI Audio */ ++static bool get_azalia_clock_info_hdmi( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t crtc_pixel_clock_in_khz, ++ uint32_t actual_pixel_clock_in_khz, ++ struct azalia_clock_info *azalia_clock_info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return false; ++} ++ ++/* search pixel clock value for Azalia DP Audio */ ++static bool get_azalia_clock_info_dp( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t requested_pixel_clock_in_khz, ++ const struct audio_pll_info *pll_info, ++ struct azalia_clock_info *azalia_clock_info) ++{ ++ /*DCE specific, must be implemented in derived*/ ++ BREAK_TO_DEBUGGER(); ++ return false; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++/*****SCOPE : within audio hw context dal-audio-hw-ctx *****/ ++ ++ ++/* check whether specified sample rates can fit into a given timing */ ++void dal_hw_ctx_audio_check_audio_bandwidth( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ enum signal_type signal, ++ union audio_sample_rates *sample_rates) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ dal_audio_hw_ctx_check_audio_bandwidth_hdmi( ++ hw_ctx, crtc_info, channel_count, sample_rates); ++ break; ++ case SIGNAL_TYPE_EDP: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ dal_audio_hw_ctx_check_audio_bandwidth_dpsst( ++ hw_ctx, crtc_info, channel_count, sample_rates); ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ dal_audio_hw_ctx_check_audio_bandwidth_dpmst( ++ hw_ctx, crtc_info, channel_count, sample_rates); ++ break; ++ default: ++ break; ++ } ++} ++ ++/*For HDMI, calculate if specified sample rates can fit into a given timing */ ++void dal_audio_hw_ctx_check_audio_bandwidth_hdmi( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ union audio_sample_rates *sample_rates) ++{ ++ uint32_t samples; ++ uint32_t h_blank; ++ bool limit_freq_to_48_khz = false; ++ bool limit_freq_to_88_2_khz = false; ++ bool limit_freq_to_96_khz = false; ++ bool limit_freq_to_174_4_khz = false; ++ ++ /* For two channels supported return whatever sink support,unmodified*/ ++ if (channel_count > 2) { ++ ++ /* Based on HDMI spec 1.3 Table 7.5 */ ++ if ((crtc_info->requested_pixel_clock <= 27000) && ++ (crtc_info->v_active <= 576) && ++ !(crtc_info->interlaced) && ++ !(crtc_info->pixel_repetition == 2 || ++ crtc_info->pixel_repetition == 4)) { ++ limit_freq_to_48_khz = true; ++ ++ } else if ((crtc_info->requested_pixel_clock <= 27000) && ++ (crtc_info->v_active <= 576) && ++ (crtc_info->interlaced) && ++ (crtc_info->pixel_repetition == 2)) { ++ limit_freq_to_88_2_khz = true; ++ ++ } else if ((crtc_info->requested_pixel_clock <= 54000) && ++ (crtc_info->v_active <= 576) && ++ !(crtc_info->interlaced)) { ++ limit_freq_to_174_4_khz = true; ++ } ++ } ++ ++ /* Also do some calculation for the available Audio Bandwidth for the ++ * 8 ch (i.e. for the Layout 1 => ch > 2) ++ */ ++ h_blank = crtc_info->h_total - crtc_info->h_active; ++ ++ if (crtc_info->pixel_repetition) ++ h_blank *= crtc_info->pixel_repetition; ++ ++ /*based on HDMI spec 1.3 Table 7.5 */ ++ h_blank -= 58; ++ /*for Control Period */ ++ h_blank -= 16; ++ ++ samples = h_blank * 10; ++ /* Number of Audio Packets (multiplied by 10) per Line (for 8 ch number ++ * of Audio samples per line multiplied by 10 - Layout 1) ++ */ ++ samples /= 32; ++ samples *= crtc_info->v_active; ++ /*Number of samples multiplied by 10, per second */ ++ samples *= crtc_info->refresh_rate; ++ /*Number of Audio samples per second */ ++ samples /= 10; ++ ++ /* @todo do it after deep color is implemented ++ * 8xx - deep color bandwidth scaling ++ * Extra bandwidth is avaliable in deep color b/c link runs faster than ++ * pixel rate. This has the effect of allowing more tmds characters to ++ * be transmitted during blank ++ */ ++ ++ switch (crtc_info->color_depth) { ++ case COLOR_DEPTH_888: ++ samples *= 4; ++ break; ++ case COLOR_DEPTH_101010: ++ samples *= 5; ++ break; ++ case COLOR_DEPTH_121212: ++ samples *= 6; ++ break; ++ default: ++ samples *= 4; ++ break; ++ } ++ ++ samples /= 4; ++ ++ /*check limitation*/ ++ if (samples < 88200) ++ limit_freq_to_48_khz = true; ++ else if (samples < 96000) ++ limit_freq_to_88_2_khz = true; ++ else if (samples < 176400) ++ limit_freq_to_96_khz = true; ++ else if (samples < 192000) ++ limit_freq_to_174_4_khz = true; ++ ++ if (sample_rates != NULL) { ++ /* limit frequencies */ ++ if (limit_freq_to_174_4_khz) ++ sample_rates->rate.RATE_192 = 0; ++ ++ if (limit_freq_to_96_khz) { ++ sample_rates->rate.RATE_192 = 0; ++ sample_rates->rate.RATE_176_4 = 0; ++ } ++ if (limit_freq_to_88_2_khz) { ++ sample_rates->rate.RATE_192 = 0; ++ sample_rates->rate.RATE_176_4 = 0; ++ sample_rates->rate.RATE_96 = 0; ++ } ++ if (limit_freq_to_48_khz) { ++ sample_rates->rate.RATE_192 = 0; ++ sample_rates->rate.RATE_176_4 = 0; ++ sample_rates->rate.RATE_96 = 0; ++ sample_rates->rate.RATE_88_2 = 0; ++ } ++ } ++} ++ ++/*For DP SST, calculate if specified sample rates can fit into a given timing */ ++void dal_audio_hw_ctx_check_audio_bandwidth_dpsst( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ union audio_sample_rates *sample_rates) ++{ ++ /* do nothing */ ++} ++ ++/*For DP MST, calculate if specified sample rates can fit into a given timing */ ++void dal_audio_hw_ctx_check_audio_bandwidth_dpmst( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ union audio_sample_rates *sample_rates) ++{ ++ /* do nothing */ ++} ++ ++/* calculate max number of Audio packets per line */ ++uint32_t dal_audio_hw_ctx_calc_max_audio_packets_per_line( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info) ++{ ++ uint32_t max_packets_per_line; ++ ++ max_packets_per_line = ++ crtc_info->h_total - crtc_info->h_active; ++ ++ if (crtc_info->pixel_repetition) ++ max_packets_per_line *= crtc_info->pixel_repetition; ++ ++ /* for other hdmi features */ ++ max_packets_per_line -= 58; ++ /* for Control Period */ ++ max_packets_per_line -= 16; ++ /* Number of Audio Packets per Line */ ++ max_packets_per_line /= 32; ++ ++ return max_packets_per_line; ++} ++ ++/** ++* speakersToChannels ++* ++* @brief ++* translate speakers to channels ++* ++* FL - Front Left ++* FR - Front Right ++* RL - Rear Left ++* RR - Rear Right ++* RC - Rear Center ++* FC - Front Center ++* FLC - Front Left Center ++* FRC - Front Right Center ++* RLC - Rear Left Center ++* RRC - Rear Right Center ++* LFE - Low Freq Effect ++* ++* FC ++* FLC FRC ++* FL FR ++* ++* LFE ++* () ++* ++* ++* RL RR ++* RLC RRC ++* RC ++* ++* ch 8 7 6 5 4 3 2 1 ++* 0b00000011 - - - - - - FR FL ++* 0b00000111 - - - - - LFE FR FL ++* 0b00001011 - - - - FC - FR FL ++* 0b00001111 - - - - FC LFE FR FL ++* 0b00010011 - - - RC - - FR FL ++* 0b00010111 - - - RC - LFE FR FL ++* 0b00011011 - - - RC FC - FR FL ++* 0b00011111 - - - RC FC LFE FR FL ++* 0b00110011 - - RR RL - - FR FL ++* 0b00110111 - - RR RL - LFE FR FL ++* 0b00111011 - - RR RL FC - FR FL ++* 0b00111111 - - RR RL FC LFE FR FL ++* 0b01110011 - RC RR RL - - FR FL ++* 0b01110111 - RC RR RL - LFE FR FL ++* 0b01111011 - RC RR RL FC - FR FL ++* 0b01111111 - RC RR RL FC LFE FR FL ++* 0b11110011 RRC RLC RR RL - - FR FL ++* 0b11110111 RRC RLC RR RL - LFE FR FL ++* 0b11111011 RRC RLC RR RL FC - FR FL ++* 0b11111111 RRC RLC RR RL FC LFE FR FL ++* 0b11000011 FRC FLC - - - - FR FL ++* 0b11000111 FRC FLC - - - LFE FR FL ++* 0b11001011 FRC FLC - - FC - FR FL ++* 0b11001111 FRC FLC - - FC LFE FR FL ++* 0b11010011 FRC FLC - RC - - FR FL ++* 0b11010111 FRC FLC - RC - LFE FR FL ++* 0b11011011 FRC FLC - RC FC - FR FL ++* 0b11011111 FRC FLC - RC FC LFE FR FL ++* 0b11110011 FRC FLC RR RL - - FR FL ++* 0b11110111 FRC FLC RR RL - LFE FR FL ++* 0b11111011 FRC FLC RR RL FC - FR FL ++* 0b11111111 FRC FLC RR RL FC LFE FR FL ++* ++* @param ++* speakers - speaker information as it comes from CEA audio block ++*/ ++/* translate speakers to channels */ ++union audio_cea_channels dal_audio_hw_ctx_speakers_to_channels( ++ const struct hw_ctx_audio *hw_ctx, ++ struct audio_speaker_flags speaker_flags) ++{ ++ union audio_cea_channels cea_channels = {0}; ++ ++ /* these are one to one */ ++ cea_channels.channels.FL = speaker_flags.FL_FR; ++ cea_channels.channels.FR = speaker_flags.FL_FR; ++ cea_channels.channels.LFE = speaker_flags.LFE; ++ cea_channels.channels.FC = speaker_flags.FC; ++ ++ /* if Rear Left and Right exist move RC speaker to channel 7 ++ * otherwise to channel 5 ++ */ ++ if (speaker_flags.RL_RR) { ++ cea_channels.channels.RL_RC = speaker_flags.RL_RR; ++ cea_channels.channels.RR = speaker_flags.RL_RR; ++ cea_channels.channels.RC_RLC_FLC = speaker_flags.RC; ++ } else { ++ cea_channels.channels.RL_RC = speaker_flags.RC; ++ } ++ ++ /* FRONT Left Right Center and REAR Left Right Center are exclusive */ ++ if (speaker_flags.FLC_FRC) { ++ cea_channels.channels.RC_RLC_FLC = speaker_flags.FLC_FRC; ++ cea_channels.channels.RRC_FRC = speaker_flags.FLC_FRC; ++ } else { ++ cea_channels.channels.RC_RLC_FLC = speaker_flags.RLC_RRC; ++ cea_channels.channels.RRC_FRC = speaker_flags.RLC_RRC; ++ } ++ ++ return cea_channels; ++} ++ ++/* check whether specified audio format supported */ ++bool dal_audio_hw_ctx_is_audio_format_supported( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_info *audio_info, ++ enum audio_format_code audio_format_code, ++ uint32_t *format_index) ++{ ++ uint32_t index; ++ uint32_t max_channe_index = 0; ++ bool found = false; ++ ++ if (audio_info == NULL) ++ return found; ++ ++ /* pass through whole array */ ++ for (index = 0; index < audio_info->mode_count; index++) { ++ if (audio_info->modes[index].format_code == audio_format_code) { ++ if (found) { ++ /* format has multiply entries, choose one with ++ * highst number of channels */ ++ if (audio_info->modes[index].channel_count > ++ audio_info->modes[max_channe_index].channel_count) { ++ max_channe_index = index; ++ } ++ } else { ++ /* format found, save it's index */ ++ found = true; ++ max_channe_index = index; ++ } ++ } ++ } ++ ++ /* return index */ ++ if (found && format_index != NULL) ++ *format_index = max_channe_index; ++ ++ return found; ++} ++ ++/* search pixel clock value for HDMI */ ++bool dal_audio_hw_ctx_get_audio_clock_info( ++ const struct hw_ctx_audio *hw_ctx, ++ enum dc_color_depth color_depth, ++ uint32_t crtc_pixel_clock_in_khz, ++ uint32_t actual_pixel_clock_in_khz, ++ struct audio_clock_info *audio_clock_info) ++{ ++ const struct audio_clock_info *clock_info; ++ uint32_t index; ++ uint32_t crtc_pixel_clock_in_10khz = crtc_pixel_clock_in_khz / 10; ++ uint32_t audio_array_size; ++ ++ if (audio_clock_info == NULL) ++ return false; /* should not happen */ ++ ++ switch (color_depth) { ++ case COLOR_DEPTH_161616: ++ clock_info = audio_clock_info_table_48bpc; ++ audio_array_size = ARRAY_SIZE( ++ audio_clock_info_table_48bpc); ++ break; ++ case COLOR_DEPTH_121212: ++ clock_info = audio_clock_info_table_36bpc; ++ audio_array_size = ARRAY_SIZE( ++ audio_clock_info_table_36bpc); ++ break; ++ default: ++ clock_info = audio_clock_info_table; ++ audio_array_size = ARRAY_SIZE( ++ audio_clock_info_table); ++ break; ++ } ++ ++ if (clock_info != NULL) { ++ /* search for exact pixel clock in table */ ++ for (index = 0; index < audio_array_size; index++) { ++ if (clock_info[index].pixel_clock_in_10khz > ++ crtc_pixel_clock_in_10khz) ++ break; /* not match */ ++ else if (clock_info[index].pixel_clock_in_10khz == ++ crtc_pixel_clock_in_10khz) { ++ /* match found */ ++ if (audio_clock_info != NULL) { ++ *audio_clock_info = clock_info[index]; ++ return true; ++ } ++ } ++ } ++ } ++ ++ ++ /* not found */ ++ if (actual_pixel_clock_in_khz == 0) ++ actual_pixel_clock_in_khz = crtc_pixel_clock_in_khz; ++ ++ /* See HDMI spec the table entry under ++ * pixel clock of "Other". */ ++ audio_clock_info->pixel_clock_in_10khz = ++ actual_pixel_clock_in_khz / 10; ++ audio_clock_info->cts_32khz = actual_pixel_clock_in_khz; ++ audio_clock_info->cts_44khz = actual_pixel_clock_in_khz; ++ audio_clock_info->cts_48khz = actual_pixel_clock_in_khz; ++ ++ audio_clock_info->n_32khz = 4096; ++ audio_clock_info->n_44khz = 6272; ++ audio_clock_info->n_48khz = 6144; ++ ++ return true; ++} ++ ++static const struct hw_ctx_audio_funcs funcs = { ++ .destroy = destroy, ++ .setup_audio_wall_dto = ++ setup_audio_wall_dto, ++ .setup_hdmi_audio = ++ setup_hdmi_audio, ++ .setup_dp_audio = setup_dp_audio, ++ .setup_vce_audio = setup_vce_audio, ++ .enable_azalia_audio = ++ enable_azalia_audio, ++ .disable_azalia_audio = ++ disable_azalia_audio, ++ .enable_dp_audio = ++ enable_dp_audio, ++ .disable_dp_audio = ++ disable_dp_audio, ++ .setup_azalia = ++ setup_azalia, ++ .disable_az_clock_gating = ++ disable_az_clock_gating, ++ .unmute_azalia_audio = ++ unmute_azalia_audio, ++ .mute_azalia_audio = ++ mute_azalia_audio, ++ .setup_channel_splitting_mapping = ++ setup_channel_splitting_mapping, ++ .get_channel_splitting_mapping = ++ get_channel_splitting_mapping, ++ .set_unsolicited_response_payload = ++ set_unsolicited_response_payload, ++ .hw_initialize = ++ hw_initialize, ++ .enable_gtc_embedding_with_group = ++ enable_gtc_embedding_with_group, ++ .disable_gtc_embedding = ++ disable_gtc_embedding, ++ .get_azalia_clock_info_hdmi = ++ get_azalia_clock_info_hdmi, ++ .get_azalia_clock_info_dp = ++ get_azalia_clock_info_dp, ++}; ++/* --- object creator, destroy, construct, destruct --- */ ++ ++bool dal_audio_construct_hw_ctx_audio( ++ struct hw_ctx_audio *ctx) ++{ ++ ctx->funcs = &funcs; ++ ++ /* internal variables */ ++ ++ return true; ++} ++ ++void dal_audio_destruct_hw_ctx_audio( ++ struct hw_ctx_audio *ctx) ++{ ++ /* nothing to do */ ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h +new file mode 100644 +index 0000000..8ab2e58 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h +@@ -0,0 +1,285 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_CTX_AUDIO_H__ ++#define __DAL_HW_CTX_AUDIO_H__ ++ ++#include "include/audio_interface.h" ++#include "include/link_service_types.h" ++ ++struct hw_ctx_audio; ++ ++ ++struct azalia_reg_offsets { ++ uint32_t azf0endpointx_azalia_f0_codec_endpoint_index; ++ uint32_t azf0endpointx_azalia_f0_codec_endpoint_data; ++}; ++ ++/***** hook functions *****/ ++ ++struct hw_ctx_audio_funcs { ++ ++ /* functions for hw_ctx creation */ ++ void (*destroy)( ++ struct hw_ctx_audio **ptr); ++ ++ /***** from dal2 hwcontextaudio.hpp *****/ ++ ++ void (*setup_audio_wall_dto)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info); ++ ++ /* MM register access read_register write_register */ ++ ++ /***** from dal2 hwcontextaudio_hal.hpp *****/ ++ ++ /* setup HDMI audio */ ++ void (*setup_hdmi_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ const struct audio_crtc_info *crtc_info); ++ ++ /* setup DP audio */ ++ void (*setup_dp_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* setup VCE audio */ ++ void (*setup_vce_audio)( ++ const struct hw_ctx_audio *hw_ctx); ++ ++ /* enable Azalia audio */ ++ void (*enable_azalia_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* disable Azalia audio */ ++ void (*disable_azalia_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* enable DP audio */ ++ void (*enable_dp_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* disable DP audio */ ++ void (*disable_dp_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* setup Azalia HW block */ ++ void (*setup_azalia)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info, ++ const struct audio_info *audio_info); ++ ++ /* unmute audio */ ++ void (*unmute_azalia_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* mute audio */ ++ void (*mute_azalia_audio)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id); ++ ++ /* enable channel splitting mapping */ ++ void (*setup_channel_splitting_mapping)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable); ++ ++ /* get current channel spliting */ ++ bool (*get_channel_splitting_mapping)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping); ++ ++ /* set the payload value for the unsolicited response */ ++ void (*set_unsolicited_response_payload)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum audio_payload payload); ++ ++ /* initialize HW state */ ++ void (*hw_initialize)( ++ const struct hw_ctx_audio *hw_ctx); ++ ++ /* check_audio_bandwidth */ ++ ++ /* Assign GTC group and enable GTC value embedding */ ++ void (*enable_gtc_embedding_with_group)( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t groupNum, ++ uint32_t audioLatency); ++ ++ /* Disable GTC value embedding */ ++ void (*disable_gtc_embedding)( ++ const struct hw_ctx_audio *hw_ctx); ++ ++ /* Disable Azalia Clock Gating Feature */ ++ void (*disable_az_clock_gating)( ++ const struct hw_ctx_audio *hw_ctx); ++ ++ /* ~~~~ protected: ~~~~*/ ++ ++ /* calc_max_audio_packets_per_line */ ++ /* speakers_to_channels */ ++ /* is_audio_format_supported */ ++ /* get_audio_clock_info */ ++ ++ /* search pixel clock value for Azalia HDMI Audio */ ++ bool (*get_azalia_clock_info_hdmi)( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t crtc_pixel_clock_in_khz, ++ uint32_t actual_pixel_clock_in_khz, ++ struct azalia_clock_info *azalia_clock_info); ++ ++ /* search pixel clock value for Azalia DP Audio */ ++ bool (*get_azalia_clock_info_dp)( ++ const struct hw_ctx_audio *hw_ctx, ++ uint32_t requested_pixel_clock_in_khz, ++ const struct audio_pll_info *pll_info, ++ struct azalia_clock_info *azalia_clock_info); ++ ++ void (*enable_afmt_clock)( ++ const struct hw_ctx_audio *hw_ctx, ++ enum engine_id engine_id, ++ bool enable); ++ ++ /* @@@@ private: @@@@ */ ++ ++ /* check_audio_bandwidth_hdmi */ ++ /* check_audio_bandwidth_dpsst */ ++ /* check_audio_bandwidth_dpmst */ ++ ++}; ++ ++ ++struct hw_ctx_audio { ++ const struct hw_ctx_audio_funcs *funcs; ++ struct dc_context *ctx; ++ ++ /*audio_clock_infoTable[12]; ++ *audio_clock_infoTable_36bpc[12]; ++ *audio_clock_infoTable_48bpc[12]; ++ *used by hw_ctx_audio.c file only. Will declare as static array ++ *azaliaclockinfoTable[12] -- not used ++ *BusNumberMask; BusNumberShift; DeviceNumberMask; ++ *not used by dce6 and after ++ */ ++}; ++ ++ ++ ++/* --- object construct, destruct --- */ ++ ++/* ++ *called by derived audio object for specific ASIC. In case no derived object, ++ *these two functions do not need exposed. ++ */ ++bool dal_audio_construct_hw_ctx_audio( ++ struct hw_ctx_audio *hw_ctx); ++ ++void dal_audio_destruct_hw_ctx_audio( ++ struct hw_ctx_audio *hw_ctx); ++ ++/* ++ *creator of audio HW context will be implemented by specific ASIC object only. ++ *Top base or interface object does not have implementation of creator. ++ */ ++ ++ ++/* --- functions called by audio hw context itself --- */ ++ ++/* MM register access */ ++/*read_register - dal_read_reg */ ++/*write_register - dal_write_reg*/ ++ ++ ++/*check whether specified sample rates can fit into a given timing */ ++void dal_hw_ctx_audio_check_audio_bandwidth( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ enum signal_type signal, ++ union audio_sample_rates *sample_rates); ++ ++/*For HDMI, calculate if specified sample rates can fit into a given timing */ ++void dal_audio_hw_ctx_check_audio_bandwidth_hdmi( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ union audio_sample_rates *sample_rates); ++ ++/*For DPSST, calculate if specified sample rates can fit into a given timing */ ++void dal_audio_hw_ctx_check_audio_bandwidth_dpsst( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ union audio_sample_rates *sample_rates); ++ ++/*For DPMST, calculate if specified sample rates can fit into a given timing */ ++void dal_audio_hw_ctx_check_audio_bandwidth_dpmst( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info, ++ uint32_t channel_count, ++ union audio_sample_rates *sample_rates); ++ ++/* calculate max number of Audio packets per line */ ++uint32_t dal_audio_hw_ctx_calc_max_audio_packets_per_line( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_crtc_info *crtc_info); ++ ++/* translate speakers to channels */ ++union audio_cea_channels dal_audio_hw_ctx_speakers_to_channels( ++ const struct hw_ctx_audio *hw_ctx, ++ struct audio_speaker_flags speaker_flags); ++ ++/* check whether specified audio format supported */ ++bool dal_audio_hw_ctx_is_audio_format_supported( ++ const struct hw_ctx_audio *hw_ctx, ++ const struct audio_info *audio_info, ++ enum audio_format_code audio_format_code, ++ uint32_t *format_index); ++ ++/* search pixel clock value for HDMI */ ++bool dal_audio_hw_ctx_get_audio_clock_info( ++ const struct hw_ctx_audio *hw_ctx, ++ enum dc_color_depth color_depth, ++ uint32_t crtc_pixel_clock_in_khz, ++ uint32_t actual_pixel_clock_in_khz, ++ struct audio_clock_info *audio_clock_info); ++ ++ ++#endif /* __DAL_HW_CTX_AUDIO_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/Makefile b/drivers/gpu/drm/amd/dal/dc/basics/Makefile +new file mode 100644 +index 0000000..93e2371 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the 'utils' sub-component of DAL. ++# It provides the general basic services required by other DAL ++# subcomponents. ++ ++BASICS = conversion.o fixpt31_32.o fixpt32_32.o grph_object_id.o logger.o register_logger.o signal_types.o vector.o ++ ++AMD_DAL_BASICS = $(addprefix $(AMDDALPATH)/dc/basics/,$(BASICS)) ++ ++AMD_DAL_FILES += $(AMD_DAL_BASICS) +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/conversion.c b/drivers/gpu/drm/amd/dal/dc/basics/conversion.c +new file mode 100644 +index 0000000..8c38206 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/conversion.c +@@ -0,0 +1,223 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#define DIVIDER 10000 ++ ++/* S2D13 value in [-3.00...0.9999] */ ++#define S2D13_MIN (-3 * DIVIDER) ++#define S2D13_MAX (3 * DIVIDER) ++ ++uint16_t fixed_point_to_int_frac( ++ struct fixed31_32 arg, ++ uint8_t integer_bits, ++ uint8_t fractional_bits) ++{ ++ int32_t numerator; ++ int32_t divisor = 1 << fractional_bits; ++ ++ uint16_t result; ++ ++ uint16_t d = (uint16_t)dal_fixed31_32_floor( ++ dal_fixed31_32_abs( ++ arg)); ++ ++ if (d <= (uint16_t)(1 << integer_bits) - (1 / (uint16_t)divisor)) ++ numerator = (uint16_t)dal_fixed31_32_floor( ++ dal_fixed31_32_mul_int( ++ arg, ++ divisor)); ++ else { ++ numerator = dal_fixed31_32_floor( ++ dal_fixed31_32_sub( ++ dal_fixed31_32_from_int( ++ 1LL << integer_bits), ++ dal_fixed31_32_recip( ++ dal_fixed31_32_from_int( ++ divisor)))); ++ } ++ ++ if (numerator >= 0) ++ result = (uint16_t)numerator; ++ else ++ result = (uint16_t)( ++ (1 << (integer_bits + fractional_bits + 1)) + numerator); ++ ++ if ((result != 0) && dal_fixed31_32_lt( ++ arg, dal_fixed31_32_zero)) ++ result |= 1 << (integer_bits + fractional_bits); ++ ++ return result; ++} ++/** ++* convert_float_matrix ++* This converts a double into HW register spec defined format S2D13. ++* @param : ++* @return None ++*/ ++void convert_float_matrix( ++ uint16_t *matrix, ++ struct fixed31_32 *flt, ++ uint32_t buffer_size) ++{ ++ const struct fixed31_32 min_2_13 = ++ dal_fixed31_32_from_fraction(S2D13_MIN, DIVIDER); ++ const struct fixed31_32 max_2_13 = ++ dal_fixed31_32_from_fraction(S2D13_MAX, DIVIDER); ++ uint32_t i; ++ ++ for (i = 0; i < buffer_size; ++i) { ++ uint32_t reg_value = ++ fixed_point_to_int_frac( ++ dal_fixed31_32_clamp( ++ flt[i], ++ min_2_13, ++ max_2_13), ++ 2, ++ 13); ++ ++ matrix[i] = (uint16_t)reg_value; ++ } ++} ++ ++static void calculate_adjustments_common( ++ const struct fixed31_32 *ideal_matrix, ++ const struct dc_csc_adjustments *adjustments, ++ struct fixed31_32 *matrix) ++{ ++ const struct fixed31_32 sin_hue = ++ dal_fixed31_32_sin(adjustments->hue); ++ const struct fixed31_32 cos_hue = ++ dal_fixed31_32_cos(adjustments->hue); ++ ++ const struct fixed31_32 multiplier = ++ dal_fixed31_32_mul( ++ adjustments->contrast, ++ adjustments->saturation); ++ ++ matrix[0] = dal_fixed31_32_mul( ++ ideal_matrix[0], ++ adjustments->contrast); ++ ++ matrix[1] = dal_fixed31_32_mul( ++ ideal_matrix[1], ++ adjustments->contrast); ++ ++ matrix[2] = dal_fixed31_32_mul( ++ ideal_matrix[2], ++ adjustments->contrast); ++ ++ matrix[4] = dal_fixed31_32_mul( ++ multiplier, ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul( ++ ideal_matrix[8], ++ sin_hue), ++ dal_fixed31_32_mul( ++ ideal_matrix[4], ++ cos_hue))); ++ ++ matrix[5] = dal_fixed31_32_mul( ++ multiplier, ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul( ++ ideal_matrix[9], ++ sin_hue), ++ dal_fixed31_32_mul( ++ ideal_matrix[5], ++ cos_hue))); ++ ++ matrix[6] = dal_fixed31_32_mul( ++ multiplier, ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul( ++ ideal_matrix[10], ++ sin_hue), ++ dal_fixed31_32_mul( ++ ideal_matrix[6], ++ cos_hue))); ++ ++ matrix[7] = ideal_matrix[7]; ++ ++ matrix[8] = dal_fixed31_32_mul( ++ multiplier, ++ dal_fixed31_32_sub( ++ dal_fixed31_32_mul( ++ ideal_matrix[8], ++ cos_hue), ++ dal_fixed31_32_mul( ++ ideal_matrix[4], ++ sin_hue))); ++ ++ matrix[9] = dal_fixed31_32_mul( ++ multiplier, ++ dal_fixed31_32_sub( ++ dal_fixed31_32_mul( ++ ideal_matrix[9], ++ cos_hue), ++ dal_fixed31_32_mul( ++ ideal_matrix[5], ++ sin_hue))); ++ ++ matrix[10] = dal_fixed31_32_mul( ++ multiplier, ++ dal_fixed31_32_sub( ++ dal_fixed31_32_mul( ++ ideal_matrix[10], ++ cos_hue), ++ dal_fixed31_32_mul( ++ ideal_matrix[6], ++ sin_hue))); ++ ++ matrix[11] = ideal_matrix[11]; ++} ++ ++void calculate_adjustments( ++ const struct fixed31_32 *ideal_matrix, ++ const struct dc_csc_adjustments *adjustments, ++ struct fixed31_32 *matrix) ++{ ++ calculate_adjustments_common(ideal_matrix, adjustments, matrix); ++ ++ matrix[3] = dal_fixed31_32_add( ++ ideal_matrix[3], ++ dal_fixed31_32_mul( ++ adjustments->brightness, ++ dal_fixed31_32_from_fraction(86, 100))); ++} ++ ++void calculate_adjustments_y_only( ++ const struct fixed31_32 *ideal_matrix, ++ const struct dc_csc_adjustments *adjustments, ++ struct fixed31_32 *matrix) ++{ ++ calculate_adjustments_common(ideal_matrix, adjustments, matrix); ++ ++ matrix[3] = dal_fixed31_32_add( ++ ideal_matrix[3], ++ adjustments->brightness); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/conversion.h b/drivers/gpu/drm/amd/dal/dc/basics/conversion.h +new file mode 100644 +index 0000000..24ff473 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/conversion.h +@@ -0,0 +1,49 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CONVERSION_H__ ++#define __DAL_CONVERSION_H__ ++ ++uint16_t fixed_point_to_int_frac( ++ struct fixed31_32 arg, ++ uint8_t integer_bits, ++ uint8_t fractional_bits); ++ ++void convert_float_matrix( ++ uint16_t *matrix, ++ struct fixed31_32 *flt, ++ uint32_t buffer_size); ++ ++void calculate_adjustments( ++ const struct fixed31_32 *ideal_matrix, ++ const struct dc_csc_adjustments *adjustments, ++ struct fixed31_32 *matrix); ++ ++void calculate_adjustments_y_only( ++ const struct fixed31_32 *ideal_matrix, ++ const struct dc_csc_adjustments *adjustments, ++ struct fixed31_32 *matrix); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/fixpt31_32.c b/drivers/gpu/drm/amd/dal/dc/basics/fixpt31_32.c +new file mode 100644 +index 0000000..6ce75b3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/fixpt31_32.c +@@ -0,0 +1,692 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/fixed31_32.h" ++ ++static inline uint64_t abs_i64( ++ int64_t arg) ++{ ++ if (arg > 0) ++ return (uint64_t)arg; ++ else ++ return (uint64_t)(-arg); ++} ++ ++/* ++ * @brief ++ * result = dividend / divisor ++ * *remainder = dividend % divisor ++ */ ++static inline uint64_t complete_integer_division_u64( ++ uint64_t dividend, ++ uint64_t divisor, ++ uint64_t *remainder) ++{ ++ uint64_t result; ++ ++ ASSERT(divisor); ++ ++ result = div64_u64_rem(dividend, divisor, remainder); ++ ++ return result; ++} ++ ++#define BITS_PER_FRACTIONAL_PART \ ++ 32 ++ ++#define FRACTIONAL_PART_MASK \ ++ ((1ULL << BITS_PER_FRACTIONAL_PART) - 1) ++ ++#define GET_INTEGER_PART(x) \ ++ ((x) >> BITS_PER_FRACTIONAL_PART) ++ ++#define GET_FRACTIONAL_PART(x) \ ++ (FRACTIONAL_PART_MASK & (x)) ++ ++struct fixed31_32 dal_fixed31_32_from_fraction( ++ int64_t numerator, ++ int64_t denominator) ++{ ++ struct fixed31_32 res; ++ ++ bool arg1_negative = numerator < 0; ++ bool arg2_negative = denominator < 0; ++ ++ uint64_t arg1_value = arg1_negative ? -numerator : numerator; ++ uint64_t arg2_value = arg2_negative ? -denominator : denominator; ++ ++ uint64_t remainder; ++ ++ /* determine integer part */ ++ ++ uint64_t res_value = complete_integer_division_u64( ++ arg1_value, arg2_value, &remainder); ++ ++ ASSERT(res_value <= LONG_MAX); ++ ++ /* determine fractional part */ ++ { ++ uint32_t i = BITS_PER_FRACTIONAL_PART; ++ ++ do { ++ remainder <<= 1; ++ ++ res_value <<= 1; ++ ++ if (remainder >= arg2_value) { ++ res_value |= 1; ++ remainder -= arg2_value; ++ } ++ } while (--i != 0); ++ } ++ ++ /* round up LSB */ ++ { ++ uint64_t summand = (remainder << 1) >= arg2_value; ++ ++ ASSERT(res_value <= LLONG_MAX - summand); ++ ++ res_value += summand; ++ } ++ ++ res.value = (int64_t)res_value; ++ ++ if (arg1_negative ^ arg2_negative) ++ res.value = -res.value; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_from_int( ++ int64_t arg) ++{ ++ struct fixed31_32 res; ++ ++ ASSERT((LONG_MIN <= arg) && (arg <= LONG_MAX)); ++ ++ res.value = arg << BITS_PER_FRACTIONAL_PART; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_neg( ++ struct fixed31_32 arg) ++{ ++ struct fixed31_32 res; ++ ++ res.value = -arg.value; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_abs( ++ struct fixed31_32 arg) ++{ ++ if (arg.value < 0) ++ return dal_fixed31_32_neg(arg); ++ else ++ return arg; ++} ++ ++bool dal_fixed31_32_lt( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ return arg1.value < arg2.value; ++} ++ ++bool dal_fixed31_32_le( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ return arg1.value <= arg2.value; ++} ++ ++bool dal_fixed31_32_eq( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ return arg1.value == arg2.value; ++} ++ ++struct fixed31_32 dal_fixed31_32_min( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ if (arg1.value <= arg2.value) ++ return arg1; ++ else ++ return arg2; ++} ++ ++struct fixed31_32 dal_fixed31_32_max( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ if (arg1.value <= arg2.value) ++ return arg2; ++ else ++ return arg1; ++} ++ ++struct fixed31_32 dal_fixed31_32_clamp( ++ struct fixed31_32 arg, ++ struct fixed31_32 min_value, ++ struct fixed31_32 max_value) ++{ ++ if (dal_fixed31_32_le(arg, min_value)) ++ return min_value; ++ else if (dal_fixed31_32_le(max_value, arg)) ++ return max_value; ++ else ++ return arg; ++} ++ ++struct fixed31_32 dal_fixed31_32_shl( ++ struct fixed31_32 arg, ++ uint8_t shift) ++{ ++ struct fixed31_32 res; ++ ++ ASSERT(((arg.value >= 0) && (arg.value <= LLONG_MAX >> shift)) || ++ ((arg.value < 0) && (arg.value >= LLONG_MIN >> shift))); ++ ++ res.value = arg.value << shift; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_shr( ++ struct fixed31_32 arg, ++ uint8_t shift) ++{ ++ struct fixed31_32 res; ++ ++ ASSERT(shift < 64); ++ ++ res.value = arg.value >> shift; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_add( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ struct fixed31_32 res; ++ ++ ASSERT(((arg1.value >= 0) && (LLONG_MAX - arg1.value >= arg2.value)) || ++ ((arg1.value < 0) && (LLONG_MIN - arg1.value <= arg2.value))); ++ ++ res.value = arg1.value + arg2.value; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_sub_int( ++ struct fixed31_32 arg1, ++ int32_t arg2) ++{ ++ return dal_fixed31_32_sub( ++ arg1, ++ dal_fixed31_32_from_int(arg2)); ++} ++ ++struct fixed31_32 dal_fixed31_32_sub( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ struct fixed31_32 res; ++ ++ ASSERT(((arg2.value >= 0) && (LLONG_MIN + arg2.value <= arg1.value)) || ++ ((arg2.value < 0) && (LLONG_MAX + arg2.value >= arg1.value))); ++ ++ res.value = arg1.value - arg2.value; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_mul_int( ++ struct fixed31_32 arg1, ++ int32_t arg2) ++{ ++ return dal_fixed31_32_mul( ++ arg1, ++ dal_fixed31_32_from_int(arg2)); ++} ++ ++struct fixed31_32 dal_fixed31_32_mul( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ struct fixed31_32 res; ++ ++ bool arg1_negative = arg1.value < 0; ++ bool arg2_negative = arg2.value < 0; ++ ++ uint64_t arg1_value = arg1_negative ? -arg1.value : arg1.value; ++ uint64_t arg2_value = arg2_negative ? -arg2.value : arg2.value; ++ ++ uint64_t arg1_int = GET_INTEGER_PART(arg1_value); ++ uint64_t arg2_int = GET_INTEGER_PART(arg2_value); ++ ++ uint64_t arg1_fra = GET_FRACTIONAL_PART(arg1_value); ++ uint64_t arg2_fra = GET_FRACTIONAL_PART(arg2_value); ++ ++ uint64_t tmp; ++ ++ res.value = arg1_int * arg2_int; ++ ++ ASSERT(res.value <= LONG_MAX); ++ ++ res.value <<= BITS_PER_FRACTIONAL_PART; ++ ++ tmp = arg1_int * arg2_fra; ++ ++ ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); ++ ++ res.value += tmp; ++ ++ tmp = arg2_int * arg1_fra; ++ ++ ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); ++ ++ res.value += tmp; ++ ++ tmp = arg1_fra * arg2_fra; ++ ++ tmp = (tmp >> BITS_PER_FRACTIONAL_PART) + ++ (tmp >= (uint64_t)dal_fixed31_32_half.value); ++ ++ ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); ++ ++ res.value += tmp; ++ ++ if (arg1_negative ^ arg2_negative) ++ res.value = -res.value; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_sqr( ++ struct fixed31_32 arg) ++{ ++ struct fixed31_32 res; ++ ++ uint64_t arg_value = abs_i64(arg.value); ++ ++ uint64_t arg_int = GET_INTEGER_PART(arg_value); ++ ++ uint64_t arg_fra = GET_FRACTIONAL_PART(arg_value); ++ ++ uint64_t tmp; ++ ++ res.value = arg_int * arg_int; ++ ++ ASSERT(res.value <= LONG_MAX); ++ ++ res.value <<= BITS_PER_FRACTIONAL_PART; ++ ++ tmp = arg_int * arg_fra; ++ ++ ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); ++ ++ res.value += tmp; ++ ++ ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); ++ ++ res.value += tmp; ++ ++ tmp = arg_fra * arg_fra; ++ ++ tmp = (tmp >> BITS_PER_FRACTIONAL_PART) + ++ (tmp >= (uint64_t)dal_fixed31_32_half.value); ++ ++ ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); ++ ++ res.value += tmp; ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_div_int( ++ struct fixed31_32 arg1, ++ int64_t arg2) ++{ ++ return dal_fixed31_32_from_fraction( ++ arg1.value, ++ dal_fixed31_32_from_int(arg2).value); ++} ++ ++struct fixed31_32 dal_fixed31_32_div( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ return dal_fixed31_32_from_fraction( ++ arg1.value, ++ arg2.value); ++} ++ ++struct fixed31_32 dal_fixed31_32_recip( ++ struct fixed31_32 arg) ++{ ++ /* ++ * @note ++ * Good idea to use Newton's method ++ */ ++ ++ ASSERT(arg.value); ++ ++ return dal_fixed31_32_from_fraction( ++ dal_fixed31_32_one.value, ++ arg.value); ++} ++ ++struct fixed31_32 dal_fixed31_32_sinc( ++ struct fixed31_32 arg) ++{ ++ struct fixed31_32 square; ++ ++ struct fixed31_32 res = dal_fixed31_32_one; ++ ++ int32_t n = 27; ++ ++ struct fixed31_32 arg_norm = arg; ++ ++ if (dal_fixed31_32_le( ++ dal_fixed31_32_two_pi, ++ dal_fixed31_32_abs(arg))) { ++ arg_norm = dal_fixed31_32_sub( ++ arg_norm, ++ dal_fixed31_32_mul_int( ++ dal_fixed31_32_two_pi, ++ (int32_t)div64_s64( ++ arg_norm.value, ++ dal_fixed31_32_two_pi.value))); ++ } ++ ++ square = dal_fixed31_32_sqr(arg_norm); ++ ++ do { ++ res = dal_fixed31_32_sub( ++ dal_fixed31_32_one, ++ dal_fixed31_32_div_int( ++ dal_fixed31_32_mul( ++ square, ++ res), ++ n * (n - 1))); ++ ++ n -= 2; ++ } while (n > 2); ++ ++ if (arg.value != arg_norm.value) ++ res = dal_fixed31_32_div( ++ dal_fixed31_32_mul(res, arg_norm), ++ arg); ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_sin( ++ struct fixed31_32 arg) ++{ ++ return dal_fixed31_32_mul( ++ arg, ++ dal_fixed31_32_sinc(arg)); ++} ++ ++struct fixed31_32 dal_fixed31_32_cos( ++ struct fixed31_32 arg) ++{ ++ /* TODO implement argument normalization */ ++ ++ const struct fixed31_32 square = dal_fixed31_32_sqr(arg); ++ ++ struct fixed31_32 res = dal_fixed31_32_one; ++ ++ int32_t n = 26; ++ ++ do { ++ res = dal_fixed31_32_sub( ++ dal_fixed31_32_one, ++ dal_fixed31_32_div_int( ++ dal_fixed31_32_mul( ++ square, ++ res), ++ n * (n - 1))); ++ ++ n -= 2; ++ } while (n != 0); ++ ++ return res; ++} ++ ++/* ++ * @brief ++ * result = exp(arg), ++ * where abs(arg) < 1 ++ * ++ * Calculated as Taylor series. ++ */ ++static struct fixed31_32 fixed31_32_exp_from_taylor_series( ++ struct fixed31_32 arg) ++{ ++ uint32_t n = 9; ++ ++ struct fixed31_32 res = dal_fixed31_32_from_fraction( ++ n + 2, ++ n + 1); ++ /* TODO find correct res */ ++ ++ ASSERT(dal_fixed31_32_lt(arg, dal_fixed31_32_one)); ++ ++ do ++ res = dal_fixed31_32_add( ++ dal_fixed31_32_one, ++ dal_fixed31_32_div_int( ++ dal_fixed31_32_mul( ++ arg, ++ res), ++ n)); ++ while (--n != 1); ++ ++ return dal_fixed31_32_add( ++ dal_fixed31_32_one, ++ dal_fixed31_32_mul( ++ arg, ++ res)); ++} ++ ++struct fixed31_32 dal_fixed31_32_exp( ++ struct fixed31_32 arg) ++{ ++ /* ++ * @brief ++ * Main equation is: ++ * exp(x) = exp(r + m * ln(2)) = (1 << m) * exp(r), ++ * where m = round(x / ln(2)), r = x - m * ln(2) ++ */ ++ ++ if (dal_fixed31_32_le( ++ dal_fixed31_32_ln2_div_2, ++ dal_fixed31_32_abs(arg))) { ++ int32_t m = dal_fixed31_32_round( ++ dal_fixed31_32_div( ++ arg, ++ dal_fixed31_32_ln2)); ++ ++ struct fixed31_32 r = dal_fixed31_32_sub( ++ arg, ++ dal_fixed31_32_mul_int( ++ dal_fixed31_32_ln2, ++ m)); ++ ++ ASSERT(m != 0); ++ ++ ASSERT(dal_fixed31_32_lt( ++ dal_fixed31_32_abs(r), ++ dal_fixed31_32_one)); ++ ++ if (m > 0) ++ return dal_fixed31_32_shl( ++ fixed31_32_exp_from_taylor_series(r), ++ (uint8_t)m); ++ else ++ return dal_fixed31_32_div_int( ++ fixed31_32_exp_from_taylor_series(r), ++ 1LL << -m); ++ } else if (arg.value != 0) ++ return fixed31_32_exp_from_taylor_series(arg); ++ else ++ return dal_fixed31_32_one; ++} ++ ++struct fixed31_32 dal_fixed31_32_log( ++ struct fixed31_32 arg) ++{ ++ struct fixed31_32 res = dal_fixed31_32_neg(dal_fixed31_32_one); ++ /* TODO improve 1st estimation */ ++ ++ struct fixed31_32 error; ++ ++ ASSERT(arg.value > 0); ++ /* TODO if arg is negative, return NaN */ ++ /* TODO if arg is zero, return -INF */ ++ ++ do { ++ struct fixed31_32 res1 = dal_fixed31_32_add( ++ dal_fixed31_32_sub( ++ res, ++ dal_fixed31_32_one), ++ dal_fixed31_32_div( ++ arg, ++ dal_fixed31_32_exp(res))); ++ ++ error = dal_fixed31_32_sub( ++ res, ++ res1); ++ ++ res = res1; ++ /* TODO determine max_allowed_error based on quality of exp() */ ++ } while (abs_i64(error.value) > 100ULL); ++ ++ return res; ++} ++ ++struct fixed31_32 dal_fixed31_32_pow( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2) ++{ ++ return dal_fixed31_32_exp( ++ dal_fixed31_32_mul( ++ dal_fixed31_32_log(arg1), ++ arg2)); ++} ++ ++int32_t dal_fixed31_32_floor( ++ struct fixed31_32 arg) ++{ ++ uint64_t arg_value = abs_i64(arg.value); ++ ++ if (arg.value >= 0) ++ return (int32_t)GET_INTEGER_PART(arg_value); ++ else ++ return -(int32_t)GET_INTEGER_PART(arg_value); ++} ++ ++int32_t dal_fixed31_32_round( ++ struct fixed31_32 arg) ++{ ++ uint64_t arg_value = abs_i64(arg.value); ++ ++ const int64_t summand = dal_fixed31_32_half.value; ++ ++ ASSERT(LLONG_MAX - (int64_t)arg_value >= summand); ++ ++ arg_value += summand; ++ ++ if (arg.value >= 0) ++ return (int32_t)GET_INTEGER_PART(arg_value); ++ else ++ return -(int32_t)GET_INTEGER_PART(arg_value); ++} ++ ++int32_t dal_fixed31_32_ceil( ++ struct fixed31_32 arg) ++{ ++ uint64_t arg_value = abs_i64(arg.value); ++ ++ const int64_t summand = dal_fixed31_32_one.value - ++ dal_fixed31_32_epsilon.value; ++ ++ ASSERT(LLONG_MAX - (int64_t)arg_value >= summand); ++ ++ arg_value += summand; ++ ++ if (arg.value >= 0) ++ return (int32_t)GET_INTEGER_PART(arg_value); ++ else ++ return -(int32_t)GET_INTEGER_PART(arg_value); ++} ++ ++/* this function is a generic helper to translate fixed point value to ++ * specified integer format that will consist of integer_bits integer part and ++ * fractional_bits fractional part. For example it is used in ++ * dal_fixed31_32_u2d19 to receive 2 bits integer part and 19 bits fractional ++ * part in 32 bits. It is used in hw programming (scaler) ++ */ ++ ++static inline uint32_t ux_dy( ++ int64_t value, ++ uint32_t integer_bits, ++ uint32_t fractional_bits) ++{ ++ /* 1. create mask of integer part */ ++ uint32_t result = ++ (1 << integer_bits) - 1; ++ /* 2. mask out fractional part */ ++ uint32_t fractional_part = FRACTIONAL_PART_MASK & value; ++ /* 3. shrink fixed point integer part to be of integer_bits width*/ ++ result &= GET_INTEGER_PART(value); ++ /* 4. make space for fractional part to be filled in after integer */ ++ result <<= fractional_bits; ++ /* 5. shrink fixed point fractional part to of fractional_bits width*/ ++ fractional_part >>= BITS_PER_FRACTIONAL_PART - fractional_bits; ++ /* 6. merge the result */ ++ return result | fractional_part; ++} ++ ++uint32_t dal_fixed31_32_u2d19( ++ struct fixed31_32 arg) ++{ ++ return ux_dy(arg.value, 2, 19); ++} ++ ++uint32_t dal_fixed31_32_u0d19( ++ struct fixed31_32 arg) ++{ ++ return ux_dy(arg.value, 0, 19); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/fixpt32_32.c b/drivers/gpu/drm/amd/dal/dc/basics/fixpt32_32.c +new file mode 100644 +index 0000000..1140132 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/fixpt32_32.c +@@ -0,0 +1,223 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/fixed32_32.h" ++ ++static uint64_t u64_div(uint64_t n, uint64_t d) ++{ ++ uint32_t i = 0; ++ uint64_t r; ++ uint64_t q = div64_u64_rem(n, d, &r); ++ ++ for (i = 0; i < 32; ++i) { ++ uint64_t sbit = q & (1ULL<<63); ++ ++ r <<= 1; ++ r |= sbit ? 1 : 0; ++ q <<= 1; ++ if (r >= d) { ++ r -= d; ++ q |= 1; ++ } ++ } ++ ++ if (2*r >= d) ++ q += 1; ++ return q; ++} ++ ++struct fixed32_32 dal_fixed32_32_from_fraction(uint32_t n, uint32_t d) ++{ ++ struct fixed32_32 fx; ++ ++ fx.value = u64_div((uint64_t)n << 32, (uint64_t)d << 32); ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_from_int(uint32_t value) ++{ ++ struct fixed32_32 fx; ++ ++ fx.value = (uint64_t)value<<32; ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_add( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs) ++{ ++ struct fixed32_32 fx = {lhs.value + rhs.value}; ++ ++ ASSERT(fx.value >= rhs.value); ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_add_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ struct fixed32_32 fx = {lhs.value + ((uint64_t)rhs << 32)}; ++ ++ ASSERT(fx.value >= (uint64_t)rhs << 32); ++ return fx; ++ ++} ++struct fixed32_32 dal_fixed32_32_sub( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs) ++{ ++ struct fixed32_32 fx; ++ ++ ASSERT(lhs.value >= rhs.value); ++ fx.value = lhs.value - rhs.value; ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_sub_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ struct fixed32_32 fx; ++ ++ ASSERT(lhs.value >= ((uint64_t)rhs<<32)); ++ fx.value = lhs.value - ((uint64_t)rhs<<32); ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_mul( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs) ++{ ++ struct fixed32_32 fx; ++ uint64_t lhs_int = lhs.value>>32; ++ uint64_t lhs_frac = (uint32_t)lhs.value; ++ uint64_t rhs_int = rhs.value>>32; ++ uint64_t rhs_frac = (uint32_t)rhs.value; ++ uint64_t ahbh = lhs_int * rhs_int; ++ uint64_t ahbl = lhs_int * rhs_frac; ++ uint64_t albh = lhs_frac * rhs_int; ++ uint64_t albl = lhs_frac * rhs_frac; ++ ++ ASSERT((ahbh>>32) == 0); ++ ++ fx.value = (ahbh<<32) + ahbl + albh + (albl>>32); ++ return fx; ++ ++} ++ ++struct fixed32_32 dal_fixed32_32_mul_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ struct fixed32_32 fx; ++ uint64_t lhsi = (lhs.value>>32) * (uint64_t)rhs; ++ uint64_t lhsf; ++ ++ ASSERT((lhsi>>32) == 0); ++ lhsf = ((uint32_t)lhs.value) * (uint64_t)rhs; ++ ASSERT((lhsi<<32) + lhsf >= lhsf); ++ fx.value = (lhsi<<32) + lhsf; ++ return fx; ++} ++ ++ ++ ++struct fixed32_32 dal_fixed32_32_div( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs) ++{ ++ struct fixed32_32 fx; ++ ++ fx.value = u64_div(lhs.value, rhs.value); ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_div_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ struct fixed32_32 fx; ++ ++ fx.value = u64_div(lhs.value, (uint64_t)rhs << 32); ++ return fx; ++} ++ ++struct fixed32_32 dal_fixed32_32_min( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs) ++{ ++ return (lhs.value < rhs.value) ? lhs : rhs; ++} ++ ++struct fixed32_32 dal_fixed32_32_max( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs) ++{ ++ return (lhs.value > rhs.value) ? lhs : rhs; ++} ++ ++bool dal_fixed32_32_gt(struct fixed32_32 lhs, struct fixed32_32 rhs) ++{ ++ return lhs.value > rhs.value; ++} ++bool dal_fixed32_32_gt_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ return lhs.value > ((uint64_t)rhs<<32); ++} ++ ++bool dal_fixed32_32_lt(struct fixed32_32 lhs, struct fixed32_32 rhs) ++{ ++ return lhs.value < rhs.value; ++} ++ ++bool dal_fixed32_32_le(struct fixed32_32 lhs, struct fixed32_32 rhs) ++{ ++ return lhs.value <= rhs.value; ++} ++ ++bool dal_fixed32_32_lt_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ return lhs.value < ((uint64_t)rhs<<32); ++} ++ ++bool dal_fixed32_32_le_int(struct fixed32_32 lhs, uint32_t rhs) ++{ ++ return lhs.value <= ((uint64_t)rhs<<32); ++} ++ ++uint32_t dal_fixed32_32_ceil(struct fixed32_32 v) ++{ ++ ASSERT((uint32_t)v.value ? (v.value >> 32) + 1 >= 1 : true); ++ return (v.value>>32) + ((uint32_t)v.value ? 1 : 0); ++} ++ ++uint32_t dal_fixed32_32_floor(struct fixed32_32 v) ++{ ++ return v.value>>32; ++} ++ ++uint32_t dal_fixed32_32_round(struct fixed32_32 v) ++{ ++ ASSERT(v.value + (1ULL<<31) >= (1ULL<<31)); ++ return (v.value + (1ULL<<31))>>32; ++} ++ ++bool dal_fixed32_32_eq(struct fixed32_32 lhs, struct fixed32_32 rhs) ++{ ++ return lhs.value == rhs.value; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/grph_object_id.c b/drivers/gpu/drm/amd/dal/dc/basics/grph_object_id.c +new file mode 100644 +index 0000000..8276f9d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/grph_object_id.c +@@ -0,0 +1,135 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/grph_object_id.h" ++ ++bool dal_graphics_object_id_is_valid(struct graphics_object_id id) ++{ ++ bool rc = true; ++ ++ switch (id.type) { ++ case OBJECT_TYPE_UNKNOWN: ++ rc = false; ++ break; ++ case OBJECT_TYPE_GPU: ++ case OBJECT_TYPE_ENGINE: ++ /* do NOT check for id.id == 0 */ ++ if (id.enum_id == ENUM_ID_UNKNOWN) ++ rc = false; ++ break; ++ default: ++ if (id.id == 0 || id.enum_id == ENUM_ID_UNKNOWN) ++ rc = false; ++ break; ++ } ++ ++ return rc; ++} ++ ++bool dal_graphics_object_id_is_equal( ++ struct graphics_object_id id1, ++ struct graphics_object_id id2) ++{ ++ if (false == dal_graphics_object_id_is_valid(id1)) { ++ dal_output_to_console( ++ "%s: Warning: comparing invalid object 'id1'!\n", __func__); ++ return false; ++ } ++ ++ if (false == dal_graphics_object_id_is_valid(id2)) { ++ dal_output_to_console( ++ "%s: Warning: comparing invalid object 'id2'!\n", __func__); ++ return false; ++ } ++ ++ if (id1.id == id2.id && id1.enum_id == id2.enum_id ++ && id1.type == id2.type) ++ return true; ++ ++ return false; ++} ++ ++/* Based on internal data members memory layout */ ++uint32_t dal_graphics_object_id_to_uint(struct graphics_object_id id) ++{ ++ uint32_t object_id = 0; ++ ++ object_id = id.id + (id.enum_id << 0x8) + (id.type << 0xc); ++ return object_id; ++} ++ ++/* ++ * ******* get specific ID - internal safe cast into specific type ******* ++ */ ++ ++enum controller_id dal_graphics_object_id_get_controller_id( ++ struct graphics_object_id id) ++{ ++ if (id.type == OBJECT_TYPE_CONTROLLER) ++ return id.id; ++ return CONTROLLER_ID_UNDEFINED; ++} ++ ++enum clock_source_id dal_graphics_object_id_get_clock_source_id( ++ struct graphics_object_id id) ++{ ++ if (id.type == OBJECT_TYPE_CLOCK_SOURCE) ++ return id.id; ++ return CLOCK_SOURCE_ID_UNDEFINED; ++} ++ ++enum encoder_id dal_graphics_object_id_get_encoder_id( ++ struct graphics_object_id id) ++{ ++ if (id.type == OBJECT_TYPE_ENCODER) ++ return id.id; ++ return ENCODER_ID_UNKNOWN; ++} ++ ++enum connector_id dal_graphics_object_id_get_connector_id( ++ struct graphics_object_id id) ++{ ++ if (id.type == OBJECT_TYPE_CONNECTOR) ++ return id.id; ++ return CONNECTOR_ID_UNKNOWN; ++} ++ ++enum audio_id dal_graphics_object_id_get_audio_id(struct graphics_object_id id) ++{ ++ if (id.type == OBJECT_TYPE_AUDIO) ++ return id.id; ++ return AUDIO_ID_UNKNOWN; ++} ++ ++enum engine_id dal_graphics_object_id_get_engine_id( ++ struct graphics_object_id id) ++{ ++ if (id.type == OBJECT_TYPE_ENGINE) ++ return id.id; ++ return ENGINE_ID_UNKNOWN; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/logger.c b/drivers/gpu/drm/amd/dal/dc/basics/logger.c +new file mode 100644 +index 0000000..50db743 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/logger.c +@@ -0,0 +1,947 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include <stdarg.h> ++#include "dal_services.h" ++#include "include/dal_types.h" ++#include "include/logger_interface.h" ++#include "logger.h" ++ ++/* TODO: for now - empty, use DRM defines from dal services. ++ Need to define appropriate levels of prints, and implement ++ this component ++void dal_log(const char *format, ...) ++{ ++} ++*/ ++ ++/* ----------- Logging Major/Minor names ------------ */ ++ ++#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) ++ ++static const struct log_minor_info component_minor_info_tbl[] = { ++ {LOG_MINOR_COMPONENT_LINK_SERVICE, "LS"}, ++ {LOG_MINOR_COMPONENT_DAL_INTERFACE, "DalIf"}, ++ {LOG_MINOR_COMPONENT_HWSS, "HWSS"}, ++ {LOG_MINOR_COMPONENT_ADAPTER_SERVICE, "AS"}, ++ {LOG_MINOR_COMPONENT_DISPLAY_SERVICE, "DS"}, ++ {LOG_MINOR_COMPONENT_TOPOLOGY_MANAGER, "TM"}, ++ {LOG_MINOR_COMPONENT_ENCODER, "Encoder"}, ++ {LOG_MINOR_COMPONENT_I2C_AUX, "I2cAux"}, ++ {LOG_MINOR_COMPONENT_AUDIO, "Audio"}, ++ {LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, "Dcs"}, ++ {LOG_MINOR_COMPONENT_DMCU, "Dmcu"}, ++ {LOG_MINOR_COMPONENT_GPU, "GPU"}, ++ {LOG_MINOR_COMPONENT_CONTROLLER, "Cntrlr"}, ++ {LOG_MINOR_COMPONENT_ISR, "ISR"}, ++ {LOG_MINOR_COMPONENT_BIOS, "BIOS"}, ++ {LOG_MINOR_COMPONENT_DC, "DC"}, ++ {LOG_MINOR_COMPONENT_IRQ_SERVICE, "IRQ SERVICE"}, ++ ++}; ++ ++static const struct log_minor_info hw_trace_minor_info_tbl[] = { ++ {LOG_MINOR_HW_TRACE_MST, "Mst" }, ++ {LOG_MINOR_HW_TRACE_TRAVIS, "Travis" }, ++ {LOG_MINOR_HW_TRACE_HOTPLUG, "Hotplug" }, ++ {LOG_MINOR_HW_TRACE_LINK_TRAINING, "LinkTraining" }, ++ {LOG_MINOR_HW_TRACE_SET_MODE, "SetMode" }, ++ {LOG_MINOR_HW_TRACE_RESUME_S3, "ResumeS3" }, ++ {LOG_MINOR_HW_TRACE_RESUME_S4, "ResumeS4" }, ++ {LOG_MINOR_HW_TRACE_BOOTUP, "BootUp" }, ++ {LOG_MINOR_HW_TRACE_AUDIO, "Audio"}, ++ {LOG_MINOR_HW_TRACE_HPD_IRQ, "HpdIrq" }, ++ {LOG_MINOR_HW_TRACE_INTERRUPT, "Interrupt" }, ++ {LOG_MINOR_HW_TRACE_MPO, "Planes" }, ++}; ++ ++static const struct log_minor_info mst_minor_info_tbl[] = { ++ {LOG_MINOR_MST_IRQ_HPD_RX, "IrqHpdRx"}, ++ {LOG_MINOR_MST_IRQ_TIMER, "IrqTimer"}, ++ {LOG_MINOR_MST_NATIVE_AUX, "NativeAux"}, ++ {LOG_MINOR_MST_SIDEBAND_MSG, "SB"}, ++ {LOG_MINOR_MST_MSG_TRANSACTION, "MT"}, ++ {LOG_MINOR_MST_SIDEBAND_MSG_PARSED, "SB Parsed"}, ++ {LOG_MINOR_MST_MSG_TRANSACTION_PARSED, "MT Parsed"}, ++ {LOG_MINOR_MST_AUX_MSG_DPCD_ACCESS, "AuxMsgDpcdAccess"}, ++ {LOG_MINOR_MST_PROGRAMMING, "Programming"}, ++ {LOG_MINOR_MST_TOPOLOGY_DISCOVERY, "TopologyDiscovery"}, ++ {LOG_MINOR_MST_CONVERTER_CAPS, "ConverterCaps"}, ++}; ++ ++static const struct log_minor_info dcs_minor_info_tbl[] = { ++ {LOG_MINOR_DCS_EDID_EMULATOR, "EdidEmul"}, ++ {LOG_MINOR_DCS_DONGLE_DETECTION, "DongleDetect"}, ++}; ++ ++static const struct log_minor_info dcp_minor_info_tbl[] = { ++ { LOG_MINOR_DCP_GAMMA_GRPH, "GammaGrph"}, ++ { LOG_MINOR_DCP_GAMMA_OVL, "GammaOvl"}, ++ { LOG_MINOR_DCP_CSC_GRPH, "CscGrph"}, ++ { LOG_MINOR_DCP_CSC_OVL, "CscOvl"}, ++ { LOG_MINOR_DCP_SCALER, "Scaler"}, ++ { LOG_MINOR_DCP_SCALER_TABLES, "ScalerTables"}, ++}; ++ ++static const struct log_minor_info bios_minor_info_tbl[] = { ++ {LOG_MINOR_BIOS_CMD_TABLE, "CmdTbl"}, ++}; ++ ++static const struct log_minor_info reg_minor_info_tbl[] = { ++ {LOG_MINOR_REGISTER_INDEX, "Index"}, ++}; ++ ++static const struct log_minor_info info_packet_minor_info_tbl[] = { ++ {LOG_MINOR_INFO_PACKETS_HDMI, "Hdmi"}, ++}; ++ ++ ++static const struct log_minor_info dsat_minor_info_tbl[] = { ++ {LOG_MINOR_DSAT_LOGGER, "Logger"}, ++ {LOG_MINOR_DSAT_EDID_OVERRIDE, "EDID_Override"}, ++}; ++ ++static const struct log_minor_info ec_minor_info_tbl[] = { ++ {LOG_MINOR_EC_PPLIB_NOTIFY, "PPLib_Notify" }, /* PPLib notifies DAL */ ++ {LOG_MINOR_EC_PPLIB_QUERY, "PPLib_Query" } /* DAL requested info from ++ PPLib */ ++}; ++ ++static const struct log_minor_info bwm_minor_info_tbl[] = { ++ {LOG_MINOR_BWM_MODE_VALIDATION, "ModeValidation"}, ++ {LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS, "Req_Bandw_Calcs"} ++}; ++ ++static const struct log_minor_info mode_enum_minor_info_tbl[] = { ++ {LOG_MINOR_MODE_ENUM_BEST_VIEW_CANDIDATES, "BestviewCandidates"}, ++ {LOG_MINOR_MODE_ENUM_VIEW_SOLUTION, "ViewSolution"}, ++ {LOG_MINOR_MODE_ENUM_TS_LIST_BUILD, "TsListBuild"}, ++ {LOG_MINOR_MODE_ENUM_TS_LIST, "TsList"}, ++ {LOG_MINOR_MODE_ENUM_MASTER_VIEW_LIST, "MasterViewList"}, ++ {LOG_MINOR_MODE_ENUM_MASTER_VIEW_LIST_UPDATE, "MasterViewListUpdate"}, ++}; ++ ++static const struct log_minor_info i2caux_minor_info_tbl[] = { ++ {LOG_MINOR_I2C_AUX_LOG, "Log"}, ++ {LOG_MINOR_I2C_AUX_AUX_TIMESTAMP, "Timestamp"}, ++ {LOG_MINOR_I2C_AUX_CFG, "Config"} ++}; ++ ++static const struct log_minor_info line_buffer_minor_info_tbl[] = { ++ {LOG_MINOR_LINE_BUFFER_POWERGATING, "PowerGating"} ++}; ++ ++static const struct log_minor_info hwss_minor_info_tbl[] = { ++ {LOG_MINOR_HWSS_TAPS_VALIDATION, "HWSS Taps"} ++}; ++ ++static const struct log_minor_info optimization_minor_info_tbl[] = { ++ {LOG_MINOR_OPTMZ_GENERAL, "General Optimizations"}, ++ {LOG_MINOR_OPTMZ_DO_NOT_TURN_OFF_VCC_DURING_SET_MODE, ++ "Skip Vcc Off During Set Mode"} ++}; ++ ++static const struct log_minor_info perf_measure_minor_info_tbl[] = { ++ {LOG_MINOR_PERF_MEASURE_GENERAL, "General Performance Measurement"}, ++ {LOG_MINOR_PERF_MEASURE_HEAP_MEMORY, "Heap Memory Management"} ++}; ++ ++static const struct log_minor_info sync_minor_info_tbl[] = { ++ {LOG_MINOR_SYNC_HW_CLOCK_ADJUST, "Pixel Rate Tune-up"}, ++ {LOG_MINOR_SYNC_TIMING, "Timing"} ++}; ++ ++static const struct log_minor_info backlight_minor_info_tbl[] = { ++ {LOG_MINOR_BACKLIGHT_BRIGHTESS_CAPS, "Caps"}, ++ {LOG_MINOR_BACKLIGHT_DMCU_DELTALUT, "DMCU Delta LUT"}, ++ {LOG_MINOR_BACKLIGHT_DMCU_BUILD_DELTALUT, "Build DMCU Delta LUT"}, ++ {LOG_MINOR_BACKLIGHT_INTERFACE, "Interface"}, ++ {LOG_MINOR_BACKLIGHT_LID, "Lid Status"} ++}; ++ ++ ++static const struct log_minor_info override_feature_minor_info_tbl[] = { ++ {LOG_MINOR_FEATURE_OVERRIDE, "overriden feature"}, ++}; ++ ++static const struct log_minor_info detection_minor_info_tbl[] = { ++ {LOG_MINOR_DETECTION_EDID_PARSER, "EDID Parser"}, ++ {LOG_MINOR_DETECTION_DP_CAPS, "DP caps"}, ++}; ++ ++static const struct log_minor_info tm_minor_info_tbl[] = { ++ {LOG_MINOR_TM_INFO, "INFO"}, ++ {LOG_MINOR_TM_IFACE_TRACE, "IFACE_TRACE"}, ++ {LOG_MINOR_TM_RESOURCES, "RESOURCES"}, ++ {LOG_MINOR_TM_ENCODER_CTL, "ENCODER_CTL"}, ++ {LOG_MINOR_TM_ENG_ASN, "ENG_ASN"}, ++ {LOG_MINOR_TM_CONTROLLER_ASN, "CONTROLLER_ASN"}, ++ {LOG_MINOR_TM_PWR_GATING, "PWR_GATING"}, ++ {LOG_MINOR_TM_BUILD_DSP_PATH, "BUILD_PATH"}, ++ {LOG_MINOR_TM_DISPLAY_DETECT, "DISPLAY_DETECT"}, ++ {LOG_MINOR_TM_LINK_SRV, "LINK_SRV"}, ++ {LOG_MINOR_TM_NOT_IMPLEMENTED, "NOT_IMPL"}, ++ {LOG_MINOR_TM_COFUNC_PATH, "COFUNC_PATH"} ++}; ++ ++static const struct log_minor_info ds_minor_info_tbl[] = { ++ {LOG_MINOR_DS_MODE_SETTING, "Mode_Setting"}, ++}; ++ ++ ++struct log_major_mask_info { ++ struct log_major_info major_info; ++ uint32_t default_mask; ++ const struct log_minor_info *minor_tbl; ++ uint32_t tbl_element_cnt; ++}; ++ ++/* A mask for each Major. ++ * Use a mask or zero. */ ++#define LG_ERR_MSK 0xffffffff ++#define LG_WRN_MSK 0xffffffff ++#define LG_TM_MSK (1 << LOG_MINOR_TM_INFO) ++#define LG_FO_MSK (1 << LOG_MINOR_FEATURE_OVERRIDE) ++#define LG_EC_MSK ((1 << LOG_MINOR_EC_PPLIB_NOTIFY) | \ ++ (1 << LOG_MINOR_EC_PPLIB_QUERY)) ++#define LG_DSAT_MSK (1 << LOG_MINOR_DSAT_EDID_OVERRIDE) ++#define LG_DT_MSK (1 << LOG_MINOR_DETECTION_EDID_PARSER) ++ ++/* IFT - InterFaceTrace */ ++#define LG_IFT_MSK (1 << LOG_MINOR_COMPONENT_DC) ++ ++ ++#define LG_HW_TR_AUD_MSK (1 << LOG_MINOR_HW_TRACE_AUDIO) ++#define LG_HW_TR_INTERRUPT_MSK (1 << LOG_MINOR_HW_TRACE_INTERRUPT) | \ ++ (1 << LOG_MINOR_HW_TRACE_HPD_IRQ) ++#define LG_HW_TR_PLANES_MSK (1 << LOG_MINOR_HW_TRACE_MPO) ++#define LG_ALL_MSK 0xffffffff ++ ++#define LG_SYNC_MSK (1 << LOG_MINOR_SYNC_TIMING) ++ ++#define LG_BWM_MSK (1 << LOG_MINOR_BWM_MODE_VALIDATION) ++ ++ ++static const struct log_major_mask_info log_major_mask_info_tbl[] = { ++ /* LogMajor major name default MinorTble tblElementCnt */ ++ {{LOG_MAJOR_ERROR, "Error" }, LG_ALL_MSK, component_minor_info_tbl, NUM_ELEMENTS(component_minor_info_tbl)}, ++ {{LOG_MAJOR_WARNING, "Warning" }, LG_ALL_MSK, component_minor_info_tbl, NUM_ELEMENTS(component_minor_info_tbl)}, ++ {{LOG_MAJOR_INTERFACE_TRACE, "IfTrace" }, LG_ALL_MSK, component_minor_info_tbl, NUM_ELEMENTS(component_minor_info_tbl)}, ++ {{LOG_MAJOR_HW_TRACE, "HwTrace" }, (LG_ALL_MSK & ++ ~((1 << LOG_MINOR_HW_TRACE_LINK_TRAINING) | ++ (1 << LOG_MINOR_HW_TRACE_AUDIO))), ++ hw_trace_minor_info_tbl, NUM_ELEMENTS(hw_trace_minor_info_tbl)}, ++ {{LOG_MAJOR_MST, "MST" }, LG_ALL_MSK, mst_minor_info_tbl, NUM_ELEMENTS(mst_minor_info_tbl)}, ++ {{LOG_MAJOR_DCS, "DCS" }, LG_ALL_MSK, dcs_minor_info_tbl, NUM_ELEMENTS(dcs_minor_info_tbl)}, ++ {{LOG_MAJOR_DCP, "DCP" }, LG_ALL_MSK, dcp_minor_info_tbl, NUM_ELEMENTS(dcp_minor_info_tbl)}, ++ {{LOG_MAJOR_BIOS, "Bios" }, LG_ALL_MSK, bios_minor_info_tbl, NUM_ELEMENTS(bios_minor_info_tbl)}, ++ {{LOG_MAJOR_REGISTER, "Register" }, LG_ALL_MSK, reg_minor_info_tbl, NUM_ELEMENTS(reg_minor_info_tbl)}, ++ {{LOG_MAJOR_INFO_PACKETS, "InfoPacket" }, LG_ALL_MSK, info_packet_minor_info_tbl, NUM_ELEMENTS(info_packet_minor_info_tbl)}, ++ {{LOG_MAJOR_DSAT, "DSAT" }, LG_ALL_MSK, dsat_minor_info_tbl, NUM_ELEMENTS(dsat_minor_info_tbl)}, ++ {{LOG_MAJOR_EC, "EC" }, LG_ALL_MSK, ec_minor_info_tbl, NUM_ELEMENTS(ec_minor_info_tbl)}, ++ {{LOG_MAJOR_BWM, "BWM" }, LG_BWM_MSK, bwm_minor_info_tbl, NUM_ELEMENTS(bwm_minor_info_tbl)}, ++ {{LOG_MAJOR_MODE_ENUM, "ModeEnum" }, LG_ALL_MSK, mode_enum_minor_info_tbl, NUM_ELEMENTS(mode_enum_minor_info_tbl)}, ++ {{LOG_MAJOR_I2C_AUX, "I2cAux" }, LG_ALL_MSK, i2caux_minor_info_tbl, NUM_ELEMENTS(i2caux_minor_info_tbl)}, ++ {{LOG_MAJOR_LINE_BUFFER, "LineBuffer" }, LG_ALL_MSK, line_buffer_minor_info_tbl, NUM_ELEMENTS(line_buffer_minor_info_tbl)}, ++ {{LOG_MAJOR_HWSS, "HWSS" }, LG_ALL_MSK, hwss_minor_info_tbl, NUM_ELEMENTS(hwss_minor_info_tbl)}, ++ {{LOG_MAJOR_OPTIMIZATION, "Optimization"}, LG_ALL_MSK, optimization_minor_info_tbl, NUM_ELEMENTS(optimization_minor_info_tbl)}, ++ {{LOG_MAJOR_PERF_MEASURE, "PerfMeasure" }, LG_ALL_MSK, perf_measure_minor_info_tbl, NUM_ELEMENTS(perf_measure_minor_info_tbl)}, ++ {{LOG_MAJOR_SYNC, "Sync" }, LG_SYNC_MSK,sync_minor_info_tbl, NUM_ELEMENTS(sync_minor_info_tbl)}, ++ {{LOG_MAJOR_BACKLIGHT, "Backlight" }, LG_ALL_MSK, backlight_minor_info_tbl, NUM_ELEMENTS(backlight_minor_info_tbl)}, ++ {{LOG_MAJOR_INTERRUPTS, "Interrupts" }, LG_ALL_MSK, component_minor_info_tbl, NUM_ELEMENTS(component_minor_info_tbl)}, ++ {{LOG_MAJOR_TM, "TM" }, 0, tm_minor_info_tbl, NUM_ELEMENTS(tm_minor_info_tbl)}, ++ {{LOG_MAJOR_DISPLAY_SERVICE, "DS" }, LG_ALL_MSK, ds_minor_info_tbl, NUM_ELEMENTS(ds_minor_info_tbl)}, ++ {{LOG_MAJOR_FEATURE_OVERRIDE, "FeatureOverride" }, LG_ALL_MSK, override_feature_minor_info_tbl, NUM_ELEMENTS(override_feature_minor_info_tbl)}, ++ {{LOG_MAJOR_DETECTION, "Detection" }, LG_ALL_MSK, detection_minor_info_tbl, NUM_ELEMENTS(detection_minor_info_tbl)}, ++}; ++ ++/* ----------- Object init and destruction ----------- */ ++static bool construct(struct dc_context *ctx, struct dal_logger *logger) ++{ ++ uint32_t i; ++ /* malloc buffer and init offsets */ ++ ++ logger->log_buffer_size = DAL_LOGGER_BUFFER_MAX_SIZE; ++ logger->log_buffer = (char *)dc_service_alloc(ctx, ++ logger->log_buffer_size * ++ sizeof(char)); ++ ++ if (!logger->log_buffer) ++ return false; ++ ++ /* todo: Fill buffer with \0 if not done by dal_alloc */ ++ ++ /* Initialize both offsets to start of buffer (empty) */ ++ logger->buffer_read_offset = 0; ++ logger->buffer_write_offset = 0; ++ ++ logger->write_wrap_count = 0; ++ logger->read_wrap_count = 0; ++ logger->open_count = 0; ++ ++ logger->flags.bits.ENABLE_CONSOLE = 1; ++ logger->flags.bits.ENABLE_BUFFER = 0; ++ ++ logger->ctx = ctx; ++ ++ /* malloc and init minor mask array */ ++ logger->log_enable_mask_minors = ++ (uint32_t *)dc_service_alloc( ++ ctx, ++ NUM_ELEMENTS(log_major_mask_info_tbl) ++ * sizeof(uint32_t)); ++ if (!logger->log_enable_mask_minors) ++ return false; ++ ++ ++ /* Set default values for mask */ ++ for (i = 0; i < NUM_ELEMENTS(log_major_mask_info_tbl); i++) { ++ ++ uint32_t dflt_mask = log_major_mask_info_tbl[i].default_mask; ++ ++ logger->log_enable_mask_minors[i] = dflt_mask; ++ } ++ ++ return true; ++} ++ ++static void destruct(struct dal_logger *logger) ++{ ++ if (logger->log_buffer) { ++ dc_service_free(logger->ctx, logger->log_buffer); ++ logger->log_buffer = NULL; ++ } ++ ++ if (logger->log_enable_mask_minors) { ++ dc_service_free(logger->ctx, logger->log_enable_mask_minors); ++ logger->log_enable_mask_minors = NULL; ++ } ++} ++ ++struct dal_logger *dal_logger_create(struct dc_context *ctx) ++{ ++ /* malloc struct */ ++ struct dal_logger *logger = dc_service_alloc(ctx, sizeof(struct dal_logger)); ++ ++ if (!logger) ++ return NULL; ++ if (!construct(ctx, logger)) { ++ dc_service_free(ctx, logger); ++ return NULL; ++ } ++ ++ return logger; ++} ++ ++uint32_t dal_logger_destroy(struct dal_logger **logger) ++{ ++ if (logger == NULL || *logger == NULL) ++ return 1; ++ destruct(*logger); ++ dc_service_free((*logger)->ctx, *logger); ++ *logger = NULL; ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static void lock(struct dal_logger *logger) ++{ ++ /* Todo: lock mutex? */ ++} ++ ++static void unlock(struct dal_logger *logger) ++{ ++ /* Todo: unlock mutex */ ++} ++ ++bool dal_logger_should_log( ++ struct dal_logger *logger, ++ enum log_major major, ++ enum log_minor minor) ++{ ++ if (major < LOG_MAJOR_COUNT) { ++ ++ uint32_t minor_mask = logger->log_enable_mask_minors[major]; ++ ++ if ((minor_mask & (1 << minor)) != 0) ++ return true; ++ } ++ ++ return false; ++} ++ ++static void log_to_debug_console(struct log_entry *entry) ++{ ++ struct dal_logger *logger = entry->logger; ++ ++ if (logger->flags.bits.ENABLE_CONSOLE == 0) ++ return; ++ ++ switch (entry->major) { ++ case LOG_MAJOR_ERROR: ++ dal_error("%s", entry->buf); ++ break; ++ default: ++ dal_output_to_console("%s", entry->buf); ++ break; ++ } ++} ++ ++/* Print everything unread existing in log_buffer to debug console*/ ++static void flush_to_debug_console(struct dal_logger *logger) ++{ ++ int i = logger->buffer_read_offset; ++ char *string_start = &logger->log_buffer[i]; ++ ++ dal_output_to_console( ++ "---------------- FLUSHING LOG BUFFER ----------------\n"); ++ while (i < logger->buffer_write_offset) { ++ ++ if (logger->log_buffer[i] == '\0') { ++ dal_output_to_console("%s", string_start); ++ string_start = (char *)logger->log_buffer + i + 1; ++ } ++ i++; ++ } ++ dal_output_to_console( ++ "-------------- END FLUSHING LOG BUFFER --------------\n\n"); ++} ++ ++static void log_to_internal_buffer(struct log_entry *entry) ++{ ++ ++ uint32_t size = entry->buf_offset; ++ struct dal_logger *logger = entry->logger; ++ ++ if (logger->flags.bits.ENABLE_BUFFER == 0) ++ return; ++ ++ if (logger->log_buffer == NULL) ++ return; ++ ++ if (size > 0 && size < logger->log_buffer_size) { ++ ++ int total_free_space = 0; ++ int space_before_wrap = 0; ++ ++ if (logger->buffer_write_offset > logger->buffer_read_offset) { ++ total_free_space = logger->log_buffer_size - ++ logger->buffer_write_offset + ++ logger->buffer_read_offset; ++ space_before_wrap = logger->log_buffer_size - ++ logger->buffer_write_offset; ++ } else if (logger->buffer_write_offset < ++ logger->buffer_read_offset) { ++ total_free_space = logger->log_buffer_size - ++ logger->buffer_read_offset + ++ logger->buffer_write_offset; ++ space_before_wrap = total_free_space; ++ } else if (logger->write_wrap_count != ++ logger->read_wrap_count) { ++ /* Buffer is completely full already */ ++ total_free_space = 0; ++ space_before_wrap = 0; ++ } else { ++ /* Buffer is empty, start writing at beginning */ ++ total_free_space = logger->log_buffer_size; ++ space_before_wrap = logger->log_buffer_size; ++ logger->buffer_write_offset = 0; ++ logger->buffer_read_offset = 0; ++ } ++ ++ ++ ++ ++ if (space_before_wrap > size) { ++ /* No wrap around, copy 'size' bytes ++ * from 'entry->buf' to 'log_buffer' ++ */ ++ dc_service_memmove(logger->log_buffer + ++ logger->buffer_write_offset, ++ entry->buf, size); ++ logger->buffer_write_offset += size; ++ ++ } else if (total_free_space > size) { ++ /* We have enough room without flushing, ++ * but need to wrap around */ ++ ++ int space_after_wrap = total_free_space - ++ space_before_wrap; ++ ++ dc_service_memmove(logger->log_buffer + ++ logger->buffer_write_offset, ++ entry->buf, space_before_wrap); ++ dc_service_memmove(logger->log_buffer, entry->buf + ++ space_before_wrap, space_after_wrap); ++ ++ logger->buffer_write_offset = space_after_wrap; ++ logger->write_wrap_count++; ++ ++ } else { ++ /* Not enough room remaining, we should flush ++ * existing logs */ ++ ++ /* Flush existing unread logs to console */ ++ flush_to_debug_console(logger); ++ ++ /* Start writing to beginning of buffer */ ++ dc_service_memmove(logger->log_buffer, entry->buf, size); ++ logger->buffer_write_offset = size; ++ logger->buffer_read_offset = 0; ++ } ++ ++ } ++ ++ unlock(logger); ++} ++ ++ ++static void log_timestamp(struct log_entry *entry) ++{ ++ dal_logger_append(entry, "00:00:00 "); ++} ++ ++static void log_major_minor(struct log_entry *entry) ++{ ++ uint32_t i; ++ enum log_major major = entry->major; ++ enum log_minor minor = entry->minor; ++ ++ for (i = 0; i < NUM_ELEMENTS(log_major_mask_info_tbl); i++) { ++ ++ const struct log_major_mask_info *maj_mask_info = ++ &log_major_mask_info_tbl[i]; ++ ++ if (maj_mask_info->major_info.major == major) { ++ ++ dal_logger_append(entry, "[%s_", ++ maj_mask_info->major_info.major_name); ++ ++ if (maj_mask_info->minor_tbl != NULL) { ++ uint32_t j; ++ ++ for (j = 0; j < maj_mask_info->tbl_element_cnt; j++) { ++ ++ const struct log_minor_info *min_info = &maj_mask_info->minor_tbl[j]; ++ ++ if (min_info->minor == minor) ++ dal_logger_append(entry, "%s]\t", min_info->minor_name); ++ } ++ } ++ ++ break; ++ } ++ } ++} ++ ++static void log_heading(struct log_entry *entry, ++ enum log_major major, ++ enum log_minor minor) ++{ ++ log_timestamp(entry); ++ log_major_minor(entry); ++} ++ ++ ++static void append_entry( ++ struct log_entry *entry, ++ char *buffer, ++ uint32_t buf_size) ++{ ++ if (!entry->buf || ++ entry->buf_offset + buf_size > entry->max_buf_bytes ++ ) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ /* Todo: check if off by 1 byte due to \0 anywhere */ ++ dc_service_memmove(entry->buf + entry->buf_offset, buffer, buf_size); ++ entry->buf_offset += buf_size; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* Warning: Be careful that 'msg' is null terminated and the total size is ++ * less than DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE (256) including '\0' ++ */ ++void dal_logger_write( ++ struct dal_logger *logger, ++ enum log_major major, ++ enum log_minor minor, ++ const char *msg, ++ ...) ++{ ++ ++ if (logger && dal_logger_should_log(logger, major, minor)) { ++ ++ uint32_t size; ++ va_list args; ++ char buffer[DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE]; ++ struct log_entry entry; ++ ++ va_start(args, msg); ++ dal_logger_open(logger, &entry, major, minor); ++ ++ ++ size = dal_log_to_buffer( ++ buffer, DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE, msg, args); ++ ++ if (size > 0 && size < ++ DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE - 1) { ++ ++ if (buffer[size] == '\0') ++ size++; /* Add one for null terminator */ ++ ++ /* Concatenate onto end of entry buffer */ ++ append_entry(&entry, buffer, size); ++ } else { ++ append_entry(&entry, "LOG_ERROR\n", 12); ++ } ++ ++ dal_logger_close(&entry); ++ va_end(args); ++ ++ } ++} ++ ++ ++/* Same as dal_logger_write, except without open() and close(), which must ++ * be done separately. ++ */ ++void dal_logger_append( ++ struct log_entry *entry, ++ const char *msg, ++ ...) ++{ ++ struct dal_logger *logger; ++ ++ if (!entry) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ logger = entry->logger; ++ ++ if (logger && logger->open_count > 0 && ++ dal_logger_should_log(logger, entry->major, entry->minor)) { ++ ++ uint32_t size; ++ va_list args; ++ char buffer[DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE]; ++ ++ va_start(args, msg); ++ ++ size = dal_log_to_buffer( ++ buffer, DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE, msg, args); ++ append_entry(entry, buffer, size); ++ ++ va_end(args); ++ } ++} ++ ++ ++uint32_t dal_logger_read( ++ struct dal_logger *logger, /* <[in] */ ++ uint32_t output_buffer_size, /* <[in] */ ++ char *output_buffer, /* >[out] */ ++ uint32_t *bytes_read, /* >[out] */ ++ bool single_line) ++{ ++ uint32_t bytes_remaining = 0; ++ uint32_t bytes_read_count = 0; ++ bool keep_reading = true; ++ ++ if (!logger || output_buffer == NULL || output_buffer_size == 0) { ++ BREAK_TO_DEBUGGER(); ++ *bytes_read = 0; ++ return 0; ++ } ++ ++ lock(logger); ++ ++ /* Read until null terminator (if single_line==true, ++ * max buffer size, or until we've read everything new ++ */ ++ ++ do { ++ char cur; ++ ++ /* Stop when we've read everything */ ++ if (logger->buffer_read_offset == ++ logger->buffer_write_offset) { ++ ++ break; ++ } ++ ++ cur = logger->log_buffer[logger->buffer_read_offset]; ++ logger->buffer_read_offset++; ++ ++ /* Wrap read pointer if at end */ ++ if (logger->buffer_read_offset == logger->log_buffer_size) { ++ ++ logger->buffer_read_offset = 0; ++ logger->read_wrap_count++; ++ } ++ ++ /* Don't send null terminators to buffer */ ++ if (cur != '\0') { ++ output_buffer[bytes_read_count] = cur; ++ bytes_read_count++; ++ } else if (single_line) { ++ keep_reading = false; ++ } ++ ++ } while (bytes_read_count <= output_buffer_size && keep_reading); ++ ++ /* We assume that reading can never be ahead of writing */ ++ if (logger->write_wrap_count > logger->read_wrap_count) { ++ bytes_remaining = logger->log_buffer_size - ++ logger->buffer_read_offset + ++ logger->buffer_write_offset; ++ } else { ++ bytes_remaining = logger->buffer_write_offset - ++ logger->buffer_read_offset; ++ } ++ ++ /* reset write/read wrap count to 0 if we've read everything */ ++ if (bytes_remaining == 0) { ++ ++ logger->write_wrap_count = 0; ++ logger->read_wrap_count = 0; ++ } ++ ++ *bytes_read = bytes_read_count; ++ unlock(logger); ++ ++ return bytes_remaining; ++} ++ ++void dal_logger_open( ++ struct dal_logger *logger, ++ struct log_entry *entry, /* out */ ++ enum log_major major, ++ enum log_minor minor) ++{ ++ if (!entry) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ entry->major = LOG_MAJOR_COUNT; ++ entry->minor = 0; ++ entry->logger = logger; ++ ++ entry->buf = dc_service_alloc( ++ logger->ctx, ++ DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE * sizeof(char)); ++ ++ entry->buf_offset = 0; ++ entry->max_buf_bytes = ++ DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE * sizeof(char); ++ ++ logger->open_count++; ++ entry->major = major; ++ entry->minor = minor; ++ ++ log_heading(entry, major, minor); ++} ++ ++void dal_logger_close(struct log_entry *entry) ++{ ++ struct dal_logger *logger = entry->logger; ++ ++ ++ if (logger && logger->open_count > 0) { ++ logger->open_count--; ++ } else { ++ BREAK_TO_DEBUGGER(); ++ goto cleanup; ++ } ++ ++ /* --Flush log_entry buffer-- */ ++ /* print to kernel console */ ++ log_to_debug_console(entry); ++ /* log internally for dsat */ ++ log_to_internal_buffer(entry); ++ ++ /* TODO: Write end heading */ ++ ++cleanup: ++ if (entry->buf) { ++ dc_service_free(entry->logger->ctx, entry->buf); ++ entry->buf = NULL; ++ entry->buf_offset = 0; ++ entry->max_buf_bytes = 0; ++ } ++} ++ ++uint32_t dal_logger_get_mask( ++ struct dal_logger *logger, ++ enum log_major lvl_major, enum log_minor lvl_minor) ++{ ++ uint32_t log_mask = 0; ++ ++ if (logger && lvl_major < LOG_MAJOR_COUNT) ++ log_mask = logger->log_enable_mask_minors[lvl_major]; ++ ++ log_mask &= 1 << lvl_minor; ++ return log_mask; ++} ++ ++uint32_t dal_logger_set_mask( ++ struct dal_logger *logger, ++ enum log_major lvl_major, enum log_minor lvl_minor) ++{ ++ ++ if (logger && lvl_major < LOG_MAJOR_COUNT) { ++ if (lvl_minor == LOG_MINOR_MASK_ALL) { ++ logger->log_enable_mask_minors[lvl_major] = 0xFFFFFFFF; ++ } else { ++ logger->log_enable_mask_minors[lvl_major] |= ++ (1 << lvl_minor); ++ } ++ return 0; ++ } ++ return 1; ++} ++ ++uint32_t dal_logger_get_masks( ++ struct dal_logger *logger, ++ enum log_major lvl_major) ++{ ++ uint32_t log_mask = 0; ++ ++ if (logger && lvl_major < LOG_MAJOR_COUNT) ++ log_mask = logger->log_enable_mask_minors[lvl_major]; ++ ++ return log_mask; ++} ++ ++void dal_logger_set_masks( ++ struct dal_logger *logger, ++ enum log_major lvl_major, uint32_t log_mask) ++{ ++ if (logger && lvl_major < LOG_MAJOR_COUNT) ++ logger->log_enable_mask_minors[lvl_major] = log_mask; ++} ++ ++uint32_t dal_logger_unset_mask( ++ struct dal_logger *logger, ++ enum log_major lvl_major, enum log_minor lvl_minor) ++{ ++ ++ if (lvl_major < LOG_MAJOR_COUNT) { ++ if (lvl_minor == LOG_MINOR_MASK_ALL) { ++ logger->log_enable_mask_minors[lvl_major] = 0; ++ } else { ++ logger->log_enable_mask_minors[lvl_major] &= ++ ~(1 << lvl_minor); ++ } ++ return 0; ++ } ++ return 1; ++} ++ ++uint32_t dal_logger_get_flags( ++ struct dal_logger *logger) ++{ ++ ++ return logger->flags.value; ++} ++ ++void dal_logger_set_flags( ++ struct dal_logger *logger, ++ union logger_flags flags) ++{ ++ ++ logger->flags = flags; ++} ++ ++ ++uint32_t dal_logger_get_buffer_size(struct dal_logger *logger) ++{ ++ return DAL_LOGGER_BUFFER_MAX_SIZE; ++} ++ ++uint32_t dal_logger_set_buffer_size( ++ struct dal_logger *logger, ++ uint32_t new_size) ++{ ++ /* ToDo: implement dynamic size */ ++ ++ /* return new size */ ++ return DAL_LOGGER_BUFFER_MAX_SIZE; ++} ++ ++ ++const struct log_major_info *dal_logger_enum_log_major_info( ++ struct dal_logger *logger, ++ unsigned int enum_index) ++{ ++ const struct log_major_info *major_info; ++ ++ if (enum_index >= NUM_ELEMENTS(log_major_mask_info_tbl)) ++ return NULL; ++ ++ major_info = &log_major_mask_info_tbl[enum_index].major_info; ++ return major_info; ++} ++ ++const struct log_minor_info *dal_logger_enum_log_minor_info( ++ struct dal_logger *logger, ++ enum log_major major, ++ unsigned int enum_index) ++{ ++ const struct log_minor_info *minor_info = NULL; ++ uint32_t i; ++ ++ for (i = 0; i < NUM_ELEMENTS(log_major_mask_info_tbl); i++) { ++ ++ const struct log_major_mask_info *maj_mask_info = ++ &log_major_mask_info_tbl[i]; ++ ++ if (maj_mask_info->major_info.major == major) { ++ ++ if (maj_mask_info->minor_tbl != NULL) { ++ uint32_t j; ++ ++ for (j = 0; j < maj_mask_info->tbl_element_cnt; j++) { ++ ++ minor_info = &maj_mask_info->minor_tbl[j]; ++ ++ if (minor_info->minor == enum_index) ++ return minor_info; ++ } ++ } ++ ++ break; ++ } ++ } ++ return NULL; ++ ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/logger.h b/drivers/gpu/drm/amd/dal/dc/basics/logger.h +new file mode 100644 +index 0000000..fba5ec3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/logger.h +@@ -0,0 +1,64 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_LOGGER_H__ ++#define __DAL_LOGGER_H__ ++ ++/* Structure for keeping track of offsets, buffer, etc */ ++ ++#define DAL_LOGGER_BUFFER_MAX_SIZE 2048 ++#define DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE 256 ++ ++ ++#include "include/logger_types.h" ++ ++struct dal_logger { ++ ++ /* How far into the circular buffer has been read by dsat ++ * Read offset should never cross write offset. Write \0's to ++ * read data just to be sure? ++ */ ++ uint32_t buffer_read_offset; ++ ++ /* How far into the circular buffer we have written ++ * Write offset should never cross read offset ++ */ ++ uint32_t buffer_write_offset; ++ ++ uint32_t write_wrap_count; ++ uint32_t read_wrap_count; ++ ++ uint32_t open_count; ++ ++ char *log_buffer; /* Pointer to malloc'ed buffer */ ++ uint32_t log_buffer_size; /* Size of circular buffer */ ++ ++ uint32_t *log_enable_mask_minors; /*array of masks for major elements*/ ++ ++ union logger_flags flags; ++ struct dc_context *ctx; ++}; ++ ++#endif /* __DAL_LOGGER_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/register_logger.c b/drivers/gpu/drm/amd/dal/dc/basics/register_logger.c +new file mode 100644 +index 0000000..a3086a0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/register_logger.c +@@ -0,0 +1,197 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/dal_types.h" ++#include "include/logger_interface.h" ++#include "logger.h" ++ ++/****************************************************************************** ++ * Register Logger. ++ * A facility to create register R/W logs. ++ * Currently used for DAL Test. ++ *****************************************************************************/ ++ ++/****************************************************************************** ++ * Private structures ++ *****************************************************************************/ ++struct dal_reg_dump_stack_location { ++ const char *current_caller_func; ++ long current_pid; ++ long current_tgid; ++ uint32_t rw_count;/* register access counter for current function. */ ++}; ++ ++/* This the maximum number of nested calls to the 'reg_dump' facility. */ ++#define DAL_REG_DUMP_STACK_MAX_SIZE 32 ++ ++struct dal_reg_dump_stack { ++ int32_t stack_pointer; ++ struct dal_reg_dump_stack_location ++ stack_locations[DAL_REG_DUMP_STACK_MAX_SIZE]; ++ uint32_t total_rw_count; /* Total count for *all* functions. */ ++}; ++ ++static struct dal_reg_dump_stack reg_dump_stack = {0}; ++ ++/****************************************************************************** ++ * Private functions ++ *****************************************************************************/ ++ ++/* Check if current process is the one which requested register dump. ++ * The reason for the check: ++ * mmCRTC_STATUS_FRAME_COUNT is accessed by dal_controller_get_vblank_counter(). ++ * Which runs all the time when at least one display is connected. ++ * (Triggered by drm_mode_page_flip_ioctl()). */ ++static bool is_reg_dump_process(void) ++{ ++ uint32_t i; ++ ++ /* walk the list of our processes */ ++ for (i = 0; i < reg_dump_stack.stack_pointer; i++) { ++ struct dal_reg_dump_stack_location *stack_location ++ = ®_dump_stack.stack_locations[i]; ++ ++ if (stack_location->current_pid == dal_get_pid() ++ && stack_location->current_tgid == dal_get_tgid()) ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool dal_reg_dump_stack_is_empty(void) ++{ ++ if (reg_dump_stack.stack_pointer <= 0) ++ return true; ++ else ++ return false; ++} ++ ++static struct dal_reg_dump_stack_location *dal_reg_dump_stack_push(void) ++{ ++ struct dal_reg_dump_stack_location *current_location = NULL; ++ ++ if (reg_dump_stack.stack_pointer >= DAL_REG_DUMP_STACK_MAX_SIZE) { ++ /* stack is full */ ++ dal_output_to_console("[REG_DUMP]: %s: stack is full!\n", ++ __func__); ++ } else { ++ current_location = ++ ®_dump_stack.stack_locations[reg_dump_stack.stack_pointer]; ++ ++reg_dump_stack.stack_pointer; ++ } ++ ++ return current_location; ++} ++ ++static struct dal_reg_dump_stack_location *dal_reg_dump_stack_pop(void) ++{ ++ struct dal_reg_dump_stack_location *current_location = NULL; ++ ++ if (dal_reg_dump_stack_is_empty()) { ++ /* stack is empty */ ++ dal_output_to_console("[REG_DUMP]: %s: stack is empty!\n", ++ __func__); ++ } else { ++ --reg_dump_stack.stack_pointer; ++ current_location = ++ ®_dump_stack.stack_locations[reg_dump_stack.stack_pointer]; ++ } ++ ++ return current_location; ++} ++ ++/****************************************************************************** ++ * Public functions ++ *****************************************************************************/ ++ ++void dal_reg_logger_push(const char *caller_func) ++{ ++ struct dal_reg_dump_stack_location *free_stack_location; ++ ++ free_stack_location = dal_reg_dump_stack_push(); ++ ++ if (NULL == free_stack_location) ++ return; ++ ++ dc_service_memset(free_stack_location, 0, sizeof(*free_stack_location)); ++ ++ free_stack_location->current_caller_func = caller_func; ++ free_stack_location->current_pid = dal_get_pid(); ++ free_stack_location->current_tgid = dal_get_tgid(); ++ ++ dal_output_to_console("[REG_DUMP]:%s - start (pid:%ld, tgid:%ld)\n", ++ caller_func, ++ free_stack_location->current_pid, ++ free_stack_location->current_tgid); ++} ++ ++void dal_reg_logger_pop(void) ++{ ++ struct dal_reg_dump_stack_location *top_stack_location; ++ ++ top_stack_location = dal_reg_dump_stack_pop(); ++ ++ if (NULL == top_stack_location) { ++ dal_output_to_console("[REG_DUMP]:%s - Stack is Empty!\n", ++ __func__); ++ return; ++ } ++ ++ dal_output_to_console( ++ "[REG_DUMP]:%s - end."\ ++ " Reg R/W Count: Total=%d Function=%d. (pid:%ld, tgid:%ld)\n", ++ top_stack_location->current_caller_func, ++ reg_dump_stack.total_rw_count, ++ top_stack_location->rw_count, ++ dal_get_pid(), ++ dal_get_tgid()); ++ ++ dc_service_memset(top_stack_location, 0, sizeof(*top_stack_location)); ++} ++ ++void dal_reg_logger_rw_count_increment(void) ++{ ++ ++reg_dump_stack.total_rw_count; ++ ++ ++reg_dump_stack.stack_locations ++ [reg_dump_stack.stack_pointer - 1].rw_count; ++} ++ ++bool dal_reg_logger_should_dump_register(void) ++{ ++ if (true == dal_reg_dump_stack_is_empty()) ++ return false; ++ ++ if (false == is_reg_dump_process()) ++ return false; ++ ++ return true; ++} ++ ++/****************************************************************************** ++ * End of File. ++ *****************************************************************************/ +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/signal_types.c b/drivers/gpu/drm/amd/dal/dc/basics/signal_types.c +new file mode 100644 +index 0000000..f589091 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/signal_types.c +@@ -0,0 +1,116 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dc_services.h" ++#include "include/signal_types.h" ++ ++bool dc_is_hdmi_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_HDMI_TYPE_A); ++} ++ ++bool dc_is_dp_sst_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_DISPLAY_PORT || ++ signal == SIGNAL_TYPE_EDP); ++} ++ ++bool dc_is_dp_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_DISPLAY_PORT || ++ signal == SIGNAL_TYPE_EDP || ++ signal == SIGNAL_TYPE_DISPLAY_PORT_MST); ++} ++ ++bool dc_is_dp_external_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_DISPLAY_PORT || ++ signal == SIGNAL_TYPE_DISPLAY_PORT_MST); ++} ++ ++bool dc_is_analog_signal(enum signal_type signal) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_RGB: ++ return true; ++ break; ++ default: ++ return false; ++ } ++} ++ ++bool dc_is_embedded_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_EDP || signal == SIGNAL_TYPE_LVDS); ++} ++ ++bool dc_is_dvi_signal(enum signal_type signal) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ return true; ++ break; ++ default: ++ return false; ++ } ++} ++ ++bool dc_is_dvi_single_link_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_DVI_SINGLE_LINK); ++} ++ ++bool dc_is_dual_link_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_DVI_DUAL_LINK); ++} ++ ++bool dc_is_audio_capable_signal(enum signal_type signal) ++{ ++ return (signal == SIGNAL_TYPE_DISPLAY_PORT || ++ signal == SIGNAL_TYPE_DISPLAY_PORT_MST || ++ dc_is_hdmi_signal(signal) || ++ signal == SIGNAL_TYPE_WIRELESS); ++} ++ ++/* ++ * @brief ++ * Returns whether the signal is compatible ++ * with other digital encoder signal types. ++ * This is true for DVI, LVDS, and HDMI signal types. ++ */ ++bool dc_is_digital_encoder_compatible_signal(enum signal_type signal) ++{ ++ switch (signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ case SIGNAL_TYPE_LVDS: ++ return true; ++ default: ++ return false; ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/basics/vector.c b/drivers/gpu/drm/amd/dal/dc/basics/vector.c +new file mode 100644 +index 0000000..2f932c0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/basics/vector.c +@@ -0,0 +1,309 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/vector.h" ++ ++bool dal_vector_construct( ++ struct vector *vector, ++ struct dc_context *ctx, ++ uint32_t capacity, ++ uint32_t struct_size) ++{ ++ vector->container = NULL; ++ ++ if (!struct_size || !capacity) { ++ /* Container must be non-zero size*/ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ vector->container = dc_service_alloc(ctx, struct_size * capacity); ++ if (vector->container == NULL) ++ return false; ++ vector->capacity = capacity; ++ vector->struct_size = struct_size; ++ vector->count = 0; ++ vector->ctx = ctx; ++ return true; ++} ++ ++bool dal_vector_presized_costruct( ++ struct vector *vector, ++ struct dc_context *ctx, ++ uint32_t count, ++ void *initial_value, ++ uint32_t struct_size) ++{ ++ uint32_t i; ++ ++ vector->container = NULL; ++ ++ if (!struct_size || !count) { ++ /* Container must be non-zero size*/ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ vector->container = dc_service_alloc(ctx, struct_size * count); ++ ++ if (vector->container == NULL) ++ return false; ++ ++ /* If caller didn't supply initial value then the default ++ * of all zeros is expected, which is exactly what dal_alloc() ++ * initialises the memory to. */ ++ if (NULL != initial_value) { ++ for (i = 0; i < count; ++i) ++ dc_service_memmove( ++ vector->container + i * struct_size, ++ initial_value, ++ struct_size); ++ } ++ ++ vector->capacity = count; ++ vector->struct_size = struct_size; ++ vector->count = count; ++ return true; ++} ++ ++struct vector *dal_vector_presized_create( ++ struct dc_context *ctx, ++ uint32_t size, ++ void *initial_value, ++ uint32_t struct_size) ++{ ++ struct vector *vector = dc_service_alloc(ctx, sizeof(struct vector)); ++ ++ if (vector == NULL) ++ return NULL; ++ ++ if (dal_vector_presized_costruct( ++ vector, ctx, size, initial_value, struct_size)) ++ return vector; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, vector); ++ return NULL; ++} ++ ++struct vector *dal_vector_create( ++ struct dc_context *ctx, ++ uint32_t capacity, ++ uint32_t struct_size) ++{ ++ struct vector *vector = dc_service_alloc(ctx, sizeof(struct vector)); ++ ++ if (vector == NULL) ++ return NULL; ++ ++ if (dal_vector_construct(vector, ctx, capacity, struct_size)) ++ return vector; ++ ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, vector); ++ return NULL; ++} ++ ++void dal_vector_destruct( ++ struct vector *vector) ++{ ++ if (vector->container != NULL) ++ dc_service_free(vector->ctx, vector->container); ++ vector->count = 0; ++ vector->capacity = 0; ++} ++ ++void dal_vector_destroy( ++ struct vector **vector) ++{ ++ if (vector == NULL || *vector == NULL) ++ return; ++ dal_vector_destruct(*vector); ++ dc_service_free((*vector)->ctx, *vector); ++ *vector = NULL; ++} ++ ++uint32_t dal_vector_get_count( ++ const struct vector *vector) ++{ ++ return vector->count; ++} ++ ++void *dal_vector_at_index( ++ const struct vector *vector, ++ uint32_t index) ++{ ++ if (vector->container == NULL || index >= vector->count) ++ return NULL; ++ return vector->container + (index * vector->struct_size); ++} ++ ++bool dal_vector_remove_at_index( ++ struct vector *vector, ++ uint32_t index) ++{ ++ if (index >= vector->count) ++ return false; ++ ++ if (index != vector->count - 1) ++ dc_service_memmove( ++ vector->container + (index * vector->struct_size), ++ vector->container + ((index + 1) * vector->struct_size), ++ (vector->count - index - 1) * vector->struct_size); ++ vector->count -= 1; ++ ++ return true; ++} ++ ++void dal_vector_set_at_index( ++ const struct vector *vector, ++ const void *what, ++ uint32_t index) ++{ ++ void *where = dal_vector_at_index(vector, index); ++ ++ if (!where) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ dc_service_memmove( ++ where, ++ what, ++ vector->struct_size); ++} ++ ++static inline uint32_t calc_increased_capacity( ++ uint32_t old_capacity) ++{ ++ return old_capacity * 2; ++} ++ ++bool dal_vector_insert_at( ++ struct vector *vector, ++ const void *what, ++ uint32_t position) ++{ ++ uint8_t *insert_address; ++ ++ if (vector->count == vector->capacity) { ++ if (!dal_vector_reserve( ++ vector, ++ calc_increased_capacity(vector->capacity))) ++ return false; ++ } ++ ++ insert_address = vector->container + (vector->struct_size * position); ++ ++ if (vector->count && position < vector->count) ++ dc_service_memmove( ++ insert_address + vector->struct_size, ++ insert_address, ++ vector->struct_size * (vector->count - position)); ++ ++ dc_service_memmove( ++ insert_address, ++ what, ++ vector->struct_size); ++ ++ vector->count++; ++ ++ return true; ++} ++ ++bool dal_vector_append( ++ struct vector *vector, ++ const void *item) ++{ ++ return dal_vector_insert_at(vector, item, vector->count); ++} ++ ++struct vector *dal_vector_clone( ++ const struct vector *vector) ++{ ++ struct vector *vec_cloned; ++ uint32_t count; ++ ++ /* create new vector */ ++ count = dal_vector_get_count(vector); ++ ++ if (count == 0) ++ /* when count is 0 we still want to create clone of the vector ++ */ ++ vec_cloned = dal_vector_create( ++ vector->ctx, ++ vector->capacity, ++ vector->struct_size); ++ else ++ /* Call "presized create" version, independently of how the ++ * original vector was created. ++ * The owner of original vector must know how to treat the new ++ * vector - as "presized" or as "regular". ++ * But from vector point of view it doesn't matter. */ ++ vec_cloned = dal_vector_presized_create(vector->ctx, count, ++ NULL,/* no initial value */ ++ vector->struct_size); ++ ++ if (NULL == vec_cloned) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ /* copy vector's data */ ++ dc_service_memmove(vec_cloned->container, vector->container, ++ vec_cloned->struct_size * vec_cloned->capacity); ++ ++ return vec_cloned; ++} ++ ++uint32_t dal_vector_capacity(const struct vector *vector) ++{ ++ return vector->capacity; ++} ++ ++bool dal_vector_reserve(struct vector *vector, uint32_t capacity) ++{ ++ void *new_container; ++ ++ if (capacity <= vector->capacity) ++ return true; ++ ++ new_container = dc_service_realloc(vector->ctx, vector->container, ++ capacity * vector->struct_size); ++ ++ if (new_container) { ++ vector->container = new_container; ++ vector->capacity = capacity; ++ return true; ++ } ++ ++ return false; ++} ++ ++void dal_vector_clear(struct vector *vector) ++{ ++ vector->count = 0; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/Makefile b/drivers/gpu/drm/amd/dal/dc/bios/Makefile +new file mode 100644 +index 0000000..75bb892 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/Makefile +@@ -0,0 +1,27 @@ ++# ++# Makefile for the 'bios' sub-component of DAL. ++# It provides the parsing and executing controls for atom bios image. ++ ++BIOS = bios_parser.o bios_parser_helper.o command_table.o command_table_helper.o ++ ++AMD_DAL_BIOS = $(addprefix $(AMDDALPATH)/dc/bios/,$(BIOS)) ++ ++AMD_DAL_FILES += $(AMD_DAL_BIOS) ++ ++ifndef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++AMD_DAL_FILES := $(filter-out $(AMDDALPATH)/dc/bios/bios_parser_helper.o,$(AMD_DAL_FILES)) ++endif ++$(warning AMD_DAL_FILES=$(AMD_DAL_FILES)) ++ ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++ ++ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce110/bios_parser_helper_dce110.o ++endif ++ ++AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce110/command_table_helper_dce110.o ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser.c +new file mode 100644 +index 0000000..7a2b247 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser.c +@@ -0,0 +1,4758 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "atom.h" ++ ++#include "include/adapter_service_interface.h" ++#include "include/grph_object_ctrl_defs.h" ++#include "include/bios_parser_interface.h" ++#include "include/i2caux_interface.h" ++#include "include/logger_interface.h" ++ ++#include "command_table.h" ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++#include "bios_parser_helper.h" ++#endif ++#include "command_table_helper.h" ++#include "bios_parser.h" ++ ++#define THREE_PERCENT_OF_10000 300 ++ ++#define LAST_RECORD_TYPE 0xff ++ ++/* GUID to validate external display connection info table (aka OPM module) */ ++static const uint8_t ext_display_connection_guid[NUMBER_OF_UCHAR_FOR_GUID] = { ++ 0x91, 0x6E, 0x57, 0x09, ++ 0x3F, 0x6D, 0xD2, 0x11, ++ 0x39, 0x8E, 0x00, 0xA0, ++ 0xC9, 0x69, 0x72, 0x3B}; ++ ++#define GET_IMAGE(type, offset) ((type *) get_image(bp, offset, sizeof(type))) ++#define DATA_TABLES(table) (bp->master_data_tbl->ListOfDataTables.table) ++ ++static uint8_t *get_image(struct bios_parser *bp, uint32_t offset, ++ uint32_t size); ++static uint32_t get_record_size(uint8_t *record); ++static uint32_t get_edid_size(const ATOM_FAKE_EDID_PATCH_RECORD *edid); ++static enum object_type object_type_from_bios_object_id( ++ uint32_t bios_object_id); ++static struct graphics_object_id object_id_from_bios_object_id( ++ uint32_t bios_object_id); ++static enum object_enum_id enum_id_from_bios_object_id(uint32_t bios_object_id); ++static enum encoder_id encoder_id_from_bios_object_id(uint32_t bios_object_id); ++static enum connector_id connector_id_from_bios_object_id( ++ uint32_t bios_object_id); ++static uint32_t id_from_bios_object_id(enum object_type type, ++ uint32_t bios_object_id); ++static uint32_t gpu_id_from_bios_object_id(uint32_t bios_object_id); ++static enum generic_id generic_id_from_bios_object_id(uint32_t bios_object_id); ++static void get_atom_data_table_revision( ++ ATOM_COMMON_TABLE_HEADER *atom_data_tbl, ++ struct atom_data_revision *tbl_revision); ++static uint32_t get_dst_number_from_object(struct bios_parser *bp, ++ ATOM_OBJECT *object); ++static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, ++ uint16_t **id_list); ++static uint32_t get_dest_obj_list(struct bios_parser *bp, ++ ATOM_OBJECT *object, uint16_t **id_list); ++static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, ++ struct graphics_object_id id); ++static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, ++ ATOM_I2C_RECORD *record, ++ struct graphics_object_i2c_info *info); ++static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, ++ ATOM_OBJECT *object); ++static struct device_id device_type_from_device_id(uint16_t device_id); ++static uint32_t signal_to_ss_id(enum as_signal_type signal); ++static uint32_t get_support_mask_for_device_id(struct device_id device_id); ++static ATOM_ENCODER_CAP_RECORD *get_encoder_cap_record( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object); ++static void process_ext_display_connection_info(struct bios_parser *bp); ++ ++#define BIOS_IMAGE_SIZE_OFFSET 2 ++#define BIOS_IMAGE_SIZE_UNIT 512 ++ ++static bool bios_parser_construct( ++ struct bios_parser *bp, ++ struct bp_init_data *init, ++ struct adapter_service *as) ++{ ++ uint16_t *rom_header_offset = NULL; ++ ATOM_ROM_HEADER *rom_header = NULL; ++ ATOM_OBJECT_HEADER *object_info_tbl; ++ enum dce_version dce_version; ++ ++ if (!as) ++ return false; ++ ++ if (!init) ++ return false; ++ ++ if (!init->bios) ++ return false; ++ ++ dce_version = dal_adapter_service_get_dce_version(as); ++ bp->ctx = init->ctx; ++ bp->as = as; ++ bp->bios = init->bios; ++ bp->bios_size = bp->bios[BIOS_IMAGE_SIZE_OFFSET] * BIOS_IMAGE_SIZE_UNIT; ++ bp->bios_local_image = NULL; ++ bp->lcd_scale = LCD_SCALE_UNKNOWN; ++ ++ rom_header_offset = ++ GET_IMAGE(uint16_t, OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER); ++ ++ if (!rom_header_offset) ++ return false; ++ ++ rom_header = GET_IMAGE(ATOM_ROM_HEADER, *rom_header_offset); ++ ++ if (!rom_header) ++ return false; ++ ++ bp->master_data_tbl = ++ GET_IMAGE(ATOM_MASTER_DATA_TABLE, ++ rom_header->usMasterDataTableOffset); ++ ++ if (!bp->master_data_tbl) ++ return false; ++ ++ bp->object_info_tbl_offset = DATA_TABLES(Object_Header); ++ ++ if (!bp->object_info_tbl_offset) ++ return false; ++ ++ object_info_tbl = ++ GET_IMAGE(ATOM_OBJECT_HEADER, bp->object_info_tbl_offset); ++ ++ if (!object_info_tbl) ++ return false; ++ ++ get_atom_data_table_revision(&object_info_tbl->sHeader, ++ &bp->object_info_tbl.revision); ++ ++ if (bp->object_info_tbl.revision.major == 1 ++ && bp->object_info_tbl.revision.minor >= 3) { ++ ATOM_OBJECT_HEADER_V3 *tbl_v3; ++ ++ tbl_v3 = GET_IMAGE(ATOM_OBJECT_HEADER_V3, ++ bp->object_info_tbl_offset); ++ if (!tbl_v3) ++ return false; ++ ++ bp->object_info_tbl.v1_3 = tbl_v3; ++ } else if (bp->object_info_tbl.revision.major == 1 ++ && bp->object_info_tbl.revision.minor >= 1) ++ bp->object_info_tbl.v1_1 = object_info_tbl; ++ else ++ return false; ++ ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ bp->vbios_helper_data.active = 0; ++ bp->vbios_helper_data.requested = 0; ++ dal_bios_parser_init_bios_helper(bp, dce_version); ++#endif ++ dal_bios_parser_init_cmd_tbl(bp); ++ dal_bios_parser_init_cmd_tbl_helper(&bp->cmd_helper, dce_version); ++ ++ return true; ++} ++ ++struct bios_parser *dal_bios_parser_create( ++ struct bp_init_data *init, struct adapter_service *as) ++{ ++ struct bios_parser *bp = NULL; ++ ++ bp = dc_service_alloc(init->ctx, sizeof(struct bios_parser)); ++ if (!bp) ++ return NULL; ++ ++ if (bios_parser_construct(bp, init, as)) ++ return bp; ++ ++ dc_service_free(init->ctx, bp); ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++} ++ ++static void destruct(struct bios_parser *bp) ++{ ++ if (bp->bios_local_image) ++ dc_service_free(bp->ctx, bp->bios_local_image); ++} ++ ++void dal_bios_parser_destroy(struct bios_parser **bp) ++{ ++ if (!bp || !*bp) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ destruct(*bp); ++ ++ dc_service_free((*bp)->ctx, *bp); ++ *bp = NULL; ++} ++ ++void dal_bios_parser_power_down(struct bios_parser *bp) ++{ ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ dal_bios_parser_set_scratch_lcd_scale(bp, bp->lcd_scale); ++#endif ++} ++ ++void dal_bios_parser_power_up(struct bios_parser *bp) ++{ ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ if (bp->lcd_scale == LCD_SCALE_UNKNOWN) ++ bp->lcd_scale = dal_bios_parser_get_scratch_lcd_scale(bp); ++#endif ++} ++ ++static uint8_t get_number_of_objects(struct bios_parser *bp, uint32_t offset) ++{ ++ ATOM_OBJECT_TABLE *table; ++ ++ uint32_t object_table_offset = bp->object_info_tbl_offset + offset; ++ ++ table = GET_IMAGE(ATOM_OBJECT_TABLE, object_table_offset); ++ ++ if (!table) ++ return 0; ++ else ++ return table->ucNumberOfObjects; ++} ++ ++uint8_t dal_bios_parser_get_encoders_number(struct bios_parser *bp) ++{ ++ return get_number_of_objects(bp, ++ le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset)); ++} ++ ++uint8_t dal_bios_parser_get_connectors_number(struct bios_parser *bp) ++{ ++ return get_number_of_objects(bp, ++ le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset)); ++} ++ ++uint32_t dal_bios_parser_get_oem_ddc_lines_number(struct bios_parser *bp) ++{ ++ uint32_t number = 0; ++ ++ if (DATA_TABLES(OemInfo) != 0) { ++ ATOM_OEM_INFO *info; ++ ++ info = GET_IMAGE(ATOM_OEM_INFO, ++ DATA_TABLES(OemInfo)); ++ ++ if (le16_to_cpu(info->sHeader.usStructureSize) ++ > sizeof(ATOM_COMMON_TABLE_HEADER)) { ++ ++ number = (le16_to_cpu(info->sHeader.usStructureSize) ++ - sizeof(ATOM_COMMON_TABLE_HEADER)) ++ / sizeof(ATOM_I2C_ID_CONFIG_ACCESS); ++ ++ } ++ } ++ ++ return number; ++} ++ ++struct graphics_object_id dal_bios_parser_get_encoder_id(struct bios_parser *bp, ++ uint32_t i) ++{ ++ struct graphics_object_id object_id = dal_graphics_object_id_init( ++ 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); ++ ++ uint32_t encoder_table_offset = bp->object_info_tbl_offset ++ + le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); ++ ++ ATOM_OBJECT_TABLE *tbl = ++ GET_IMAGE(ATOM_OBJECT_TABLE, encoder_table_offset); ++ ++ if (tbl && tbl->ucNumberOfObjects > i) { ++ const uint16_t id = le16_to_cpu(tbl->asObjects[i].usObjectID); ++ ++ object_id = object_id_from_bios_object_id(id); ++ } ++ ++ return object_id; ++} ++ ++struct graphics_object_id dal_bios_parser_get_connector_id( ++ struct bios_parser *bp, ++ uint8_t i) ++{ ++ struct graphics_object_id object_id = dal_graphics_object_id_init( ++ 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); ++ ++ uint32_t connector_table_offset = bp->object_info_tbl_offset ++ + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); ++ ++ ATOM_OBJECT_TABLE *tbl = ++ GET_IMAGE(ATOM_OBJECT_TABLE, connector_table_offset); ++ ++ if (tbl && tbl->ucNumberOfObjects > i) { ++ const uint16_t id = le16_to_cpu(tbl->asObjects[i].usObjectID); ++ ++ object_id = object_id_from_bios_object_id(id); ++ } ++ ++ return object_id; ++} ++ ++uint32_t dal_bios_parser_get_src_number(struct bios_parser *bp, ++ struct graphics_object_id id) ++{ ++ uint32_t offset; ++ uint8_t *number; ++ ATOM_OBJECT *object; ++ ++ object = get_bios_object(bp, id); ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */ ++ return 0; ++ } ++ ++ offset = le16_to_cpu(object->usSrcDstTableOffset) ++ + bp->object_info_tbl_offset; ++ ++ number = GET_IMAGE(uint8_t, offset); ++ if (!number) ++ return 0; ++ ++ return *number; ++} ++ ++uint32_t dal_bios_parser_get_dst_number(struct bios_parser *bp, ++ struct graphics_object_id id) ++{ ++ ATOM_OBJECT *object = get_bios_object(bp, id); ++ ++ return get_dst_number_from_object(bp, object); ++} ++ ++enum bp_result dal_bios_parser_get_src_obj(struct bios_parser *bp, ++ struct graphics_object_id object_id, uint32_t index, ++ struct graphics_object_id *src_object_id) ++{ ++ uint32_t number; ++ uint16_t *id; ++ ATOM_OBJECT *object; ++ ++ if (!src_object_id) ++ return BP_RESULT_BADINPUT; ++ ++ object = get_bios_object(bp, object_id); ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */ ++ return BP_RESULT_BADINPUT; ++ } ++ ++ number = get_src_obj_list(bp, object, &id); ++ ++ if (number <= index) ++ return BP_RESULT_BADINPUT; ++ ++ *src_object_id = object_id_from_bios_object_id(id[index]); ++ ++ return BP_RESULT_OK; ++} ++ ++enum bp_result dal_bios_parser_get_dst_obj(struct bios_parser *bp, ++ struct graphics_object_id object_id, uint32_t index, ++ struct graphics_object_id *dest_object_id) ++{ ++ uint32_t number; ++ uint16_t *id; ++ ATOM_OBJECT *object; ++ ++ if (!dest_object_id) ++ return BP_RESULT_BADINPUT; ++ ++ object = get_bios_object(bp, object_id); ++ ++ number = get_dest_obj_list(bp, object, &id); ++ ++ if (number <= index) ++ return BP_RESULT_BADINPUT; ++ ++ *dest_object_id = object_id_from_bios_object_id(id[index]); ++ ++ return BP_RESULT_OK; ++} ++ ++enum bp_result dal_bios_parser_get_oem_ddc_info(struct bios_parser *bp, ++ uint32_t index, ++ struct graphics_object_i2c_info *info) ++{ ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ if (DATA_TABLES(OemInfo) != 0) { ++ ATOM_OEM_INFO *tbl; ++ ++ tbl = GET_IMAGE(ATOM_OEM_INFO, DATA_TABLES(OemInfo)); ++ ++ if (le16_to_cpu(tbl->sHeader.usStructureSize) ++ > sizeof(ATOM_COMMON_TABLE_HEADER)) { ++ ATOM_I2C_RECORD record; ++ ATOM_I2C_ID_CONFIG_ACCESS *config; ++ ++ dc_service_memset(&record, 0, sizeof(record)); ++ ++ config = &tbl->sucI2cId + index - 1; ++ ++ record.sucI2cId.bfHW_Capable = ++ config->sbfAccess.bfHW_Capable; ++ record.sucI2cId.bfI2C_LineMux = ++ config->sbfAccess.bfI2C_LineMux; ++ record.sucI2cId.bfHW_EngineID = ++ config->sbfAccess.bfHW_EngineID; ++ ++ return get_gpio_i2c_info(bp, &record, info); ++ } ++ } ++ ++ return BP_RESULT_NORECORD; ++} ++ ++enum bp_result dal_bios_parser_get_i2c_info(struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct graphics_object_i2c_info *info) ++{ ++ uint32_t offset; ++ ATOM_OBJECT *object; ++ ATOM_COMMON_RECORD_HEADER *header; ++ ATOM_I2C_RECORD *record; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ object = get_bios_object(bp, id); ++ ++ if (!object) ++ return BP_RESULT_BADINPUT; ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ !header->ucRecordSize) ++ break; ++ ++ if (ATOM_I2C_RECORD_TYPE == header->ucRecordType ++ && sizeof(ATOM_I2C_RECORD) <= header->ucRecordSize) { ++ /* get the I2C info */ ++ record = (ATOM_I2C_RECORD *) header; ++ ++ if (get_gpio_i2c_info(bp, record, info) == BP_RESULT_OK) ++ return BP_RESULT_OK; ++ } ++ ++ offset += header->ucRecordSize; ++ } ++ ++ return BP_RESULT_NORECORD; ++} ++ ++static enum bp_result get_voltage_ddc_info_v1(uint8_t *i2c_line, ++ ATOM_COMMON_TABLE_HEADER *header, ++ uint8_t *address) ++{ ++ enum bp_result result = BP_RESULT_NORECORD; ++ ATOM_VOLTAGE_OBJECT_INFO *info = ++ (ATOM_VOLTAGE_OBJECT_INFO *) address; ++ ++ uint8_t *voltage_current_object = (uint8_t *) &info->asVoltageObj[0]; ++ ++ while ((address + le16_to_cpu(header->usStructureSize)) > voltage_current_object) { ++ ATOM_VOLTAGE_OBJECT *object = ++ (ATOM_VOLTAGE_OBJECT *) voltage_current_object; ++ ++ if ((object->ucVoltageType == SET_VOLTAGE_INIT_MODE) && ++ (object->ucVoltageType & ++ VOLTAGE_CONTROLLED_BY_I2C_MASK)) { ++ ++ *i2c_line = object->asControl.ucVoltageControlI2cLine ++ ^ 0x90; ++ result = BP_RESULT_OK; ++ break; ++ } ++ ++ voltage_current_object += object->ucSize; ++ } ++ return result; ++} ++ ++static enum bp_result get_voltage_ddc_info_v3(uint8_t *i2c_line, ++ uint32_t index, ++ ATOM_COMMON_TABLE_HEADER *header, ++ uint8_t *address) ++{ ++ enum bp_result result = BP_RESULT_NORECORD; ++ ATOM_VOLTAGE_OBJECT_INFO_V3_1 *info = ++ (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *) address; ++ ++ uint8_t *voltage_current_object = ++ (uint8_t *) (&(info->asVoltageObj[0])); ++ ++ while ((address + le16_to_cpu(header->usStructureSize)) > voltage_current_object) { ++ ATOM_I2C_VOLTAGE_OBJECT_V3 *object = ++ (ATOM_I2C_VOLTAGE_OBJECT_V3 *) voltage_current_object; ++ ++ if (object->sHeader.ucVoltageMode == ++ ATOM_INIT_VOLTAGE_REGULATOR) { ++ if (object->sHeader.ucVoltageType == index) { ++ *i2c_line = object->ucVoltageControlI2cLine ++ ^ 0x90; ++ result = BP_RESULT_OK; ++ break; ++ } ++ } ++ ++ voltage_current_object += le16_to_cpu(object->sHeader.usSize); ++ } ++ return result; ++} ++ ++enum bp_result dal_bios_parser_get_voltage_ddc_info(struct bios_parser *bp, ++ uint32_t index, ++ struct graphics_object_i2c_info *info) ++{ ++ uint8_t i2c_line = 0; ++ enum bp_result result = BP_RESULT_NORECORD; ++ uint8_t *voltage_info_address; ++ ATOM_COMMON_TABLE_HEADER *header; ++ struct atom_data_revision revision = {0}; ++ ++ if (!DATA_TABLES(VoltageObjectInfo)) ++ return result; ++ ++ voltage_info_address = get_image(bp, ++ DATA_TABLES(VoltageObjectInfo), ++ sizeof(ATOM_COMMON_TABLE_HEADER)); ++ ++ header = (ATOM_COMMON_TABLE_HEADER *) voltage_info_address; ++ ++ get_atom_data_table_revision(header, &revision); ++ ++ switch (revision.major) { ++ case 1: ++ case 2: ++ result = get_voltage_ddc_info_v1(&i2c_line, header, ++ voltage_info_address); ++ break; ++ case 3: ++ if (revision.minor != 1) ++ break; ++ result = get_voltage_ddc_info_v3(&i2c_line, index, header, ++ voltage_info_address); ++ break; ++ } ++ ++ if (result == BP_RESULT_OK) ++ result = dal_bios_parser_get_thermal_ddc_info(bp, ++ i2c_line, info); ++ ++ ++ return result; ++} ++ ++enum bp_result dal_bios_parser_get_thermal_ddc_info( ++ struct bios_parser *bp, ++ uint32_t i2c_channel_id, ++ struct graphics_object_i2c_info *info) ++{ ++ ATOM_I2C_ID_CONFIG_ACCESS *config; ++ ATOM_I2C_RECORD record; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ config = (ATOM_I2C_ID_CONFIG_ACCESS *) &i2c_channel_id; ++ ++ record.sucI2cId.bfHW_Capable = config->sbfAccess.bfHW_Capable; ++ record.sucI2cId.bfI2C_LineMux = config->sbfAccess.bfI2C_LineMux; ++ record.sucI2cId.bfHW_EngineID = config->sbfAccess.bfHW_EngineID; ++ ++ return get_gpio_i2c_info(bp, &record, info); ++} ++ ++enum bp_result dal_bios_parser_get_ddc_info_for_i2c_line(struct bios_parser *bp, ++ uint8_t i2c_line, struct graphics_object_i2c_info *info) ++{ ++ uint32_t offset; ++ ATOM_OBJECT *object; ++ ATOM_OBJECT_TABLE *table; ++ uint32_t i; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ offset = le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); ++ ++ offset += bp->object_info_tbl_offset; ++ ++ table = GET_IMAGE(ATOM_OBJECT_TABLE, offset); ++ ++ if (!table) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ for (i = 0; i < table->ucNumberOfObjects; i++) { ++ object = &table->asObjects[i]; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */ ++ return BP_RESULT_BADINPUT; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ ATOM_COMMON_RECORD_HEADER *header = ++ GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ offset += header->ucRecordSize; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ !header->ucRecordSize) ++ break; ++ ++ if (ATOM_I2C_RECORD_TYPE == header->ucRecordType ++ && sizeof(ATOM_I2C_RECORD) <= ++ header->ucRecordSize) { ++ ATOM_I2C_RECORD *record = ++ (ATOM_I2C_RECORD *) header; ++ ++ if (i2c_line != record->sucI2cId.bfI2C_LineMux) ++ continue; ++ ++ /* get the I2C info */ ++ if (get_gpio_i2c_info(bp, record, info) == ++ BP_RESULT_OK) ++ return BP_RESULT_OK; ++ } ++ } ++ } ++ ++ return BP_RESULT_NORECORD; ++} ++ ++enum bp_result dal_bios_parser_get_hpd_info(struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct graphics_object_hpd_info *info) ++{ ++ ATOM_OBJECT *object; ++ ATOM_HPD_INT_RECORD *record = NULL; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ object = get_bios_object(bp, id); ++ ++ if (!object) ++ return BP_RESULT_BADINPUT; ++ ++ record = get_hpd_record(bp, object); ++ ++ if (record != NULL) { ++ info->hpd_int_gpio_uid = record->ucHPDIntGPIOID; ++ info->hpd_active = record->ucPlugged_PinState; ++ return BP_RESULT_OK; ++ } ++ ++ return BP_RESULT_NORECORD; ++} ++ ++uint32_t dal_bios_parser_get_gpio_record( ++ struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct bp_gpio_cntl_info *gpio_record, ++ uint32_t record_size) ++{ ++ ATOM_COMMON_RECORD_HEADER *header = NULL; ++ ATOM_OBJECT_GPIO_CNTL_RECORD *record = NULL; ++ ATOM_OBJECT *object = get_bios_object(bp, id); ++ uint32_t offset; ++ uint32_t pins_number; ++ uint32_t i; ++ ++ if (!object) ++ return 0; ++ ++ /* Initialise offset */ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ /* Get record header */ ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ if (!header || header->ucRecordType == LAST_RECORD_TYPE || ++ !header->ucRecordSize) ++ break; ++ ++ /* If this is gpio control record - stop. We found the record */ ++ if (header->ucRecordType == ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE ++ && header->ucRecordSize ++ >= sizeof(ATOM_OBJECT_GPIO_CNTL_RECORD)) { ++ record = (ATOM_OBJECT_GPIO_CNTL_RECORD *) header; ++ break; ++ } ++ ++ /* Advance to next record */ ++ offset += header->ucRecordSize; ++ } ++ ++ /* If we did not find a record - return */ ++ if (!record) ++ return 0; ++ ++ /* Extract gpio IDs from bios record (make sure we do not exceed passed ++ * array size) */ ++ pins_number = (record->ucNumberOfPins < record_size ? ++ record->ucNumberOfPins : record_size); ++ for (i = 0; i < pins_number; i++) { ++ uint8_t output_state = ((record->asGpio[i].ucGPIO_PinState ++ & GPIO_PIN_OUTPUT_STATE_MASK) ++ >> GPIO_PIN_OUTPUT_STATE_SHIFT); ++ gpio_record[i].id = record->asGpio[i].ucGPIOID; ++ ++ switch (output_state) { ++ case GPIO_PIN_STATE_ACTIVE_LOW: ++ gpio_record[i].state = ++ GPIO_PIN_OUTPUT_STATE_ACTIVE_LOW; ++ break; ++ ++ case GPIO_PIN_STATE_ACTIVE_HIGH: ++ gpio_record[i].state = ++ GPIO_PIN_OUTPUT_STATE_ACTIVE_HIGH; ++ break; ++ ++ default: ++ BREAK_TO_DEBUGGER(); /* Invalid Pin Output State */ ++ break; ++ } ++ } ++ ++ return pins_number; ++} ++ ++enum bp_result dal_bios_parser_get_device_tag_record( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object, ++ ATOM_CONNECTOR_DEVICE_TAG_RECORD **record) ++{ ++ ATOM_COMMON_RECORD_HEADER *header; ++ uint32_t offset; ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ offset += header->ucRecordSize; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ !header->ucRecordSize) ++ break; ++ ++ if (ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE != ++ header->ucRecordType) ++ continue; ++ ++ if (sizeof(ATOM_CONNECTOR_DEVICE_TAG) > header->ucRecordSize) ++ continue; ++ ++ *record = (ATOM_CONNECTOR_DEVICE_TAG_RECORD *) header; ++ return BP_RESULT_OK; ++ } ++ ++ return BP_RESULT_NORECORD; ++} ++ ++enum bp_result dal_bios_parser_get_device_tag( ++ struct bios_parser *bp, ++ struct graphics_object_id connector_object_id, ++ uint32_t device_tag_index, ++ struct connector_device_tag_info *info) ++{ ++ ATOM_OBJECT *object; ++ ATOM_CONNECTOR_DEVICE_TAG_RECORD *record = NULL; ++ ATOM_CONNECTOR_DEVICE_TAG *device_tag; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ /* getBiosObject will return MXM object */ ++ object = get_bios_object(bp, connector_object_id); ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */ ++ return BP_RESULT_BADINPUT; ++ } ++ ++ if (dal_bios_parser_get_device_tag_record(bp, object, &record) ++ != BP_RESULT_OK) ++ return BP_RESULT_NORECORD; ++ ++ if (device_tag_index >= record->ucNumberOfDevice) ++ return BP_RESULT_NORECORD; ++ ++ device_tag = &record->asDeviceTag[device_tag_index]; ++ ++ info->acpi_device = le32_to_cpu(device_tag->ulACPIDeviceEnum); ++ info->dev_id = ++ device_type_from_device_id(le16_to_cpu(device_tag->usDeviceID)); ++ ++ return BP_RESULT_OK; ++} ++ ++static enum bp_result get_firmware_info_v1_4( ++ struct bios_parser *bp, ++ struct firmware_info *info); ++static enum bp_result get_firmware_info_v2_1( ++ struct bios_parser *bp, ++ struct firmware_info *info); ++static enum bp_result get_firmware_info_v2_2( ++ struct bios_parser *bp, ++ struct firmware_info *info); ++ ++enum bp_result dal_bios_parser_get_firmware_info( ++ struct bios_parser *bp, ++ struct firmware_info *info) ++{ ++ enum bp_result result = BP_RESULT_BADBIOSTABLE; ++ ATOM_COMMON_TABLE_HEADER *header; ++ struct atom_data_revision revision; ++ ++ if (info && DATA_TABLES(FirmwareInfo)) { ++ header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, ++ DATA_TABLES(FirmwareInfo)); ++ get_atom_data_table_revision(header, &revision); ++ switch (revision.major) { ++ case 1: ++ switch (revision.minor) { ++ case 4: ++ result = get_firmware_info_v1_4(bp, info); ++ break; ++ default: ++ break; ++ } ++ break; ++ ++ case 2: ++ switch (revision.minor) { ++ case 1: ++ result = get_firmware_info_v2_1(bp, info); ++ break; ++ case 2: ++ result = get_firmware_info_v2_2(bp, info); ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return result; ++} ++ ++static enum bp_result get_firmware_info_v1_4( ++ struct bios_parser *bp, ++ struct firmware_info *info) ++{ ++ ATOM_FIRMWARE_INFO_V1_4 *firmware_info = ++ GET_IMAGE(ATOM_FIRMWARE_INFO_V1_4, ++ DATA_TABLES(FirmwareInfo)); ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ if (!firmware_info) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ dc_service_memset(info, 0, sizeof(*info)); ++ ++ /* Pixel clock pll information. We need to convert from 10KHz units into ++ * KHz units */ ++ info->pll_info.crystal_frequency = ++ le16_to_cpu(firmware_info->usReferenceClock) * 10; ++ info->pll_info.min_input_pxl_clk_pll_frequency = ++ le16_to_cpu(firmware_info->usMinPixelClockPLL_Input) * 10; ++ info->pll_info.max_input_pxl_clk_pll_frequency = ++ le16_to_cpu(firmware_info->usMaxPixelClockPLL_Input) * 10; ++ info->pll_info.min_output_pxl_clk_pll_frequency = ++ le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10; ++ info->pll_info.max_output_pxl_clk_pll_frequency = ++ le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10; ++ ++ if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) ++ /* Since there is no information on the SS, report conservative ++ * value 3% for bandwidth calculation */ ++ /* unit of 0.01% */ ++ info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; ++ ++ if (firmware_info->usFirmwareCapability.sbfAccess.EngineClockSS_Support) ++ /* Since there is no information on the SS,report conservative ++ * value 3% for bandwidth calculation */ ++ /* unit of 0.01% */ ++ info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; ++ ++ return BP_RESULT_OK; ++} ++ ++static enum bp_result get_ss_info_v3_1( ++ struct bios_parser *bp, ++ uint32_t id, ++ uint32_t index, ++ struct spread_spectrum_info *ss_info); ++ ++static enum bp_result get_firmware_info_v2_1( ++ struct bios_parser *bp, ++ struct firmware_info *info) ++{ ++ ATOM_FIRMWARE_INFO_V2_1 *firmwareInfo = ++ GET_IMAGE(ATOM_FIRMWARE_INFO_V2_1, DATA_TABLES(FirmwareInfo)); ++ struct spread_spectrum_info internalSS; ++ uint32_t index; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ if (!firmwareInfo) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ dc_service_memset(info, 0, sizeof(*info)); ++ ++ /* Pixel clock pll information. We need to convert from 10KHz units into ++ * KHz units */ ++ info->pll_info.crystal_frequency = ++ le16_to_cpu(firmwareInfo->usCoreReferenceClock) * 10; ++ info->pll_info.min_input_pxl_clk_pll_frequency = ++ le16_to_cpu(firmwareInfo->usMinPixelClockPLL_Input) * 10; ++ info->pll_info.max_input_pxl_clk_pll_frequency = ++ le16_to_cpu(firmwareInfo->usMaxPixelClockPLL_Input) * 10; ++ info->pll_info.min_output_pxl_clk_pll_frequency = ++ le32_to_cpu(firmwareInfo->ulMinPixelClockPLL_Output) * 10; ++ info->pll_info.max_output_pxl_clk_pll_frequency = ++ le32_to_cpu(firmwareInfo->ulMaxPixelClockPLL_Output) * 10; ++ info->default_display_engine_pll_frequency = ++ le32_to_cpu(firmwareInfo->ulDefaultDispEngineClkFreq) * 10; ++ info->external_clock_source_frequency_for_dp = ++ le16_to_cpu(firmwareInfo->usUniphyDPModeExtClkFreq) * 10; ++ info->min_allowed_bl_level = firmwareInfo->ucMinAllowedBL_Level; ++ ++ /* There should be only one entry in the SS info table for Memory Clock ++ */ ++ index = 0; ++ if (firmwareInfo->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) ++ /* Since there is no information for external SS, report ++ * conservative value 3% for bandwidth calculation */ ++ /* unit of 0.01% */ ++ info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; ++ else if (get_ss_info_v3_1(bp, ++ ASIC_INTERNAL_MEMORY_SS, index, &internalSS) == BP_RESULT_OK) { ++ if (internalSS.spread_spectrum_percentage) { ++ info->feature.memory_clk_ss_percentage = ++ internalSS.spread_spectrum_percentage; ++ if (internalSS.type.CENTER_MODE) { ++ /* if it is centermode, the exact SS Percentage ++ * will be round up of half of the percentage ++ * reported in the SS table */ ++ ++info->feature.memory_clk_ss_percentage; ++ info->feature.memory_clk_ss_percentage /= 2; ++ } ++ } ++ } ++ ++ /* There should be only one entry in the SS info table for Engine Clock ++ */ ++ index = 1; ++ if (firmwareInfo->usFirmwareCapability.sbfAccess.EngineClockSS_Support) ++ /* Since there is no information for external SS, report ++ * conservative value 3% for bandwidth calculation */ ++ /* unit of 0.01% */ ++ info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; ++ else if (get_ss_info_v3_1(bp, ++ ASIC_INTERNAL_ENGINE_SS, index, &internalSS) == BP_RESULT_OK) { ++ if (internalSS.spread_spectrum_percentage) { ++ info->feature.engine_clk_ss_percentage = ++ internalSS.spread_spectrum_percentage; ++ if (internalSS.type.CENTER_MODE) { ++ /* if it is centermode, the exact SS Percentage ++ * will be round up of half of the percentage ++ * reported in the SS table */ ++ ++info->feature.engine_clk_ss_percentage; ++ info->feature.engine_clk_ss_percentage /= 2; ++ } ++ } ++ } ++ ++ return BP_RESULT_OK; ++} ++ ++static enum bp_result get_firmware_info_v2_2( ++ struct bios_parser *bp, ++ struct firmware_info *info) ++{ ++ ATOM_FIRMWARE_INFO_V2_2 *firmware_info; ++ struct spread_spectrum_info internal_ss; ++ uint32_t index; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ firmware_info = GET_IMAGE(ATOM_FIRMWARE_INFO_V2_2, ++ DATA_TABLES(FirmwareInfo)); ++ ++ if (!firmware_info) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ dc_service_memset(info, 0, sizeof(*info)); ++ ++ /* Pixel clock pll information. We need to convert from 10KHz units into ++ * KHz units */ ++ info->pll_info.crystal_frequency = ++ le16_to_cpu(firmware_info->usCoreReferenceClock) * 10; ++ info->pll_info.min_input_pxl_clk_pll_frequency = ++ le16_to_cpu(firmware_info->usMinPixelClockPLL_Input) * 10; ++ info->pll_info.max_input_pxl_clk_pll_frequency = ++ le16_to_cpu(firmware_info->usMaxPixelClockPLL_Input) * 10; ++ info->pll_info.min_output_pxl_clk_pll_frequency = ++ le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10; ++ info->pll_info.max_output_pxl_clk_pll_frequency = ++ le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10; ++ info->default_display_engine_pll_frequency = ++ le32_to_cpu(firmware_info->ulDefaultDispEngineClkFreq) * 10; ++ info->external_clock_source_frequency_for_dp = ++ le16_to_cpu(firmware_info->usUniphyDPModeExtClkFreq) * 10; ++ ++ /* There should be only one entry in the SS info table for Memory Clock ++ */ ++ index = 0; ++ if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) ++ /* Since there is no information for external SS, report ++ * conservative value 3% for bandwidth calculation */ ++ /* unit of 0.01% */ ++ info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; ++ else if (get_ss_info_v3_1(bp, ++ ASIC_INTERNAL_MEMORY_SS, index, &internal_ss) == BP_RESULT_OK) { ++ if (internal_ss.spread_spectrum_percentage) { ++ info->feature.memory_clk_ss_percentage = ++ internal_ss.spread_spectrum_percentage; ++ if (internal_ss.type.CENTER_MODE) { ++ /* if it is centermode, the exact SS Percentage ++ * will be round up of half of the percentage ++ * reported in the SS table */ ++ ++info->feature.memory_clk_ss_percentage; ++ info->feature.memory_clk_ss_percentage /= 2; ++ } ++ } ++ } ++ ++ /* There should be only one entry in the SS info table for Engine Clock ++ */ ++ index = 1; ++ if (firmware_info->usFirmwareCapability.sbfAccess.EngineClockSS_Support) ++ /* Since there is no information for external SS, report ++ * conservative value 3% for bandwidth calculation */ ++ /* unit of 0.01% */ ++ info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; ++ else if (get_ss_info_v3_1(bp, ++ ASIC_INTERNAL_ENGINE_SS, index, &internal_ss) == BP_RESULT_OK) { ++ if (internal_ss.spread_spectrum_percentage) { ++ info->feature.engine_clk_ss_percentage = ++ internal_ss.spread_spectrum_percentage; ++ if (internal_ss.type.CENTER_MODE) { ++ /* if it is centermode, the exact SS Percentage ++ * will be round up of half of the percentage ++ * reported in the SS table */ ++ ++info->feature.engine_clk_ss_percentage; ++ info->feature.engine_clk_ss_percentage /= 2; ++ } ++ } ++ } ++ ++ /* Remote Display */ ++ info->remote_display_config = firmware_info->ucRemoteDisplayConfig; ++ ++ /* Is allowed minimum BL level */ ++ info->min_allowed_bl_level = firmware_info->ucMinAllowedBL_Level; ++ /* Used starting from CI */ ++ info->smu_gpu_pll_output_freq = ++ (uint32_t) (le32_to_cpu(firmware_info->ulGPUPLL_OutputFreq) * 10); ++ ++ return BP_RESULT_OK; ++} ++ ++static enum bp_result get_ss_info_v3_1( ++ struct bios_parser *bp, ++ uint32_t id, ++ uint32_t index, ++ struct spread_spectrum_info *ss_info) ++{ ++ ATOM_ASIC_INTERNAL_SS_INFO_V3 *ss_table_header_include; ++ ATOM_ASIC_SS_ASSIGNMENT_V3 *tbl; ++ uint32_t table_size; ++ uint32_t i; ++ uint32_t table_index = 0; ++ ++ if (!ss_info) ++ return BP_RESULT_BADINPUT; ++ ++ if (!DATA_TABLES(ASIC_InternalSS_Info)) ++ return BP_RESULT_UNSUPPORTED; ++ ++ ss_table_header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V3, ++ DATA_TABLES(ASIC_InternalSS_Info)); ++ table_size = ++ (le16_to_cpu(ss_table_header_include->sHeader.usStructureSize) ++ - sizeof(ATOM_COMMON_TABLE_HEADER)) ++ / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); ++ ++ tbl = (ATOM_ASIC_SS_ASSIGNMENT_V3 *) ++ &ss_table_header_include->asSpreadSpectrum[0]; ++ ++ dc_service_memset(ss_info, 0, sizeof(struct spread_spectrum_info)); ++ ++ for (i = 0; i < table_size; i++) { ++ if (tbl[i].ucClockIndication != (uint8_t) id) ++ continue; ++ ++ if (table_index != index) { ++ table_index++; ++ continue; ++ } ++ /* VBIOS introduced new defines for Version 3, same values as ++ * before, so now use these new ones for Version 3. ++ * Shouldn't affect field VBIOS's V3 as define values are still ++ * same. ++ * #define SS_MODE_V3_CENTRE_SPREAD_MASK 0x01 ++ * #define SS_MODE_V3_EXTERNAL_SS_MASK 0x02 ++ ++ * Old VBIOS defines: ++ * #define ATOM_SS_CENTRE_SPREAD_MODE_MASK 0x00000001 ++ * #define ATOM_EXTERNAL_SS_MASK 0x00000002 ++ */ ++ ++ if (SS_MODE_V3_EXTERNAL_SS_MASK & tbl[i].ucSpreadSpectrumMode) ++ ss_info->type.EXTERNAL = true; ++ ++ if (SS_MODE_V3_CENTRE_SPREAD_MASK & tbl[i].ucSpreadSpectrumMode) ++ ss_info->type.CENTER_MODE = true; ++ ++ /* Older VBIOS (in field) always provides SS percentage in 0.01% ++ * units set Divider to 100 */ ++ ss_info->spread_percentage_divider = 100; ++ ++ /* #define SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK 0x10 */ ++ if (SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK ++ & tbl[i].ucSpreadSpectrumMode) ++ ss_info->spread_percentage_divider = 1000; ++ ++ ss_info->type.STEP_AND_DELAY_INFO = false; ++ /* convert [10KHz] into [KHz] */ ++ ss_info->target_clock_range = ++ le32_to_cpu(tbl[i].ulTargetClockRange) * 10; ++ ss_info->spread_spectrum_percentage = ++ (uint32_t)le16_to_cpu(tbl[i].usSpreadSpectrumPercentage); ++ ss_info->spread_spectrum_range = ++ (uint32_t)(le16_to_cpu(tbl[i].usSpreadRateIn10Hz) * 10); ++ ++ return BP_RESULT_OK; ++ } ++ return BP_RESULT_NORECORD; ++} ++ ++enum bp_result dal_bios_parser_transmitter_control( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl) ++{ ++ if (!bp->cmd_tbl.transmitter_control) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.transmitter_control(bp, cntl); ++} ++ ++enum bp_result dal_bios_parser_encoder_control( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ if (!bp->cmd_tbl.dig_encoder_control) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.dig_encoder_control(bp, cntl); ++} ++ ++enum bp_result dal_bios_parser_adjust_pixel_clock( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params) ++{ ++ if (!bp->cmd_tbl.adjust_display_pll) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.adjust_display_pll(bp, bp_params); ++} ++ ++enum bp_result dal_bios_parser_set_pixel_clock( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ if (!bp->cmd_tbl.set_pixel_clock) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.set_pixel_clock(bp, bp_params); ++} ++ ++enum bp_result dal_bios_parser_enable_spread_spectrum_on_ppll( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable) ++{ ++ if (!bp->cmd_tbl.enable_spread_spectrum_on_ppll) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.enable_spread_spectrum_on_ppll( ++ bp, bp_params, enable); ++ ++} ++ ++enum bp_result dal_bios_parser_program_crtc_timing( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params) ++{ ++ if (!bp->cmd_tbl.set_crtc_timing) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.set_crtc_timing(bp, bp_params); ++} ++ ++enum bp_result dal_bios_parser_program_display_engine_pll( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ ++ if (!bp->cmd_tbl.program_clock) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.program_clock(bp, bp_params); ++ ++} ++ ++enum signal_type dal_bios_parser_dac_load_detect( ++ struct bios_parser *bp, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type display_signal) ++{ ++ if (!bp->cmd_tbl.dac_load_detection) ++ return SIGNAL_TYPE_NONE; ++ ++ return bp->cmd_tbl.dac_load_detection(bp, encoder, connector, ++ display_signal); ++} ++ ++enum bp_result dal_bios_parser_get_divider_for_target_display_clock( ++ struct bios_parser *bp, ++ struct bp_display_clock_parameters *bp_params) ++{ ++ if (!bp->cmd_tbl.compute_memore_engine_pll) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.compute_memore_engine_pll(bp, bp_params); ++} ++ ++enum bp_result dal_bios_parser_dvo_encoder_control( ++ struct bios_parser *bp, ++ struct bp_dvo_encoder_control *cntl) ++{ ++ if (!bp->cmd_tbl.dvo_encoder_control) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.dvo_encoder_control(bp, cntl); ++} ++ ++enum bp_result dal_bios_parser_enable_crtc( ++ struct bios_parser *bp, ++ enum controller_id id, ++ bool enable) ++{ ++ if (!bp->cmd_tbl.enable_crtc) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.enable_crtc(bp, id, enable); ++} ++ ++enum bp_result dal_bios_parser_blank_crtc( ++ struct bios_parser *bp, ++ struct bp_blank_crtc_parameters *bp_params, ++ bool blank) ++{ ++ if (!bp->cmd_tbl.blank_crtc) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.blank_crtc(bp, bp_params, blank); ++} ++ ++enum bp_result dal_bios_parser_crtc_source_select( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params) ++{ ++ if (!bp->cmd_tbl.select_crtc_source) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.select_crtc_source(bp, bp_params); ++} ++ ++enum bp_result dal_bios_parser_set_overscan( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_overscan_parameters *bp_params) ++{ ++ ++ if (!bp->cmd_tbl.set_crtc_overscan) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.set_crtc_overscan(bp, bp_params); ++} ++ ++enum bp_result dal_bios_parser_enable_memory_requests( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable) ++{ ++ if (!bp->cmd_tbl.enable_crtc_mem_req) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.enable_crtc_mem_req(bp, controller_id, enable); ++} ++ ++enum bp_result dal_bios_parser_external_encoder_control( ++ struct bios_parser *bp, ++ struct bp_external_encoder_control *cntl) ++{ ++ if (!bp->cmd_tbl.external_encoder_control) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.external_encoder_control(bp, cntl); ++} ++ ++enum bp_result dal_bios_parser_enable_disp_power_gating( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ enum bp_pipe_control_action action) ++{ ++ if (!bp->cmd_tbl.enable_disp_power_gating) ++ return BP_RESULT_FAILURE; ++ ++ return bp->cmd_tbl.enable_disp_power_gating(bp, controller_id, ++ action); ++} ++ ++bool dal_bios_parser_is_device_id_supported( ++ struct bios_parser *bp, ++ struct device_id id) ++{ ++ uint32_t mask = get_support_mask_for_device_id(id); ++ ++ return (le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport) & mask) != 0; ++} ++ ++enum bp_result dal_bios_parser_crt_control( ++ struct bios_parser *bp, ++ enum engine_id engine_id, ++ bool enable, ++ uint32_t pixel_clock) ++{ ++ uint8_t standard; ++ ++ if (!bp->cmd_tbl.dac1_encoder_control && ++ engine_id == ENGINE_ID_DACA) ++ return BP_RESULT_FAILURE; ++ if (!bp->cmd_tbl.dac2_encoder_control && ++ engine_id == ENGINE_ID_DACB) ++ return BP_RESULT_FAILURE; ++ /* validate params */ ++ switch (engine_id) { ++ case ENGINE_ID_DACA: ++ case ENGINE_ID_DACB: ++ break; ++ default: ++ /* unsupported engine */ ++ return BP_RESULT_FAILURE; ++ } ++ ++ standard = ATOM_DAC1_PS2; /* == ATOM_DAC2_PS2 */ ++ ++ if (enable) { ++ if (engine_id == ENGINE_ID_DACA) { ++ bp->cmd_tbl.dac1_encoder_control(bp, enable, ++ pixel_clock, standard); ++ if (bp->cmd_tbl.dac1_output_control != NULL) ++ bp->cmd_tbl.dac1_output_control(bp, enable); ++ } else { ++ bp->cmd_tbl.dac2_encoder_control(bp, enable, ++ pixel_clock, standard); ++ if (bp->cmd_tbl.dac2_output_control != NULL) ++ bp->cmd_tbl.dac2_output_control(bp, enable); ++ } ++ } else { ++ if (engine_id == ENGINE_ID_DACA) { ++ if (bp->cmd_tbl.dac1_output_control != NULL) ++ bp->cmd_tbl.dac1_output_control(bp, enable); ++ bp->cmd_tbl.dac1_encoder_control(bp, enable, ++ pixel_clock, standard); ++ } else { ++ if (bp->cmd_tbl.dac2_output_control != NULL) ++ bp->cmd_tbl.dac2_output_control(bp, enable); ++ bp->cmd_tbl.dac2_encoder_control(bp, enable, ++ pixel_clock, standard); ++ } ++ } ++ ++ return BP_RESULT_OK; ++} ++ ++static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, ++ ATOM_OBJECT *object) ++{ ++ ATOM_COMMON_RECORD_HEADER *header; ++ uint32_t offset; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object */ ++ return NULL; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return NULL; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ !header->ucRecordSize) ++ break; ++ ++ if (ATOM_HPD_INT_RECORD_TYPE == header->ucRecordType ++ && sizeof(ATOM_HPD_INT_RECORD) <= header->ucRecordSize) ++ return (ATOM_HPD_INT_RECORD *) header; ++ ++ offset += header->ucRecordSize; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * Get I2C information of input object id ++ * ++ * search all records to find the ATOM_I2C_RECORD_TYPE record IR ++ */ ++static ATOM_I2C_RECORD *get_i2c_record( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object) ++{ ++ uint32_t offset; ++ ATOM_COMMON_RECORD_HEADER *record_header; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); ++ /* Invalid object */ ++ return NULL; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!record_header) ++ return NULL; ++ ++ if (LAST_RECORD_TYPE == record_header->ucRecordType || ++ 0 == record_header->ucRecordSize) ++ break; ++ ++ if (ATOM_I2C_RECORD_TYPE == record_header->ucRecordType && ++ sizeof(ATOM_I2C_RECORD) <= ++ record_header->ucRecordSize) { ++ return (ATOM_I2C_RECORD *)record_header; ++ } ++ ++ offset += record_header->ucRecordSize; ++ } ++ ++ return NULL; ++} ++ ++ ++static enum bp_result get_ss_info_from_ss_info_table( ++ struct bios_parser *bp, ++ uint32_t id, ++ struct spread_spectrum_info *ss_info); ++static enum bp_result get_ss_info_from_tbl( ++ struct bios_parser *bp, ++ uint32_t id, ++ struct spread_spectrum_info *ss_info); ++/** ++ * dal_bios_parser_get_spread_spectrum_info ++ * Get spread spectrum information from the ASIC_InternalSS_Info(ver 2.1 or ++ * ver 3.1) or SS_Info table from the VBIOS. Currently ASIC_InternalSS_Info ++ * ver 2.1 can co-exist with SS_Info table. Expect ASIC_InternalSS_Info ver 3.1, ++ * there is only one entry for each signal /ss id. However, there is ++ * no planning of supporting multiple spread Sprectum entry for EverGreen ++ * @param [in] this ++ * @param [in] signal, ASSignalType to be converted to info index ++ * @param [in] index, number of entries that match the converted info index ++ * @param [out] ss_info, sprectrum information structure, ++ * @return Bios parser result code ++ */ ++enum bp_result dal_bios_parser_get_spread_spectrum_info( ++ struct bios_parser *bp, ++ enum as_signal_type signal, ++ uint32_t index, ++ struct spread_spectrum_info *ss_info) ++{ ++ enum bp_result result = BP_RESULT_UNSUPPORTED; ++ uint32_t clk_id_ss = 0; ++ ATOM_COMMON_TABLE_HEADER *header; ++ struct atom_data_revision tbl_revision; ++ ++ if (!ss_info) /* check for bad input */ ++ return BP_RESULT_BADINPUT; ++ /* signal translation */ ++ clk_id_ss = signal_to_ss_id(signal); ++ ++ if (!DATA_TABLES(ASIC_InternalSS_Info)) ++ if (!index) ++ return get_ss_info_from_ss_info_table(bp, clk_id_ss, ++ ss_info); ++ ++ header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, ++ DATA_TABLES(ASIC_InternalSS_Info)); ++ get_atom_data_table_revision(header, &tbl_revision); ++ ++ switch (tbl_revision.major) { ++ case 2: ++ switch (tbl_revision.minor) { ++ case 1: ++ /* there can not be more then one entry for Internal ++ * SS Info table version 2.1 */ ++ if (!index) ++ return get_ss_info_from_tbl(bp, clk_id_ss, ++ ss_info); ++ break; ++ default: ++ break; ++ } ++ break; ++ ++ case 3: ++ switch (tbl_revision.minor) { ++ case 1: ++ return get_ss_info_v3_1(bp, clk_id_ss, index, ss_info); ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ /* there can not be more then one entry for SS Info table */ ++ return result; ++} ++ ++static enum bp_result get_ss_info_from_internal_ss_info_tbl_V2_1( ++ struct bios_parser *bp, ++ uint32_t id, ++ struct spread_spectrum_info *info); ++ ++/** ++ * get_ss_info_from_table ++ * Get spread sprectrum information from the ASIC_InternalSS_Info Ver 2.1 or ++ * SS_Info table from the VBIOS ++ * There can not be more than 1 entry for ASIC_InternalSS_Info Ver 2.1 or ++ * SS_Info. ++ * ++ * @param this ++ * @param id, spread sprectrum info index ++ * @param pSSinfo, sprectrum information structure, ++ * @return Bios parser result code ++ */ ++static enum bp_result get_ss_info_from_tbl( ++ struct bios_parser *bp, ++ uint32_t id, ++ struct spread_spectrum_info *ss_info) ++{ ++ if (!ss_info) /* check for bad input, if ss_info is not NULL */ ++ return BP_RESULT_BADINPUT; ++ /* for SS_Info table only support DP and LVDS */ ++ if (id == ASIC_INTERNAL_SS_ON_DP || id == ASIC_INTERNAL_SS_ON_LVDS) ++ return get_ss_info_from_ss_info_table(bp, id, ss_info); ++ else ++ return get_ss_info_from_internal_ss_info_tbl_V2_1(bp, id, ++ ss_info); ++} ++ ++/** ++ * get_ss_info_from_internal_ss_info_tbl_V2_1 ++ * Get spread sprectrum information from the ASIC_InternalSS_Info table Ver 2.1 ++ * from the VBIOS ++ * There will not be multiple entry for Ver 2.1 ++ * ++ * @param id, spread sprectrum info index ++ * @param pSSinfo, sprectrum information structure, ++ * @return Bios parser result code ++ */ ++static enum bp_result get_ss_info_from_internal_ss_info_tbl_V2_1( ++ struct bios_parser *bp, ++ uint32_t id, ++ struct spread_spectrum_info *info) ++{ ++ enum bp_result result = BP_RESULT_UNSUPPORTED; ++ ATOM_ASIC_INTERNAL_SS_INFO_V2 *header; ++ ATOM_ASIC_SS_ASSIGNMENT_V2 *tbl; ++ uint32_t tbl_size, i; ++ ++ if (!DATA_TABLES(ASIC_InternalSS_Info)) ++ return result; ++ ++ header = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, ++ DATA_TABLES(ASIC_InternalSS_Info)); ++ ++ dc_service_memset(info, 0, sizeof(struct spread_spectrum_info)); ++ ++ tbl_size = (le16_to_cpu(header->sHeader.usStructureSize) ++ - sizeof(ATOM_COMMON_TABLE_HEADER)) ++ / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); ++ ++ tbl = (ATOM_ASIC_SS_ASSIGNMENT_V2 *) ++ &(header->asSpreadSpectrum[0]); ++ for (i = 0; i < tbl_size; i++) { ++ result = BP_RESULT_NORECORD; ++ ++ if (tbl[i].ucClockIndication != (uint8_t)id) ++ continue; ++ ++ if (ATOM_EXTERNAL_SS_MASK ++ & tbl[i].ucSpreadSpectrumMode) { ++ info->type.EXTERNAL = true; ++ } ++ if (ATOM_SS_CENTRE_SPREAD_MODE_MASK ++ & tbl[i].ucSpreadSpectrumMode) { ++ info->type.CENTER_MODE = true; ++ } ++ info->type.STEP_AND_DELAY_INFO = false; ++ /* convert [10KHz] into [KHz] */ ++ info->target_clock_range = ++ le32_to_cpu(tbl[i].ulTargetClockRange) * 10; ++ info->spread_spectrum_percentage = ++ (uint32_t)le16_to_cpu(tbl[i].usSpreadSpectrumPercentage); ++ info->spread_spectrum_range = ++ (uint32_t)(le16_to_cpu(tbl[i].usSpreadRateIn10Hz) * 10); ++ result = BP_RESULT_OK; ++ break; ++ } ++ ++ return result; ++ ++} ++ ++/** ++ * get_ss_info_from_ss_info_table ++ * Get spread sprectrum information from the SS_Info table from the VBIOS ++ * if the pointer to info is NULL, indicate the caller what to know the number ++ * of entries that matches the id ++ * for, the SS_Info table, there should not be more than 1 entry match. ++ * ++ * @param [in] id, spread sprectrum id ++ * @param [out] pSSinfo, sprectrum information structure, ++ * @return Bios parser result code ++ */ ++static enum bp_result get_ss_info_from_ss_info_table( ++ struct bios_parser *bp, ++ uint32_t id, ++ struct spread_spectrum_info *ss_info) ++{ ++ enum bp_result result = BP_RESULT_UNSUPPORTED; ++ ATOM_SPREAD_SPECTRUM_INFO *tbl; ++ ATOM_COMMON_TABLE_HEADER *header; ++ uint32_t table_size; ++ uint32_t i; ++ uint32_t id_local = SS_ID_UNKNOWN; ++ struct atom_data_revision revision; ++ ++ /* exist of the SS_Info table */ ++ /* check for bad input, pSSinfo can not be NULL */ ++ if (!DATA_TABLES(SS_Info) || !ss_info) ++ return result; ++ ++ header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, DATA_TABLES(SS_Info)); ++ get_atom_data_table_revision(header, &revision); ++ ++ tbl = GET_IMAGE(ATOM_SPREAD_SPECTRUM_INFO, DATA_TABLES(SS_Info)); ++ ++ if (1 != revision.major || 2 > revision.minor) ++ return result; ++ ++ /* have to convert from Internal_SS format to SS_Info format */ ++ switch (id) { ++ case ASIC_INTERNAL_SS_ON_DP: ++ id_local = SS_ID_DP1; ++ break; ++ case ASIC_INTERNAL_SS_ON_LVDS: ++ { ++ struct embedded_panel_info panel_info; ++ ++ if (dal_bios_parser_get_embedded_panel_info(bp, &panel_info) ++ == BP_RESULT_OK) ++ id_local = panel_info.ss_id; ++ break; ++ } ++ default: ++ break; ++ } ++ ++ if (id_local == SS_ID_UNKNOWN) ++ return result; ++ ++ table_size = (le16_to_cpu(tbl->sHeader.usStructureSize) - ++ sizeof(ATOM_COMMON_TABLE_HEADER)) / ++ sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); ++ ++ for (i = 0; i < table_size; i++) { ++ if (id_local != (uint32_t)tbl->asSS_Info[i].ucSS_Id) ++ continue; ++ ++ dc_service_memset(ss_info, 0, sizeof(struct spread_spectrum_info)); ++ ++ if (ATOM_EXTERNAL_SS_MASK & ++ tbl->asSS_Info[i].ucSpreadSpectrumType) ++ ss_info->type.EXTERNAL = true; ++ ++ if (ATOM_SS_CENTRE_SPREAD_MODE_MASK & ++ tbl->asSS_Info[i].ucSpreadSpectrumType) ++ ss_info->type.CENTER_MODE = true; ++ ++ ss_info->type.STEP_AND_DELAY_INFO = true; ++ ss_info->spread_spectrum_percentage = ++ (uint32_t)le16_to_cpu(tbl->asSS_Info[i].usSpreadSpectrumPercentage); ++ ss_info->step_and_delay_info.step = tbl->asSS_Info[i].ucSS_Step; ++ ss_info->step_and_delay_info.delay = ++ tbl->asSS_Info[i].ucSS_Delay; ++ ss_info->step_and_delay_info.recommended_ref_div = ++ tbl->asSS_Info[i].ucRecommendedRef_Div; ++ ss_info->spread_spectrum_range = ++ (uint32_t)tbl->asSS_Info[i].ucSS_Range * 10000; ++ ++ /* there will be only one entry for each display type in SS_info ++ * table */ ++ result = BP_RESULT_OK; ++ break; ++ } ++ ++ return result; ++} ++static enum bp_result get_embedded_panel_info_v1_2( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info); ++static enum bp_result get_embedded_panel_info_v1_3( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info); ++ ++enum bp_result dal_bios_parser_get_embedded_panel_info( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info) ++{ ++ ATOM_COMMON_TABLE_HEADER *hdr; ++ ++ if (!DATA_TABLES(LCD_Info)) ++ return BP_RESULT_FAILURE; ++ ++ hdr = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, DATA_TABLES(LCD_Info)); ++ ++ if (!hdr) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ switch (hdr->ucTableFormatRevision) { ++ case 1: ++ switch (hdr->ucTableContentRevision) { ++ case 0: ++ case 1: ++ case 2: ++ return get_embedded_panel_info_v1_2(bp, info); ++ case 3: ++ return get_embedded_panel_info_v1_3(bp, info); ++ default: ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return BP_RESULT_FAILURE; ++} ++ ++static enum bp_result get_embedded_panel_info_v1_2( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info) ++{ ++ ATOM_LVDS_INFO_V12 *lvds; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ if (!DATA_TABLES(LVDS_Info)) ++ return BP_RESULT_UNSUPPORTED; ++ ++ lvds = ++ GET_IMAGE(ATOM_LVDS_INFO_V12, DATA_TABLES(LVDS_Info)); ++ ++ if (!lvds) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (1 != lvds->sHeader.ucTableFormatRevision ++ || 2 > lvds->sHeader.ucTableContentRevision) ++ return BP_RESULT_UNSUPPORTED; ++ ++ dc_service_memset(info, 0, sizeof(struct embedded_panel_info)); ++ ++ /* We need to convert from 10KHz units into KHz units*/ ++ info->lcd_timing.pixel_clk = ++ le16_to_cpu(lvds->sLCDTiming.usPixClk) * 10; ++ /* usHActive does not include borders, according to VBIOS team*/ ++ info->lcd_timing.horizontal_addressable = ++ le16_to_cpu(lvds->sLCDTiming.usHActive); ++ /* usHBlanking_Time includes borders, so we should really be subtracting ++ * borders duing this translation, but LVDS generally*/ ++ /* doesn't have borders, so we should be okay leaving this as is for ++ * now. May need to revisit if we ever have LVDS with borders*/ ++ info->lcd_timing.horizontal_blanking_time = ++ le16_to_cpu(lvds->sLCDTiming.usHBlanking_Time); ++ /* usVActive does not include borders, according to VBIOS team*/ ++ info->lcd_timing.vertical_addressable = ++ le16_to_cpu(lvds->sLCDTiming.usVActive); ++ /* usVBlanking_Time includes borders, so we should really be subtracting ++ * borders duing this translation, but LVDS generally*/ ++ /* doesn't have borders, so we should be okay leaving this as is for ++ * now. May need to revisit if we ever have LVDS with borders*/ ++ info->lcd_timing.vertical_blanking_time = ++ le16_to_cpu(lvds->sLCDTiming.usVBlanking_Time); ++ info->lcd_timing.horizontal_sync_offset = ++ le16_to_cpu(lvds->sLCDTiming.usHSyncOffset); ++ info->lcd_timing.horizontal_sync_width = ++ le16_to_cpu(lvds->sLCDTiming.usHSyncWidth); ++ info->lcd_timing.vertical_sync_offset = ++ le16_to_cpu(lvds->sLCDTiming.usVSyncOffset); ++ info->lcd_timing.vertical_sync_width = ++ le16_to_cpu(lvds->sLCDTiming.usVSyncWidth); ++ info->lcd_timing.horizontal_border = lvds->sLCDTiming.ucHBorder; ++ info->lcd_timing.vertical_border = lvds->sLCDTiming.ucVBorder; ++ info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HorizontalCutOff; ++ info->lcd_timing.misc_info.H_SYNC_POLARITY = ++ ~(uint32_t) ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HSyncPolarity; ++ info->lcd_timing.misc_info.V_SYNC_POLARITY = ++ ~(uint32_t) ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VSyncPolarity; ++ info->lcd_timing.misc_info.VERTICAL_CUT_OFF = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VerticalCutOff; ++ info->lcd_timing.misc_info.H_REPLICATION_BY2 = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.H_ReplicationBy2; ++ info->lcd_timing.misc_info.V_REPLICATION_BY2 = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.V_ReplicationBy2; ++ info->lcd_timing.misc_info.COMPOSITE_SYNC = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.CompositeSync; ++ info->lcd_timing.misc_info.INTERLACE = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.Interlace; ++ info->lcd_timing.misc_info.DOUBLE_CLOCK = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.DoubleClock; ++ info->ss_id = lvds->ucSS_Id; ++ ++ { ++ uint8_t rr = le16_to_cpu(lvds->usSupportedRefreshRate); ++ /* Get minimum supported refresh rate*/ ++ if (SUPPORTED_LCD_REFRESHRATE_30Hz & rr) ++ info->supported_rr.REFRESH_RATE_30HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_40Hz & rr) ++ info->supported_rr.REFRESH_RATE_40HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_48Hz & rr) ++ info->supported_rr.REFRESH_RATE_48HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_50Hz & rr) ++ info->supported_rr.REFRESH_RATE_50HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_60Hz & rr) ++ info->supported_rr.REFRESH_RATE_60HZ = 1; ++ } ++ ++ /*Drr panel support can be reported by VBIOS*/ ++ if (LCDPANEL_CAP_DRR_SUPPORTED ++ & lvds->ucLCDPanel_SpecialHandlingCap) ++ info->drr_enabled = 1; ++ ++ if (ATOM_PANEL_MISC_DUAL & lvds->ucLVDS_Misc) ++ info->lcd_timing.misc_info.DOUBLE_CLOCK = true; ++ ++ if (ATOM_PANEL_MISC_888RGB & lvds->ucLVDS_Misc) ++ info->lcd_timing.misc_info.RGB888 = true; ++ ++ info->lcd_timing.misc_info.GREY_LEVEL = ++ (uint32_t) (ATOM_PANEL_MISC_GREY_LEVEL & ++ lvds->ucLVDS_Misc) >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT; ++ ++ if (ATOM_PANEL_MISC_SPATIAL & lvds->ucLVDS_Misc) ++ info->lcd_timing.misc_info.SPATIAL = true; ++ ++ if (ATOM_PANEL_MISC_TEMPORAL & lvds->ucLVDS_Misc) ++ info->lcd_timing.misc_info.TEMPORAL = true; ++ ++ if (ATOM_PANEL_MISC_API_ENABLED & lvds->ucLVDS_Misc) ++ info->lcd_timing.misc_info.API_ENABLED = true; ++ ++ return BP_RESULT_OK; ++} ++ ++static enum bp_result get_embedded_panel_info_v1_3( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info) ++{ ++ ATOM_LCD_INFO_V13 *lvds; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ if (!DATA_TABLES(LCD_Info)) ++ return BP_RESULT_UNSUPPORTED; ++ ++ lvds = GET_IMAGE(ATOM_LCD_INFO_V13, DATA_TABLES(LCD_Info)); ++ ++ if (!lvds) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (!((1 == lvds->sHeader.ucTableFormatRevision) ++ && (3 <= lvds->sHeader.ucTableContentRevision))) ++ return BP_RESULT_UNSUPPORTED; ++ ++ dc_service_memset(info, 0, sizeof(struct embedded_panel_info)); ++ ++ /* We need to convert from 10KHz units into KHz units */ ++ info->lcd_timing.pixel_clk = ++ le16_to_cpu(lvds->sLCDTiming.usPixClk) * 10; ++ /* usHActive does not include borders, according to VBIOS team */ ++ info->lcd_timing.horizontal_addressable = ++ le16_to_cpu(lvds->sLCDTiming.usHActive); ++ /* usHBlanking_Time includes borders, so we should really be subtracting ++ * borders duing this translation, but LVDS generally*/ ++ /* doesn't have borders, so we should be okay leaving this as is for ++ * now. May need to revisit if we ever have LVDS with borders*/ ++ info->lcd_timing.horizontal_blanking_time = ++ le16_to_cpu(lvds->sLCDTiming.usHBlanking_Time); ++ /* usVActive does not include borders, according to VBIOS team*/ ++ info->lcd_timing.vertical_addressable = ++ le16_to_cpu(lvds->sLCDTiming.usVActive); ++ /* usVBlanking_Time includes borders, so we should really be subtracting ++ * borders duing this translation, but LVDS generally*/ ++ /* doesn't have borders, so we should be okay leaving this as is for ++ * now. May need to revisit if we ever have LVDS with borders*/ ++ info->lcd_timing.vertical_blanking_time = ++ le16_to_cpu(lvds->sLCDTiming.usVBlanking_Time); ++ info->lcd_timing.horizontal_sync_offset = ++ le16_to_cpu(lvds->sLCDTiming.usHSyncOffset); ++ info->lcd_timing.horizontal_sync_width = ++ le16_to_cpu(lvds->sLCDTiming.usHSyncWidth); ++ info->lcd_timing.vertical_sync_offset = ++ le16_to_cpu(lvds->sLCDTiming.usVSyncOffset); ++ info->lcd_timing.vertical_sync_width = ++ le16_to_cpu(lvds->sLCDTiming.usVSyncWidth); ++ info->lcd_timing.horizontal_border = lvds->sLCDTiming.ucHBorder; ++ info->lcd_timing.vertical_border = lvds->sLCDTiming.ucVBorder; ++ info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HorizontalCutOff; ++ info->lcd_timing.misc_info.H_SYNC_POLARITY = ++ ~(uint32_t) ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HSyncPolarity; ++ info->lcd_timing.misc_info.V_SYNC_POLARITY = ++ ~(uint32_t) ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VSyncPolarity; ++ info->lcd_timing.misc_info.VERTICAL_CUT_OFF = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VerticalCutOff; ++ info->lcd_timing.misc_info.H_REPLICATION_BY2 = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.H_ReplicationBy2; ++ info->lcd_timing.misc_info.V_REPLICATION_BY2 = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.V_ReplicationBy2; ++ info->lcd_timing.misc_info.COMPOSITE_SYNC = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.CompositeSync; ++ info->lcd_timing.misc_info.INTERLACE = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.Interlace; ++ info->lcd_timing.misc_info.DOUBLE_CLOCK = ++ lvds->sLCDTiming.susModeMiscInfo.sbfAccess.DoubleClock; ++ info->ss_id = lvds->ucSS_Id; ++ ++ /* Drr panel support can be reported by VBIOS*/ ++ if (LCDPANEL_CAP_V13_DRR_SUPPORTED ++ & lvds->ucLCDPanel_SpecialHandlingCap) ++ info->drr_enabled = 1; ++ ++ /* Get supported refresh rate*/ ++ if (info->drr_enabled == 1) { ++ uint8_t min_rr = ++ lvds->sRefreshRateSupport.ucMinRefreshRateForDRR; ++ uint8_t rr = lvds->sRefreshRateSupport.ucSupportedRefreshRate; ++ ++ if (min_rr != 0) { ++ if (SUPPORTED_LCD_REFRESHRATE_30Hz & min_rr) ++ info->supported_rr.REFRESH_RATE_30HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_40Hz & min_rr) ++ info->supported_rr.REFRESH_RATE_40HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_48Hz & min_rr) ++ info->supported_rr.REFRESH_RATE_48HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_50Hz & min_rr) ++ info->supported_rr.REFRESH_RATE_50HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_60Hz & min_rr) ++ info->supported_rr.REFRESH_RATE_60HZ = 1; ++ } else { ++ if (SUPPORTED_LCD_REFRESHRATE_30Hz & rr) ++ info->supported_rr.REFRESH_RATE_30HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_40Hz & rr) ++ info->supported_rr.REFRESH_RATE_40HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_48Hz & rr) ++ info->supported_rr.REFRESH_RATE_48HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_50Hz & rr) ++ info->supported_rr.REFRESH_RATE_50HZ = 1; ++ else if (SUPPORTED_LCD_REFRESHRATE_60Hz & rr) ++ info->supported_rr.REFRESH_RATE_60HZ = 1; ++ } ++ } ++ ++ if (ATOM_PANEL_MISC_V13_DUAL & lvds->ucLCD_Misc) ++ info->lcd_timing.misc_info.DOUBLE_CLOCK = true; ++ ++ if (ATOM_PANEL_MISC_V13_8BIT_PER_COLOR & lvds->ucLCD_Misc) ++ info->lcd_timing.misc_info.RGB888 = true; ++ ++ info->lcd_timing.misc_info.GREY_LEVEL = ++ (uint32_t) (ATOM_PANEL_MISC_V13_GREY_LEVEL & ++ lvds->ucLCD_Misc) >> ATOM_PANEL_MISC_V13_GREY_LEVEL_SHIFT; ++ ++ return BP_RESULT_OK; ++} ++ ++/** ++ * dal_bios_parser_get_encoder_cap_info ++ * ++ * @brief ++ * Get encoder capability information of input object id ++ * ++ * @param object_id, Object id ++ * @param object_id, encoder cap information structure ++ * ++ * @return Bios parser result code ++ * ++ */ ++enum bp_result dal_bios_parser_get_encoder_cap_info( ++ struct bios_parser *bp, ++ struct graphics_object_id object_id, ++ struct bp_encoder_cap_info *info) ++{ ++ ATOM_OBJECT *object; ++ ATOM_ENCODER_CAP_RECORD *record = NULL; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ object = get_bios_object(bp, object_id); ++ ++ if (!object) ++ return BP_RESULT_BADINPUT; ++ ++ record = get_encoder_cap_record(bp, object); ++ if (!record) ++ return BP_RESULT_NORECORD; ++ ++ info->DP_HBR2_CAP = record->usHBR2Cap; ++ info->DP_HBR2_EN = record->usHBR2En; ++ return BP_RESULT_OK; ++} ++ ++/** ++ * get_encoder_cap_record ++ * ++ * @brief ++ * Get encoder cap record for the object ++ * ++ * @param object, ATOM object ++ * ++ * @return atom encoder cap record ++ * ++ * @note ++ * search all records to find the ATOM_ENCODER_CAP_RECORD record ++ */ ++static ATOM_ENCODER_CAP_RECORD *get_encoder_cap_record( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object) ++{ ++ ATOM_COMMON_RECORD_HEADER *header; ++ uint32_t offset; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object */ ++ return NULL; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return NULL; ++ ++ offset += header->ucRecordSize; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ !header->ucRecordSize) ++ break; ++ ++ if (ATOM_ENCODER_CAP_RECORD_TYPE != header->ucRecordType) ++ continue; ++ ++ if (sizeof(ATOM_ENCODER_CAP_RECORD) <= header->ucRecordSize) ++ return (ATOM_ENCODER_CAP_RECORD *)header; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * dal_bios_parser_get_din_connector_info ++ * @brief ++ * Get GPIO record for the DIN connector, this GPIO tells whether there is a ++ * CV dumb dongle ++ * attached to the DIN connector to perform load detection for the the ++ * appropriate signal ++ * ++ * @param id - DIN connector object id ++ * @param info - GPIO record infor ++ * @return Bios parser result code ++ */ ++enum bp_result dal_bios_parser_get_din_connector_info( ++ struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct din_connector_info *info) ++{ ++ ATOM_COMMON_RECORD_HEADER *header; ++ ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD *record = NULL; ++ ATOM_OBJECT *object; ++ uint32_t offset; ++ enum bp_result result = BP_RESULT_NORECORD; ++ ++ /* no output buffer provided */ ++ if (!info) { ++ BREAK_TO_DEBUGGER(); /* Invalid output buffer */ ++ return BP_RESULT_BADINPUT; ++ } ++ ++ object = get_bios_object(bp, id); ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */; ++ return BP_RESULT_BADINPUT; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) { ++ result = BP_RESULT_BADBIOSTABLE; ++ break; ++ } ++ ++ offset += header->ucRecordSize; ++ ++ /* get out of the loop if no more records */ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ !header->ucRecordSize) ++ break; ++ ++ if (ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE != ++ header->ucRecordType) ++ continue; ++ ++ if (sizeof(ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD) ++ > header->ucRecordSize) ++ continue; ++ ++ record = (ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD *)header; ++ result = BP_RESULT_OK; ++ break; ++ } ++ ++ /* return if the record not found */ ++ if (result != BP_RESULT_OK) ++ return result; ++ ++ info->gpio_id = record->ucGPIOID; ++ info->gpio_tv_active_state = (record->ucTVActiveState != 0); ++ ++ return result; ++} ++ ++static uint32_t get_ss_entry_number( ++ struct bios_parser *bp, ++ uint32_t id); ++static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_v2_1( ++ struct bios_parser *bp, ++ uint32_t id); ++static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_V3_1( ++ struct bios_parser *bp, ++ uint32_t id); ++static uint32_t get_ss_entry_number_from_ss_info_tbl( ++ struct bios_parser *bp, ++ uint32_t id); ++ ++/** ++ * BiosParserObject::GetNumberofSpreadSpectrumEntry ++ * Get Number of SpreadSpectrum Entry from the ASIC_InternalSS_Info table from ++ * the VBIOS that match the SSid (to be converted from signal) ++ * ++ * @param[in] signal, ASSignalType to be converted to SSid ++ * @return number of SS Entry that match the signal ++ */ ++uint32_t dal_bios_parser_get_ss_entry_number( ++ struct bios_parser *bp, ++ enum as_signal_type signal) ++{ ++ uint32_t ss_id = 0; ++ ATOM_COMMON_TABLE_HEADER *header; ++ struct atom_data_revision revision; ++ ++ ss_id = signal_to_ss_id(signal); ++ ++ if (!DATA_TABLES(ASIC_InternalSS_Info)) ++ return get_ss_entry_number_from_ss_info_tbl(bp, ss_id); ++ ++ header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, ++ DATA_TABLES(ASIC_InternalSS_Info)); ++ get_atom_data_table_revision(header, &revision); ++ ++ switch (revision.major) { ++ case 2: ++ switch (revision.minor) { ++ case 1: ++ return get_ss_entry_number(bp, ss_id); ++ default: ++ break; ++ } ++ break; ++ case 3: ++ switch (revision.minor) { ++ case 1: ++ return ++ get_ss_entry_number_from_internal_ss_info_tbl_V3_1( ++ bp, ss_id); ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * get_ss_entry_number_from_ss_info_tbl ++ * Get Number of spread spectrum entry from the SS_Info table from the VBIOS. ++ * ++ * @note There can only be one entry for each id for SS_Info Table ++ * ++ * @param [in] id, spread spectrum id ++ * @return number of SS Entry that match the id ++ */ ++static uint32_t get_ss_entry_number_from_ss_info_tbl( ++ struct bios_parser *bp, ++ uint32_t id) ++{ ++ ATOM_SPREAD_SPECTRUM_INFO *tbl; ++ ATOM_COMMON_TABLE_HEADER *header; ++ uint32_t table_size; ++ uint32_t i; ++ uint32_t number = 0; ++ uint32_t id_local = SS_ID_UNKNOWN; ++ struct atom_data_revision revision; ++ ++ /* SS_Info table exist */ ++ if (!DATA_TABLES(SS_Info)) ++ return number; ++ ++ header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, ++ DATA_TABLES(SS_Info)); ++ get_atom_data_table_revision(header, &revision); ++ ++ tbl = GET_IMAGE(ATOM_SPREAD_SPECTRUM_INFO, ++ DATA_TABLES(SS_Info)); ++ ++ if (1 != revision.major || 2 > revision.minor) ++ return number; ++ ++ /* have to convert from Internal_SS format to SS_Info format */ ++ switch (id) { ++ case ASIC_INTERNAL_SS_ON_DP: ++ id_local = SS_ID_DP1; ++ break; ++ case ASIC_INTERNAL_SS_ON_LVDS: { ++ struct embedded_panel_info panel_info; ++ ++ if (dal_bios_parser_get_embedded_panel_info(bp, &panel_info) ++ == BP_RESULT_OK) ++ id_local = panel_info.ss_id; ++ break; ++ } ++ default: ++ break; ++ } ++ ++ if (id_local == SS_ID_UNKNOWN) ++ return number; ++ ++ table_size = (le16_to_cpu(tbl->sHeader.usStructureSize) - ++ sizeof(ATOM_COMMON_TABLE_HEADER)) / ++ sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); ++ ++ for (i = 0; i < table_size; i++) ++ if (id_local == (uint32_t)tbl->asSS_Info[i].ucSS_Id) { ++ number = 1; ++ break; ++ } ++ ++ return number; ++} ++ ++ ++/** ++ * get_ss_entry_number ++ * Get spread sprectrum information from the ASIC_InternalSS_Info Ver 2.1 or ++ * SS_Info table from the VBIOS ++ * There can not be more than 1 entry for ASIC_InternalSS_Info Ver 2.1 or ++ * SS_Info. ++ * ++ * @param id, spread sprectrum info index ++ * @return Bios parser result code ++ */ ++static uint32_t get_ss_entry_number(struct bios_parser *bp, uint32_t id) ++{ ++ if (id == ASIC_INTERNAL_SS_ON_DP || id == ASIC_INTERNAL_SS_ON_LVDS) ++ return get_ss_entry_number_from_ss_info_tbl(bp, id); ++ ++ return get_ss_entry_number_from_internal_ss_info_tbl_v2_1(bp, id); ++} ++ ++/** ++ * get_ss_entry_number_from_internal_ss_info_tbl_v2_1 ++ * Get NUmber of spread sprectrum entry from the ASIC_InternalSS_Info table ++ * Ver 2.1 from the VBIOS ++ * There will not be multiple entry for Ver 2.1 ++ * ++ * @param id, spread sprectrum info index ++ * @return number of SS Entry that match the id ++ */ ++static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_v2_1( ++ struct bios_parser *bp, ++ uint32_t id) ++{ ++ ATOM_ASIC_INTERNAL_SS_INFO_V2 *header_include; ++ ATOM_ASIC_SS_ASSIGNMENT_V2 *tbl; ++ uint32_t size; ++ uint32_t i; ++ ++ if (!DATA_TABLES(ASIC_InternalSS_Info)) ++ return 0; ++ ++ header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, ++ DATA_TABLES(ASIC_InternalSS_Info)); ++ ++ size = (le16_to_cpu(header_include->sHeader.usStructureSize) ++ - sizeof(ATOM_COMMON_TABLE_HEADER)) ++ / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); ++ ++ tbl = (ATOM_ASIC_SS_ASSIGNMENT_V2 *) ++ &header_include->asSpreadSpectrum[0]; ++ for (i = 0; i < size; i++) ++ if (tbl[i].ucClockIndication == (uint8_t)id) ++ return 1; ++ ++ return 0; ++} ++/** ++ * get_ss_entry_number_from_internal_ss_info_table_V3_1 ++ * Get Number of SpreadSpectrum Entry from the ASIC_InternalSS_Info table of ++ * the VBIOS that matches id ++ * ++ * @param[in] id, spread sprectrum id ++ * @return number of SS Entry that match the id ++ */ ++static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_V3_1( ++ struct bios_parser *bp, ++ uint32_t id) ++{ ++ uint32_t number = 0; ++ ATOM_ASIC_INTERNAL_SS_INFO_V3 *header_include; ++ ATOM_ASIC_SS_ASSIGNMENT_V3 *tbl; ++ uint32_t size; ++ uint32_t i; ++ ++ if (!DATA_TABLES(ASIC_InternalSS_Info)) ++ return number; ++ ++ header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V3, ++ DATA_TABLES(ASIC_InternalSS_Info)); ++ size = (le16_to_cpu(header_include->sHeader.usStructureSize) - ++ sizeof(ATOM_COMMON_TABLE_HEADER)) / ++ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); ++ ++ tbl = (ATOM_ASIC_SS_ASSIGNMENT_V3 *) ++ &header_include->asSpreadSpectrum[0]; ++ ++ for (i = 0; i < size; i++) ++ if (tbl[i].ucClockIndication == (uint8_t)id) ++ number++; ++ ++ return number; ++} ++ ++static ATOM_FAKE_EDID_PATCH_RECORD *get_faked_edid_record( ++ struct bios_parser *bp) ++{ ++ uint32_t size; ++ uint8_t *record; ++ ATOM_LVDS_INFO_V12 *info; ++ ++ if (!DATA_TABLES(LVDS_Info)) ++ return NULL; ++ ++ info = GET_IMAGE(ATOM_LVDS_INFO_V12, DATA_TABLES(LVDS_Info)); ++ ++ if (!info) ++ return NULL; ++ ++ if (1 != info->sHeader.ucTableFormatRevision ++ || 2 > info->sHeader.ucTableContentRevision) ++ return NULL; ++ ++ if (!le16_to_cpu(info->usExtInfoTableOffset)) ++ return NULL; ++ ++ record = GET_IMAGE(uint8_t, DATA_TABLES(LVDS_Info) ++ + le16_to_cpu(info->usExtInfoTableOffset)); ++ ++ if (!record) ++ return NULL; ++ ++ for (;;) { ++ if (ATOM_RECORD_END_TYPE == *record) ++ return NULL; ++ ++ if (LCD_FAKE_EDID_PATCH_RECORD_TYPE == *record) ++ break; ++ ++ size = get_record_size(record); ++ ++ if (!size) ++ return NULL; ++ ++ record += size; ++ } ++ ++ return (ATOM_FAKE_EDID_PATCH_RECORD *)record; ++} ++ ++enum bp_result dal_bios_parser_get_faked_edid_len( ++ struct bios_parser *bp, ++ uint32_t *len) ++{ ++ ATOM_FAKE_EDID_PATCH_RECORD *edid_record = get_faked_edid_record(bp); ++ ++ if (!edid_record) ++ return BP_RESULT_NORECORD; ++ ++ *len = get_edid_size(edid_record); ++ ++ return BP_RESULT_OK; ++} ++ ++enum bp_result dal_bios_parser_get_faked_edid_buf( ++ struct bios_parser *bp, ++ uint8_t *buff, ++ uint32_t len) ++{ ++ ATOM_FAKE_EDID_PATCH_RECORD *edid_record = get_faked_edid_record(bp); ++ uint32_t edid_size; ++ ++ if (!edid_record) ++ return BP_RESULT_NORECORD; ++ ++ edid_size = get_edid_size(edid_record); ++ ++ if (len < edid_size) ++ return BP_RESULT_BADINPUT; /* buffer not big enough to fill */ ++ ++ dc_service_memmove(buff, &edid_record->ucFakeEDIDString, edid_size); ++ ++ return BP_RESULT_OK; ++} ++ ++/** ++ * dal_bios_parser_get_gpio_pin_info ++ * Get GpioPin information of input gpio id ++ * ++ * @param gpio_id, GPIO ID ++ * @param info, GpioPin information structure ++ * @return Bios parser result code ++ * @note ++ * to get the GPIO PIN INFO, we need: ++ * 1. get the GPIO_ID from other object table, see GetHPDInfo() ++ * 2. in DATA_TABLE.GPIO_Pin_LUT, search all records, to get the registerA ++ * offset/mask ++ */ ++enum bp_result dal_bios_parser_get_gpio_pin_info( ++ struct bios_parser *bp, ++ uint32_t gpio_id, ++ struct gpio_pin_info *info) ++{ ++ ATOM_GPIO_PIN_LUT *header; ++ uint32_t count = 0; ++ uint32_t i = 0; ++ ++ if (!DATA_TABLES(GPIO_Pin_LUT)) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ header = GET_IMAGE(ATOM_GPIO_PIN_LUT, DATA_TABLES(GPIO_Pin_LUT)); ++ if (!header) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (sizeof(ATOM_COMMON_TABLE_HEADER) + sizeof(ATOM_GPIO_PIN_LUT) ++ > le16_to_cpu(header->sHeader.usStructureSize)) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (1 != header->sHeader.ucTableContentRevision) ++ return BP_RESULT_UNSUPPORTED; ++ ++ count = (le16_to_cpu(header->sHeader.usStructureSize) ++ - sizeof(ATOM_COMMON_TABLE_HEADER)) ++ / sizeof(ATOM_GPIO_PIN_ASSIGNMENT); ++ for (i = 0; i < count; ++i) { ++ if (header->asGPIO_Pin[i].ucGPIO_ID != gpio_id) ++ continue; ++ ++ info->offset = ++ (uint32_t) le16_to_cpu(header->asGPIO_Pin[i].usGpioPin_AIndex); ++ info->offset_y = info->offset + 2; ++ info->offset_en = info->offset + 1; ++ info->offset_mask = info->offset - 1; ++ ++ info->mask = (uint32_t) (1 << ++ header->asGPIO_Pin[i].ucGpioPinBitShift); ++ info->mask_y = info->mask + 2; ++ info->mask_en = info->mask + 1; ++ info->mask_mask = info->mask - 1; ++ ++ return BP_RESULT_OK; ++ } ++ ++ return BP_RESULT_NORECORD; ++} ++ ++/** ++ * BiosParserObject::EnumEmbeddedPanelPatchMode ++ * Get embedded panel patch mode ++ * ++ * @param index, mode index ++ * @param info, embedded panel patch mode structure ++ * @return Bios parser result code ++ */ ++enum bp_result dal_bios_parser_enum_embedded_panel_patch_mode( ++ struct bios_parser *bp, ++ uint32_t index, ++ struct embedded_panel_patch_mode *mode) ++{ ++ uint32_t record_size; ++ uint32_t record_index; ++ uint8_t *record; ++ ATOM_LVDS_INFO_V12 *info; ++ ATOM_PATCH_RECORD_MODE *mode_record; ++ ATOM_MASTER_LIST_OF_DATA_TABLES *list_of_tables; ++ ++ if (!mode) ++ return BP_RESULT_BADINPUT; ++ ++ list_of_tables = &bp->master_data_tbl->ListOfDataTables; ++ if (!list_of_tables->LVDS_Info) ++ return BP_RESULT_UNSUPPORTED; ++ ++ info = GET_IMAGE(ATOM_LVDS_INFO_V12, list_of_tables->LVDS_Info); ++ ++ if (!info) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (1 != info->sHeader.ucTableFormatRevision ++ || 2 > info->sHeader.ucTableContentRevision) ++ return BP_RESULT_UNSUPPORTED; ++ ++ if (!le16_to_cpu(info->usExtInfoTableOffset)) ++ return BP_RESULT_UNSUPPORTED; ++ ++ record = GET_IMAGE(uint8_t, list_of_tables->LVDS_Info + ++ le16_to_cpu(info->usExtInfoTableOffset)); ++ ++ if (!record) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ for (record_index = 0;;) { ++ if (ATOM_RECORD_END_TYPE == *record) ++ return BP_RESULT_NORECORD; ++ ++ if (LCD_MODE_PATCH_RECORD_MODE_TYPE == *record) { ++ if (record_index == index) ++ break; ++ record_index++; ++ } ++ ++ record_size = get_record_size(record); ++ ++ if (!record_size) ++ return BP_RESULT_NORECORD; ++ ++ record += record_size; ++ } ++ ++ mode_record = (ATOM_PATCH_RECORD_MODE *) record; ++ ++ mode->width = le16_to_cpu(mode_record->usHDisp); ++ mode->height = le16_to_cpu(mode_record->usVDisp); ++ ++ return BP_RESULT_OK; ++} ++ ++static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, ++ ATOM_I2C_RECORD *record, ++ struct graphics_object_i2c_info *info) ++{ ++ ATOM_GPIO_I2C_INFO *header; ++ uint32_t count = 0; ++ ++ if (!info) ++ return BP_RESULT_BADINPUT; ++ ++ /* get the GPIO_I2C info */ ++ if (!DATA_TABLES(GPIO_I2C_Info)) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ header = GET_IMAGE(ATOM_GPIO_I2C_INFO, DATA_TABLES(GPIO_I2C_Info)); ++ if (!header) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (sizeof(ATOM_COMMON_TABLE_HEADER) + sizeof(ATOM_GPIO_I2C_ASSIGMENT) ++ > le16_to_cpu(header->sHeader.usStructureSize)) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (1 != header->sHeader.ucTableContentRevision) ++ return BP_RESULT_UNSUPPORTED; ++ ++ /* get data count */ ++ count = (le16_to_cpu(header->sHeader.usStructureSize) ++ - sizeof(ATOM_COMMON_TABLE_HEADER)) ++ / sizeof(ATOM_GPIO_I2C_ASSIGMENT); ++ if (count < record->sucI2cId.bfI2C_LineMux) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ /* get the GPIO_I2C_INFO */ ++ info->i2c_hw_assist = record->sucI2cId.bfHW_Capable; ++ info->i2c_line = record->sucI2cId.bfI2C_LineMux; ++ info->i2c_engine_id = record->sucI2cId.bfHW_EngineID; ++ info->i2c_slave_address = record->ucI2CAddr; ++ ++ info->gpio_info.clk_mask_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkMaskRegisterIndex); ++ info->gpio_info.clk_en_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkEnRegisterIndex); ++ info->gpio_info.clk_y_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkY_RegisterIndex); ++ info->gpio_info.clk_a_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkA_RegisterIndex); ++ info->gpio_info.data_mask_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataMaskRegisterIndex); ++ info->gpio_info.data_en_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataEnRegisterIndex); ++ info->gpio_info.data_y_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataY_RegisterIndex); ++ info->gpio_info.data_a_register_index = ++ le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataA_RegisterIndex); ++ ++ info->gpio_info.clk_mask_shift = ++ header->asGPIO_Info[info->i2c_line].ucClkMaskShift; ++ info->gpio_info.clk_en_shift = ++ header->asGPIO_Info[info->i2c_line].ucClkEnShift; ++ info->gpio_info.clk_y_shift = ++ header->asGPIO_Info[info->i2c_line].ucClkY_Shift; ++ info->gpio_info.clk_a_shift = ++ header->asGPIO_Info[info->i2c_line].ucClkA_Shift; ++ info->gpio_info.data_mask_shift = ++ header->asGPIO_Info[info->i2c_line].ucDataMaskShift; ++ info->gpio_info.data_en_shift = ++ header->asGPIO_Info[info->i2c_line].ucDataEnShift; ++ info->gpio_info.data_y_shift = ++ header->asGPIO_Info[info->i2c_line].ucDataY_Shift; ++ info->gpio_info.data_a_shift = ++ header->asGPIO_Info[info->i2c_line].ucDataA_Shift; ++ ++ return BP_RESULT_OK; ++} ++ ++static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, ++ struct graphics_object_id id) ++{ ++ uint32_t offset; ++ ATOM_OBJECT_TABLE *tbl; ++ uint32_t i; ++ ++ switch (id.type) { ++ case OBJECT_TYPE_ENCODER: ++ offset = le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); ++ break; ++ ++ case OBJECT_TYPE_CONNECTOR: ++ offset = le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); ++ break; ++ ++ case OBJECT_TYPE_ROUTER: ++ offset = le16_to_cpu(bp->object_info_tbl.v1_1->usRouterObjectTableOffset); ++ break; ++ ++ case OBJECT_TYPE_GENERIC: ++ if (bp->object_info_tbl.revision.minor < 3) ++ return NULL; ++ offset = le16_to_cpu(bp->object_info_tbl.v1_3->usMiscObjectTableOffset); ++ break; ++ ++ default: ++ return NULL; ++ } ++ ++ offset += bp->object_info_tbl_offset; ++ ++ tbl = GET_IMAGE(ATOM_OBJECT_TABLE, offset); ++ if (!tbl) ++ return NULL; ++ ++ for (i = 0; i < tbl->ucNumberOfObjects; i++) ++ if (dal_graphics_object_id_is_equal(id, ++ object_id_from_bios_object_id( ++ le16_to_cpu(tbl->asObjects[i].usObjectID)))) ++ return &tbl->asObjects[i]; ++ ++ return NULL; ++} ++ ++static uint32_t get_dest_obj_list(struct bios_parser *bp, ++ ATOM_OBJECT *object, uint16_t **id_list) ++{ ++ uint32_t offset; ++ uint8_t *number; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */ ++ return 0; ++ } ++ ++ offset = le16_to_cpu(object->usSrcDstTableOffset) ++ + bp->object_info_tbl_offset; ++ ++ number = GET_IMAGE(uint8_t, offset); ++ if (!number) ++ return 0; ++ ++ offset += sizeof(uint8_t); ++ offset += sizeof(uint16_t) * (*number); ++ ++ number = GET_IMAGE(uint8_t, offset); ++ if ((!number) || (!*number)) ++ return 0; ++ ++ offset += sizeof(uint8_t); ++ *id_list = (uint16_t *)get_image(bp, offset, ++ *number * sizeof(uint16_t)); ++ ++ if (!*id_list) ++ return 0; ++ ++ return *number; ++} ++ ++static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, ++ uint16_t **id_list) ++{ ++ uint32_t offset; ++ uint8_t *number; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid object id */ ++ return 0; ++ } ++ ++ offset = le16_to_cpu(object->usSrcDstTableOffset) ++ + bp->object_info_tbl_offset; ++ ++ number = GET_IMAGE(uint8_t, offset); ++ if (!number) ++ return 0; ++ ++ offset += sizeof(uint8_t); ++ *id_list = (uint16_t *)get_image(bp, offset, ++ *number * sizeof(uint16_t)); ++ ++ if (!*id_list) ++ return 0; ++ ++ return *number; ++} ++ ++static uint32_t get_dst_number_from_object(struct bios_parser *bp, ++ ATOM_OBJECT *object) ++{ ++ uint32_t offset; ++ uint8_t *number; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); /* Invalid encoder object id*/ ++ return 0; ++ } ++ ++ offset = le16_to_cpu(object->usSrcDstTableOffset) ++ + bp->object_info_tbl_offset; ++ ++ number = GET_IMAGE(uint8_t, offset); ++ if (!number) ++ return 0; ++ ++ offset += sizeof(uint8_t); ++ offset += sizeof(uint16_t) * (*number); ++ ++ number = GET_IMAGE(uint8_t, offset); ++ ++ if (!number) ++ return 0; ++ ++ return *number; ++} ++ ++static uint8_t *get_image(struct bios_parser *bp, ++ uint32_t offset, ++ uint32_t size) ++{ ++ if (bp->bios && offset + size < bp->bios_size) ++ return bp->bios + offset; ++ else ++ return NULL; ++} ++ ++static uint32_t get_record_size(uint8_t *record) ++{ ++ switch (*record) { ++ case LCD_MODE_PATCH_RECORD_MODE_TYPE: ++ return sizeof(ATOM_PATCH_RECORD_MODE); ++ ++ case LCD_RTS_RECORD_TYPE: ++ return sizeof(ATOM_LCD_RTS_RECORD); ++ ++ case LCD_CAP_RECORD_TYPE: ++ return sizeof(ATOM_LCD_MODE_CONTROL_CAP); ++ ++ case LCD_FAKE_EDID_PATCH_RECORD_TYPE: { ++ ATOM_FAKE_EDID_PATCH_RECORD *fake_record = ++ (ATOM_FAKE_EDID_PATCH_RECORD *) record; ++ uint32_t edid_size = get_edid_size(fake_record); ++ ++ return sizeof(ATOM_FAKE_EDID_PATCH_RECORD) + edid_size ++ - sizeof(fake_record->ucFakeEDIDString); ++ } ++ ++ case LCD_PANEL_RESOLUTION_RECORD_TYPE: ++ return sizeof(ATOM_PANEL_RESOLUTION_PATCH_RECORD); ++ ++ default: ++ return 0; ++ } ++} ++ ++static uint32_t get_edid_size(const ATOM_FAKE_EDID_PATCH_RECORD *edid) ++{ ++ uint32_t length = edid->ucFakeEDIDLength; ++ ++ if (length < 128) ++ length = length * 128; ++ ++ return length; ++} ++ ++static struct graphics_object_id object_id_from_bios_object_id( ++ uint32_t bios_object_id) ++{ ++ enum object_type type; ++ enum object_enum_id enum_id; ++ struct graphics_object_id go_id = { 0 }; ++ ++ type = object_type_from_bios_object_id(bios_object_id); ++ ++ if (OBJECT_TYPE_UNKNOWN == type) ++ return go_id; ++ ++ enum_id = enum_id_from_bios_object_id(bios_object_id); ++ ++ if (ENUM_ID_UNKNOWN == enum_id) ++ return go_id; ++ ++ go_id = dal_graphics_object_id_init( ++ id_from_bios_object_id(type, bios_object_id), enum_id, type); ++ ++ return go_id; ++} ++ ++static enum object_type object_type_from_bios_object_id(uint32_t bios_object_id) ++{ ++ uint32_t bios_object_type = (bios_object_id & OBJECT_TYPE_MASK) ++ >> OBJECT_TYPE_SHIFT; ++ enum object_type object_type; ++ ++ switch (bios_object_type) { ++ case GRAPH_OBJECT_TYPE_GPU: ++ object_type = OBJECT_TYPE_GPU; ++ break; ++ case GRAPH_OBJECT_TYPE_ENCODER: ++ object_type = OBJECT_TYPE_ENCODER; ++ break; ++ case GRAPH_OBJECT_TYPE_CONNECTOR: ++ object_type = OBJECT_TYPE_CONNECTOR; ++ break; ++ case GRAPH_OBJECT_TYPE_ROUTER: ++ object_type = OBJECT_TYPE_ROUTER; ++ break; ++ case GRAPH_OBJECT_TYPE_GENERIC: ++ object_type = OBJECT_TYPE_GENERIC; ++ break; ++ default: ++ object_type = OBJECT_TYPE_UNKNOWN; ++ break; ++ } ++ ++ return object_type; ++} ++ ++static enum object_enum_id enum_id_from_bios_object_id(uint32_t bios_object_id) ++{ ++ uint32_t bios_enum_id = ++ (bios_object_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT; ++ enum object_enum_id id; ++ ++ switch (bios_enum_id) { ++ case GRAPH_OBJECT_ENUM_ID1: ++ id = ENUM_ID_1; ++ break; ++ case GRAPH_OBJECT_ENUM_ID2: ++ id = ENUM_ID_2; ++ break; ++ case GRAPH_OBJECT_ENUM_ID3: ++ id = ENUM_ID_3; ++ break; ++ case GRAPH_OBJECT_ENUM_ID4: ++ id = ENUM_ID_4; ++ break; ++ case GRAPH_OBJECT_ENUM_ID5: ++ id = ENUM_ID_5; ++ break; ++ case GRAPH_OBJECT_ENUM_ID6: ++ id = ENUM_ID_6; ++ break; ++ case GRAPH_OBJECT_ENUM_ID7: ++ id = ENUM_ID_7; ++ break; ++ default: ++ id = ENUM_ID_UNKNOWN; ++ break; ++ } ++ ++ return id; ++} ++ ++static uint32_t id_from_bios_object_id(enum object_type type, ++ uint32_t bios_object_id) ++{ ++ switch (type) { ++ case OBJECT_TYPE_GPU: ++ return gpu_id_from_bios_object_id(bios_object_id); ++ case OBJECT_TYPE_ENCODER: ++ return (uint32_t)encoder_id_from_bios_object_id(bios_object_id); ++ case OBJECT_TYPE_CONNECTOR: ++ return (uint32_t)connector_id_from_bios_object_id( ++ bios_object_id); ++ case OBJECT_TYPE_GENERIC: ++ return generic_id_from_bios_object_id(bios_object_id); ++ default: ++ return 0; ++ } ++} ++ ++static enum connector_id connector_id_from_bios_object_id( ++ uint32_t bios_object_id) ++{ ++ uint32_t bios_connector_id = gpu_id_from_bios_object_id(bios_object_id); ++ ++ enum connector_id id; ++ ++ switch (bios_connector_id) { ++ case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I: ++ id = CONNECTOR_ID_SINGLE_LINK_DVII; ++ break; ++ case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I: ++ id = CONNECTOR_ID_DUAL_LINK_DVII; ++ break; ++ case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D: ++ id = CONNECTOR_ID_SINGLE_LINK_DVID; ++ break; ++ case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D: ++ id = CONNECTOR_ID_DUAL_LINK_DVID; ++ break; ++ case CONNECTOR_OBJECT_ID_VGA: ++ id = CONNECTOR_ID_VGA; ++ break; ++ case CONNECTOR_OBJECT_ID_HDMI_TYPE_A: ++ id = CONNECTOR_ID_HDMI_TYPE_A; ++ break; ++ case CONNECTOR_OBJECT_ID_LVDS: ++ id = CONNECTOR_ID_LVDS; ++ break; ++ case CONNECTOR_OBJECT_ID_PCIE_CONNECTOR: ++ id = CONNECTOR_ID_PCIE; ++ break; ++ case CONNECTOR_OBJECT_ID_HARDCODE_DVI: ++ id = CONNECTOR_ID_HARDCODE_DVI; ++ break; ++ case CONNECTOR_OBJECT_ID_DISPLAYPORT: ++ id = CONNECTOR_ID_DISPLAY_PORT; ++ break; ++ case CONNECTOR_OBJECT_ID_eDP: ++ id = CONNECTOR_ID_EDP; ++ break; ++ case CONNECTOR_OBJECT_ID_MXM: ++ id = CONNECTOR_ID_MXM; ++ break; ++ default: ++ id = CONNECTOR_ID_UNKNOWN; ++ break; ++ } ++ ++ return id; ++} ++ ++static enum encoder_id encoder_id_from_bios_object_id(uint32_t bios_object_id) ++{ ++ uint32_t bios_encoder_id = gpu_id_from_bios_object_id(bios_object_id); ++ enum encoder_id id; ++ ++ switch (bios_encoder_id) { ++ case ENCODER_OBJECT_ID_INTERNAL_LVDS: ++ id = ENCODER_ID_INTERNAL_LVDS; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_TMDS1: ++ id = ENCODER_ID_INTERNAL_TMDS1; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_TMDS2: ++ id = ENCODER_ID_INTERNAL_TMDS2; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_DAC1: ++ id = ENCODER_ID_INTERNAL_DAC1; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_DAC2: ++ id = ENCODER_ID_INTERNAL_DAC2; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_SDVOA: ++ id = ENCODER_ID_INTERNAL_SDVOA; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_SDVOB: ++ id = ENCODER_ID_INTERNAL_SDVOB; ++ break; ++ case ENCODER_OBJECT_ID_SI170B: ++ id = ENCODER_ID_EXTERNAL_SI170B; ++ break; ++ case ENCODER_OBJECT_ID_CH7303: ++ id = ENCODER_ID_EXTERNAL_CH7303; ++ break; ++ case ENCODER_OBJECT_ID_CH7301: ++ id = ENCODER_ID_EXTERNAL_CH7301; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_DVO1: ++ id = ENCODER_ID_INTERNAL_DVO1; ++ break; ++ case ENCODER_OBJECT_ID_EXTERNAL_SDVOA: ++ id = ENCODER_ID_EXTERNAL_SDVOA; ++ break; ++ case ENCODER_OBJECT_ID_EXTERNAL_SDVOB: ++ id = ENCODER_ID_EXTERNAL_SDVOB; ++ break; ++ case ENCODER_OBJECT_ID_TITFP513: ++ id = ENCODER_ID_EXTERNAL_TITFP513; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_LVTM1: ++ id = ENCODER_ID_INTERNAL_LVTM1; ++ break; ++ case ENCODER_OBJECT_ID_VT1623: ++ id = ENCODER_ID_EXTERNAL_VT1623; ++ break; ++ case ENCODER_OBJECT_ID_HDMI_SI1930: ++ id = ENCODER_ID_EXTERNAL_SI1930; ++ break; ++ case ENCODER_OBJECT_ID_HDMI_INTERNAL: ++ id = ENCODER_ID_INTERNAL_HDMI; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: ++ id = ENCODER_ID_INTERNAL_KLDSCP_TMDS1; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: ++ id = ENCODER_ID_INTERNAL_KLDSCP_DVO1; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: ++ id = ENCODER_ID_INTERNAL_KLDSCP_DAC1; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: ++ id = ENCODER_ID_INTERNAL_KLDSCP_DAC2; ++ break; ++ case ENCODER_OBJECT_ID_SI178: ++ id = ENCODER_ID_EXTERNAL_SI178; ++ break; ++ case ENCODER_OBJECT_ID_MVPU_FPGA: ++ id = ENCODER_ID_EXTERNAL_MVPU_FPGA; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_DDI: ++ id = ENCODER_ID_INTERNAL_DDI; ++ break; ++ case ENCODER_OBJECT_ID_VT1625: ++ id = ENCODER_ID_EXTERNAL_VT1625; ++ break; ++ case ENCODER_OBJECT_ID_HDMI_SI1932: ++ id = ENCODER_ID_EXTERNAL_SI1932; ++ break; ++ case ENCODER_OBJECT_ID_DP_AN9801: ++ id = ENCODER_ID_EXTERNAL_AN9801; ++ break; ++ case ENCODER_OBJECT_ID_DP_DP501: ++ id = ENCODER_ID_EXTERNAL_DP501; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: ++ id = ENCODER_ID_INTERNAL_UNIPHY; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: ++ id = ENCODER_ID_INTERNAL_KLDSCP_LVTMA; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: ++ id = ENCODER_ID_INTERNAL_UNIPHY1; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: ++ id = ENCODER_ID_INTERNAL_UNIPHY2; ++ break; ++ case ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO: ++ id = ENCODER_ID_EXTERNAL_GENERIC_DVO; ++ break; ++ case ENCODER_OBJECT_ID_ALMOND: /* ENCODER_OBJECT_ID_NUTMEG */ ++ id = ENCODER_ID_EXTERNAL_NUTMEG; ++ break; ++ case ENCODER_OBJECT_ID_TRAVIS: ++ id = ENCODER_ID_EXTERNAL_TRAVIS; ++ break; ++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: ++ id = ENCODER_ID_INTERNAL_UNIPHY3; ++ break; ++ default: ++ id = ENCODER_ID_UNKNOWN; ++ break; ++ } ++ ++ return id; ++} ++ ++uint32_t gpu_id_from_bios_object_id(uint32_t bios_object_id) ++{ ++ return (bios_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; ++} ++ ++enum generic_id generic_id_from_bios_object_id(uint32_t bios_object_id) ++{ ++ uint32_t bios_generic_id = gpu_id_from_bios_object_id(bios_object_id); ++ ++ enum generic_id id; ++ ++ switch (bios_generic_id) { ++ case GENERIC_OBJECT_ID_MXM_OPM: ++ id = GENERIC_ID_MXM_OPM; ++ break; ++ case GENERIC_OBJECT_ID_GLSYNC: ++ id = GENERIC_ID_GLSYNC; ++ break; ++ case GENERIC_OBJECT_ID_STEREO_PIN: ++ id = GENERIC_ID_STEREO; ++ break; ++ default: ++ id = GENERIC_ID_UNKNOWN; ++ break; ++ } ++ ++ return id; ++} ++ ++static struct device_id device_type_from_device_id(uint16_t device_id) ++{ ++ ++ struct device_id result_device_id; ++ ++ switch (device_id) { ++ case ATOM_DEVICE_LCD1_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_LCD; ++ result_device_id.enum_id = 1; ++ break; ++ ++ case ATOM_DEVICE_LCD2_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_LCD; ++ result_device_id.enum_id = 2; ++ break; ++ ++ case ATOM_DEVICE_CRT1_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_CRT; ++ result_device_id.enum_id = 1; ++ break; ++ ++ case ATOM_DEVICE_CRT2_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_CRT; ++ result_device_id.enum_id = 2; ++ break; ++ ++ case ATOM_DEVICE_DFP1_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_DFP; ++ result_device_id.enum_id = 1; ++ break; ++ ++ case ATOM_DEVICE_DFP2_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_DFP; ++ result_device_id.enum_id = 2; ++ break; ++ ++ case ATOM_DEVICE_DFP3_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_DFP; ++ result_device_id.enum_id = 3; ++ break; ++ ++ case ATOM_DEVICE_DFP4_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_DFP; ++ result_device_id.enum_id = 4; ++ break; ++ ++ case ATOM_DEVICE_DFP5_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_DFP; ++ result_device_id.enum_id = 5; ++ break; ++ ++ case ATOM_DEVICE_DFP6_SUPPORT: ++ result_device_id.device_type = DEVICE_TYPE_DFP; ++ result_device_id.enum_id = 6; ++ break; ++ ++ default: ++ BREAK_TO_DEBUGGER(); /* Invalid device Id */ ++ result_device_id.device_type = DEVICE_TYPE_UNKNOWN; ++ result_device_id.enum_id = 0; ++ } ++ return result_device_id; ++} ++ ++static void get_atom_data_table_revision( ++ ATOM_COMMON_TABLE_HEADER *atom_data_tbl, ++ struct atom_data_revision *tbl_revision) ++{ ++ if (!tbl_revision) ++ return; ++ ++ /* initialize the revision to 0 which is invalid revision */ ++ tbl_revision->major = 0; ++ tbl_revision->minor = 0; ++ ++ if (!atom_data_tbl) ++ return; ++ ++ tbl_revision->major = ++ (uint32_t) GET_DATA_TABLE_MAJOR_REVISION(atom_data_tbl); ++ tbl_revision->minor = ++ (uint32_t) GET_DATA_TABLE_MINOR_REVISION(atom_data_tbl); ++} ++ ++static uint32_t signal_to_ss_id(enum as_signal_type signal) ++{ ++ uint32_t clk_id_ss = 0; ++ ++ switch (signal) { ++ case AS_SIGNAL_TYPE_DVI: ++ clk_id_ss = ASIC_INTERNAL_SS_ON_TMDS; ++ break; ++ case AS_SIGNAL_TYPE_HDMI: ++ clk_id_ss = ASIC_INTERNAL_SS_ON_HDMI; ++ break; ++ case AS_SIGNAL_TYPE_LVDS: ++ clk_id_ss = ASIC_INTERNAL_SS_ON_LVDS; ++ break; ++ case AS_SIGNAL_TYPE_DISPLAY_PORT: ++ clk_id_ss = ASIC_INTERNAL_SS_ON_DP; ++ break; ++ case AS_SIGNAL_TYPE_GPU_PLL: ++ clk_id_ss = ASIC_INTERNAL_GPUPLL_SS; ++ break; ++ default: ++ break; ++ } ++ return clk_id_ss; ++} ++ ++static uint32_t get_support_mask_for_device_id(struct device_id device_id) ++{ ++ enum dal_device_type device_type = device_id.device_type; ++ uint32_t enum_id = device_id.enum_id; ++ ++ switch (device_type) { ++ case DEVICE_TYPE_LCD: ++ switch (enum_id) { ++ case 1: ++ return ATOM_DEVICE_LCD1_SUPPORT; ++ case 2: ++ return ATOM_DEVICE_LCD2_SUPPORT; ++ default: ++ break; ++ } ++ break; ++ case DEVICE_TYPE_CRT: ++ switch (enum_id) { ++ case 1: ++ return ATOM_DEVICE_CRT1_SUPPORT; ++ case 2: ++ return ATOM_DEVICE_CRT2_SUPPORT; ++ default: ++ break; ++ } ++ break; ++ case DEVICE_TYPE_DFP: ++ switch (enum_id) { ++ case 1: ++ return ATOM_DEVICE_DFP1_SUPPORT; ++ case 2: ++ return ATOM_DEVICE_DFP2_SUPPORT; ++ case 3: ++ return ATOM_DEVICE_DFP3_SUPPORT; ++ case 4: ++ return ATOM_DEVICE_DFP4_SUPPORT; ++ case 5: ++ return ATOM_DEVICE_DFP5_SUPPORT; ++ case 6: ++ return ATOM_DEVICE_DFP6_SUPPORT; ++ default: ++ break; ++ } ++ break; ++ case DEVICE_TYPE_CV: ++ switch (enum_id) { ++ case 1: ++ return ATOM_DEVICE_CV_SUPPORT; ++ default: ++ break; ++ } ++ break; ++ case DEVICE_TYPE_TV: ++ switch (enum_id) { ++ case 1: ++ return ATOM_DEVICE_TV1_SUPPORT; ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ }; ++ ++ /* Unidentified device ID, return empty support mask. */ ++ return 0; ++} ++ ++/** ++* HwContext interface for writing MM registers ++*/ ++ ++static bool i2c_read( ++ struct bios_parser *bp, ++ struct graphics_object_i2c_info *i2c_info, ++ uint8_t *buffer, ++ uint32_t length) ++{ ++ struct ddc *ddc; ++ uint8_t offset[2] = { 0, 0 }; ++ bool result = false; ++ struct i2c_command cmd; ++ ++ ddc = dal_adapter_service_obtain_ddc_from_i2c_info(bp->as, i2c_info); ++ ++ if (!ddc) ++ return result; ++ ++ /*Using SW engine */ ++ cmd.engine = I2C_COMMAND_ENGINE_SW; ++ cmd.speed = dal_adapter_service_get_sw_i2c_speed(bp->as); ++ ++ { ++ struct i2c_payload payloads[] = { ++ { ++ .address = i2c_info->i2c_slave_address >> 1, ++ .data = offset, ++ .length = sizeof(offset), ++ .write = true ++ }, ++ { ++ .address = i2c_info->i2c_slave_address >> 1, ++ .data = buffer, ++ .length = length, ++ .write = false ++ } ++ }; ++ ++ cmd.payloads = payloads; ++ cmd.number_of_payloads = ARRAY_SIZE(payloads); ++ ++ result = dal_i2caux_submit_i2c_command( ++ dal_adapter_service_get_i2caux(bp->as), ++ ddc, ++ &cmd); ++ } ++ ++ dal_adapter_service_release_ddc(bp->as, ddc); ++ ++ return result; ++} ++ ++/** ++ * Read external display connection info table through i2c. ++ * validate the GUID and checksum. ++ * ++ * @return enum bp_result whether all data was sucessfully read ++ */ ++static enum bp_result get_ext_display_connection_info( ++ struct bios_parser *bp, ++ ATOM_OBJECT *opm_object, ++ ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO *ext_display_connection_info_tbl) ++{ ++ bool config_tbl_present = false; ++ ATOM_I2C_RECORD *i2c_record = NULL; ++ uint32_t i = 0; ++ ++ if (opm_object == NULL) ++ return BP_RESULT_BADINPUT; ++ ++ i2c_record = get_i2c_record(bp, opm_object); ++ ++ if (i2c_record != NULL) { ++ ATOM_GPIO_I2C_INFO *gpio_i2c_header; ++ struct graphics_object_i2c_info i2c_info; ++ ++ gpio_i2c_header = GET_IMAGE(ATOM_GPIO_I2C_INFO, ++ bp->master_data_tbl->ListOfDataTables.GPIO_I2C_Info); ++ ++ if (NULL == gpio_i2c_header) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (get_gpio_i2c_info(bp, i2c_record, &i2c_info) != ++ BP_RESULT_OK) ++ return BP_RESULT_BADBIOSTABLE; ++ ++ if (i2c_read( ++ bp, ++ &i2c_info, ++ (uint8_t *)ext_display_connection_info_tbl, ++ sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO))) { ++ config_tbl_present = true; ++ } ++ } ++ ++ /* Validate GUID */ ++ if (config_tbl_present) ++ for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; i++) { ++ if (ext_display_connection_info_tbl->ucGuid[i] ++ != ext_display_connection_guid[i]) { ++ config_tbl_present = false; ++ break; ++ } ++ } ++ ++ /* Validate checksum */ ++ if (config_tbl_present) { ++ uint8_t check_sum = 0; ++ uint8_t *buf = ++ (uint8_t *)ext_display_connection_info_tbl; ++ ++ for (i = 0; i < sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO); ++ i++) { ++ check_sum += buf[i]; ++ } ++ ++ if (check_sum != 0) ++ config_tbl_present = false; ++ } ++ ++ if (config_tbl_present) ++ return BP_RESULT_OK; ++ else ++ return BP_RESULT_FAILURE; ++} ++ ++/* ++ * Gets the first device ID in the same group as the given ID for enumerating. ++ * For instance, if any DFP device ID is passed, returns the device ID for DFP1. ++ * ++ * The first device ID in the same group as the passed device ID, or 0 if no ++ * matching device group found. ++ */ ++static uint32_t enum_first_device_id(uint32_t dev_id) ++{ ++ /* Return the first in the group that this ID belongs to. */ ++ if (dev_id & ATOM_DEVICE_CRT_SUPPORT) ++ return ATOM_DEVICE_CRT1_SUPPORT; ++ else if (dev_id & ATOM_DEVICE_DFP_SUPPORT) ++ return ATOM_DEVICE_DFP1_SUPPORT; ++ else if (dev_id & ATOM_DEVICE_LCD_SUPPORT) ++ return ATOM_DEVICE_LCD1_SUPPORT; ++ else if (dev_id & ATOM_DEVICE_TV_SUPPORT) ++ return ATOM_DEVICE_TV1_SUPPORT; ++ else if (dev_id & ATOM_DEVICE_CV_SUPPORT) ++ return ATOM_DEVICE_CV_SUPPORT; ++ ++ /* No group found for this device ID. */ ++ ++ dal_error("%s: incorrect input %d\n", __func__, dev_id); ++ /* No matching support flag for given device ID */ ++ return 0; ++} ++ ++/* ++ * Gets the next device ID in the group for a given device ID. ++ * ++ * The current device ID being enumerated on. ++ * ++ * The next device ID in the group, or 0 if no device exists. ++ */ ++static uint32_t enum_next_dev_id(uint32_t dev_id) ++{ ++ /* Get next device ID in the group. */ ++ switch (dev_id) { ++ case ATOM_DEVICE_CRT1_SUPPORT: ++ return ATOM_DEVICE_CRT2_SUPPORT; ++ case ATOM_DEVICE_LCD1_SUPPORT: ++ return ATOM_DEVICE_LCD2_SUPPORT; ++ case ATOM_DEVICE_DFP1_SUPPORT: ++ return ATOM_DEVICE_DFP2_SUPPORT; ++ case ATOM_DEVICE_DFP2_SUPPORT: ++ return ATOM_DEVICE_DFP3_SUPPORT; ++ case ATOM_DEVICE_DFP3_SUPPORT: ++ return ATOM_DEVICE_DFP4_SUPPORT; ++ case ATOM_DEVICE_DFP4_SUPPORT: ++ return ATOM_DEVICE_DFP5_SUPPORT; ++ case ATOM_DEVICE_DFP5_SUPPORT: ++ return ATOM_DEVICE_DFP6_SUPPORT; ++ } ++ ++ /* Done enumerating through devices. */ ++ return 0; ++} ++ ++/* ++ * Returns the new device tag record for patched BIOS object. ++ * ++ * [IN] pExtDisplayPath - External display path to copy device tag from. ++ * [IN] deviceSupport - Bit vector for device ID support flags. ++ * [OUT] pDeviceTag - Device tag structure to fill with patched data. ++ * ++ * True if a compatible device ID was found, false otherwise. ++ */ ++static bool get_patched_device_tag( ++ struct bios_parser *bp, ++ EXT_DISPLAY_PATH *ext_display_path, ++ uint32_t device_support, ++ ATOM_CONNECTOR_DEVICE_TAG *device_tag) ++{ ++ uint32_t dev_id; ++ /* Use fallback behaviour if not supported. */ ++ if (!bp->remap_device_tags) { ++ device_tag->ulACPIDeviceEnum = ++ cpu_to_le32((uint32_t) le16_to_cpu(ext_display_path->usDeviceACPIEnum)); ++ device_tag->usDeviceID = ++ cpu_to_le16(le16_to_cpu(ext_display_path->usDeviceTag)); ++ return true; ++ } ++ ++ /* Find the first unused in the same group. */ ++ dev_id = enum_first_device_id(le16_to_cpu(ext_display_path->usDeviceTag)); ++ while (dev_id != 0) { ++ /* Assign this device ID if supported. */ ++ if ((device_support & dev_id) != 0) { ++ device_tag->ulACPIDeviceEnum = ++ cpu_to_le32((uint32_t) le16_to_cpu(ext_display_path->usDeviceACPIEnum)); ++ device_tag->usDeviceID = cpu_to_le16((USHORT) dev_id); ++ return true; ++ } ++ ++ dev_id = enum_next_dev_id(dev_id); ++ } ++ ++ /* No compatible device ID found. */ ++ return false; ++} ++ ++/* ++ * Adds a device tag to a BIOS object's device tag record if there is ++ * matching device ID supported. ++ * ++ * pObject - Pointer to the BIOS object to add the device tag to. ++ * pExtDisplayPath - Display path to retrieve base device ID from. ++ * pDeviceSupport - Pointer to bit vector for supported device IDs. ++ */ ++static void add_device_tag_from_ext_display_path( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object, ++ EXT_DISPLAY_PATH *ext_display_path, ++ uint32_t *device_support) ++{ ++ /* Get device tag record for object. */ ++ ATOM_CONNECTOR_DEVICE_TAG *device_tag = NULL; ++ ATOM_CONNECTOR_DEVICE_TAG_RECORD *device_tag_record = NULL; ++ enum bp_result result = ++ dal_bios_parser_get_device_tag_record( ++ bp, object, &device_tag_record); ++ ++ if ((le16_to_cpu(ext_display_path->usDeviceTag) != CONNECTOR_OBJECT_ID_NONE) ++ && (result == BP_RESULT_OK)) { ++ uint8_t index; ++ ++ if ((device_tag_record->ucNumberOfDevice == 1) && ++ (le16_to_cpu(device_tag_record->asDeviceTag[0].usDeviceID) == 0)) { ++ /*Workaround bug in current VBIOS releases where ++ * ucNumberOfDevice = 1 but there is no actual device ++ * tag data. This w/a is temporary until the updated ++ * VBIOS is distributed. */ ++ device_tag_record->ucNumberOfDevice = ++ device_tag_record->ucNumberOfDevice - 1; ++ } ++ ++ /* Attempt to find a matching device ID. */ ++ index = device_tag_record->ucNumberOfDevice; ++ device_tag = &device_tag_record->asDeviceTag[index]; ++ if (get_patched_device_tag( ++ bp, ++ ext_display_path, ++ *device_support, ++ device_tag)) { ++ /* Update cached device support to remove assigned ID. ++ */ ++ *device_support &= ~le16_to_cpu(device_tag->usDeviceID); ++ device_tag_record->ucNumberOfDevice++; ++ } ++ } ++} ++ ++/* ++ * Read out a single EXT_DISPLAY_PATH from the external display connection info ++ * table. The specific entry in the table is determined by the enum_id passed ++ * in. ++ * ++ * EXT_DISPLAY_PATH describing a single Configuration table entry ++ */ ++ ++#define INVALID_CONNECTOR 0xffff ++ ++static EXT_DISPLAY_PATH *get_ext_display_path_entry( ++ ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO *config_table, ++ uint32_t bios_object_id) ++{ ++ EXT_DISPLAY_PATH *ext_display_path; ++ uint32_t ext_display_path_index = ++ ((bios_object_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT) - 1; ++ ++ if (ext_display_path_index >= MAX_NUMBER_OF_EXT_DISPLAY_PATH) ++ return NULL; ++ ++ ext_display_path = &config_table->sPath[ext_display_path_index]; ++ ++ if (le16_to_cpu(ext_display_path->usDeviceConnector) == INVALID_CONNECTOR) ++ ext_display_path->usDeviceConnector = cpu_to_le16(0); ++ ++ return ext_display_path; ++} ++ ++/* ++ * Get AUX/DDC information of input object id ++ * ++ * search all records to find the ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE record ++ * IR ++ */ ++static ATOM_CONNECTOR_AUXDDC_LUT_RECORD *get_ext_connector_aux_ddc_lut_record( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object) ++{ ++ uint32_t offset; ++ ATOM_COMMON_RECORD_HEADER *header; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); ++ /* Invalid object */ ++ return NULL; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return NULL; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ 0 == header->ucRecordSize) ++ break; ++ ++ if (ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE == ++ header->ucRecordType && ++ sizeof(ATOM_CONNECTOR_AUXDDC_LUT_RECORD) <= ++ header->ucRecordSize) ++ return (ATOM_CONNECTOR_AUXDDC_LUT_RECORD *)(header); ++ ++ offset += header->ucRecordSize; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Get AUX/DDC information of input object id ++ * ++ * search all records to find the ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE record ++ * IR ++ */ ++static ATOM_CONNECTOR_HPDPIN_LUT_RECORD *get_ext_connector_hpd_pin_lut_record( ++ struct bios_parser *bp, ++ ATOM_OBJECT *object) ++{ ++ uint32_t offset; ++ ATOM_COMMON_RECORD_HEADER *header; ++ ++ if (!object) { ++ BREAK_TO_DEBUGGER(); ++ /* Invalid object */ ++ return NULL; ++ } ++ ++ offset = le16_to_cpu(object->usRecordOffset) ++ + bp->object_info_tbl_offset; ++ ++ for (;;) { ++ header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); ++ ++ if (!header) ++ return NULL; ++ ++ if (LAST_RECORD_TYPE == header->ucRecordType || ++ 0 == header->ucRecordSize) ++ break; ++ ++ if (ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE == ++ header->ucRecordType && ++ sizeof(ATOM_CONNECTOR_HPDPIN_LUT_RECORD) <= ++ header->ucRecordSize) ++ return (ATOM_CONNECTOR_HPDPIN_LUT_RECORD *)header; ++ ++ offset += header->ucRecordSize; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Check whether we need to patch the VBIOS connector info table with ++ * data from an external display connection info table. This is ++ * necessary to support MXM boards with an OPM (output personality ++ * module). With these designs, the VBIOS connector info table ++ * specifies an MXM_CONNECTOR with a unique ID. The driver retrieves ++ * the external connection info table through i2c and then looks up the ++ * connector ID to find the real connector type (e.g. DFP1). ++ * ++ */ ++static enum bp_result patch_bios_image_from_ext_display_connection_info( ++ struct bios_parser *bp) ++{ ++ ATOM_OBJECT_TABLE *connector_tbl; ++ uint32_t connector_tbl_offset; ++ struct graphics_object_id object_id; ++ ATOM_OBJECT *object; ++ ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO ext_display_connection_info_tbl; ++ EXT_DISPLAY_PATH *ext_display_path; ++ ATOM_CONNECTOR_AUXDDC_LUT_RECORD *aux_ddc_lut_record = NULL; ++ ATOM_I2C_RECORD *i2c_record = NULL; ++ ATOM_CONNECTOR_HPDPIN_LUT_RECORD *hpd_pin_lut_record = NULL; ++ ATOM_HPD_INT_RECORD *hpd_record = NULL; ++ ATOM_OBJECT_TABLE *encoder_table; ++ uint32_t encoder_table_offset; ++ ATOM_OBJECT *opm_object = NULL; ++ uint32_t i = 0; ++ struct graphics_object_id opm_object_id = ++ dal_graphics_object_id_init( ++ GENERIC_ID_MXM_OPM, ++ ENUM_ID_1, ++ OBJECT_TYPE_GENERIC); ++ ATOM_CONNECTOR_DEVICE_TAG_RECORD *dev_tag_record; ++ uint32_t cached_device_support = ++ le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport); ++ ++ uint32_t dst_number; ++ uint16_t *dst_object_id_list; ++ ++ opm_object = get_bios_object(bp, opm_object_id); ++ if (!opm_object) ++ return BP_RESULT_UNSUPPORTED; ++ ++ dc_service_memset(&ext_display_connection_info_tbl, 0, ++ sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO)); ++ ++ connector_tbl_offset = bp->object_info_tbl_offset ++ + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); ++ connector_tbl = GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); ++ ++ /* Read Connector info table from EEPROM through i2c */ ++ if (get_ext_display_connection_info( ++ bp, ++ opm_object, ++ &ext_display_connection_info_tbl) != BP_RESULT_OK) { ++ if (bp->headless_no_opm) { ++ /* Failed to read OPM, remove all non-CF connectors. */ ++ for (i = 0; i < connector_tbl->ucNumberOfObjects; ++i) { ++ object = &connector_tbl->asObjects[i]; ++ object_id = object_id_from_bios_object_id( ++ le16_to_cpu(object->usObjectID)); ++ if (OBJECT_TYPE_CONNECTOR == object_id.type) ++ object->usObjectID = cpu_to_le16(0); ++ } ++ ++ return BP_RESULT_OK; ++ } ++ ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: Failed to read Connection Info Table", __func__); ++ return BP_RESULT_UNSUPPORTED; ++ } ++ ++ /* Get pointer to AUX/DDC and HPD LUTs */ ++ aux_ddc_lut_record = ++ get_ext_connector_aux_ddc_lut_record(bp, opm_object); ++ hpd_pin_lut_record = ++ get_ext_connector_hpd_pin_lut_record(bp, opm_object); ++ ++ if ((aux_ddc_lut_record == NULL) || (hpd_pin_lut_record == NULL)) ++ return BP_RESULT_UNSUPPORTED; ++ ++ /* Cache support bits for currently unmapped device types. */ ++ if (bp->remap_device_tags) { ++ for (i = 0; i < connector_tbl->ucNumberOfObjects; ++i) { ++ uint32_t j; ++ /* Remove support for all non-MXM connectors. */ ++ object = &connector_tbl->asObjects[i]; ++ object_id = object_id_from_bios_object_id( ++ le16_to_cpu(object->usObjectID)); ++ if ((OBJECT_TYPE_CONNECTOR != object_id.type) || ++ (CONNECTOR_ID_MXM == object_id.id)) ++ continue; ++ ++ /* Remove support for all device tags. */ ++ if (dal_bios_parser_get_device_tag_record( ++ bp, object, &dev_tag_record) != BP_RESULT_OK) ++ continue; ++ ++ for (j = 0; j < dev_tag_record->ucNumberOfDevice; ++j) { ++ ATOM_CONNECTOR_DEVICE_TAG *device_tag = ++ &dev_tag_record->asDeviceTag[j]; ++ cached_device_support &= ++ ~le16_to_cpu(device_tag->usDeviceID); ++ } ++ } ++ } ++ ++ /* Find all MXM connector objects and patch them with connector info ++ * from the external display connection info table. */ ++ for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { ++ uint32_t j; ++ ++ object = &connector_tbl->asObjects[i]; ++ object_id = object_id_from_bios_object_id(le16_to_cpu(object->usObjectID)); ++ if ((OBJECT_TYPE_CONNECTOR != object_id.type) || ++ (CONNECTOR_ID_MXM != object_id.id)) ++ continue; ++ ++ /* Get the correct connection info table entry based on the enum ++ * id. */ ++ ext_display_path = get_ext_display_path_entry( ++ &ext_display_connection_info_tbl, ++ le16_to_cpu(object->usObjectID)); ++ if (!ext_display_path) ++ return BP_RESULT_FAILURE; ++ ++ /* Patch device connector ID */ ++ object->usObjectID = ++ cpu_to_le16(le16_to_cpu(ext_display_path->usDeviceConnector)); ++ ++ /* Patch device tag, ulACPIDeviceEnum. */ ++ add_device_tag_from_ext_display_path( ++ bp, ++ object, ++ ext_display_path, ++ &cached_device_support); ++ ++ /* Patch HPD info */ ++ if (ext_display_path->ucExtHPDPINLutIndex < ++ MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES) { ++ hpd_record = get_hpd_record(bp, object); ++ if (hpd_record) { ++ uint8_t index = ++ ext_display_path->ucExtHPDPINLutIndex; ++ hpd_record->ucHPDIntGPIOID = ++ hpd_pin_lut_record->ucHPDPINMap[index]; ++ } else { ++ BREAK_TO_DEBUGGER(); ++ /* Invalid hpd record */ ++ return BP_RESULT_FAILURE; ++ } ++ } ++ ++ /* Patch I2C/AUX info */ ++ if (ext_display_path->ucExtHPDPINLutIndex < ++ MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES) { ++ i2c_record = get_i2c_record(bp, object); ++ if (i2c_record) { ++ uint8_t index = ++ ext_display_path->ucExtAUXDDCLutIndex; ++ i2c_record->sucI2cId = ++ aux_ddc_lut_record->ucAUXDDCMap[index]; ++ } else { ++ BREAK_TO_DEBUGGER(); ++ /* Invalid I2C record */ ++ return BP_RESULT_FAILURE; ++ } ++ } ++ ++ /* Merge with other MXM connectors that map to the same physical ++ * connector. */ ++ for (j = i + 1; ++ j < connector_tbl->ucNumberOfObjects; j++) { ++ ATOM_OBJECT *next_object; ++ struct graphics_object_id next_object_id; ++ EXT_DISPLAY_PATH *next_ext_display_path; ++ ++ next_object = &connector_tbl->asObjects[j]; ++ next_object_id = object_id_from_bios_object_id( ++ le16_to_cpu(next_object->usObjectID)); ++ ++ if ((OBJECT_TYPE_CONNECTOR != next_object_id.type) && ++ (CONNECTOR_ID_MXM == next_object_id.id)) ++ continue; ++ ++ next_ext_display_path = get_ext_display_path_entry( ++ &ext_display_connection_info_tbl, ++ le16_to_cpu(next_object->usObjectID)); ++ ++ if (next_ext_display_path == NULL) ++ return BP_RESULT_FAILURE; ++ ++ /* Merge if using same connector. */ ++ if ((le16_to_cpu(next_ext_display_path->usDeviceConnector) == ++ le16_to_cpu(ext_display_path->usDeviceConnector)) && ++ (le16_to_cpu(ext_display_path->usDeviceConnector) != 0)) { ++ /* Clear duplicate connector from table. */ ++ next_object->usObjectID = cpu_to_le16(0); ++ add_device_tag_from_ext_display_path( ++ bp, ++ object, ++ ext_display_path, ++ &cached_device_support); ++ } ++ } ++ } ++ ++ /* Find all encoders which have an MXM object as their destination. ++ * Replace the MXM object with the real connector Id from the external ++ * display connection info table */ ++ ++ encoder_table_offset = bp->object_info_tbl_offset ++ + le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); ++ encoder_table = GET_IMAGE(ATOM_OBJECT_TABLE, encoder_table_offset); ++ ++ for (i = 0; i < encoder_table->ucNumberOfObjects; i++) { ++ uint32_t j; ++ ++ object = &encoder_table->asObjects[i]; ++ ++ dst_number = get_dest_obj_list(bp, object, &dst_object_id_list); ++ ++ for (j = 0; j < dst_number; j++) { ++ object_id = object_id_from_bios_object_id( ++ dst_object_id_list[j]); ++ ++ if ((OBJECT_TYPE_CONNECTOR != object_id.type) || ++ (CONNECTOR_ID_MXM != object_id.id)) ++ continue; ++ ++ /* Get the correct connection info table entry based on ++ * the enum id. */ ++ ext_display_path = ++ get_ext_display_path_entry( ++ &ext_display_connection_info_tbl, ++ dst_object_id_list[j]); ++ ++ if (ext_display_path == NULL) ++ return BP_RESULT_FAILURE; ++ ++ dst_object_id_list[j] = ++ le16_to_cpu(ext_display_path->usDeviceConnector); ++ } ++ } ++ ++ return BP_RESULT_OK; ++} ++ ++/* ++ * Check whether we need to patch the VBIOS connector info table with ++ * data from an external display connection info table. This is ++ * necessary to support MXM boards with an OPM (output personality ++ * module). With these designs, the VBIOS connector info table ++ * specifies an MXM_CONNECTOR with a unique ID. The driver retrieves ++ * the external connection info table through i2c and then looks up the ++ * connector ID to find the real connector type (e.g. DFP1). ++ * ++ */ ++ ++static void process_ext_display_connection_info(struct bios_parser *bp) ++{ ++ ATOM_OBJECT_TABLE *connector_tbl; ++ uint32_t connector_tbl_offset; ++ struct graphics_object_id object_id; ++ ATOM_OBJECT *object; ++ bool mxm_connector_found = false; ++ bool null_entry_found = false; ++ uint32_t i = 0; ++ ++ connector_tbl_offset = bp->object_info_tbl_offset + ++ le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); ++ connector_tbl = GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); ++ ++ /* Look for MXM connectors to determine whether we need patch the VBIOS ++ * connector info table. Look for null entries to determine whether we ++ * need to compact connector table. */ ++ for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { ++ object = &connector_tbl->asObjects[i]; ++ object_id = object_id_from_bios_object_id(le16_to_cpu(object->usObjectID)); ++ ++ if ((OBJECT_TYPE_CONNECTOR == object_id.type) && ++ (CONNECTOR_ID_MXM == object_id.id)) { ++ /* Once we found MXM connector - we can break */ ++ mxm_connector_found = true; ++ break; ++ } else if (OBJECT_TYPE_CONNECTOR != object_id.type) { ++ /* We need to continue looping - to check if MXM ++ * connector present */ ++ null_entry_found = true; ++ } ++ } ++ ++ /* Patch BIOS image */ ++ if (mxm_connector_found || null_entry_found) { ++ uint32_t connectors_num = 0; ++ uint8_t *original_bios; ++ /* Step 1: Replace bios image with the new copy which will be ++ * patched */ ++ bp->bios_local_image = dc_service_alloc(bp->ctx, bp->bios_size); ++ if (bp->bios_local_image == NULL) { ++ BREAK_TO_DEBUGGER(); ++ /* Failed to alloc bp->bios_local_image */ ++ return; ++ } ++ ++ dc_service_memmove(bp->bios_local_image, bp->bios, bp->bios_size); ++ original_bios = bp->bios; ++ bp->bios = bp->bios_local_image; ++ connector_tbl = ++ GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); ++ ++ /* Step 2: (only if MXM connector found) Patch BIOS image with ++ * info from external module */ ++ if (mxm_connector_found && ++ patch_bios_image_from_ext_display_connection_info(bp) != ++ BP_RESULT_OK) { ++ /* Patching the bios image has failed. We will copy ++ * again original image provided and afterwards ++ * only remove null entries */ ++ dc_service_memmove( ++ bp->bios_local_image, ++ original_bios, ++ bp->bios_size); ++ } ++ ++ /* Step 3: Compact connector table (remove null entries, valid ++ * entries moved to beginning) */ ++ for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { ++ object = &connector_tbl->asObjects[i]; ++ object_id = object_id_from_bios_object_id( ++ le16_to_cpu(object->usObjectID)); ++ ++ if (OBJECT_TYPE_CONNECTOR != object_id.type) ++ continue; ++ ++ if (i != connectors_num) { ++ dc_service_memmove( ++ &connector_tbl-> ++ asObjects[connectors_num], ++ object, ++ sizeof(ATOM_OBJECT)); ++ } ++ ++connectors_num; ++ } ++ connector_tbl->ucNumberOfObjects = (uint8_t)connectors_num; ++ } ++} ++ ++void dal_bios_parser_post_init(struct bios_parser *bp) ++{ ++ process_ext_display_connection_info(bp); ++} ++ ++bool dal_bios_parser_is_accelerated_mode( ++ struct bios_parser *bp) ++{ ++#ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++ return bp->bios_helper->is_accelerated_mode( ++ bp->ctx); ++#else ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: VBIOS is not supported", __func__); ++ return false; ++#endif ++} ++ ++/** ++* dal_bios_parser_set_scratch_connected ++* ++* @brief ++* update VBIOS scratch register about connected displays ++* ++* @param ++* bool - update scratch register or just prepare info to be updated ++* bool - connection state ++* const ConnectorDeviceTagInfo* - pointer to device type and enum ID ++*/ ++void dal_bios_parser_set_scratch_connected( ++ struct bios_parser *bp, ++ struct graphics_object_id connector_id, ++ bool connected, ++ const struct connector_device_tag_info *device_tag) ++{ ++#ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++ bp->bios_helper->set_scratch_connected( ++ bp->ctx, ++ connector_id, connected, device_tag); ++#else ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: VBIOS is not supported", __func__); ++#endif ++} ++ ++/** ++* dal_bios_parser_set_scratch_critical_state ++* ++* @brief ++* update critical state bit in VBIOS scratch register ++* ++* @param ++* bool - to set or reset state ++*/ ++void dal_bios_parser_set_scratch_critical_state( ++ struct bios_parser *bp, ++ bool state) ++{ ++#ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++ bp->bios_helper->set_scratch_critical_state( ++ bp->ctx, state); ++#else ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: VBIOS is not supported", __func__); ++#endif ++} ++ ++void dal_bios_parser_set_scratch_acc_mode_change( ++ struct bios_parser *bp) ++{ ++#ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++ bp->bios_helper->set_scratch_acc_mode_change( ++ bp->ctx); ++#else ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: VBIOS is not supported", __func__); ++#endif ++} ++ ++/** ++* dal_bios_parser_prepare_scratch_active_and_requested ++* ++* @brief ++* update VBIOS scratch registers about active and requested displays ++* ++* @param ++* enum controller_id - controller Id ++* enum signal_type signal - signal type used on display ++* const struct connector_device_tag_info * - pointer to display type and ++* enum Id ++*/ ++void dal_bios_parser_prepare_scratch_active_and_requested( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ enum signal_type signal, ++ const struct connector_device_tag_info *device_tag) ++{ ++#ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++ bp->bios_helper->prepare_scratch_active_and_requested( ++ bp->ctx, ++ &bp->vbios_helper_data, ++ controller_id, ++ signal, ++ device_tag); ++#else ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: VBIOS is not supported", __func__); ++#endif ++} ++ ++void dal_bios_parser_set_scratch_active_and_requested( ++ struct bios_parser *bp) ++{ ++#ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++ bp->bios_helper->set_scratch_active_and_requested( ++ bp->ctx, ++ &bp->vbios_helper_data); ++#else ++ dal_logger_write(bp->ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_BIOS_CMD_TABLE, ++ "%s: VBIOS is not supported", __func__); ++#endif ++} ++ ++/* ++ * get_integrated_info_v8 ++ * ++ * @brief ++ * Get V8 integrated BIOS information ++ * ++ * @param ++ * bios_parser *bp - [in]BIOS parser handler to get master data table ++ * integrated_info *info - [out] store and output integrated info ++ * ++ * @return ++ * enum bp_result - BP_RESULT_OK if information is available, ++ * BP_RESULT_BADBIOSTABLE otherwise. ++ */ ++static enum bp_result get_integrated_info_v8( ++ struct bios_parser *bp, ++ struct integrated_info *info) ++{ ++ enum bp_result result = BP_RESULT_BADBIOSTABLE; ++ ATOM_INTEGRATED_SYSTEM_INFO_V1_8 *info_v8; ++ uint32_t i; ++ ++ info_v8 = GET_IMAGE(ATOM_INTEGRATED_SYSTEM_INFO_V1_8, ++ bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); ++ ++ if (info_v8 != NULL) { ++ info->boot_up_engine_clock = ++ le32_to_cpu(info_v8->ulBootUpEngineClock) * 10; ++ info->dentist_vco_freq = ++ le32_to_cpu(info_v8->ulDentistVCOFreq) * 10; ++ info->boot_up_uma_clock = ++ le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; ++ ++ for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { ++ /* Convert [10KHz] into [KHz] */ ++ info->disp_clk_voltage[i].max_supported_clk = ++ le32_to_cpu(info_v8->sDISPCLK_Voltage[i]. ++ ulMaximumSupportedCLK) * 10; ++ info->disp_clk_voltage[i].voltage_index = ++ le32_to_cpu(info_v8->sDISPCLK_Voltage[i].ulVoltageIndex); ++ } ++ ++ info->boot_up_req_display_vector = ++ le32_to_cpu(info_v8->ulBootUpReqDisplayVector); ++ info->gpu_cap_info = ++ le32_to_cpu(info_v8->ulGPUCapInfo); ++ ++ /* ++ * system_config: Bit[0] = 0 : PCIE power gating disabled ++ * = 1 : PCIE power gating enabled ++ * Bit[1] = 0 : DDR-PLL shut down disabled ++ * = 1 : DDR-PLL shut down enabled ++ * Bit[2] = 0 : DDR-PLL power down disabled ++ * = 1 : DDR-PLL power down enabled ++ */ ++ info->system_config = le32_to_cpu(info_v8->ulSystemConfig); ++ info->cpu_cap_info = le32_to_cpu(info_v8->ulCPUCapInfo); ++ info->boot_up_nb_voltage = ++ le16_to_cpu(info_v8->usBootUpNBVoltage); ++ info->ext_disp_conn_info_offset = ++ le16_to_cpu(info_v8->usExtDispConnInfoOffset); ++ info->memory_type = info_v8->ucMemoryType; ++ info->ma_channel_number = info_v8->ucUMAChannelNumber; ++ info->gmc_restore_reset_time = ++ le32_to_cpu(info_v8->ulGMCRestoreResetTime); ++ ++ info->minimum_n_clk = ++ le32_to_cpu(info_v8->ulNbpStateNClkFreq[0]); ++ for (i = 1; i < 4; ++i) ++ info->minimum_n_clk = ++ info->minimum_n_clk < le32_to_cpu(info_v8->ulNbpStateNClkFreq[i]) ? ++ info->minimum_n_clk : le32_to_cpu(info_v8->ulNbpStateNClkFreq[i]); ++ ++ info->idle_n_clk = le32_to_cpu(info_v8->ulIdleNClk); ++ info->ddr_dll_power_up_time = ++ le32_to_cpu(info_v8->ulDDR_DLL_PowerUpTime); ++ info->ddr_pll_power_up_time = ++ le32_to_cpu(info_v8->ulDDR_PLL_PowerUpTime); ++ info->pcie_clk_ss_type = le16_to_cpu(info_v8->usPCIEClkSSType); ++ info->lvds_ss_percentage = ++ le16_to_cpu(info_v8->usLvdsSSPercentage); ++ info->lvds_sspread_rate_in_10hz = ++ le16_to_cpu(info_v8->usLvdsSSpreadRateIn10Hz); ++ info->hdmi_ss_percentage = ++ le16_to_cpu(info_v8->usHDMISSPercentage); ++ info->hdmi_sspread_rate_in_10hz = ++ le16_to_cpu(info_v8->usHDMISSpreadRateIn10Hz); ++ info->dvi_ss_percentage = ++ le16_to_cpu(info_v8->usDVISSPercentage); ++ info->dvi_sspread_rate_in_10_hz = ++ le16_to_cpu(info_v8->usDVISSpreadRateIn10Hz); ++ ++ info->max_lvds_pclk_freq_in_single_link = ++ le16_to_cpu(info_v8->usMaxLVDSPclkFreqInSingleLink); ++ info->lvds_misc = info_v8->ucLvdsMisc; ++ info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = ++ info_v8->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; ++ info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = ++ info_v8->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; ++ info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = ++ info_v8->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; ++ info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = ++ info_v8->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; ++ info->lvds_pwr_off_seq_de_to_dig_on_in4ms = ++ info_v8->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; ++ info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = ++ info_v8->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; ++ info->lvds_off_to_on_delay_in_4ms = ++ info_v8->ucLVDSOffToOnDelay_in4Ms; ++ info->lvds_bit_depth_control_val = ++ le32_to_cpu(info_v8->ulLCDBitDepthControlVal); ++ ++ for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { ++ /* Convert [10KHz] into [KHz] */ ++ info->avail_s_clk[i].supported_s_clk = ++ le32_to_cpu(info_v8->sAvail_SCLK[i].ulSupportedSCLK) * 10; ++ info->avail_s_clk[i].voltage_index = ++ le16_to_cpu(info_v8->sAvail_SCLK[i].usVoltageIndex); ++ info->avail_s_clk[i].voltage_id = ++ le16_to_cpu(info_v8->sAvail_SCLK[i].usVoltageID); ++ } ++ ++ for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { ++ info->ext_disp_conn_info.gu_id[i] = ++ info_v8->sExtDispConnInfo.ucGuid[i]; ++ } ++ ++ for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { ++ info->ext_disp_conn_info.path[i].device_connector_id = ++ object_id_from_bios_object_id( ++ le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceConnector)); ++ ++ info->ext_disp_conn_info.path[i].ext_encoder_obj_id = ++ object_id_from_bios_object_id( ++ le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usExtEncoderObjId)); ++ ++ info->ext_disp_conn_info.path[i].device_tag = ++ le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceTag); ++ info->ext_disp_conn_info.path[i].device_acpi_enum = ++ le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceACPIEnum); ++ info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = ++ info_v8->sExtDispConnInfo.sPath[i].ucExtAUXDDCLutIndex; ++ info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = ++ info_v8->sExtDispConnInfo.sPath[i].ucExtHPDPINLutIndex; ++ info->ext_disp_conn_info.path[i].channel_mapping.raw = ++ info_v8->sExtDispConnInfo.sPath[i].ucChannelMapping; ++ } ++ info->ext_disp_conn_info.checksum = ++ info_v8->sExtDispConnInfo.ucChecksum; ++ ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++/* ++ * get_integrated_info_v8 ++ * ++ * @brief ++ * Get V8 integrated BIOS information ++ * ++ * @param ++ * bios_parser *bp - [in]BIOS parser handler to get master data table ++ * integrated_info *info - [out] store and output integrated info ++ * ++ * @return ++ * enum bp_result - BP_RESULT_OK if information is available, ++ * BP_RESULT_BADBIOSTABLE otherwise. ++ */ ++static enum bp_result get_integrated_info_v9( ++ struct bios_parser *bp, ++ struct integrated_info *info) ++{ ++ enum bp_result result = BP_RESULT_BADBIOSTABLE; ++ ATOM_INTEGRATED_SYSTEM_INFO_V1_9 *info_v9; ++ uint32_t i; ++ ++ info_v9 = GET_IMAGE(ATOM_INTEGRATED_SYSTEM_INFO_V1_9, ++ bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); ++ ++ if (info_v9 != NULL) { ++ info->boot_up_engine_clock = ++ le32_to_cpu(info_v9->ulBootUpEngineClock) * 10; ++ info->dentist_vco_freq = ++ le32_to_cpu(info_v9->ulDentistVCOFreq) * 10; ++ info->boot_up_uma_clock = ++ le32_to_cpu(info_v9->ulBootUpUMAClock) * 10; ++ ++ for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { ++ /* Convert [10KHz] into [KHz] */ ++ info->disp_clk_voltage[i].max_supported_clk = ++ le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulMaximumSupportedCLK) * 10; ++ info->disp_clk_voltage[i].voltage_index = ++ le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulVoltageIndex); ++ } ++ ++ info->boot_up_req_display_vector = ++ le32_to_cpu(info_v9->ulBootUpReqDisplayVector); ++ info->gpu_cap_info = le32_to_cpu(info_v9->ulGPUCapInfo); ++ ++ /* ++ * system_config: Bit[0] = 0 : PCIE power gating disabled ++ * = 1 : PCIE power gating enabled ++ * Bit[1] = 0 : DDR-PLL shut down disabled ++ * = 1 : DDR-PLL shut down enabled ++ * Bit[2] = 0 : DDR-PLL power down disabled ++ * = 1 : DDR-PLL power down enabled ++ */ ++ info->system_config = le32_to_cpu(info_v9->ulSystemConfig); ++ info->cpu_cap_info = le32_to_cpu(info_v9->ulCPUCapInfo); ++ info->boot_up_nb_voltage = ++ le16_to_cpu(info_v9->usBootUpNBVoltage); ++ info->ext_disp_conn_info_offset = ++ le16_to_cpu(info_v9->usExtDispConnInfoOffset); ++ info->memory_type = info_v9->ucMemoryType; ++ info->ma_channel_number = info_v9->ucUMAChannelNumber; ++ info->gmc_restore_reset_time = ++ le32_to_cpu(info_v9->ulGMCRestoreResetTime); ++ ++ info->minimum_n_clk = ++ le32_to_cpu(info_v9->ulNbpStateNClkFreq[0]); ++ for (i = 1; i < 4; ++i) ++ info->minimum_n_clk = ++ info->minimum_n_clk < le32_to_cpu(info_v9->ulNbpStateNClkFreq[i]) ? ++ info->minimum_n_clk : le32_to_cpu(info_v9->ulNbpStateNClkFreq[i]); ++ ++ info->idle_n_clk = le32_to_cpu(info_v9->ulIdleNClk); ++ info->ddr_dll_power_up_time = ++ le32_to_cpu(info_v9->ulDDR_DLL_PowerUpTime); ++ info->ddr_pll_power_up_time = ++ le32_to_cpu(info_v9->ulDDR_PLL_PowerUpTime); ++ info->pcie_clk_ss_type = le16_to_cpu(info_v9->usPCIEClkSSType); ++ info->lvds_ss_percentage = ++ le16_to_cpu(info_v9->usLvdsSSPercentage); ++ info->lvds_sspread_rate_in_10hz = ++ le16_to_cpu(info_v9->usLvdsSSpreadRateIn10Hz); ++ info->hdmi_ss_percentage = ++ le16_to_cpu(info_v9->usHDMISSPercentage); ++ info->hdmi_sspread_rate_in_10hz = ++ le16_to_cpu(info_v9->usHDMISSpreadRateIn10Hz); ++ info->dvi_ss_percentage = ++ le16_to_cpu(info_v9->usDVISSPercentage); ++ info->dvi_sspread_rate_in_10_hz = ++ le16_to_cpu(info_v9->usDVISSpreadRateIn10Hz); ++ ++ info->max_lvds_pclk_freq_in_single_link = ++ le16_to_cpu(info_v9->usMaxLVDSPclkFreqInSingleLink); ++ info->lvds_misc = info_v9->ucLvdsMisc; ++ info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = ++ info_v9->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; ++ info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = ++ info_v9->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; ++ info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = ++ info_v9->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; ++ info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = ++ info_v9->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; ++ info->lvds_pwr_off_seq_de_to_dig_on_in4ms = ++ info_v9->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; ++ info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = ++ info_v9->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; ++ info->lvds_off_to_on_delay_in_4ms = ++ info_v9->ucLVDSOffToOnDelay_in4Ms; ++ info->lvds_bit_depth_control_val = ++ le32_to_cpu(info_v9->ulLCDBitDepthControlVal); ++ ++ for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { ++ /* Convert [10KHz] into [KHz] */ ++ info->avail_s_clk[i].supported_s_clk = ++ le32_to_cpu(info_v9->sAvail_SCLK[i].ulSupportedSCLK) * 10; ++ info->avail_s_clk[i].voltage_index = ++ le16_to_cpu(info_v9->sAvail_SCLK[i].usVoltageIndex); ++ info->avail_s_clk[i].voltage_id = ++ le16_to_cpu(info_v9->sAvail_SCLK[i].usVoltageID); ++ } ++ ++ for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { ++ info->ext_disp_conn_info.gu_id[i] = ++ info_v9->sExtDispConnInfo.ucGuid[i]; ++ } ++ ++ for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { ++ info->ext_disp_conn_info.path[i].device_connector_id = ++ object_id_from_bios_object_id( ++ le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceConnector)); ++ ++ info->ext_disp_conn_info.path[i].ext_encoder_obj_id = ++ object_id_from_bios_object_id( ++ le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usExtEncoderObjId)); ++ ++ info->ext_disp_conn_info.path[i].device_tag = ++ le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceTag); ++ info->ext_disp_conn_info.path[i].device_acpi_enum = ++ le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceACPIEnum); ++ info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = ++ info_v9->sExtDispConnInfo.sPath[i].ucExtAUXDDCLutIndex; ++ info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = ++ info_v9->sExtDispConnInfo.sPath[i].ucExtHPDPINLutIndex; ++ info->ext_disp_conn_info.path[i].channel_mapping.raw = ++ info_v9->sExtDispConnInfo.sPath[i].ucChannelMapping; ++ } ++ info->ext_disp_conn_info.checksum = ++ info_v9->sExtDispConnInfo.ucChecksum; ++ ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++/* ++ * construct_integrated_info ++ * ++ * @brief ++ * Get integrated BIOS information based on table revision ++ * ++ * @param ++ * bios_parser *bp - [in]BIOS parser handler to get master data table ++ * integrated_info *info - [out] store and output integrated info ++ * ++ * @return ++ * enum bp_result - BP_RESULT_OK if information is available, ++ * BP_RESULT_BADBIOSTABLE otherwise. ++ */ ++static enum bp_result construct_integrated_info( ++ struct bios_parser *bp, ++ struct integrated_info *info) ++{ ++ enum bp_result result = BP_RESULT_BADBIOSTABLE; ++ ++ ATOM_COMMON_TABLE_HEADER *header; ++ struct atom_data_revision revision; ++ ++ if (info != NULL && ++ bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo) { ++ header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, ++ bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); ++ ++ get_atom_data_table_revision(header, &revision); ++ ++ /* Don't need to check major revision as they are all 1 */ ++ switch (revision.minor) { ++ case 8: ++ result = get_integrated_info_v8(bp, info); ++ break; ++ case 9: ++ result = get_integrated_info_v9(bp, info); ++ break; ++ default: ++ return result; ++ ++ } ++ } ++ ++ /* Sort voltage table from low to high*/ ++ if (result == BP_RESULT_OK) { ++ struct clock_voltage_caps temp = {0, 0}; ++ uint32_t i; ++ uint32_t j; ++ ++ for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { ++ for (j = i; j > 0; --j) { ++ if ( ++ info->disp_clk_voltage[j].max_supported_clk < ++ info->disp_clk_voltage[j-1].max_supported_clk) { ++ /* swap j and j - 1*/ ++ temp = info->disp_clk_voltage[j-1]; ++ info->disp_clk_voltage[j-1] = ++ info->disp_clk_voltage[j]; ++ info->disp_clk_voltage[j] = temp; ++ } ++ } ++ } ++ ++ } ++ ++ return result; ++} ++ ++/* ++ * dal_bios_parser_create_integrated_info ++ * ++ * @brief ++ * Create integrated info ++ * ++ * @param ++ * bios_parser *bp - [in] BIOS parser handler ++ * ++ * @return ++ * struct integrated_info * - pointer to the newly created integrated info ++ */ ++struct integrated_info *dal_bios_parser_create_integrated_info( ++ struct bios_parser *bp) ++{ ++ struct integrated_info *info = NULL; ++ ++ info = dc_service_alloc(bp->ctx, sizeof(struct integrated_info)); ++ ++ if (info == NULL) { ++ ASSERT_CRITICAL(0); ++ return NULL; ++ } ++ ++ if (construct_integrated_info(bp, info) == BP_RESULT_OK) ++ return info; ++ ++ dc_service_free(bp->ctx, info); ++ ++ return NULL; ++} ++ ++/* ++ * dal_bios_parser_destroy_integrated_info ++ * ++ * @brief ++ * Destroy provided integrated info ++ * ++ * @param ++ * struct integrated_info **info - [in] info to be destroied ++ */ ++void dal_bios_parser_destroy_integrated_info(struct dc_context *ctx, struct integrated_info **info) ++{ ++ if (info == NULL) { ++ ASSERT_CRITICAL(0); ++ return; ++ } ++ ++ if (*info != NULL) { ++ dc_service_free(ctx, *info); ++ *info = NULL; ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/bios_parser.h b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser.h +new file mode 100644 +index 0000000..db169f1 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser.h +@@ -0,0 +1,78 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_BIOS_PARSER_H__ ++#define __DAL_BIOS_PARSER_H__ ++ ++#include "bios_parser_helper.h" ++ ++struct atom_data_revision { ++ uint32_t major; ++ uint32_t minor; ++}; ++ ++struct object_info_table { ++ struct atom_data_revision revision; ++ union { ++ ATOM_OBJECT_HEADER *v1_1; ++ ATOM_OBJECT_HEADER_V3 *v1_3; ++ }; ++}; ++ ++enum spread_spectrum_id { ++ SS_ID_UNKNOWN = 0, ++ SS_ID_DP1 = 0xf1, ++ SS_ID_DP2 = 0xf2, ++ SS_ID_LVLINK_2700MHZ = 0xf3, ++ SS_ID_LVLINK_1620MHZ = 0xf4 ++}; ++ ++struct bios_parser { ++ struct dc_context *ctx; ++ struct adapter_service *as; ++ ++ struct object_info_table object_info_tbl; ++ uint32_t object_info_tbl_offset; ++ ATOM_MASTER_DATA_TABLE *master_data_tbl; ++ ++ uint8_t *bios; ++ uint32_t bios_size; ++ ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ const struct bios_parser_helper *bios_helper; ++ struct vbios_helper_data vbios_helper_data; ++#endif /* CONFIG_DRM_AMD_DAL_VBIOS_PRESENT */ ++ ++ const struct command_table_helper *cmd_helper; ++ struct cmd_tbl cmd_tbl; ++ ++ uint8_t *bios_local_image; ++ enum lcd_scale lcd_scale; ++ ++ bool remap_device_tags; ++ bool headless_no_opm; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.c b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.c +new file mode 100644 +index 0000000..0089800 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.c +@@ -0,0 +1,193 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "atom.h" ++ ++#include "include/bios_parser_types.h" ++#include "include/adapter_service_types.h" ++#include "bios_parser_helper.h" ++#include "command_table_helper.h" ++#include "command_table.h" ++#include "bios_parser.h" ++ ++bool dal_bios_parser_init_bios_helper( ++ struct bios_parser *bp, ++ enum dce_version version) ++{ ++ switch (version) { ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ bp->bios_helper = dal_bios_parser_helper_dce110_get_table(); ++ return true; ++ ++#endif ++ default: ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++} ++ ++bool dal_bios_parser_is_lid_open( ++ struct bios_parser *bp) ++{ ++ const struct graphics_object_id encoder = dal_graphics_object_id_init( ++ ENCODER_ID_INTERNAL_UNIPHY, ++ ENUM_ID_UNKNOWN, ++ OBJECT_TYPE_UNKNOWN); ++ const struct graphics_object_id connector = dal_graphics_object_id_init( ++ CONNECTOR_ID_LVDS, ++ ENUM_ID_UNKNOWN, ++ OBJECT_TYPE_UNKNOWN); ++ ++ enum signal_type signal; ++ ++ /* check if VBIOS reported LCD as connected */ ++ signal = bp->bios_helper->detect_sink(bp->ctx, ++ encoder, connector, SIGNAL_TYPE_LVDS); ++ ++ if (signal == SIGNAL_TYPE_NONE) ++ return false; ++ ++ return bp->bios_helper->is_lid_open(bp->ctx); ++} ++ ++bool dal_bios_parser_is_lid_status_changed( ++ struct bios_parser *bp) ++{ ++ return bp->bios_helper->is_lid_status_changed( ++ bp->ctx); ++} ++ ++bool dal_bios_parser_is_display_config_changed( ++ struct bios_parser *bp) ++{ ++ return bp->bios_helper->is_display_config_changed( ++ bp->ctx); ++} ++ ++/** ++* dal_bios_parser_set_scratch_lcd_scale ++* ++* @brief ++* update VBIOS scratch pad registers about LCD scale ++* ++* @param ++* bool - to set to full panel mode or aspect-ratio mode ++*/ ++void dal_bios_parser_set_scratch_lcd_scale( ++ struct bios_parser *bp, ++ enum lcd_scale scale) ++{ ++ bp->bios_helper->set_scratch_lcd_scale( ++ bp->ctx, scale); ++} ++ ++/** ++* dal_bios_parser_get_scratch_lcd_scale ++* ++* @brief ++* get LCD Scale Mode from VBIOS scratch register ++* ++* @param ++* NONE ++*/ ++enum lcd_scale dal_bios_parser_get_scratch_lcd_scale( ++ struct bios_parser *bp) ++{ ++ return bp->bios_helper->get_scratch_lcd_scale( ++ bp->ctx); ++} ++ ++void dal_bios_parser_get_bios_event_info( ++ struct bios_parser *bp, ++ struct bios_event_info *info) ++{ ++ bp->bios_helper->get_bios_event_info( ++ bp->ctx, info); ++} ++ ++/* ABM related */ ++ ++void dal_bios_parser_update_requested_backlight_level( ++ struct bios_parser *bp, ++ uint32_t backlight_8bit) ++{ ++ bp->bios_helper->update_requested_backlight_level( ++ bp->ctx, ++ backlight_8bit); ++} ++ ++uint32_t dal_bios_parser_get_requested_backlight_level( ++ struct bios_parser *bp) ++{ ++ return bp->bios_helper->get_requested_backlight_level( ++ bp->ctx); ++} ++ ++void dal_bios_parser_take_backlight_control( ++ struct bios_parser *bp, ++ bool cntl) ++{ ++ bp->bios_helper->take_backlight_control( ++ bp->ctx, cntl); ++} ++ ++/** ++ * dal_bios_parser_is_active_display ++ * Check video bios active display. ++ */ ++bool dal_bios_parser_is_active_display( ++ struct bios_parser *bp, ++ enum signal_type signal, ++ const struct connector_device_tag_info *device_tag) ++{ ++ return bp->bios_helper->is_active_display( ++ bp->ctx, signal, device_tag); ++} ++ ++/** ++ * dal_bios_parser_get_embedded_display_controller_id ++ * Get controller ID for embedded display from scratch registers ++ */ ++enum controller_id dal_bios_parser_get_embedded_display_controller_id( ++ struct bios_parser *bp) ++{ ++ return bp->bios_helper->get_embedded_display_controller_id( ++ bp->ctx); ++} ++ ++/** ++ * dal_bios_parser_get_embedded_display_refresh_rate ++ * Get refresh rate for embedded display from scratch registers ++ */ ++uint32_t dal_bios_parser_get_embedded_display_refresh_rate( ++ struct bios_parser *bp) ++{ ++ return bp->bios_helper->get_embedded_display_refresh_rate( ++ bp->ctx); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.h +new file mode 100644 +index 0000000..d0e9de9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.h +@@ -0,0 +1,108 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_BIOS_PARSER_HELPER_H__ ++#define __DAL_BIOS_PARSER_HELPER_H__ ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/bios_parser_helper_dce110.h" ++#endif ++ ++struct bios_parser; ++ ++struct vbios_helper_data { ++ uint32_t active; ++ uint32_t requested; ++}; ++ ++struct bios_parser_helper { ++ enum signal_type (*detect_sink)( ++ struct dc_context *ctx, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type signal); ++ bool (*is_lid_open)( ++ struct dc_context *ctx); ++ bool (*is_lid_status_changed)( ++ struct dc_context *ctx); ++ bool (*is_display_config_changed)( ++ struct dc_context *ctx); ++ void (*set_scratch_acc_mode_change)( ++ struct dc_context *ctx); ++ bool (*is_accelerated_mode)( ++ struct dc_context *ctx); ++ void (*set_scratch_critical_state)( ++ struct dc_context *ctx, ++ bool state); ++ void (*prepare_scratch_active_and_requested)( ++ struct dc_context *ctx, ++ struct vbios_helper_data *data, ++ enum controller_id id, enum signal_type s, ++ const struct connector_device_tag_info *dev_tag); ++ void (*set_scratch_active_and_requested)( ++ struct dc_context *ctx, ++ struct vbios_helper_data *d); ++ void (*set_scratch_connected)( ++ struct dc_context *ctx, ++ struct graphics_object_id id, ++ bool connected, ++ const struct connector_device_tag_info *device_tag); ++ void (*set_scratch_lcd_scale)( ++ struct dc_context *ctx, ++ enum lcd_scale lcd_scale_request); ++ enum lcd_scale (*get_scratch_lcd_scale)( ++ struct dc_context *ctx); ++ uint32_t (*fmt_control)( ++ struct dc_context *ctx, ++ enum controller_id id, uint32_t *value); ++ uint32_t (*fmt_bit_depth_control)( ++ struct dc_context *ctx, ++ enum controller_id id, ++ uint32_t *value); ++ void (*get_bios_event_info)( ++ struct dc_context *ctx, ++ struct bios_event_info *info); ++ void (*take_backlight_control)( ++ struct dc_context *ctx, bool control); ++ uint32_t (*get_requested_backlight_level)( ++ struct dc_context *ctx); ++ void (*update_requested_backlight_level)( ++ struct dc_context *ctx, ++ uint32_t backlight_8bit); ++ bool (*is_active_display)( ++ struct dc_context *ctx, ++ enum signal_type signal, ++ const struct connector_device_tag_info *dev_tag); ++ enum controller_id (*get_embedded_display_controller_id)( ++ struct dc_context *ctx); ++ uint32_t (*get_embedded_display_refresh_rate)( ++ struct dc_context *ctx); ++}; ++ ++bool dal_bios_parser_init_bios_helper( ++ struct bios_parser *bp, ++ enum dce_version ver); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/command_table.c b/drivers/gpu/drm/amd/dal/dc/bios/command_table.c +new file mode 100644 +index 0000000..a807ab6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table.c +@@ -0,0 +1,2616 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "atom.h" ++ ++#include "include/bios_parser_interface.h" ++ ++#include "command_table.h" ++#include "command_table_helper.h" ++#include "bios_parser_helper.h" ++#include "bios_parser.h" ++ ++#define EXEC_BIOS_CMD_TABLE(command, params)\ ++ (cgs_atom_exec_cmd_table(bp->ctx->cgs_device, \ ++ GetIndexIntoMasterTable(COMMAND, command), \ ++ ¶ms) == 0) ++ ++#define BIOS_CMD_TABLE_REVISION(command, frev, crev)\ ++ cgs_atom_get_cmd_table_revs(bp->ctx->cgs_device, \ ++ GetIndexIntoMasterTable(COMMAND, command), &frev, &crev) ++ ++#define BIOS_CMD_TABLE_PARA_REVISION(command)\ ++ dal_bios_cmd_table_para_revision(bp->ctx, \ ++ GetIndexIntoMasterTable(COMMAND, command)) ++ ++ ++static void init_dig_encoder_control(struct bios_parser *bp); ++static void init_dvo_encoder_control(struct bios_parser *bp); ++static void init_transmitter_control(struct bios_parser *bp); ++static void init_set_pixel_clock(struct bios_parser *bp); ++static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp); ++static void init_adjust_display_pll(struct bios_parser *bp); ++static void init_dac_encoder_control(struct bios_parser *bp); ++static void init_dac_output_control(struct bios_parser *bp); ++static void init_dac_load_detection(struct bios_parser *bp); ++static void init_blank_crtc(struct bios_parser *bp); ++static void init_set_crtc_timing(struct bios_parser *bp); ++static void init_set_crtc_overscan(struct bios_parser *bp); ++static void init_select_crtc_source(struct bios_parser *bp); ++static void init_enable_crtc(struct bios_parser *bp); ++static void init_enable_crtc_mem_req(struct bios_parser *bp); ++static void init_compute_memore_engine_pll(struct bios_parser *bp); ++static void init_external_encoder_control(struct bios_parser *bp); ++static void init_enable_disp_power_gating(struct bios_parser *bp); ++static void init_program_clock(struct bios_parser *bp); ++ ++void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp) ++{ ++ init_dig_encoder_control(bp); ++ init_dvo_encoder_control(bp); ++ init_transmitter_control(bp); ++ init_set_pixel_clock(bp); ++ init_enable_spread_spectrum_on_ppll(bp); ++ init_adjust_display_pll(bp); ++ init_dac_encoder_control(bp); ++ init_dac_output_control(bp); ++ init_dac_load_detection(bp); ++ init_blank_crtc(bp); ++ init_set_crtc_timing(bp); ++ init_set_crtc_overscan(bp); ++ init_select_crtc_source(bp); ++ init_enable_crtc(bp); ++ init_enable_crtc_mem_req(bp); ++ init_program_clock(bp); ++ init_compute_memore_engine_pll(bp); ++ init_external_encoder_control(bp); ++ init_enable_disp_power_gating(bp); ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** D I G E N C O D E R C O N T R O L ++** ++******************************************************************************** ++*******************************************************************************/ ++static enum bp_result encoder_control_digx_v3( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++ ++static enum bp_result encoder_control_digx_v4( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++static void init_encoder_control_dig_v1(struct bios_parser *bp); ++ ++static void init_dig_encoder_control(struct bios_parser *bp) ++{ ++ uint32_t version = ++ BIOS_CMD_TABLE_PARA_REVISION(DIGxEncoderControl); ++ ++ switch (version) { ++ case 4: ++ bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v4; ++ break; ++ case 2: ++ bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v3; ++ break; ++ default: ++ init_encoder_control_dig_v1(bp); ++ break; ++ } ++} ++ ++static enum bp_result encoder_control_dig_v1( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++static enum bp_result encoder_control_dig1_v1( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++static enum bp_result encoder_control_dig2_v1( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++ ++static void init_encoder_control_dig_v1(struct bios_parser *bp) ++{ ++ struct cmd_tbl *cmd_tbl = &bp->cmd_tbl; ++ ++ if (1 == BIOS_CMD_TABLE_PARA_REVISION(DIG1EncoderControl)) ++ cmd_tbl->encoder_control_dig1 = encoder_control_dig1_v1; ++ else ++ cmd_tbl->encoder_control_dig1 = NULL; ++ ++ if (1 == BIOS_CMD_TABLE_PARA_REVISION(DIG2EncoderControl)) ++ cmd_tbl->encoder_control_dig2 = encoder_control_dig2_v1; ++ else ++ cmd_tbl->encoder_control_dig2 = NULL; ++ ++ cmd_tbl->dig_encoder_control = encoder_control_dig_v1; ++} ++ ++static enum bp_result encoder_control_dig_v1( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ struct cmd_tbl *cmd_tbl = &bp->cmd_tbl; ++ ++ if (cntl != NULL) ++ switch (cntl->engine_id) { ++ case ENGINE_ID_DIGA: ++ if (cmd_tbl->encoder_control_dig1 != NULL) ++ result = ++ cmd_tbl->encoder_control_dig1(bp, cntl); ++ break; ++ case ENGINE_ID_DIGB: ++ if (cmd_tbl->encoder_control_dig2 != NULL) ++ result = ++ cmd_tbl->encoder_control_dig2(bp, cntl); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return result; ++} ++ ++static enum bp_result encoder_control_dig1_v1( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_ENCODER_CONTROL_PARAMETERS_V2 params = {0}; ++ ++ bp->cmd_helper->assign_control_parameter(bp->cmd_helper, cntl, ¶ms); ++ ++ if (EXEC_BIOS_CMD_TABLE(DIG1EncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result encoder_control_dig2_v1( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_ENCODER_CONTROL_PARAMETERS_V2 params = {0}; ++ ++ bp->cmd_helper->assign_control_parameter(bp->cmd_helper, cntl, ¶ms); ++ ++ if (EXEC_BIOS_CMD_TABLE(DIG2EncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result encoder_control_digx_v3( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_ENCODER_CONTROL_PARAMETERS_V3 params = {0}; ++ ++ if (LANE_COUNT_FOUR < cntl->lanes_number) ++ params.acConfig.ucDPLinkRate = 1; /* dual link 2.7GHz */ ++ else ++ params.acConfig.ucDPLinkRate = 0; /* single link 1.62GHz */ ++ ++ params.acConfig.ucDigSel = (uint8_t)(cntl->engine_id); ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); ++ params.usPixelClock = cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ params.ucEncoderMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ cntl->signal, ++ cntl->enable_dp_audio); ++ params.ucLaneNum = (uint8_t)(cntl->lanes_number); ++ ++ if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_101010: ++ params.usPixelClock = ++ cpu_to_le16((le32_to_cpu(params.usPixelClock) * 30) / 24); ++ break; ++ case COLOR_DEPTH_121212: ++ params.usPixelClock = ++ cpu_to_le16((le32_to_cpu(params.usPixelClock) * 36) / 24); ++ break; ++ case COLOR_DEPTH_161616: ++ params.usPixelClock = ++ cpu_to_le16((le32_to_cpu(params.usPixelClock) * 48) / 24); ++ break; ++ default: ++ break; ++ } ++ ++ if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result encoder_control_digx_v4( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_ENCODER_CONTROL_PARAMETERS_V4 params = {0}; ++ ++ if (LANE_COUNT_FOUR < cntl->lanes_number) ++ params.acConfig.ucDPLinkRate = 1; /* dual link 2.7GHz */ ++ else ++ params.acConfig.ucDPLinkRate = 0; /* single link 1.62GHz */ ++ ++ ++ params.acConfig.ucDigSel = (uint8_t)(cntl->engine_id); ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); ++ params.usPixelClock = cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ params.ucEncoderMode = ++ (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( ++ cntl->signal, ++ cntl->enable_dp_audio)); ++ params.ucLaneNum = (uint8_t)(cntl->lanes_number); ++ ++ if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_101010: ++ params.usPixelClock = ++ cpu_to_le16((le32_to_cpu(params.usPixelClock) * 30) / 24); ++ break; ++ case COLOR_DEPTH_121212: ++ params.usPixelClock = ++ cpu_to_le16((le32_to_cpu(params.usPixelClock) * 36) / 24); ++ break; ++ case COLOR_DEPTH_161616: ++ params.usPixelClock = ++ cpu_to_le16((le32_to_cpu(params.usPixelClock) * 48) / 24); ++ break; ++ default: ++ break; ++ } ++ ++ if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** DVO ENCODER CONTROL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result dvo_encoder_control_v3( ++ struct bios_parser *bp, ++ struct bp_dvo_encoder_control *cntl); ++ ++static void init_dvo_encoder_control(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(DVOEncoderControl)) { ++ case 3: ++ bp->cmd_tbl.dvo_encoder_control = dvo_encoder_control_v3; ++ break; ++ default: ++ bp->cmd_tbl.dvo_encoder_control = NULL; ++ break; ++ } ++} ++ ++static enum bp_result dvo_encoder_control_v3( ++ struct bios_parser *bp, ++ struct bp_dvo_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DVO_ENCODER_CONTROL_PARAMETERS_V3 params; ++ uint8_t config = 0; ++ ++ if (cntl->memory_rate == DVO_ENCODER_MEMORY_RATE_SDR) ++ config |= DVO_ENCODER_CONFIG_SDR_SPEED; ++ ++ switch (cntl->interface_width) { ++ case DVO_ENCODER_INTERFACE_WIDTH_FULL24BIT: ++ config |= DVO_ENCODER_CONFIG_24BIT; ++ break; ++ case DVO_ENCODER_INTERFACE_WIDTH_HIGH12BIT: ++ config |= DVO_ENCODER_CONFIG_UPPER12BIT; ++ break; ++ default: ++ config |= DVO_ENCODER_CONFIG_LOW12BIT; ++ break; ++ } ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ params.ucAction = (uint8_t) cntl->action; ++ params.usPixelClock = cpu_to_le16((uint16_t) (cntl->pixel_clock / 10)); ++ params.ucDVOConfig = config; ++ ++ if (EXEC_BIOS_CMD_TABLE(DVOEncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** TRANSMITTER CONTROL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result transmitter_control_v2( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl); ++static enum bp_result transmitter_control_v3( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl); ++static enum bp_result transmitter_control_v4( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl); ++static enum bp_result transmitter_control_v1_5( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl); ++ ++static void init_transmitter_control(struct bios_parser *bp) ++{ ++ uint8_t frev; ++ uint8_t crev; ++ ++ if (BIOS_CMD_TABLE_REVISION(UNIPHYTransmitterControl, ++ frev, crev) != 0) ++ BREAK_TO_DEBUGGER(); ++ switch (crev) { ++ case 2: ++ bp->cmd_tbl.transmitter_control = transmitter_control_v2; ++ break; ++ case 3: ++ bp->cmd_tbl.transmitter_control = transmitter_control_v3; ++ break; ++ case 4: ++ bp->cmd_tbl.transmitter_control = transmitter_control_v4; ++ break; ++ case 5: ++ bp->cmd_tbl.transmitter_control = transmitter_control_v1_5; ++ break; ++ default: ++ bp->cmd_tbl.transmitter_control = NULL; ++ break; ++ } ++} ++ ++static enum bp_result transmitter_control_v2( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 params; ++ enum connector_id connector_id = ++ dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ switch (cntl->transmitter) { ++ case TRANSMITTER_UNIPHY_A: ++ case TRANSMITTER_UNIPHY_B: ++ case TRANSMITTER_UNIPHY_C: ++ case TRANSMITTER_UNIPHY_D: ++ case TRANSMITTER_UNIPHY_E: ++ case TRANSMITTER_UNIPHY_F: ++ case TRANSMITTER_TRAVIS_LCD: ++ break; ++ default: ++ return BP_RESULT_BADINPUT; ++ } ++ ++ switch (cntl->action) { ++ case TRANSMITTER_CONTROL_INIT: ++ if ((CONNECTOR_ID_DUAL_LINK_DVII == connector_id) || ++ (CONNECTOR_ID_DUAL_LINK_DVID == connector_id)) ++ /* on INIT this bit should be set according to the ++ * phisycal connector ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ ++ /* connector object id */ ++ params.usInitInfo = ++ cpu_to_le16((uint8_t)cntl->connector_obj_id.id); ++ break; ++ case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: ++ /* votage swing and pre-emphsis */ ++ params.asMode.ucLaneSel = (uint8_t)cntl->lane_select; ++ params.asMode.ucLaneSet = (uint8_t)cntl->lane_settings; ++ break; ++ default: ++ /* if dual-link */ ++ if (LANE_COUNT_FOUR < cntl->lanes_number) { ++ /* on ENABLE/DISABLE this bit should be set according to ++ * actual timing (number of lanes) ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ ++ /* link rate, half for dual link ++ * We need to convert from KHz units into 20KHz units ++ */ ++ params.usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); ++ } else ++ /* link rate, half for dual link ++ * We need to convert from KHz units into 10KHz units ++ */ ++ params.usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ break; ++ } ++ ++ /* 00 - coherent mode ++ * 01 - incoherent mode ++ */ ++ ++ params.acConfig.fCoherentMode = cntl->coherent; ++ ++ if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) ++ || (TRANSMITTER_UNIPHY_D == cntl->transmitter) ++ || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) ++ /* Bit2: Transmitter Link selection ++ * =0 when bit0=0, single link A/C/E, when bit0=1, ++ * master link A/C/E ++ * =1 when bit0=0, single link B/D/F, when bit0=1, ++ * master link B/D/F ++ */ ++ params.acConfig.ucLinkSel = 1; ++ ++ if (ENGINE_ID_DIGB == cntl->engine_id) ++ /* Bit3: Transmitter data source selection ++ * =0 DIGA is data source. ++ * =1 DIGB is data source. ++ * This bit is only useful when ucAction= ATOM_ENABLE ++ */ ++ params.acConfig.ucEncoderSel = 1; ++ ++ if (CONNECTOR_ID_DISPLAY_PORT == connector_id) ++ /* Bit4: DP connector flag ++ * =0 connector is none-DP connector ++ * =1 connector is DP connector ++ */ ++ params.acConfig.fDPConnector = 1; ++ ++ /* Bit[7:6]: Transmitter selection ++ * =0 UNIPHY_ENCODER: UNIPHYA/B ++ * =1 UNIPHY1_ENCODER: UNIPHYC/D ++ * =2 UNIPHY2_ENCODER: UNIPHYE/F ++ * =3 reserved ++ */ ++ params.acConfig.ucTransmitterSel = ++ (uint8_t)bp->cmd_helper->transmitter_bp_to_atom( ++ cntl->transmitter); ++ ++ params.ucAction = (uint8_t)cntl->action; ++ ++ if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result transmitter_control_v3( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 params; ++ uint32_t pll_id; ++ enum connector_id conn_id = ++ dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); ++ const struct command_table_helper *cmd = bp->cmd_helper; ++ bool dual_link_conn = (CONNECTOR_ID_DUAL_LINK_DVII == conn_id) ++ || (CONNECTOR_ID_DUAL_LINK_DVID == conn_id); ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ switch (cntl->transmitter) { ++ case TRANSMITTER_UNIPHY_A: ++ case TRANSMITTER_UNIPHY_B: ++ case TRANSMITTER_UNIPHY_C: ++ case TRANSMITTER_UNIPHY_D: ++ case TRANSMITTER_UNIPHY_E: ++ case TRANSMITTER_UNIPHY_F: ++ case TRANSMITTER_TRAVIS_LCD: ++ break; ++ default: ++ return BP_RESULT_BADINPUT; ++ } ++ ++ if (!cmd->clock_source_id_to_atom(cntl->pll_id, &pll_id)) ++ return BP_RESULT_BADINPUT; ++ ++ /* fill information based on the action */ ++ switch (cntl->action) { ++ case TRANSMITTER_CONTROL_INIT: ++ if (dual_link_conn) { ++ /* on INIT this bit should be set according to the ++ * phisycal connector ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ } ++ ++ /* connector object id */ ++ params.usInitInfo = ++ cpu_to_le16((uint8_t)(cntl->connector_obj_id.id)); ++ break; ++ case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: ++ /* votage swing and pre-emphsis */ ++ params.asMode.ucLaneSel = (uint8_t)cntl->lane_select; ++ params.asMode.ucLaneSet = (uint8_t)cntl->lane_settings; ++ break; ++ default: ++ if (dual_link_conn && cntl->multi_path) ++ /* on ENABLE/DISABLE this bit should be set according to ++ * actual timing (number of lanes) ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ ++ /* if dual-link */ ++ if (LANE_COUNT_FOUR < cntl->lanes_number) { ++ /* on ENABLE/DISABLE this bit should be set according to ++ * actual timing (number of lanes) ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ ++ /* link rate, half for dual link ++ * We need to convert from KHz units into 20KHz units ++ */ ++ params.usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); ++ } else { ++ /* link rate, half for dual link ++ * We need to convert from KHz units into 10KHz units ++ */ ++ params.usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_101010: ++ params.usPixelClock = ++ cpu_to_le16((le16_to_cpu(params.usPixelClock) * 30) / 24); ++ break; ++ case COLOR_DEPTH_121212: ++ params.usPixelClock = ++ cpu_to_le16((le16_to_cpu(params.usPixelClock) * 36) / 24); ++ break; ++ case COLOR_DEPTH_161616: ++ params.usPixelClock = ++ cpu_to_le16((le16_to_cpu(params.usPixelClock) * 48) / 24); ++ break; ++ default: ++ break; ++ } ++ } ++ break; ++ } ++ ++ /* 00 - coherent mode ++ * 01 - incoherent mode ++ */ ++ ++ params.acConfig.fCoherentMode = cntl->coherent; ++ ++ if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) ++ || (TRANSMITTER_UNIPHY_D == cntl->transmitter) ++ || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) ++ /* Bit2: Transmitter Link selection ++ * =0 when bit0=0, single link A/C/E, when bit0=1, ++ * master link A/C/E ++ * =1 when bit0=0, single link B/D/F, when bit0=1, ++ * master link B/D/F ++ */ ++ params.acConfig.ucLinkSel = 1; ++ ++ if (ENGINE_ID_DIGB == cntl->engine_id) ++ /* Bit3: Transmitter data source selection ++ * =0 DIGA is data source. ++ * =1 DIGB is data source. ++ * This bit is only useful when ucAction= ATOM_ENABLE ++ */ ++ params.acConfig.ucEncoderSel = 1; ++ ++ /* Bit[7:6]: Transmitter selection ++ * =0 UNIPHY_ENCODER: UNIPHYA/B ++ * =1 UNIPHY1_ENCODER: UNIPHYC/D ++ * =2 UNIPHY2_ENCODER: UNIPHYE/F ++ * =3 reserved ++ */ ++ params.acConfig.ucTransmitterSel = ++ (uint8_t)cmd->transmitter_bp_to_atom(cntl->transmitter); ++ ++ params.ucLaneNum = (uint8_t)cntl->lanes_number; ++ ++ params.acConfig.ucRefClkSource = (uint8_t)pll_id; ++ ++ params.ucAction = (uint8_t)cntl->action; ++ ++ if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result transmitter_control_v4( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 params; ++ uint32_t ref_clk_src_id; ++ enum connector_id conn_id = ++ dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); ++ const struct command_table_helper *cmd = bp->cmd_helper; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ switch (cntl->transmitter) { ++ case TRANSMITTER_UNIPHY_A: ++ case TRANSMITTER_UNIPHY_B: ++ case TRANSMITTER_UNIPHY_C: ++ case TRANSMITTER_UNIPHY_D: ++ case TRANSMITTER_UNIPHY_E: ++ case TRANSMITTER_UNIPHY_F: ++ case TRANSMITTER_TRAVIS_LCD: ++ break; ++ default: ++ return BP_RESULT_BADINPUT; ++ } ++ ++ if (!cmd->clock_source_id_to_ref_clk_src(cntl->pll_id, &ref_clk_src_id)) ++ return BP_RESULT_BADINPUT; ++ ++ switch (cntl->action) { ++ case TRANSMITTER_CONTROL_INIT: ++ { ++ if ((CONNECTOR_ID_DUAL_LINK_DVII == conn_id) || ++ (CONNECTOR_ID_DUAL_LINK_DVID == conn_id)) ++ /* on INIT this bit should be set according to the ++ * phisycal connector ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ ++ /* connector object id */ ++ params.usInitInfo = ++ cpu_to_le16((uint8_t)(cntl->connector_obj_id.id)); ++ } ++ break; ++ case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: ++ /* votage swing and pre-emphsis */ ++ params.asMode.ucLaneSel = (uint8_t)(cntl->lane_select); ++ params.asMode.ucLaneSet = (uint8_t)(cntl->lane_settings); ++ break; ++ default: ++ if ((CONNECTOR_ID_DUAL_LINK_DVII == conn_id) || ++ (CONNECTOR_ID_DUAL_LINK_DVID == conn_id)) ++ /* on ENABLE/DISABLE this bit should be set according to ++ * actual timing (number of lanes) ++ * Bit0: dual link connector flag ++ * =0 connector is single link connector ++ * =1 connector is dual link connector ++ */ ++ params.acConfig.fDualLinkConnector = 1; ++ ++ /* if dual-link */ ++ if (LANE_COUNT_FOUR < cntl->lanes_number) ++ /* link rate, half for dual link ++ * We need to convert from KHz units into 20KHz units ++ */ ++ params.usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); ++ else { ++ /* link rate, half for dual link ++ * We need to convert from KHz units into 10KHz units ++ */ ++ params.usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ ++ if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_101010: ++ params.usPixelClock = ++ cpu_to_le16((le16_to_cpu(params.usPixelClock) * 30) / 24); ++ break; ++ case COLOR_DEPTH_121212: ++ params.usPixelClock = ++ cpu_to_le16((le16_to_cpu(params.usPixelClock) * 36) / 24); ++ break; ++ case COLOR_DEPTH_161616: ++ params.usPixelClock = ++ cpu_to_le16((le16_to_cpu(params.usPixelClock) * 48) / 24); ++ break; ++ default: ++ break; ++ } ++ } ++ break; ++ } ++ ++ /* 00 - coherent mode ++ * 01 - incoherent mode ++ */ ++ ++ params.acConfig.fCoherentMode = cntl->coherent; ++ ++ if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) ++ || (TRANSMITTER_UNIPHY_D == cntl->transmitter) ++ || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) ++ /* Bit2: Transmitter Link selection ++ * =0 when bit0=0, single link A/C/E, when bit0=1, ++ * master link A/C/E ++ * =1 when bit0=0, single link B/D/F, when bit0=1, ++ * master link B/D/F ++ */ ++ params.acConfig.ucLinkSel = 1; ++ ++ if (ENGINE_ID_DIGB == cntl->engine_id) ++ /* Bit3: Transmitter data source selection ++ * =0 DIGA is data source. ++ * =1 DIGB is data source. ++ * This bit is only useful when ucAction= ATOM_ENABLE ++ */ ++ params.acConfig.ucEncoderSel = 1; ++ ++ /* Bit[7:6]: Transmitter selection ++ * =0 UNIPHY_ENCODER: UNIPHYA/B ++ * =1 UNIPHY1_ENCODER: UNIPHYC/D ++ * =2 UNIPHY2_ENCODER: UNIPHYE/F ++ * =3 reserved ++ */ ++ params.acConfig.ucTransmitterSel = ++ (uint8_t)(cmd->transmitter_bp_to_atom(cntl->transmitter)); ++ params.ucLaneNum = (uint8_t)(cntl->lanes_number); ++ params.acConfig.ucRefClkSource = (uint8_t)(ref_clk_src_id); ++ params.ucAction = (uint8_t)(cntl->action); ++ ++ if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result transmitter_control_v1_5( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ const struct command_table_helper *cmd = bp->cmd_helper; ++ DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 params; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ params.ucPhyId = cmd->phy_id_to_atom(cntl->transmitter); ++ params.ucAction = (uint8_t)cntl->action; ++ params.ucLaneNum = (uint8_t)cntl->lanes_number; ++ params.ucConnObjId = (uint8_t)cntl->connector_obj_id.id; ++ ++ params.ucDigMode = ++ cmd->signal_type_to_atom_dig_mode(cntl->signal); ++ params.asConfig.ucPhyClkSrcId = ++ cmd->clock_source_id_to_atom_phy_clk_src_id(cntl->pll_id); ++ /* 00 - coherent mode */ ++ params.asConfig.ucCoherentMode = cntl->coherent; ++ params.asConfig.ucHPDSel = ++ cmd->hpd_sel_to_atom(cntl->hpd_sel); ++ params.ucDigEncoderSel = ++ cmd->dig_encoder_sel_to_atom(cntl->engine_id); ++ params.ucDPLaneSet = (uint8_t) cntl->lane_settings; ++ params.usSymClock = cpu_to_le16((uint16_t) (cntl->pixel_clock / 10)); ++ /* ++ * In SI/TN case, caller have to set usPixelClock as following: ++ * DP mode: usPixelClock = DP_LINK_CLOCK/10 ++ * (DP_LINK_CLOCK = 1.62GHz, 2.7GHz, 5.4GHz) ++ * DVI single link mode: usPixelClock = pixel clock ++ * DVI dual link mode: usPixelClock = pixel clock ++ * HDMI mode: usPixelClock = pixel clock * deep_color_ratio ++ * (=1: 8bpp, =1.25: 10bpp, =1.5:12bpp, =2: 16bpp) ++ * LVDS mode: usPixelClock = pixel clock ++ */ ++ switch (cntl->signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_101010: ++ params.usSymClock = ++ cpu_to_le16((le16_to_cpu(params.usSymClock) * 30) / 24); ++ break; ++ case COLOR_DEPTH_121212: ++ params.usSymClock = ++ cpu_to_le16((le16_to_cpu(params.usSymClock) * 36) / 24); ++ break; ++ case COLOR_DEPTH_161616: ++ params.usSymClock = ++ cpu_to_le16((le16_to_cpu(params.usSymClock) * 48) / 24); ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** SET PIXEL CLOCK ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result set_pixel_clock_v3( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++static enum bp_result set_pixel_clock_v5( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++static enum bp_result set_pixel_clock_v6( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++ ++static void init_set_pixel_clock(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)) { ++ case 3: ++ bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v3; ++ break; ++ case 5: ++ bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v5; ++ break; ++ case 6: ++ bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v6; ++ break; ++ default: ++ bp->cmd_tbl.set_pixel_clock = NULL; ++ break; ++ } ++} ++ ++static enum bp_result set_pixel_clock_v3( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ PIXEL_CLOCK_PARAMETERS_V3 *params; ++ SET_PIXEL_CLOCK_PS_ALLOCATION allocation; ++ ++ dc_service_memset(&allocation, 0, sizeof(allocation)); ++ ++ if (CLOCK_SOURCE_ID_PLL1 == bp_params->pll_id) ++ allocation.sPCLKInput.ucPpll = ATOM_PPLL1; ++ else if (CLOCK_SOURCE_ID_PLL2 == bp_params->pll_id) ++ allocation.sPCLKInput.ucPpll = ATOM_PPLL2; ++ else ++ return BP_RESULT_BADINPUT; ++ ++ allocation.sPCLKInput.usRefDiv = ++ cpu_to_le16((uint16_t)bp_params->reference_divider); ++ allocation.sPCLKInput.usFbDiv = ++ cpu_to_le16((uint16_t)bp_params->feedback_divider); ++ allocation.sPCLKInput.ucFracFbDiv = ++ (uint8_t)bp_params->fractional_feedback_divider; ++ allocation.sPCLKInput.ucPostDiv = ++ (uint8_t)bp_params->pixel_clock_post_divider; ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ allocation.sPCLKInput.usPixelClock = ++ cpu_to_le16((uint16_t)(bp_params->target_pixel_clock / 10)); ++ ++ params = (PIXEL_CLOCK_PARAMETERS_V3 *)&allocation.sPCLKInput; ++ params->ucTransmitterId = ++ bp->cmd_helper->encoder_id_to_atom( ++ dal_graphics_object_id_get_encoder_id( ++ bp_params->encoder_object_id)); ++ params->ucEncoderMode = ++ (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( ++ bp_params->signal_type, false)); ++ ++ if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) ++ params->ucMiscInfo |= PIXEL_CLOCK_MISC_FORCE_PROG_PPLL; ++ ++ if (bp_params->flags.USE_E_CLOCK_AS_SOURCE_FOR_D_CLOCK) ++ params->ucMiscInfo |= PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK; ++ ++ if (CONTROLLER_ID_D1 != bp_params->controller_id) ++ params->ucMiscInfo |= PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2; ++ ++ if (EXEC_BIOS_CMD_TABLE(SetPixelClock, allocation)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++#ifndef SET_PIXEL_CLOCK_PS_ALLOCATION_V5 ++/* video bios did not define this: */ ++typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION_V5 { ++ PIXEL_CLOCK_PARAMETERS_V5 sPCLKInput; ++ /* Caller doesn't need to init this portion */ ++ ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved; ++} SET_PIXEL_CLOCK_PS_ALLOCATION_V5; ++#endif ++ ++#ifndef SET_PIXEL_CLOCK_PS_ALLOCATION_V6 ++/* video bios did not define this: */ ++typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION_V6 { ++ PIXEL_CLOCK_PARAMETERS_V6 sPCLKInput; ++ /* Caller doesn't need to init this portion */ ++ ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved; ++} SET_PIXEL_CLOCK_PS_ALLOCATION_V6; ++#endif ++ ++static enum bp_result set_pixel_clock_v5( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ SET_PIXEL_CLOCK_PS_ALLOCATION_V5 clk; ++ uint8_t controller_id; ++ uint32_t pll_id; ++ ++ dc_service_memset(&clk, 0, sizeof(clk)); ++ ++ if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) ++ && bp->cmd_helper->controller_id_to_atom( ++ bp_params->controller_id, &controller_id)) { ++ clk.sPCLKInput.ucCRTC = controller_id; ++ clk.sPCLKInput.ucPpll = (uint8_t)pll_id; ++ clk.sPCLKInput.ucRefDiv = ++ (uint8_t)(bp_params->reference_divider); ++ clk.sPCLKInput.usFbDiv = ++ cpu_to_le16((uint16_t)(bp_params->feedback_divider)); ++ clk.sPCLKInput.ulFbDivDecFrac = ++ cpu_to_le32(bp_params->fractional_feedback_divider); ++ clk.sPCLKInput.ucPostDiv = ++ (uint8_t)(bp_params->pixel_clock_post_divider); ++ clk.sPCLKInput.ucTransmitterID = ++ bp->cmd_helper->encoder_id_to_atom( ++ dal_graphics_object_id_get_encoder_id( ++ bp_params->encoder_object_id)); ++ clk.sPCLKInput.ucEncoderMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ bp_params->signal_type, false); ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ clk.sPCLKInput.usPixelClock = ++ cpu_to_le16((uint16_t)(bp_params->target_pixel_clock / 10)); ++ ++ if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) ++ clk.sPCLKInput.ucMiscInfo |= ++ PIXEL_CLOCK_MISC_FORCE_PROG_PPLL; ++ ++ if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) ++ clk.sPCLKInput.ucMiscInfo |= ++ PIXEL_CLOCK_MISC_REF_DIV_SRC; ++ ++ /* clkV5.ucMiscInfo bit[3:2]= HDMI panel bit depth: =0: 24bpp ++ * =1:30bpp, =2:32bpp ++ * driver choose program it itself, i.e. here we program it ++ * to 888 by default. ++ */ ++ ++ if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++static enum bp_result set_pixel_clock_v6( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ SET_PIXEL_CLOCK_PS_ALLOCATION_V6 clk; ++ uint8_t controller_id; ++ uint32_t pll_id; ++ ++ dc_service_memset(&clk, 0, sizeof(clk)); ++ ++ if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) ++ && bp->cmd_helper->controller_id_to_atom( ++ bp_params->controller_id, &controller_id)) { ++ /* Note: VBIOS still wants to use ucCRTC name which is now ++ * 1 byte in ULONG ++ *typedef struct _CRTC_PIXEL_CLOCK_FREQ ++ *{ ++ * target the pixel clock to drive the CRTC timing. ++ * ULONG ulPixelClock:24; ++ * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to ++ * previous version. ++ * ATOM_CRTC1~6, indicate the CRTC controller to ++ * ULONG ucCRTC:8; ++ * drive the pixel clock. not used for DCPLL case. ++ *}CRTC_PIXEL_CLOCK_FREQ; ++ *union ++ *{ ++ * pixel clock and CRTC id frequency ++ * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; ++ * ULONG ulDispEngClkFreq; dispclk frequency ++ *}; ++ */ ++ clk.sPCLKInput.ulCrtcPclkFreq.ucCRTC = controller_id; ++ clk.sPCLKInput.ucPpll = (uint8_t) pll_id; ++ clk.sPCLKInput.ucRefDiv = ++ (uint8_t) bp_params->reference_divider; ++ clk.sPCLKInput.usFbDiv = ++ cpu_to_le16((uint16_t) bp_params->feedback_divider); ++ clk.sPCLKInput.ulFbDivDecFrac = ++ cpu_to_le32(bp_params->fractional_feedback_divider); ++ clk.sPCLKInput.ucPostDiv = ++ (uint8_t) bp_params->pixel_clock_post_divider; ++ clk.sPCLKInput.ucTransmitterID = ++ bp->cmd_helper->encoder_id_to_atom( ++ dal_graphics_object_id_get_encoder_id( ++ bp_params->encoder_object_id)); ++ clk.sPCLKInput.ucEncoderMode = ++ (uint8_t) bp->cmd_helper->encoder_mode_bp_to_atom( ++ bp_params->signal_type, false); ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ clk.sPCLKInput.ulCrtcPclkFreq.ulPixelClock = ++ cpu_to_le32(bp_params->target_pixel_clock / 10); ++ ++ if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) { ++ clk.sPCLKInput.ucMiscInfo |= ++ PIXEL_CLOCK_V6_MISC_FORCE_PROG_PPLL; ++ } ++ ++ if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) { ++ clk.sPCLKInput.ucMiscInfo |= ++ PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; ++ } ++ ++ /* clkV6.ucMiscInfo bit[3:2]= HDMI panel bit depth: =0: ++ * 24bpp =1:30bpp, =2:32bpp ++ * driver choose program it itself, i.e. here we pass required ++ * target rate that includes deep color. ++ */ ++ ++ if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** ENABLE PIXEL CLOCK SS ++** ++******************************************************************************** ++*******************************************************************************/ ++static enum bp_result enable_spread_spectrum_on_ppll_v1( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable); ++static enum bp_result enable_spread_spectrum_on_ppll_v2( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable); ++static enum bp_result enable_spread_spectrum_on_ppll_v3( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable); ++ ++static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(EnableSpreadSpectrumOnPPLL)) { ++ case 1: ++ bp->cmd_tbl.enable_spread_spectrum_on_ppll = ++ enable_spread_spectrum_on_ppll_v1; ++ break; ++ case 2: ++ bp->cmd_tbl.enable_spread_spectrum_on_ppll = ++ enable_spread_spectrum_on_ppll_v2; ++ break; ++ case 3: ++ bp->cmd_tbl.enable_spread_spectrum_on_ppll = ++ enable_spread_spectrum_on_ppll_v3; ++ break; ++ default: ++ bp->cmd_tbl.enable_spread_spectrum_on_ppll = NULL; ++ break; ++ } ++} ++ ++static enum bp_result enable_spread_spectrum_on_ppll_v1( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ENABLE_SPREAD_SPECTRUM_ON_PPLL params; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ if ((enable == true) && (bp_params->percentage > 0)) ++ params.ucEnable = ATOM_ENABLE; ++ else ++ params.ucEnable = ATOM_DISABLE; ++ ++ params.usSpreadSpectrumPercentage = ++ cpu_to_le16((uint16_t)bp_params->percentage); ++ params.ucSpreadSpectrumStep = ++ (uint8_t)bp_params->ver1.step; ++ params.ucSpreadSpectrumDelay = ++ (uint8_t)bp_params->ver1.delay; ++ /* convert back to unit of 10KHz */ ++ params.ucSpreadSpectrumRange = ++ (uint8_t)(bp_params->ver1.range / 10000); ++ ++ if (bp_params->flags.EXTERNAL_SS) ++ params.ucSpreadSpectrumType |= ATOM_EXTERNAL_SS_MASK; ++ ++ if (bp_params->flags.CENTER_SPREAD) ++ params.ucSpreadSpectrumType |= ATOM_SS_CENTRE_SPREAD_MODE; ++ ++ if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL1) ++ params.ucPpll = ATOM_PPLL1; ++ else if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL2) ++ params.ucPpll = ATOM_PPLL2; ++ else ++ BREAK_TO_DEBUGGER(); /* Unexpected PLL value!! */ ++ ++ if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result enable_spread_spectrum_on_ppll_v2( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 params; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL1) ++ params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V2_P1PLL; ++ else if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL2) ++ params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V2_P2PLL; ++ else ++ BREAK_TO_DEBUGGER(); /* Unexpected PLL value!! */ ++ ++ if ((enable == true) && (bp_params->percentage > 0)) { ++ params.ucEnable = ATOM_ENABLE; ++ ++ params.usSpreadSpectrumPercentage = ++ cpu_to_le16((uint16_t)(bp_params->percentage)); ++ params.usSpreadSpectrumStep = ++ cpu_to_le16((uint16_t)(bp_params->ds.ds_frac_size)); ++ ++ if (bp_params->flags.EXTERNAL_SS) ++ params.ucSpreadSpectrumType |= ++ ATOM_PPLL_SS_TYPE_V2_EXT_SPREAD; ++ ++ if (bp_params->flags.CENTER_SPREAD) ++ params.ucSpreadSpectrumType |= ++ ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD; ++ ++ /* Both amounts need to be left shifted first before bit ++ * comparison. Otherwise, the result will always be zero here ++ */ ++ params.usSpreadSpectrumAmount = cpu_to_le16((uint16_t)( ++ ((bp_params->ds.feedback_amount << ++ ATOM_PPLL_SS_AMOUNT_V2_FBDIV_SHIFT) & ++ ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK) | ++ ((bp_params->ds.nfrac_amount << ++ ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) & ++ ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK))); ++ } else ++ params.ucEnable = ATOM_DISABLE; ++ ++ if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result enable_spread_spectrum_on_ppll_v3( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 params; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ switch (bp_params->pll_id) { ++ case CLOCK_SOURCE_ID_PLL0: ++ /* ATOM_PPLL_SS_TYPE_V3_P0PLL; this is pixel clock only, ++ * not for SI display clock. ++ */ ++ params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_DCPLL; ++ break; ++ case CLOCK_SOURCE_ID_PLL1: ++ params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_P1PLL; ++ break; ++ ++ case CLOCK_SOURCE_ID_PLL2: ++ params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_P2PLL; ++ break; ++ ++ case CLOCK_SOURCE_ID_DCPLL: ++ params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_DCPLL; ++ break; ++ ++ default: ++ BREAK_TO_DEBUGGER(); ++ /* Unexpected PLL value!! */ ++ return result; ++ } ++ ++ if (enable == true) { ++ params.ucEnable = ATOM_ENABLE; ++ ++ params.usSpreadSpectrumAmountFrac = ++ cpu_to_le16((uint16_t)(bp_params->ds_frac_amount)); ++ params.usSpreadSpectrumStep = ++ cpu_to_le16((uint16_t)(bp_params->ds.ds_frac_size)); ++ ++ if (bp_params->flags.EXTERNAL_SS) ++ params.ucSpreadSpectrumType |= ++ ATOM_PPLL_SS_TYPE_V3_EXT_SPREAD; ++ if (bp_params->flags.CENTER_SPREAD) ++ params.ucSpreadSpectrumType |= ++ ATOM_PPLL_SS_TYPE_V3_CENTRE_SPREAD; ++ ++ /* Both amounts need to be left shifted first before bit ++ * comparison. Otherwise, the result will always be zero here ++ */ ++ params.usSpreadSpectrumAmount = cpu_to_le16((uint16_t)( ++ ((bp_params->ds.feedback_amount << ++ ATOM_PPLL_SS_AMOUNT_V3_FBDIV_SHIFT) & ++ ATOM_PPLL_SS_AMOUNT_V3_FBDIV_MASK) | ++ ((bp_params->ds.nfrac_amount << ++ ATOM_PPLL_SS_AMOUNT_V3_NFRAC_SHIFT) & ++ ATOM_PPLL_SS_AMOUNT_V3_NFRAC_MASK))); ++ } else ++ params.ucEnable = ATOM_DISABLE; ++ ++ if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** ADJUST DISPLAY PLL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result adjust_display_pll_v2( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params); ++static enum bp_result adjust_display_pll_v3( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params); ++ ++static void init_adjust_display_pll(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(AdjustDisplayPll)) { ++ case 2: ++ bp->cmd_tbl.adjust_display_pll = adjust_display_pll_v2; ++ break; ++ case 3: ++ bp->cmd_tbl.adjust_display_pll = adjust_display_pll_v3; ++ break; ++ default: ++ bp->cmd_tbl.adjust_display_pll = NULL; ++ break; ++ } ++} ++ ++static bool adjust_display_pll_bug_patch(ADJUST_DISPLAY_PLL_PARAMETERS *params) ++{ ++ /* vbios bug: pixel clock should not be doubled for DVO with 24bit ++ * interface */ ++ if ((params->ucTransmitterID == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1) ++ && (params->ucDVOConfig == DVO_ENCODER_CONFIG_24BIT)) ++ /* the current pixel clock is good. no adjustment is required */ ++ return true; ++ return false; ++} ++ ++static enum bp_result adjust_display_pll_v2( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ADJUST_DISPLAY_PLL_PS_ALLOCATION params = { 0 }; ++ ++ /* We need to convert from KHz units into 10KHz units and then convert ++ * output pixel clock back 10KHz-->KHz */ ++ uint32_t pixel_clock_10KHz_in = bp_params->pixel_clock / 10; ++ ++ params.usPixelClock = cpu_to_le16((uint16_t)(pixel_clock_10KHz_in)); ++ params.ucTransmitterID = ++ bp->cmd_helper->encoder_id_to_atom( ++ dal_graphics_object_id_get_encoder_id( ++ bp_params->encoder_object_id)); ++ params.ucEncodeMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ bp_params->signal_type, false); ++ params.ucDVOConfig = (uint8_t)(bp_params->dvo_config); ++ ++ if (adjust_display_pll_bug_patch(¶ms) ++ || EXEC_BIOS_CMD_TABLE(AdjustDisplayPll, params)) { ++ /* Convert output pixel clock back 10KHz-->KHz: multiply ++ * original pixel clock in KHz by ratio ++ * [output pxlClk/input pxlClk] */ ++ uint64_t pixel_clock_10KHz_out = ++ le16_to_cpu((uint64_t)params.usPixelClock); ++ uint64_t pixel_clock = (uint64_t)bp_params->pixel_clock; ++ ++ bp_params->adjusted_pixel_clock = ++ div_u64(pixel_clock * pixel_clock_10KHz_out, ++ pixel_clock_10KHz_in); ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++static enum bp_result adjust_display_pll_v3( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 params; ++ uint32_t pixel_clk_10_kHz_in = bp_params->pixel_clock / 10; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ /* We need to convert from KHz units into 10KHz units and then convert ++ * output pixel clock back 10KHz-->KHz */ ++ params.sInput.usPixelClock = cpu_to_le16((uint16_t)pixel_clk_10_kHz_in); ++ params.sInput.ucTransmitterID = ++ bp->cmd_helper->encoder_id_to_atom( ++ dal_graphics_object_id_get_encoder_id( ++ bp_params->encoder_object_id)); ++ params.sInput.ucEncodeMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ bp_params->signal_type, false); ++ ++ if (DISP_PLL_CONFIG_DVO_DDR_MODE_LOW_12BIT == ++ bp_params->display_pll_config) ++ params.sInput.ucDispPllConfig = ++ DISPPLL_CONFIG_DVO_DDR_SPEED | ++ DISPPLL_CONFIG_DVO_LOW12BIT; ++ else if (DISP_PLL_CONFIG_DVO_DDR_MODE_UPPER_12BIT == ++ bp_params->display_pll_config) ++ params.sInput.ucDispPllConfig = ++ DISPPLL_CONFIG_DVO_DDR_SPEED | ++ DISPPLL_CONFIG_DVO_UPPER12BIT; ++ else if (DISP_PLL_CONFIG_DVO_DDR_MODE_24BIT == ++ bp_params->display_pll_config) ++ params.sInput.ucDispPllConfig = ++ DISPPLL_CONFIG_DVO_DDR_SPEED | DISPPLL_CONFIG_DVO_24BIT; ++ else ++ /* this does not mean anything here */ ++ params.sInput.ucDispPllConfig = ++ (uint8_t)(bp_params->display_pll_config); ++ ++ if (bp_params->ss_enable == true) ++ params.sInput.ucDispPllConfig |= DISPPLL_CONFIG_SS_ENABLE; ++ ++ if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) ++ params.sInput.ucDispPllConfig |= DISPPLL_CONFIG_DUAL_LINK; ++ ++ if (EXEC_BIOS_CMD_TABLE(AdjustDisplayPll, params)) { ++ /* Convert output pixel clock back 10KHz-->KHz: multiply ++ * original pixel clock in KHz by ratio ++ * [output pxlClk/input pxlClk] */ ++ uint64_t pixel_clk_10_khz_out = ++ (uint64_t)le32_to_cpu(params.sOutput.ulDispPllFreq); ++ uint64_t pixel_clk = (uint64_t)bp_params->pixel_clock; ++ ++ if (pixel_clk_10_kHz_in != 0) { ++ bp_params->adjusted_pixel_clock = ++ div_u64(pixel_clk * pixel_clk_10_khz_out, ++ pixel_clk_10_kHz_in); ++ } else { ++ bp_params->adjusted_pixel_clock = 0; ++ BREAK_TO_DEBUGGER(); ++ } ++ ++ bp_params->reference_divider = params.sOutput.ucRefDiv; ++ bp_params->pixel_clock_post_divider = params.sOutput.ucPostDiv; ++ ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** DAC ENCODER CONTROL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result dac1_encoder_control_v1( ++ struct bios_parser *bp, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard); ++static enum bp_result dac2_encoder_control_v1( ++ struct bios_parser *bp, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard); ++ ++static void init_dac_encoder_control(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(DAC1EncoderControl)) { ++ case 1: ++ bp->cmd_tbl.dac1_encoder_control = dac1_encoder_control_v1; ++ break; ++ default: ++ bp->cmd_tbl.dac1_encoder_control = NULL; ++ break; ++ } ++ switch (BIOS_CMD_TABLE_PARA_REVISION(DAC2EncoderControl)) { ++ case 1: ++ bp->cmd_tbl.dac2_encoder_control = dac2_encoder_control_v1; ++ break; ++ default: ++ bp->cmd_tbl.dac2_encoder_control = NULL; ++ break; ++ } ++} ++ ++static void dac_encoder_control_prepare_params( ++ DAC_ENCODER_CONTROL_PS_ALLOCATION *params, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard) ++{ ++ params->ucDacStandard = dac_standard; ++ if (enable) ++ params->ucAction = ATOM_ENABLE; ++ else ++ params->ucAction = ATOM_DISABLE; ++ ++ /* We need to convert from KHz units into 10KHz units ++ * it looks as if the TvControl do not care about pixel clock ++ */ ++ params->usPixelClock = cpu_to_le16((uint16_t)(pixel_clock / 10)); ++} ++ ++static enum bp_result dac1_encoder_control_v1( ++ struct bios_parser *bp, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DAC_ENCODER_CONTROL_PS_ALLOCATION params; ++ ++ dac_encoder_control_prepare_params( ++ ¶ms, ++ enable, ++ pixel_clock, ++ dac_standard); ++ ++ if (EXEC_BIOS_CMD_TABLE(DAC1EncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result dac2_encoder_control_v1( ++ struct bios_parser *bp, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DAC_ENCODER_CONTROL_PS_ALLOCATION params; ++ ++ dac_encoder_control_prepare_params( ++ ¶ms, ++ enable, ++ pixel_clock, ++ dac_standard); ++ ++ if (EXEC_BIOS_CMD_TABLE(DAC2EncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** DAC OUTPUT CONTROL ++** ++******************************************************************************** ++*******************************************************************************/ ++static enum bp_result dac1_output_control_v1( ++ struct bios_parser *bp, ++ bool enable); ++static enum bp_result dac2_output_control_v1( ++ struct bios_parser *bp, ++ bool enable); ++ ++static void init_dac_output_control(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(DAC1OutputControl)) { ++ case 1: ++ bp->cmd_tbl.dac1_output_control = dac1_output_control_v1; ++ break; ++ default: ++ bp->cmd_tbl.dac1_output_control = NULL; ++ break; ++ } ++ switch (BIOS_CMD_TABLE_PARA_REVISION(DAC2OutputControl)) { ++ case 1: ++ bp->cmd_tbl.dac2_output_control = dac2_output_control_v1; ++ break; ++ default: ++ bp->cmd_tbl.dac2_output_control = NULL; ++ break; ++ } ++} ++ ++static enum bp_result dac1_output_control_v1( ++ struct bios_parser *bp, bool enable) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION params; ++ ++ if (enable) ++ params.ucAction = ATOM_ENABLE; ++ else ++ params.ucAction = ATOM_DISABLE; ++ ++ if (EXEC_BIOS_CMD_TABLE(DAC1OutputControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result dac2_output_control_v1( ++ struct bios_parser *bp, bool enable) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION params; ++ ++ if (enable) ++ params.ucAction = ATOM_ENABLE; ++ else ++ params.ucAction = ATOM_DISABLE; ++ ++ if (EXEC_BIOS_CMD_TABLE(DAC2OutputControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** DAC LOAD DETECTION ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum signal_type dac_load_detection_v3( ++ struct bios_parser *bp, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type display_signal); ++ ++static void init_dac_load_detection(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(DAC_LoadDetection)) { ++ case 3: ++ bp->cmd_tbl.dac_load_detection = dac_load_detection_v3; ++ break; ++ default: ++ bp->cmd_tbl.dac_load_detection = NULL; ++ break; ++ } ++} ++ ++static enum signal_type dac_load_detection_v3( ++ struct bios_parser *bp, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type display_signal) ++{ ++ DAC_LOAD_DETECTION_PS_ALLOCATION params; ++ enum signal_type signal = SIGNAL_TYPE_NONE; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ /* load detection is cupported for CRT, TV and CV */ ++ switch (display_signal) { ++ case SIGNAL_TYPE_RGB: ++ switch (dal_graphics_object_id_get_encoder_id(encoder)) { ++ case ENCODER_ID_INTERNAL_DAC1: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC1: ++ params.sDacload.usDeviceID = ++ cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT); ++ break; ++ case ENCODER_ID_INTERNAL_DAC2: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC2: ++ params.sDacload.usDeviceID = ++ cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT); ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ return signal; ++ } ++ ++ /* set the encoder to detect on */ ++ switch (dal_graphics_object_id_get_encoder_id(encoder)) { ++ case ENCODER_ID_INTERNAL_DAC1: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC1: ++ params.sDacload.ucDacType = ATOM_DAC_A; ++ break; ++ case ENCODER_ID_INTERNAL_DAC2: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC2: ++ params.sDacload.ucDacType = ATOM_DAC_B; ++ break; ++ case ENCODER_ID_EXTERNAL_CH7303: ++ params.sDacload.ucDacType = ATOM_EXT_DAC; ++ break; ++ default: ++ return signal; ++ } ++ ++ if (!EXEC_BIOS_CMD_TABLE(DAC_LoadDetection, params)) ++ return signal; ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ signal = bp->bios_helper->detect_sink( ++ bp->ctx, ++ encoder, ++ connector, ++ display_signal); ++#else ++ BREAK_TO_DEBUGGER(); /* VBios is needed */ ++#endif ++ ++ return signal; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** BLANK CRTC ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result blank_crtc_v1( ++ struct bios_parser *bp, ++ struct bp_blank_crtc_parameters *bp_params, ++ bool blank); ++ ++static void init_blank_crtc(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(BlankCRTC)) { ++ case 1: ++ bp->cmd_tbl.blank_crtc = blank_crtc_v1; ++ break; ++ default: ++ bp->cmd_tbl.blank_crtc = NULL; ++ break; ++ } ++} ++ ++static enum bp_result blank_crtc_v1( ++ struct bios_parser *bp, ++ struct bp_blank_crtc_parameters *bp_params, ++ bool blank) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ BLANK_CRTC_PARAMETERS params = {0}; ++ uint8_t atom_controller_id; ++ ++ if (bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, ++ &atom_controller_id)) { ++ params.ucCRTC = (uint8_t)atom_controller_id; ++ ++ if (blank) ++ params.ucBlanking = ATOM_BLANKING; ++ else ++ params.ucBlanking = ATOM_BLANKING_OFF; ++ params.usBlackColorRCr = ++ cpu_to_le16((uint16_t)bp_params->black_color_rcr); ++ params.usBlackColorGY = ++ cpu_to_le16((uint16_t)bp_params->black_color_gy); ++ params.usBlackColorBCb = ++ cpu_to_le16((uint16_t)bp_params->black_color_bcb); ++ ++ if (EXEC_BIOS_CMD_TABLE(BlankCRTC, params)) ++ result = BP_RESULT_OK; ++ } else ++ /* Not support more than two CRTC as current ASIC, update this ++ * if needed. ++ */ ++ result = BP_RESULT_BADINPUT; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** SET CRTC TIMING ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result set_crtc_using_dtd_timing_v3( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params); ++static enum bp_result set_crtc_timing_v1( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params); ++ ++static void init_set_crtc_timing(struct bios_parser *bp) ++{ ++ uint32_t dtd_version = ++ BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_UsingDTDTiming); ++ if (dtd_version > 2) ++ switch (dtd_version) { ++ case 3: ++ bp->cmd_tbl.set_crtc_timing = ++ set_crtc_using_dtd_timing_v3; ++ break; ++ default: ++ bp->cmd_tbl.set_crtc_timing = NULL; ++ break; ++ } ++ else ++ switch (BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_Timing)) { ++ case 1: ++ bp->cmd_tbl.set_crtc_timing = set_crtc_timing_v1; ++ break; ++ default: ++ bp->cmd_tbl.set_crtc_timing = NULL; ++ break; ++ } ++} ++ ++static enum bp_result set_crtc_timing_v1( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION params = {0}; ++ uint8_t atom_controller_id; ++ ++ if (bp->cmd_helper->controller_id_to_atom( ++ bp_params->controller_id, &atom_controller_id)) ++ params.ucCRTC = atom_controller_id; ++ ++ params.usH_Total = cpu_to_le16((uint16_t)(bp_params->h_total)); ++ params.usH_Disp = cpu_to_le16((uint16_t)(bp_params->h_addressable)); ++ params.usH_SyncStart = cpu_to_le16((uint16_t)(bp_params->h_sync_start)); ++ params.usH_SyncWidth = cpu_to_le16((uint16_t)(bp_params->h_sync_width)); ++ params.usV_Total = cpu_to_le16((uint16_t)(bp_params->v_total)); ++ params.usV_Disp = cpu_to_le16((uint16_t)(bp_params->v_addressable)); ++ params.usV_SyncStart = ++ cpu_to_le16((uint16_t)(bp_params->v_sync_start)); ++ params.usV_SyncWidth = ++ cpu_to_le16((uint16_t)(bp_params->v_sync_width)); ++ ++ ++ /* VBIOS does not expect any value except zero into this call, for ++ * underscan use another entry ProgramOverscan call but when mode ++ * 1776x1000 with the overscan 72x44 .e.i. 1920x1080 @30 DAL2 is ok, ++ * but when same ,but 60 Hz there is corruption ++ * DAL1 does not allow the mode 1776x1000@60 ++ */ ++ params.ucOverscanRight = (uint8_t)bp_params->h_overscan_right; ++ params.ucOverscanLeft = (uint8_t)bp_params->h_overscan_left; ++ params.ucOverscanBottom = (uint8_t)bp_params->v_overscan_bottom; ++ params.ucOverscanTop = (uint8_t)bp_params->v_overscan_top; ++ ++ if (0 == bp_params->flags.HSYNC_POSITIVE_POLARITY) ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_HSYNC_POLARITY); ++ ++ if (0 == bp_params->flags.VSYNC_POSITIVE_POLARITY) ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_VSYNC_POLARITY); ++ ++ if (bp_params->flags.INTERLACE) { ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_INTERLACE); ++ ++ /* original DAL code has this condition to apply tis for ++ * non-TV/CV only due to complex MV testing for possible ++ * impact ++ * if (pACParameters->signal != SignalType_YPbPr && ++ * pACParameters->signal != SignalType_Composite && ++ * pACParameters->signal != SignalType_SVideo) ++ */ ++ /* HW will deduct 0.5 line from 2nd feild. ++ * i.e. for 1080i, it is 2 lines for 1st field, 2.5 ++ * lines for the 2nd feild. we need input as 5 instead ++ * of 4, but it is 4 either from Edid data ++ * (spec CEA 861) or CEA timing table. ++ */ ++ params.usV_SyncStart = ++ cpu_to_le16((uint16_t)(bp_params->v_sync_start + 1)); ++ } ++ ++ if (bp_params->flags.HORZ_COUNT_BY_TWO) ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_DOUBLE_CLOCK_MODE); ++ ++ if (EXEC_BIOS_CMD_TABLE(SetCRTC_Timing, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result set_crtc_using_dtd_timing_v3( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ SET_CRTC_USING_DTD_TIMING_PARAMETERS params = {0}; ++ uint8_t atom_controller_id; ++ ++ if (bp->cmd_helper->controller_id_to_atom( ++ bp_params->controller_id, &atom_controller_id)) ++ params.ucCRTC = atom_controller_id; ++ ++ /* bios usH_Size wants h addressable size */ ++ params.usH_Size = cpu_to_le16((uint16_t)bp_params->h_addressable); ++ /* bios usH_Blanking_Time wants borders included in blanking */ ++ params.usH_Blanking_Time = ++ cpu_to_le16((uint16_t)(bp_params->h_total - bp_params->h_addressable)); ++ /* bios usV_Size wants v addressable size */ ++ params.usV_Size = cpu_to_le16((uint16_t)bp_params->v_addressable); ++ /* bios usV_Blanking_Time wants borders included in blanking */ ++ params.usV_Blanking_Time = ++ cpu_to_le16((uint16_t)(bp_params->v_total - bp_params->v_addressable)); ++ /* bios usHSyncOffset is the offset from the end of h addressable, ++ * our horizontalSyncStart is the offset from the beginning ++ * of h addressable */ ++ params.usH_SyncOffset = ++ cpu_to_le16((uint16_t)(bp_params->h_sync_start - bp_params->h_addressable)); ++ params.usH_SyncWidth = cpu_to_le16((uint16_t)bp_params->h_sync_width); ++ /* bios usHSyncOffset is the offset from the end of v addressable, ++ * our verticalSyncStart is the offset from the beginning of ++ * v addressable */ ++ params.usV_SyncOffset = ++ cpu_to_le16((uint16_t)(bp_params->v_sync_start - bp_params->v_addressable)); ++ params.usV_SyncWidth = cpu_to_le16((uint16_t)bp_params->v_sync_width); ++ ++ /* we assume that overscan from original timing does not get bigger ++ * than 255 ++ * we will program all the borders in the Set CRTC Overscan call below ++ */ ++ ++ if (0 == bp_params->flags.HSYNC_POSITIVE_POLARITY) ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_HSYNC_POLARITY); ++ ++ if (0 == bp_params->flags.VSYNC_POSITIVE_POLARITY) ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_VSYNC_POLARITY); ++ ++ ++ if (bp_params->flags.INTERLACE) { ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_INTERLACE); ++ ++ /* original DAL code has this condition to apply this ++ * for non-TV/CV only ++ * due to complex MV testing for possible impact ++ * if ( pACParameters->signal != SignalType_YPbPr && ++ * pACParameters->signal != SignalType_Composite && ++ * pACParameters->signal != SignalType_SVideo) ++ */ ++ { ++ /* HW will deduct 0.5 line from 2nd feild. ++ * i.e. for 1080i, it is 2 lines for 1st field, ++ * 2.5 lines for the 2nd feild. we need input as 5 ++ * instead of 4. ++ * but it is 4 either from Edid data (spec CEA 861) ++ * or CEA timing table. ++ */ ++ params.usV_SyncOffset = ++ cpu_to_le16(le16_to_cpu(params.usV_SyncOffset) + 1); ++ ++ } ++ } ++ ++ if (bp_params->flags.HORZ_COUNT_BY_TWO) ++ params.susModeMiscInfo.usAccess = ++ cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_DOUBLE_CLOCK_MODE); ++ ++ if (EXEC_BIOS_CMD_TABLE(SetCRTC_UsingDTDTiming, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** SET CRTC OVERSCAN ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result set_crtc_overscan_v1( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_overscan_parameters *bp_params); ++ ++static void init_set_crtc_overscan(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_OverScan)) { ++ case 1: ++ bp->cmd_tbl.set_crtc_overscan = set_crtc_overscan_v1; ++ break; ++ default: ++ bp->cmd_tbl.set_crtc_overscan = NULL; ++ break; ++ } ++} ++ ++static enum bp_result set_crtc_overscan_v1( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_overscan_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ SET_CRTC_OVERSCAN_PARAMETERS params = {0}; ++ uint8_t atom_controller_id; ++ ++ if (bp->cmd_helper->controller_id_to_atom( ++ bp_params->controller_id, &atom_controller_id)) ++ params.ucCRTC = atom_controller_id; ++ else ++ return BP_RESULT_BADINPUT; ++ ++ params.usOverscanRight = ++ cpu_to_le16((uint16_t)bp_params->h_overscan_right); ++ params.usOverscanLeft = ++ cpu_to_le16((uint16_t)bp_params->h_overscan_left); ++ params.usOverscanBottom = ++ cpu_to_le16((uint16_t)bp_params->v_overscan_bottom); ++ params.usOverscanTop = ++ cpu_to_le16((uint16_t)bp_params->v_overscan_top); ++ ++ if (EXEC_BIOS_CMD_TABLE(SetCRTC_OverScan, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** SELECT CRTC SOURCE ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result select_crtc_source_v2( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params); ++static enum bp_result select_crtc_source_v3( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params); ++ ++static void init_select_crtc_source(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(SelectCRTC_Source)) { ++ case 2: ++ bp->cmd_tbl.select_crtc_source = select_crtc_source_v2; ++ break; ++ case 3: ++ bp->cmd_tbl.select_crtc_source = select_crtc_source_v3; ++ break; ++ default: ++ bp->cmd_tbl.select_crtc_source = NULL; ++ break; ++ } ++} ++ ++static enum bp_result select_crtc_source_v2( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ SELECT_CRTC_SOURCE_PARAMETERS_V2 params; ++ uint8_t atom_controller_id; ++ uint32_t atom_engine_id; ++ enum signal_type s = bp_params->signal; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ /* set controller id */ ++ if (bp->cmd_helper->controller_id_to_atom( ++ bp_params->controller_id, &atom_controller_id)) ++ params.ucCRTC = atom_controller_id; ++ else ++ return BP_RESULT_FAILURE; ++ ++ /* set encoder id */ ++ if (bp->cmd_helper->engine_bp_to_atom( ++ bp_params->engine_id, &atom_engine_id)) ++ params.ucEncoderID = (uint8_t)atom_engine_id; ++ else ++ return BP_RESULT_FAILURE; ++ ++ if (SIGNAL_TYPE_EDP == s || ++ (SIGNAL_TYPE_DISPLAY_PORT == s && ++ SIGNAL_TYPE_LVDS == bp_params->sink_signal)) ++ s = SIGNAL_TYPE_LVDS; ++ ++ params.ucEncodeMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ s, bp_params->enable_dp_audio); ++ ++ if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result select_crtc_source_v3( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params) ++{ ++ bool result = BP_RESULT_FAILURE; ++ SELECT_CRTC_SOURCE_PARAMETERS_V3 params; ++ uint8_t atom_controller_id; ++ uint32_t atom_engine_id; ++ enum signal_type s = bp_params->signal; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ if (bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, ++ &atom_controller_id)) ++ params.ucCRTC = atom_controller_id; ++ else ++ return result; ++ ++ if (bp->cmd_helper->engine_bp_to_atom(bp_params->engine_id, ++ &atom_engine_id)) ++ params.ucEncoderID = (uint8_t)atom_engine_id; ++ else ++ return result; ++ ++ if (SIGNAL_TYPE_EDP == s || ++ (SIGNAL_TYPE_DISPLAY_PORT == s && ++ SIGNAL_TYPE_LVDS == bp_params->sink_signal)) ++ s = SIGNAL_TYPE_LVDS; ++ ++ params.ucEncodeMode = ++ bp->cmd_helper->encoder_mode_bp_to_atom( ++ s, bp_params->enable_dp_audio); ++ /* Needed for VBIOS Random Spatial Dithering feature */ ++ params.ucDstBpc = (uint8_t)(bp_params->display_output_bit_depth); ++ ++ if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** ENABLE CRTC ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result enable_crtc_v1( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable); ++ ++static void init_enable_crtc(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(EnableCRTC)) { ++ case 1: ++ bp->cmd_tbl.enable_crtc = enable_crtc_v1; ++ break; ++ default: ++ bp->cmd_tbl.enable_crtc = NULL; ++ break; ++ } ++} ++ ++static enum bp_result enable_crtc_v1( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable) ++{ ++ bool result = BP_RESULT_FAILURE; ++ ENABLE_CRTC_PARAMETERS params = {0}; ++ uint8_t id; ++ ++ if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) ++ params.ucCRTC = id; ++ else ++ return BP_RESULT_BADINPUT; ++ ++ if (enable) ++ params.ucEnable = ATOM_ENABLE; ++ else ++ params.ucEnable = ATOM_DISABLE; ++ ++ if (EXEC_BIOS_CMD_TABLE(EnableCRTC, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** ENABLE CRTC MEM REQ ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result enable_crtc_mem_req_v1( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable); ++ ++static void init_enable_crtc_mem_req(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(EnableCRTCMemReq)) { ++ case 1: ++ bp->cmd_tbl.enable_crtc_mem_req = enable_crtc_mem_req_v1; ++ break; ++ default: ++ bp->cmd_tbl.enable_crtc_mem_req = NULL; ++ break; ++ } ++} ++ ++static enum bp_result enable_crtc_mem_req_v1( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable) ++{ ++ bool result = BP_RESULT_BADINPUT; ++ ENABLE_CRTC_PARAMETERS params = {0}; ++ uint8_t id; ++ ++ if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) { ++ params.ucCRTC = id; ++ ++ if (enable) ++ params.ucEnable = ATOM_ENABLE; ++ else ++ params.ucEnable = ATOM_DISABLE; ++ ++ if (EXEC_BIOS_CMD_TABLE(EnableCRTCMemReq, params)) ++ result = BP_RESULT_OK; ++ else ++ result = BP_RESULT_FAILURE; ++ } ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** DISPLAY PLL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result program_clock_v5( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++static enum bp_result program_clock_v6( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++ ++static void init_program_clock(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)) { ++ case 5: ++ bp->cmd_tbl.program_clock = program_clock_v5; ++ break; ++ case 6: ++ bp->cmd_tbl.program_clock = program_clock_v6; ++ break; ++ default: ++ bp->cmd_tbl.program_clock = NULL; ++ break; ++ } ++} ++ ++static enum bp_result program_clock_v5( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ++ SET_PIXEL_CLOCK_PS_ALLOCATION_V5 params; ++ uint32_t atom_pll_id; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ if (!bp->cmd_helper->clock_source_id_to_atom( ++ bp_params->pll_id, &atom_pll_id)) { ++ BREAK_TO_DEBUGGER(); /* Invalid Inpute!! */ ++ return BP_RESULT_BADINPUT; ++ } ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ params.sPCLKInput.ucPpll = (uint8_t) atom_pll_id; ++ params.sPCLKInput.usPixelClock = ++ cpu_to_le16((uint16_t) (bp_params->target_pixel_clock / 10)); ++ params.sPCLKInput.ucCRTC = (uint8_t) ATOM_CRTC_INVALID; ++ ++ if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) ++ params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; ++ ++ if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++ ++static enum bp_result program_clock_v6( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ++ SET_PIXEL_CLOCK_PS_ALLOCATION_V6 params; ++ uint32_t atom_pll_id; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ if (!bp->cmd_helper->clock_source_id_to_atom( ++ bp_params->pll_id, &atom_pll_id)) { ++ BREAK_TO_DEBUGGER(); /*Invalid Input!!*/ ++ return BP_RESULT_BADINPUT; ++ } ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ params.sPCLKInput.ucPpll = (uint8_t)atom_pll_id; ++ params.sPCLKInput.ulDispEngClkFreq = ++ cpu_to_le32(bp_params->target_pixel_clock / 10); ++ ++ if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) ++ params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; ++ ++ if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) { ++ /* True display clock is returned by VBIOS if DFS bypass ++ * is enabled. */ ++ bp_params->dfs_bypass_display_clock = ++ (uint32_t)(le32_to_cpu(params.sPCLKInput.ulDispEngClkFreq) * 10); ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** COMPUTE MEMORY ENGINE PLL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result compute_memore_engine_pll_v4( ++ struct bios_parser *bp, ++ struct bp_display_clock_parameters *bp_params); ++ ++static void init_compute_memore_engine_pll(struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(ComputeMemoryEnginePLL)) { ++ case 4: ++ bp->cmd_tbl.compute_memore_engine_pll = ++ compute_memore_engine_pll_v4; ++ break; ++ default: ++ bp->cmd_tbl.compute_memore_engine_pll = NULL; ++ break; ++ } ++} ++ ++static enum bp_result compute_memore_engine_pll_v4( ++ struct bios_parser *bp, ++ struct bp_display_clock_parameters *bp_params) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 params; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ params.ulClock = cpu_to_le32(bp_params->target_display_clock / 10); ++ ++ /* Initialize this to the target clock in case this call fails */ ++ bp_params->actual_display_clock = bp_params->target_display_clock; ++ ++ if (EXEC_BIOS_CMD_TABLE(ComputeMemoryEnginePLL, params)) { ++ /* Convert from 10KHz units back to KHz */ ++ bp_params->actual_display_clock = ++ le32_to_cpu(params.ulClock) * 10; ++ bp_params->actual_post_divider_id = params.ucPostDiv; ++ result = BP_RESULT_OK; ++ } ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** EXTERNAL ENCODER CONTROL ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result external_encoder_control_v3( ++ struct bios_parser *bp, ++ struct bp_external_encoder_control *cntl); ++ ++static void init_external_encoder_control( ++ struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(ExternalEncoderControl)) { ++ case 3: ++ bp->cmd_tbl.external_encoder_control = ++ external_encoder_control_v3; ++ break; ++ default: ++ bp->cmd_tbl.external_encoder_control = NULL; ++ break; ++ } ++} ++ ++static enum bp_result external_encoder_control_v3( ++ struct bios_parser *bp, ++ struct bp_external_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ++ /* we need use _PS_Alloc struct */ ++ EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 params; ++ EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 *cntl_params; ++ struct graphics_object_id encoder; ++ bool is_input_signal_dp = false; ++ ++ dc_service_memset(¶ms, 0, sizeof(params)); ++ ++ cntl_params = ¶ms.sExtEncoder; ++ ++ encoder = cntl->encoder_id; ++ ++ /* check if encoder supports external encoder control table */ ++ switch (dal_graphics_object_id_get_encoder_id(encoder)) { ++ case ENCODER_ID_EXTERNAL_NUTMEG: ++ case ENCODER_ID_EXTERNAL_TRAVIS: ++ is_input_signal_dp = true; ++ break; ++ ++ default: ++ BREAK_TO_DEBUGGER(); ++ return BP_RESULT_BADINPUT; ++ } ++ ++ /* Fill information based on the action ++ * ++ * Bit[6:4]: indicate external encoder, applied to all functions. ++ * =0: external encoder1, mapped to external encoder enum id1 ++ * =1: external encoder2, mapped to external encoder enum id2 ++ * ++ * enum ObjectEnumId ++ * { ++ * EnumId_Unknown = 0, ++ * EnumId_1, ++ * EnumId_2, ++ * }; ++ */ ++ cntl_params->ucConfig = (uint8_t)((encoder.enum_id - 1) << 4); ++ ++ switch (cntl->action) { ++ case EXTERNAL_ENCODER_CONTROL_INIT: ++ /* output display connector type. Only valid in encoder ++ * initialization */ ++ cntl_params->usConnectorId = ++ cpu_to_le16((uint16_t)cntl->connector_obj_id.id); ++ break; ++ case EXTERNAL_ENCODER_CONTROL_SETUP: ++ /* EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 pixel clock unit in ++ * 10KHz ++ * output display device pixel clock frequency in unit of 10KHz. ++ * Only valid in setup and enableoutput ++ */ ++ cntl_params->usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ /* Indicate display output signal type drive by external ++ * encoder, only valid in setup and enableoutput */ ++ cntl_params->ucEncoderMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ cntl->signal, false); ++ ++ if (is_input_signal_dp) { ++ /* Bit[0]: indicate link rate, =1: 2.7Ghz, =0: 1.62Ghz, ++ * only valid in encoder setup with DP mode. */ ++ if (LINK_RATE_HIGH == cntl->link_rate) ++ cntl_params->ucConfig |= 1; ++ /* output color depth Indicate encoder data bpc format ++ * in DP mode, only valid in encoder setup in DP mode. ++ */ ++ cntl_params->ucBitPerColor = ++ (uint8_t)(cntl->color_depth); ++ } ++ /* Indicate how many lanes used by external encoder, only valid ++ * in encoder setup and enableoutput. */ ++ cntl_params->ucLaneNum = (uint8_t)(cntl->lanes_number); ++ break; ++ case EXTERNAL_ENCODER_CONTROL_ENABLE: ++ cntl_params->usPixelClock = ++ cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); ++ cntl_params->ucEncoderMode = ++ (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( ++ cntl->signal, false); ++ cntl_params->ucLaneNum = (uint8_t)cntl->lanes_number; ++ break; ++ default: ++ break; ++ } ++ ++ cntl_params->ucAction = (uint8_t)cntl->action; ++ ++ if (EXEC_BIOS_CMD_TABLE(ExternalEncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ if (EXTERNAL_ENCODER_CONTROL_DAC_LOAD_DETECT == cntl->action) { ++#if defined(CONFIG_DRM_AMD_DAL_VBIOS_PRESENT) ++ if (BP_RESULT_OK == result) ++ /* get VBIOS result from scratch register. ++ * ExternalEncoderControl runs detection and save result ++ * in BIOS scratch registers. */ ++ cntl->signal = bp->bios_helper->detect_sink( ++ bp->ctx, ++ encoder, ++ cntl->connector_obj_id, ++ cntl->signal); ++ else/* BIOS table does not work. */ ++#endif ++ { ++ BREAK_TO_DEBUGGER(); /* VBios is needed */ ++ cntl->signal = SIGNAL_TYPE_NONE; ++ } ++ } ++ ++ return result; ++} ++ ++/******************************************************************************* ++******************************************************************************** ++** ++** ENABLE DISPLAY POWER GATING ++** ++******************************************************************************** ++*******************************************************************************/ ++ ++static enum bp_result enable_disp_power_gating_v2_1( ++ struct bios_parser *bp, ++ enum controller_id crtc_id, ++ enum bp_pipe_control_action action); ++ ++static void init_enable_disp_power_gating( ++ struct bios_parser *bp) ++{ ++ switch (BIOS_CMD_TABLE_PARA_REVISION(EnableDispPowerGating)) { ++ case 1: ++ bp->cmd_tbl.enable_disp_power_gating = ++ enable_disp_power_gating_v2_1; ++ break; ++ default: ++ bp->cmd_tbl.enable_disp_power_gating = NULL; ++ break; ++ } ++} ++ ++static enum bp_result enable_disp_power_gating_v2_1( ++ struct bios_parser *bp, ++ enum controller_id crtc_id, ++ enum bp_pipe_control_action action) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ++ ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 params = {0}; ++ uint8_t atom_crtc_id; ++ ++ if (bp->cmd_helper->controller_id_to_atom(crtc_id, &atom_crtc_id)) ++ params.ucDispPipeId = atom_crtc_id; ++ else ++ return BP_RESULT_BADINPUT; ++ ++ params.ucEnable = ++ bp->cmd_helper->disp_power_gating_action_to_atom(action); ++ ++ if (EXEC_BIOS_CMD_TABLE(EnableDispPowerGating, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/command_table.h b/drivers/gpu/drm/amd/dal/dc/bios/command_table.h +new file mode 100644 +index 0000000..814d31f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table.h +@@ -0,0 +1,117 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_COMMAND_TABLE_H__ ++#define __DAL_COMMAND_TABLE_H__ ++ ++struct bios_parser; ++struct bp_encoder_control; ++ ++struct cmd_tbl { ++ enum bp_result (*dig_encoder_control)( ++ struct bios_parser *bp, ++ struct bp_encoder_control *control); ++ enum bp_result (*encoder_control_dig1)( ++ struct bios_parser *bp, ++ struct bp_encoder_control *control); ++ enum bp_result (*encoder_control_dig2)( ++ struct bios_parser *bp, ++ struct bp_encoder_control *control); ++ enum bp_result (*dvo_encoder_control)( ++ struct bios_parser *bp, ++ struct bp_dvo_encoder_control *cntl); ++ enum bp_result (*transmitter_control)( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *control); ++ enum bp_result (*set_pixel_clock)( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++ enum bp_result (*enable_spread_spectrum_on_ppll)( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable); ++ enum bp_result (*adjust_display_pll)( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params); ++ enum bp_result (*dac1_encoder_control)( ++ struct bios_parser *bp, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard); ++ enum bp_result (*dac2_encoder_control)( ++ struct bios_parser *bp, ++ bool enable, ++ uint32_t pixel_clock, ++ uint8_t dac_standard); ++ enum bp_result (*dac1_output_control)( ++ struct bios_parser *bp, ++ bool enable); ++ enum bp_result (*dac2_output_control)( ++ struct bios_parser *bp, ++ bool enable); ++ enum signal_type (*dac_load_detection)( ++ struct bios_parser *bp, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type display_signal); ++ enum bp_result (*blank_crtc)( ++ struct bios_parser *bp, ++ struct bp_blank_crtc_parameters *bp_params, ++ bool blank); ++ enum bp_result (*set_crtc_timing)( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params); ++ enum bp_result (*set_crtc_overscan)( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_overscan_parameters *bp_params); ++ enum bp_result (*select_crtc_source)( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params); ++ enum bp_result (*enable_crtc)( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable); ++ enum bp_result (*enable_crtc_mem_req)( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable); ++ enum bp_result (*program_clock)( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++ enum bp_result (*compute_memore_engine_pll)( ++ struct bios_parser *bp, ++ struct bp_display_clock_parameters *bp_params); ++ enum bp_result (*external_encoder_control)( ++ struct bios_parser *bp, ++ struct bp_external_encoder_control *cntl); ++ enum bp_result (*enable_disp_power_gating)( ++ struct bios_parser *bp, ++ enum controller_id crtc_id, ++ enum bp_pipe_control_action action); ++}; ++ ++void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.c b/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.c +new file mode 100644 +index 0000000..dad1426 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.c +@@ -0,0 +1,315 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "atom.h" ++ ++#include "include/bios_parser_types.h" ++#include "include/adapter_service_types.h" ++ ++#include "command_table_helper.h" ++ ++bool dal_bios_parser_init_cmd_tbl_helper( ++ const struct command_table_helper **h, ++ enum dce_version dce) ++{ ++ switch (dce) { ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ *h = dal_cmd_tbl_helper_dce110_get_table(); ++ return true; ++ ++#endif ++ default: ++ /* Unsupported DCE */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++} ++ ++/* real implementations */ ++ ++bool dal_cmd_table_helper_controller_id_to_atom( ++ enum controller_id id, ++ uint8_t *atom_id) ++{ ++ if (atom_id == NULL) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ switch (id) { ++ case CONTROLLER_ID_D0: ++ *atom_id = ATOM_CRTC1; ++ return true; ++ case CONTROLLER_ID_D1: ++ *atom_id = ATOM_CRTC2; ++ return true; ++ case CONTROLLER_ID_D2: ++ *atom_id = ATOM_CRTC3; ++ return true; ++ case CONTROLLER_ID_D3: ++ *atom_id = ATOM_CRTC4; ++ return true; ++ case CONTROLLER_ID_D4: ++ *atom_id = ATOM_CRTC5; ++ return true; ++ case CONTROLLER_ID_D5: ++ *atom_id = ATOM_CRTC6; ++ return true; ++ case CONTROLLER_ID_UNDERLAY0: ++ *atom_id = ATOM_UNDERLAY_PIPE0; ++ return true; ++ case CONTROLLER_ID_UNDEFINED: ++ *atom_id = ATOM_CRTC_INVALID; ++ return true; ++ default: ++ /* Wrong controller id */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++} ++ ++/** ++* translate_transmitter_bp_to_atom ++* ++* @brief ++* Translate the Transmitter to the corresponding ATOM BIOS value ++* ++* @param ++* input transmitter ++* output digitalTransmitter ++* // =00: Digital Transmitter1 ( UNIPHY linkAB ) ++* // =01: Digital Transmitter2 ( UNIPHY linkCD ) ++* // =02: Digital Transmitter3 ( UNIPHY linkEF ) ++*/ ++uint8_t dal_cmd_table_helper_transmitter_bp_to_atom( ++ enum transmitter t) ++{ ++ switch (t) { ++ case TRANSMITTER_UNIPHY_A: ++ case TRANSMITTER_UNIPHY_B: ++ case TRANSMITTER_TRAVIS_LCD: ++ return 0; ++ case TRANSMITTER_UNIPHY_C: ++ case TRANSMITTER_UNIPHY_D: ++ return 1; ++ case TRANSMITTER_UNIPHY_E: ++ case TRANSMITTER_UNIPHY_F: ++ return 2; ++ default: ++ /* Invalid Transmitter Type! */ ++ BREAK_TO_DEBUGGER(); ++ return 0; ++ } ++} ++ ++uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom( ++ enum signal_type s, ++ bool enable_dp_audio) ++{ ++ switch (s) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ return ATOM_ENCODER_MODE_DVI; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ return ATOM_ENCODER_MODE_HDMI; ++ case SIGNAL_TYPE_LVDS: ++ return ATOM_ENCODER_MODE_LVDS; ++ case SIGNAL_TYPE_EDP: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ if (enable_dp_audio) ++ return ATOM_ENCODER_MODE_DP_AUDIO; ++ else ++ return ATOM_ENCODER_MODE_DP; ++ case SIGNAL_TYPE_RGB: ++ return ATOM_ENCODER_MODE_CRT; ++ default: ++ return ATOM_ENCODER_MODE_CRT; ++ } ++} ++ ++void dal_cmd_table_helper_assign_control_parameter( ++ const struct command_table_helper *h, ++ struct bp_encoder_control *control, ++ DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param) ++{ ++ /* there are three transmitter blocks, each one has two links 4-lanes ++ * each, A+B, C+D, E+F, Uniphy A, C and E are enumerated as link 0 in ++ * each transmitter block B, D and F as link 1, third transmitter block ++ * has non splitable links (UniphyE and UniphyF can not be configured ++ * separately to drive two different streams) ++ */ ++ if ((control->transmitter == TRANSMITTER_UNIPHY_B) || ++ (control->transmitter == TRANSMITTER_UNIPHY_D) || ++ (control->transmitter == TRANSMITTER_UNIPHY_F)) { ++ /* Bit2: Link Select ++ * =0: PHY linkA/C/E ++ * =1: PHY linkB/D/F ++ */ ++ ctrl_param->acConfig.ucLinkSel = 1; ++ } ++ ++ /* Bit[4:3]: Transmitter Selection ++ * =00: Digital Transmitter1 ( UNIPHY linkAB ) ++ * =01: Digital Transmitter2 ( UNIPHY linkCD ) ++ * =02: Digital Transmitter3 ( UNIPHY linkEF ) ++ * =03: Reserved ++ */ ++ ctrl_param->acConfig.ucTransmitterSel = ++ (uint8_t)(h->transmitter_bp_to_atom(control->transmitter)); ++ ++ /* We need to convert from KHz units into 10KHz units */ ++ ctrl_param->ucAction = h->encoder_action_to_atom(control->action); ++ ctrl_param->usPixelClock = cpu_to_le16((uint16_t)(control->pixel_clock / 10)); ++ ctrl_param->ucEncoderMode = ++ (uint8_t)(h->encoder_mode_bp_to_atom( ++ control->signal, control->enable_dp_audio)); ++ ctrl_param->ucLaneNum = (uint8_t)(control->lanes_number); ++} ++ ++bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src( ++ enum clock_source_id id, ++ uint32_t *ref_clk_src_id) ++{ ++ if (ref_clk_src_id == NULL) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ switch (id) { ++ case CLOCK_SOURCE_ID_PLL1: ++ *ref_clk_src_id = ENCODER_REFCLK_SRC_P1PLL; ++ return true; ++ case CLOCK_SOURCE_ID_PLL2: ++ *ref_clk_src_id = ENCODER_REFCLK_SRC_P2PLL; ++ return true; ++ case CLOCK_SOURCE_ID_DCPLL: ++ *ref_clk_src_id = ENCODER_REFCLK_SRC_DCPLL; ++ return true; ++ case CLOCK_SOURCE_ID_EXTERNAL: ++ *ref_clk_src_id = ENCODER_REFCLK_SRC_EXTCLK; ++ return true; ++ case CLOCK_SOURCE_ID_UNDEFINED: ++ *ref_clk_src_id = ENCODER_REFCLK_SRC_INVALID; ++ return true; ++ default: ++ /* Unsupported clock source id */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++} ++ ++uint8_t dal_cmd_table_helper_encoder_id_to_atom( ++ enum encoder_id id) ++{ ++ switch (id) { ++ case ENCODER_ID_INTERNAL_LVDS: ++ return ENCODER_OBJECT_ID_INTERNAL_LVDS; ++ case ENCODER_ID_INTERNAL_TMDS1: ++ return ENCODER_OBJECT_ID_INTERNAL_TMDS1; ++ case ENCODER_ID_INTERNAL_TMDS2: ++ return ENCODER_OBJECT_ID_INTERNAL_TMDS2; ++ case ENCODER_ID_INTERNAL_DAC1: ++ return ENCODER_OBJECT_ID_INTERNAL_DAC1; ++ case ENCODER_ID_INTERNAL_DAC2: ++ return ENCODER_OBJECT_ID_INTERNAL_DAC2; ++ case ENCODER_ID_INTERNAL_SDVOA: ++ return ENCODER_OBJECT_ID_INTERNAL_SDVOA; ++ case ENCODER_ID_INTERNAL_SDVOB: ++ return ENCODER_OBJECT_ID_INTERNAL_SDVOB; ++ case ENCODER_ID_EXTERNAL_SI170B: ++ return ENCODER_OBJECT_ID_SI170B; ++ case ENCODER_ID_EXTERNAL_CH7303: ++ return ENCODER_OBJECT_ID_CH7303; ++ case ENCODER_ID_EXTERNAL_CH7301: ++ return ENCODER_OBJECT_ID_CH7301; ++ case ENCODER_ID_INTERNAL_DVO1: ++ return ENCODER_OBJECT_ID_INTERNAL_DVO1; ++ case ENCODER_ID_EXTERNAL_SDVOA: ++ return ENCODER_OBJECT_ID_EXTERNAL_SDVOA; ++ case ENCODER_ID_EXTERNAL_SDVOB: ++ return ENCODER_OBJECT_ID_EXTERNAL_SDVOB; ++ case ENCODER_ID_EXTERNAL_TITFP513: ++ return ENCODER_OBJECT_ID_TITFP513; ++ case ENCODER_ID_INTERNAL_LVTM1: ++ return ENCODER_OBJECT_ID_INTERNAL_LVTM1; ++ case ENCODER_ID_EXTERNAL_VT1623: ++ return ENCODER_OBJECT_ID_VT1623; ++ case ENCODER_ID_EXTERNAL_SI1930: ++ return ENCODER_OBJECT_ID_HDMI_SI1930; ++ case ENCODER_ID_INTERNAL_HDMI: ++ return ENCODER_OBJECT_ID_HDMI_INTERNAL; ++ case ENCODER_ID_EXTERNAL_TRAVIS: ++ return ENCODER_OBJECT_ID_TRAVIS; ++ case ENCODER_ID_EXTERNAL_NUTMEG: ++ return ENCODER_OBJECT_ID_NUTMEG; ++ case ENCODER_ID_INTERNAL_KLDSCP_TMDS1: ++ return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1; ++ case ENCODER_ID_INTERNAL_KLDSCP_DVO1: ++ return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1; ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC1: ++ return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1; ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC2: ++ return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2; ++ case ENCODER_ID_EXTERNAL_SI178: ++ return ENCODER_OBJECT_ID_SI178; ++ case ENCODER_ID_EXTERNAL_MVPU_FPGA: ++ return ENCODER_OBJECT_ID_MVPU_FPGA; ++ case ENCODER_ID_INTERNAL_DDI: ++ return ENCODER_OBJECT_ID_INTERNAL_DDI; ++ case ENCODER_ID_EXTERNAL_VT1625: ++ return ENCODER_OBJECT_ID_VT1625; ++ case ENCODER_ID_EXTERNAL_SI1932: ++ return ENCODER_OBJECT_ID_HDMI_SI1932; ++ case ENCODER_ID_EXTERNAL_AN9801: ++ return ENCODER_OBJECT_ID_DP_AN9801; ++ case ENCODER_ID_EXTERNAL_DP501: ++ return ENCODER_OBJECT_ID_DP_DP501; ++ case ENCODER_ID_INTERNAL_UNIPHY: ++ return ENCODER_OBJECT_ID_INTERNAL_UNIPHY; ++ case ENCODER_ID_INTERNAL_KLDSCP_LVTMA: ++ return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA; ++ case ENCODER_ID_INTERNAL_UNIPHY1: ++ return ENCODER_OBJECT_ID_INTERNAL_UNIPHY1; ++ case ENCODER_ID_INTERNAL_UNIPHY2: ++ return ENCODER_OBJECT_ID_INTERNAL_UNIPHY2; ++ case ENCODER_ID_INTERNAL_UNIPHY3: ++ return ENCODER_OBJECT_ID_INTERNAL_UNIPHY3; ++ case ENCODER_ID_INTERNAL_WIRELESS: ++ return ENCODER_OBJECT_ID_INTERNAL_VCE; ++ case ENCODER_ID_EXTERNAL_GENERIC_DVO: ++ return ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO; ++ case ENCODER_ID_UNKNOWN: ++ return ENCODER_OBJECT_ID_NONE; ++ default: ++ /* Invalid encoder id */ ++ BREAK_TO_DEBUGGER(); ++ return ENCODER_OBJECT_ID_NONE; ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.h b/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.h +new file mode 100644 +index 0000000..e5c00de +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.h +@@ -0,0 +1,87 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_COMMAND_TABLE_HELPER_H__ ++#define __DAL_COMMAND_TABLE_HELPER_H__ ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/command_table_helper_dce110.h" ++#endif ++ ++struct command_table_helper { ++ bool (*controller_id_to_atom)(enum controller_id id, uint8_t *atom_id); ++ uint8_t (*encoder_action_to_atom)( ++ enum bp_encoder_control_action action); ++ uint32_t (*encoder_mode_bp_to_atom)(enum signal_type s, ++ bool enable_dp_audio); ++ bool (*engine_bp_to_atom)(enum engine_id engine_id, ++ uint32_t *atom_engine_id); ++ void (*assign_control_parameter)( ++ const struct command_table_helper *h, ++ struct bp_encoder_control *control, ++ DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param); ++ bool (*clock_source_id_to_atom)(enum clock_source_id id, ++ uint32_t *atom_pll_id); ++ bool (*clock_source_id_to_ref_clk_src)( ++ enum clock_source_id id, ++ uint32_t *ref_clk_src_id); ++ uint8_t (*transmitter_bp_to_atom)(enum transmitter t); ++ uint8_t (*encoder_id_to_atom)(enum encoder_id id); ++ uint8_t (*clock_source_id_to_atom_phy_clk_src_id)( ++ enum clock_source_id id); ++ uint8_t (*signal_type_to_atom_dig_mode)(enum signal_type s); ++ uint8_t (*hpd_sel_to_atom)(enum hpd_source_id id); ++ uint8_t (*dig_encoder_sel_to_atom)(enum engine_id engine_id); ++ uint8_t (*phy_id_to_atom)(enum transmitter t); ++ uint8_t (*disp_power_gating_action_to_atom)( ++ enum bp_pipe_control_action action); ++}; ++ ++bool dal_bios_parser_init_cmd_tbl_helper(const struct command_table_helper **h, ++ enum dce_version dce); ++ ++bool dal_cmd_table_helper_controller_id_to_atom( ++ enum controller_id id, ++ uint8_t *atom_id); ++ ++uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom( ++ enum signal_type s, ++ bool enable_dp_audio); ++ ++void dal_cmd_table_helper_assign_control_parameter( ++ const struct command_table_helper *h, ++ struct bp_encoder_control *control, ++DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param); ++ ++bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src( ++ enum clock_source_id id, ++ uint32_t *ref_clk_src_id); ++ ++uint8_t dal_cmd_table_helper_transmitter_bp_to_atom( ++ enum transmitter t); ++ ++uint8_t dal_cmd_table_helper_encoder_id_to_atom( ++ enum encoder_id id); ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.c b/drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.c +new file mode 100644 +index 0000000..2cc2d2d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.c +@@ -0,0 +1,484 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sub license, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "atom.h" ++ ++#include "include/bios_parser_types.h" ++#include "include/adapter_service_types.h" ++#include "include/logger_interface.h" ++ ++#include "../bios_parser_helper.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "bif/bif_5_1_d.h" ++ ++/** ++ * set_scratch_acc_mode_change ++ * ++ * @brief ++ * set Accelerated Mode in VBIOS scratch register, VBIOS will clean it when ++ * VGA/non-Accelerated mode is set ++ * ++ * @param ++ * struct dc_context *ctx - [in] DAL context ++ */ ++static void set_scratch_acc_mode_change( ++ struct dc_context *ctx) ++{ ++ uint32_t addr = mmBIOS_SCRATCH_6; ++ uint32_t value = 0; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ value |= ATOM_S6_ACC_MODE; ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++/* ++ * set_scratch_active_and_requested ++ * ++ * @brief ++ * Set VBIOS scratch pad registers about active and requested displays ++ * ++ * @param ++ * struct dc_context *ctx - [in] DAL context for register accessing ++ * struct vbios_helper_data *d - [in] values to write ++ */ ++static void set_scratch_active_and_requested( ++ struct dc_context *ctx, ++ struct vbios_helper_data *d) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ /* mmBIOS_SCRATCH_3 = mmBIOS_SCRATCH_0 + ATOM_ACTIVE_INFO_DEF */ ++ addr = mmBIOS_SCRATCH_3; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ value &= ~ATOM_S3_DEVICE_ACTIVE_MASK; ++ value |= (d->active & ATOM_S3_DEVICE_ACTIVE_MASK); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ /* mmBIOS_SCRATCH_6 = mmBIOS_SCRATCH_0 + ATOM_ACC_CHANGE_INFO_DEF */ ++ addr = mmBIOS_SCRATCH_6; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ value &= ~ATOM_S6_ACC_REQ_MASK; ++ value |= (d->requested & ATOM_S6_ACC_REQ_MASK); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ /* mmBIOS_SCRATCH_5 = mmBIOS_SCRATCH_0 + ATOM_DOS_REQ_INFO_DEF */ ++ addr = mmBIOS_SCRATCH_5; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ value &= ~ATOM_S5_DOS_REQ_DEVICEw0; ++ value |= (d->active & ATOM_S5_DOS_REQ_DEVICEw0); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ d->active = 0; ++ d->requested = 0; ++} ++ ++/** ++ * get LCD Scale Mode from VBIOS scratch register ++ */ ++static enum lcd_scale get_scratch_lcd_scale( ++ struct dc_context *ctx) ++{ ++ uint32_t addr = mmBIOS_SCRATCH_6; ++ uint32_t value = 0; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ if (value & ATOM_S6_REQ_LCD_EXPANSION_FULL) ++ return LCD_SCALE_FULLPANEL; ++ else if (value & ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO) ++ return LCD_SCALE_ASPECTRATIO; ++ else ++ return LCD_SCALE_NONE; ++} ++ ++/** ++ * prepare_scratch_active_and_requested ++ * ++ * @brief ++ * prepare and update VBIOS scratch pad registers about active and requested ++ * displays ++ * ++ * @param ++ * data - helper's shared data ++ * enum controller_ild - controller Id ++ * enum signal_type - signal type used on display ++ * const struct connector_device_tag_info* - pointer to display type and enum id ++ */ ++static void prepare_scratch_active_and_requested( ++ struct dc_context *ctx, ++ struct vbios_helper_data *data, ++ enum controller_id id, ++ enum signal_type s, ++ const struct connector_device_tag_info *dev_tag) ++{ ++ switch (s) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ if (dev_tag->dev_id.device_type == DEVICE_TYPE_DFP) ++ switch (dev_tag->dev_id.enum_id) { ++ case 1: ++ data->requested |= ATOM_S6_ACC_REQ_DFP1; ++ data->active |= ATOM_S3_DFP1_ACTIVE; ++ break; ++ case 2: ++ data->requested |= ATOM_S6_ACC_REQ_DFP2; ++ data->active |= ATOM_S3_DFP2_ACTIVE; ++ break; ++ case 3: ++ data->requested |= ATOM_S6_ACC_REQ_DFP3; ++ data->active |= ATOM_S3_DFP3_ACTIVE; ++ break; ++ case 4: ++ data->requested |= ATOM_S6_ACC_REQ_DFP4; ++ data->active |= ATOM_S3_DFP4_ACTIVE; ++ break; ++ case 5: ++ data->requested |= ATOM_S6_ACC_REQ_DFP5; ++ data->active |= ATOM_S3_DFP5_ACTIVE; ++ break; ++ case 6: ++ data->requested |= ATOM_S6_ACC_REQ_DFP6; ++ data->active |= ATOM_S3_DFP6_ACTIVE; ++ break; ++ default: ++ break; ++ } ++ break; ++ case SIGNAL_TYPE_LVDS: ++ case SIGNAL_TYPE_EDP: ++ data->requested |= ATOM_S6_ACC_REQ_LCD1; ++ data->active |= ATOM_S3_LCD1_ACTIVE; ++ break; ++ case SIGNAL_TYPE_RGB: ++ if (dev_tag->dev_id.device_type == DEVICE_TYPE_CRT) ++ switch (dev_tag->dev_id.enum_id) { ++ case 1: ++ data->requested |= ATOM_S6_ACC_REQ_CRT1; ++ data->active |= ATOM_S3_CRT1_ACTIVE; ++ break; ++ case 2: ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_COMPONENT_BIOS, ++ "%s: DAL does not support DAC2!\n", ++ __func__); ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_BIOS, ++ LOG_MINOR_COMPONENT_BIOS, ++ "%s: No such signal!\n", ++ __func__); ++ break; ++ } ++} ++ ++/* ++ * is_accelerated_mode ++ * ++ * @brief ++ * set Accelerated Mode in VBIOS scratch register, VBIOS will clean it when ++ * VGA/non-Accelerated mode is set ++ * ++ * @param ++ * struct dc_context *ctx ++ * ++ * @return ++ * true if in acceleration mode, false otherwise. ++ */ ++static bool is_accelerated_mode( ++ struct dc_context *ctx) ++{ ++ uint32_t addr = mmBIOS_SCRATCH_6; ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ return (value & ATOM_S6_ACC_MODE) ? true : false; ++} ++ ++#define BIOS_SCRATCH0_DAC_B_SHIFT 8 ++ ++/** ++ * detect_sink ++ * ++ * @brief ++ * read VBIOS scratch register to determine whether display for the specified ++ * signal is present and return the actual sink signal type ++ * For analog signals VBIOS load detection has to be called prior reading the ++ * register ++ * ++ * @param ++ * encoder - encoder id (to specify DAC) ++ * connector - connector id (to check CV on DIN) ++ * signal - signal (as display type) to check ++ * ++ * @return ++ * signal_type - actual (on the sink) signal type detected ++ */ ++static enum signal_type detect_sink( ++ struct dc_context *ctx, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type signal) ++{ ++ uint32_t bios_scratch0; ++ uint32_t encoder_id = encoder.id; ++ /* after DCE 10.x does not support DAC2, so assert and return ++ * SIGNAL_TYPE_NONE */ ++ if (encoder_id == ENCODER_ID_INTERNAL_DAC2 ++ || encoder_id == ENCODER_ID_INTERNAL_KLDSCP_DAC2) { ++ ASSERT(false); ++ return SIGNAL_TYPE_NONE; ++ } ++ ++ bios_scratch0 = dal_read_reg(ctx, ++ mmBIOS_SCRATCH_0 + ATOM_DEVICE_CONNECT_INFO_DEF); ++ ++ /* In further processing we use DACB masks. If we want detect load on ++ * DACA, we need to shift the register so DACA bits will be in place of ++ * DACB bits ++ */ ++ if (encoder_id == ENCODER_ID_INTERNAL_DAC1 ++ || encoder_id == ENCODER_ID_INTERNAL_KLDSCP_DAC1 ++ || encoder_id == ENCODER_ID_EXTERNAL_NUTMEG ++ || encoder_id == ENCODER_ID_EXTERNAL_TRAVIS) { ++ bios_scratch0 <<= BIOS_SCRATCH0_DAC_B_SHIFT; ++ } ++ ++ switch (signal) { ++ case SIGNAL_TYPE_RGB: { ++ if (bios_scratch0 & ATOM_S0_CRT2_MASK) ++ return SIGNAL_TYPE_RGB; ++ break; ++ } ++ case SIGNAL_TYPE_LVDS: { ++ if (bios_scratch0 & ATOM_S0_LCD1) ++ return SIGNAL_TYPE_LVDS; ++ break; ++ } ++ case SIGNAL_TYPE_EDP: { ++ if (bios_scratch0 & ATOM_S0_LCD1) ++ return SIGNAL_TYPE_EDP; ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return SIGNAL_TYPE_NONE; ++} ++ ++/** ++ * set_scratch_connected ++ * ++ * @brief ++ * update BIOS_SCRATCH_0 register about connected displays ++ * ++ * @param ++ * bool - update scratch register or just prepare info to be updated ++ * bool - connection state ++ * const struct connector_device_tag_info * - pointer to device type and enum ID ++ */ ++static void set_scratch_connected( ++ struct dc_context *ctx, ++ struct graphics_object_id id, ++ bool connected, ++ const struct connector_device_tag_info *device_tag) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ uint32_t update = 0; ++ ++ switch (device_tag->dev_id.device_type) { ++ case DEVICE_TYPE_LCD: ++ /* For LCD VBIOS will update LCD Panel connected bit always and ++ * Lid state bit based on SBIOS info do not do anything here ++ * for LCD ++ */ ++ break; ++ case DEVICE_TYPE_CRT: ++ /* ++ * CRT is not supported in DCE11 ++ */ ++ break; ++ case DEVICE_TYPE_DFP: ++ switch (device_tag->dev_id.enum_id) { ++ case 1: ++ update |= ATOM_S0_DFP1; ++ break; ++ case 2: ++ update |= ATOM_S0_DFP2; ++ break; ++ case 3: ++ update |= ATOM_S0_DFP3; ++ break; ++ case 4: ++ update |= ATOM_S0_DFP4; ++ break; ++ case 5: ++ update |= ATOM_S0_DFP5; ++ break; ++ case 6: ++ update |= ATOM_S0_DFP6; ++ break; ++ default: ++ break; ++ } ++ break; ++ case DEVICE_TYPE_CV: ++ /* DCE 8.0 does not support CV, ++ * so don't do anything */ ++ break; ++ ++ case DEVICE_TYPE_TV: ++ /* For TV VBIOS will update S-Video or ++ * Composite scratch bits on DAL_LoadDetect ++ * when called by driver, do not do anything ++ * here for TV ++ */ ++ break; ++ ++ default: ++ break; ++ ++ } ++ ++ /* update scratch register */ ++ addr = mmBIOS_SCRATCH_0 + ATOM_DEVICE_CONNECT_INFO_DEF; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ if (connected) ++ value |= update; ++ else ++ value &= ~update; ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void set_scratch_critical_state( ++ struct dc_context *ctx, ++ bool state) ++{ ++ uint32_t addr = mmBIOS_SCRATCH_6; ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ if (state) ++ value |= ATOM_S6_CRITICAL_STATE; ++ else ++ value &= ~ATOM_S6_CRITICAL_STATE; ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void set_scratch_lcd_scale( ++ struct dc_context *ctx, ++ enum lcd_scale lcd_scale_request) ++{ ++ DAL_LOGGER_NOT_IMPL( ++ LOG_MINOR_COMPONENT_BIOS, ++ "Bios Parser:%s\n", ++ __func__); ++} ++ ++static bool is_lid_open(struct dc_context *ctx) ++{ ++ uint32_t bios_scratch6; ++ ++ bios_scratch6 = ++ dal_read_reg( ++ ctx, ++ mmBIOS_SCRATCH_0 + ATOM_ACC_CHANGE_INFO_DEF); ++ ++ /* lid is open if the bit is not set */ ++ if (!(bios_scratch6 & ATOM_S6_LID_STATE)) ++ return true; ++ ++ return false; ++} ++ ++/* function table */ ++static const struct bios_parser_helper bios_parser_helper_funcs = { ++ .detect_sink = detect_sink, ++ .fmt_bit_depth_control = NULL, ++ .fmt_control = NULL, ++ .get_bios_event_info = NULL, ++ .get_embedded_display_controller_id = NULL, ++ .get_embedded_display_refresh_rate = NULL, ++ .get_requested_backlight_level = NULL, ++ .get_scratch_lcd_scale = get_scratch_lcd_scale, ++ .is_accelerated_mode = is_accelerated_mode, ++ .is_active_display = NULL, ++ .is_display_config_changed = NULL, ++ .is_lid_open = is_lid_open, ++ .is_lid_status_changed = NULL, ++ .prepare_scratch_active_and_requested = ++ prepare_scratch_active_and_requested, ++ .set_scratch_acc_mode_change = set_scratch_acc_mode_change, ++ .set_scratch_active_and_requested = set_scratch_active_and_requested, ++ .set_scratch_connected = set_scratch_connected, ++ .set_scratch_critical_state = set_scratch_critical_state, ++ .set_scratch_lcd_scale = set_scratch_lcd_scale, ++ .take_backlight_control = NULL, ++ .update_requested_backlight_level = NULL, ++}; ++ ++/* ++ * dal_bios_parser_dce110_init_bios_helper ++ * ++ * @brief ++ * Initialize BIOS helper functions ++ * ++ * @param ++ * const struct command_table_helper **h - [out] struct of functions ++ * ++ */ ++ ++const struct bios_parser_helper *dal_bios_parser_helper_dce110_get_table() ++{ ++ return &bios_parser_helper_funcs; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.h b/drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.h +new file mode 100644 +index 0000000..915f31a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce110/bios_parser_helper_dce110.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sub license, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_BIOS_PARSER_HELPER_DCE110_H__ ++#define __DAL_BIOS_PARSER_HELPER_DCE110_H__ ++ ++struct bios_parser_helper; ++ ++/* Initialize BIOS helper functions */ ++const struct bios_parser_helper *dal_bios_parser_helper_dce110_get_table(void); ++ ++#endif /* __DAL_BIOS_PARSER_HELPER_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.c b/drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.c +new file mode 100644 +index 0000000..e75b51b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.c +@@ -0,0 +1,369 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "atom.h" ++ ++#include "include/bios_parser_types.h" ++#include "include/adapter_service_types.h" ++ ++#include "../command_table_helper.h" ++ ++static uint8_t phy_id_to_atom(enum transmitter t) ++{ ++ uint8_t atom_phy_id; ++ ++ switch (t) { ++ case TRANSMITTER_UNIPHY_A: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYA; ++ break; ++ case TRANSMITTER_UNIPHY_B: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYB; ++ break; ++ case TRANSMITTER_UNIPHY_C: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYC; ++ break; ++ case TRANSMITTER_UNIPHY_D: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYD; ++ break; ++ case TRANSMITTER_UNIPHY_E: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYE; ++ break; ++ case TRANSMITTER_UNIPHY_F: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYF; ++ break; ++ case TRANSMITTER_UNIPHY_G: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYG; ++ break; ++ default: ++ atom_phy_id = ATOM_PHY_ID_UNIPHYA; ++ break; ++ } ++ return atom_phy_id; ++} ++ ++ ++static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) ++{ ++ uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; ++ ++ switch (s) { ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_EDP: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; ++ break; ++ case SIGNAL_TYPE_LVDS: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; ++ break; ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; ++ break; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; ++ break; ++ default: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; ++ break; ++ } ++ ++ return atom_dig_mode; ++} ++ ++static uint8_t clock_source_id_to_atom_phy_clk_src_id( ++ enum clock_source_id id) ++{ ++ uint8_t atom_phy_clk_src_id = 0; ++ ++ switch (id) { ++ case CLOCK_SOURCE_ID_PLL0: ++ atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; ++ break; ++ case CLOCK_SOURCE_ID_PLL1: ++ atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; ++ break; ++ case CLOCK_SOURCE_ID_PLL2: ++ atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; ++ break; ++ case CLOCK_SOURCE_ID_EXTERNAL: ++ atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; ++ break; ++ default: ++ atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; ++ break; ++ } ++ ++ return atom_phy_clk_src_id >> 2; ++} ++ ++static uint8_t hpd_sel_to_atom(enum hpd_source_id id) ++{ ++ uint8_t atom_hpd_sel = 0; ++ ++ switch (id) { ++ case HPD_SOURCEID1: ++ atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; ++ break; ++ case HPD_SOURCEID2: ++ atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; ++ break; ++ case HPD_SOURCEID3: ++ atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; ++ break; ++ case HPD_SOURCEID4: ++ atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; ++ break; ++ case HPD_SOURCEID5: ++ atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; ++ break; ++ case HPD_SOURCEID6: ++ atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; ++ break; ++ case HPD_SOURCEID_UNKNOWN: ++ default: ++ atom_hpd_sel = 0; ++ break; ++ } ++ return atom_hpd_sel >> 4; ++} ++ ++static uint8_t dig_encoder_sel_to_atom(enum engine_id id) ++{ ++ uint8_t atom_dig_encoder_sel = 0; ++ ++ switch (id) { ++ case ENGINE_ID_DIGA: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; ++ break; ++ case ENGINE_ID_DIGB: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGB_SEL; ++ break; ++ case ENGINE_ID_DIGC: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGC_SEL; ++ break; ++ case ENGINE_ID_DIGD: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGD_SEL; ++ break; ++ case ENGINE_ID_DIGE: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGE_SEL; ++ break; ++ case ENGINE_ID_DIGF: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGF_SEL; ++ break; ++ case ENGINE_ID_DIGG: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGG_SEL; ++ break; ++ case ENGINE_ID_UNKNOWN: ++ /* No DIG_FRONT is associated to DIG_BACKEND */ ++ atom_dig_encoder_sel = 0; ++ break; ++ default: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; ++ break; ++ } ++ ++ return atom_dig_encoder_sel; ++} ++ ++static bool clock_source_id_to_atom( ++ enum clock_source_id id, ++ uint32_t *atom_pll_id) ++{ ++ bool result = true; ++ ++ if (atom_pll_id != NULL) ++ switch (id) { ++ case CLOCK_SOURCE_ID_PLL0: ++ *atom_pll_id = ATOM_PPLL0; ++ break; ++ case CLOCK_SOURCE_ID_PLL1: ++ *atom_pll_id = ATOM_PPLL1; ++ break; ++ case CLOCK_SOURCE_ID_PLL2: ++ *atom_pll_id = ATOM_PPLL2; ++ break; ++ case CLOCK_SOURCE_ID_EXTERNAL: ++ *atom_pll_id = ATOM_PPLL_INVALID; ++ break; ++ case CLOCK_SOURCE_ID_DFS: ++ *atom_pll_id = ATOM_EXT_PLL1; ++ break; ++ case CLOCK_SOURCE_ID_VCE: ++ /* for VCE encoding, ++ * we need to pass in ATOM_PPLL_INVALID ++ */ ++ *atom_pll_id = ATOM_PPLL_INVALID; ++ break; ++ case CLOCK_SOURCE_ID_DP_DTO: ++ /* When programming DP DTO PLL ID should be invalid */ ++ *atom_pll_id = ATOM_PPLL_INVALID; ++ break; ++ case CLOCK_SOURCE_ID_UNDEFINED: ++ /* Should not happen */ ++ *atom_pll_id = ATOM_PPLL_INVALID; ++ result = false; ++ break; ++ default: ++ result = false; ++ break; ++ } ++ ++ return result; ++} ++ ++static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) ++{ ++ bool result = false; ++ ++ if (atom_engine_id != NULL) ++ switch (id) { ++ case ENGINE_ID_DIGA: ++ *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DIGB: ++ *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DIGC: ++ *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DIGD: ++ *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DIGE: ++ *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DIGF: ++ *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DIGG: ++ *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DVO: ++ *atom_engine_id = ASIC_EXT_DIG_ENCODER_ID; ++ result = true; ++ break; ++ case ENGINE_ID_DACA: ++ *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; ++ result = true; ++ break; ++ default: ++ break; ++ } ++ ++ return result; ++} ++ ++static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) ++{ ++ uint8_t atom_action = 0; ++ ++ switch (action) { ++ case ENCODER_CONTROL_ENABLE: ++ atom_action = ATOM_ENABLE; ++ break; ++ case ENCODER_CONTROL_DISABLE: ++ atom_action = ATOM_DISABLE; ++ break; ++ case ENCODER_CONTROL_SETUP: ++ atom_action = ATOM_ENCODER_CMD_SETUP; ++ break; ++ case ENCODER_CONTROL_INIT: ++ atom_action = ATOM_ENCODER_INIT; ++ break; ++ default: ++ BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ ++ break; ++ } ++ ++ return atom_action; ++} ++ ++static uint8_t disp_power_gating_action_to_atom( ++ enum bp_pipe_control_action action) ++{ ++ uint8_t atom_pipe_action = 0; ++ ++ switch (action) { ++ case ASIC_PIPE_DISABLE: ++ atom_pipe_action = ATOM_DISABLE; ++ break; ++ case ASIC_PIPE_ENABLE: ++ atom_pipe_action = ATOM_ENABLE; ++ break; ++ case ASIC_PIPE_INIT: ++ atom_pipe_action = ATOM_INIT; ++ break; ++ default: ++ ASSERT_CRITICAL(false); /* Unhandle action in driver! */ ++ break; ++ } ++ ++ return atom_pipe_action; ++} ++ ++/* function table */ ++static const struct command_table_helper command_table_helper_funcs = { ++ .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, ++ .encoder_action_to_atom = encoder_action_to_atom, ++ .engine_bp_to_atom = engine_bp_to_atom, ++ .clock_source_id_to_atom = clock_source_id_to_atom, ++ .clock_source_id_to_atom_phy_clk_src_id = ++ clock_source_id_to_atom_phy_clk_src_id, ++ .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, ++ .hpd_sel_to_atom = hpd_sel_to_atom, ++ .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, ++ .phy_id_to_atom = phy_id_to_atom, ++ .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, ++ .assign_control_parameter = NULL, ++ .clock_source_id_to_ref_clk_src = NULL, ++ .transmitter_bp_to_atom = NULL, ++ .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, ++ .encoder_mode_bp_to_atom = dal_cmd_table_helper_encoder_mode_bp_to_atom, ++}; ++ ++/* ++ * dal_cmd_tbl_helper_dce110_get_table ++ * ++ * @brief ++ * Initialize command table helper functions ++ * ++ * @param ++ * const struct command_table_helper **h - [out] struct of functions ++ * ++ */ ++const struct command_table_helper *dal_cmd_tbl_helper_dce110_get_table() ++{ ++ return &command_table_helper_funcs; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.h b/drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.h +new file mode 100644 +index 0000000..eb60c2e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce110/command_table_helper_dce110.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_COMMAND_TABLE_HELPER_DCE110_H__ ++#define __DAL_COMMAND_TABLE_HELPER_DCE110_H__ ++ ++struct command_table_helper; ++ ++/* Initialize command table helper functions */ ++const struct command_table_helper *dal_cmd_tbl_helper_dce110_get_table(void); ++ ++#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/calcs/Makefile b/drivers/gpu/drm/amd/dal/dc/calcs/Makefile +new file mode 100644 +index 0000000..7f1916b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/calcs/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the 'calcs' sub-component of DAL. ++# It calculates Bandwidth and Watermarks values for HW programming ++# ++ ++BW_CALCS = bandwidth_calcs.o bw_fixed.o scaler_filter.o ++ ++AMD_DAL_BW_CALCS = $(addprefix $(AMDDALPATH)/dc/calcs/,$(BW_CALCS)) ++ ++AMD_DAL_FILES += $(AMD_DAL_BW_CALCS) +diff --git a/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c b/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c +new file mode 100644 +index 0000000..68618bb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c +@@ -0,0 +1,3478 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dc_services.h" ++ ++#include "bandwidth_calcs.h" ++ ++/******************************************************************************* ++ * Private Functions ++ ******************************************************************************/ ++ ++enum bw_defines { ++ def_ok, ++ def_na, ++ def_notok, ++ def_display_write_back420_chroma, ++ def_display_write_back420_luma, ++ def_graphics, ++ def_xl_pattern_solid, ++ def_xl_pattern_light_horizontal, ++ def_xl_pattern_checker, ++ def_notok_color, ++ def_na_color, ++ def_vb_black, ++ def_vb_white, ++ def_high_no_nbp_state_change_color, ++ def_high_no_nbp_state_change, ++ def_high_color, ++ def_mid_color, ++ def_low_color, ++ def_high, ++ def_mid, ++ def_low, ++ def_exceeded_allowed_maximum_sclk, ++ def_exceeded_allowed_maximum_bw, ++ def_exceeded_allowed_page_close_open, ++ def_exceeded_allowed_outstanding_pte_req_queue_size, ++ def_linear, ++ def_underlay444, ++ def_underlay422, ++ def_underlay420_chroma, ++ def_underlay420_luma, ++ def_any_lines, ++ def_auto, ++ def_manual, ++ def_portrait, ++ def_invalid_linear_or_stereo_mode, ++ def_invalid_rotation_or_bpp_or_stereo, ++ def_vsr_more_than_vtaps, ++ def_vsr_more_than_4, ++ def_ceil_htaps_div_4_more_or_eq_hsr, ++ def_hsr_more_than_htaps, ++ def_hsr_more_than_4, ++ def_none, ++ def_blended, ++ def_landscape ++}; ++ ++static void calculate_bandwidth(const struct bw_calcs_input_dceip *dceip, ++ const struct bw_calcs_input_vbios *vbios, ++ const struct bw_calcs_input_mode_data_internal *mode_data, ++ struct bw_results_internal *results) ++{ ++ const struct bw_fixed pixels_per_chunk = int_to_fixed(512); ++ const struct bw_fixed max_chunks_non_fbc_mode = int_to_fixed(16); ++ const uint32_t high = 2; ++ const uint32_t mid = 1; ++ const uint32_t low = 0; ++ ++ uint32_t i, j, k; ++ struct bw_fixed yclk[3]; ++ struct bw_fixed sclk[3]; ++ bool d0_underlay_enable; ++ bool d1_underlay_enable; ++ enum bw_defines v_filter_init_mode[maximum_number_of_surfaces]; ++ enum bw_defines tiling_mode[maximum_number_of_surfaces]; ++ enum bw_stereo_mode stereo_mode[maximum_number_of_surfaces]; ++ enum bw_defines surface_type[maximum_number_of_surfaces]; ++ enum bw_defines voltage; ++ enum bw_defines mode_background_color; ++ enum bw_defines mode_font_color; ++ enum bw_defines mode_pattern; ++ enum bw_defines sclk_message; ++ enum bw_defines yclk_message; ++ enum bw_defines pipe_check; ++ enum bw_defines hsr_check; ++ enum bw_defines vsr_check; ++ enum bw_defines lb_size_check; ++ enum bw_defines fbc_check; ++ enum bw_defines rotation_check; ++ enum bw_defines mode_check; ++ uint32_t y_clk_level; ++ uint32_t sclk_level; ++ yclk[high] = vbios->high_yclk; ++ yclk[mid] = vbios->high_yclk; ++ yclk[low] = vbios->low_yclk; ++ sclk[high] = vbios->high_sclk; ++ sclk[mid] = vbios->mid_sclk; ++ sclk[low] = vbios->low_sclk; ++ if (mode_data->d0_underlay_mode == ul_none) { ++ d0_underlay_enable = false; ++ } else { ++ d0_underlay_enable = true; ++ } ++ if (mode_data->d1_underlay_mode == ul_none) { ++ d1_underlay_enable = false; ++ } else { ++ d1_underlay_enable = true; ++ } ++ results->number_of_underlay_surfaces = int_to_fixed( ++ d0_underlay_enable + d1_underlay_enable); ++ if (mode_data->underlay_surface_type == yuv_420) { ++ surface_type[0] = def_underlay420_luma; ++ surface_type[2] = def_underlay420_luma; ++ results->bytes_per_pixel[0] = int_to_fixed(1); ++ results->bytes_per_pixel[2] = int_to_fixed(1); ++ surface_type[1] = def_underlay420_chroma; ++ surface_type[3] = def_underlay420_chroma; ++ results->bytes_per_pixel[1] = int_to_fixed(2); ++ results->bytes_per_pixel[3] = int_to_fixed(2); ++ results->lb_size_per_component[0] = ++ dceip->underlay420_luma_lb_size_per_component; ++ results->lb_size_per_component[1] = ++ dceip->underlay420_chroma_lb_size_per_component; ++ results->lb_size_per_component[2] = ++ dceip->underlay420_luma_lb_size_per_component; ++ results->lb_size_per_component[3] = ++ dceip->underlay420_chroma_lb_size_per_component; ++ } else if (mode_data->underlay_surface_type == yuv_422) { ++ surface_type[0] = def_underlay422; ++ surface_type[2] = def_underlay422; ++ results->bytes_per_pixel[0] = int_to_fixed(2); ++ results->bytes_per_pixel[2] = int_to_fixed(2); ++ results->lb_size_per_component[0] = ++ dceip->underlay422_lb_size_per_component; ++ results->lb_size_per_component[2] = ++ dceip->underlay422_lb_size_per_component; ++ } else { ++ surface_type[0] = def_underlay444; ++ surface_type[2] = def_underlay444; ++ results->bytes_per_pixel[0] = int_to_fixed(4); ++ results->bytes_per_pixel[2] = int_to_fixed(4); ++ results->lb_size_per_component[0] = ++ dceip->lb_size_per_component444; ++ results->lb_size_per_component[2] = ++ dceip->lb_size_per_component444; ++ } ++ if (d0_underlay_enable) { ++ if (mode_data->underlay_surface_type == yuv_420) { ++ results->enable[0] = true; ++ results->enable[1] = true; ++ } else { ++ results->enable[0] = true; ++ results->enable[1] = false; ++ } ++ } else { ++ results->enable[0] = false; ++ results->enable[1] = false; ++ } ++ if (d1_underlay_enable) { ++ if (mode_data->underlay_surface_type == yuv_420) { ++ results->enable[2] = true; ++ results->enable[3] = true; ++ } else { ++ results->enable[2] = true; ++ results->enable[3] = false; ++ } ++ } else { ++ results->enable[2] = false; ++ results->enable[3] = false; ++ } ++ ++ results->use_alpha[0] = false; ++ results->use_alpha[1] = false; ++ results->use_alpha[2] = false; ++ results->use_alpha[3] = false; ++ results->scatter_gather_enable_for_pipe[0] = ++ vbios->scatter_gather_enable; ++ results->scatter_gather_enable_for_pipe[1] = ++ vbios->scatter_gather_enable; ++ results->scatter_gather_enable_for_pipe[2] = ++ vbios->scatter_gather_enable; ++ results->scatter_gather_enable_for_pipe[3] = ++ vbios->scatter_gather_enable; ++ results->interlace_mode[0] = mode_data->graphics_interlace_mode; ++ results->interlace_mode[1] = mode_data->graphics_interlace_mode; ++ results->interlace_mode[2] = mode_data->graphics_interlace_mode; ++ results->interlace_mode[3] = mode_data->graphics_interlace_mode; ++ results->h_total[0] = mode_data->d0_htotal; ++ results->h_total[1] = mode_data->d0_htotal; ++ results->h_total[2] = mode_data->d1_htotal; ++ results->h_total[3] = mode_data->d1_htotal; ++ results->pixel_rate[0] = mode_data->d0_pixel_rate; ++ results->pixel_rate[1] = mode_data->d0_pixel_rate; ++ results->pixel_rate[2] = mode_data->d1_pixel_rate; ++ results->pixel_rate[3] = mode_data->d1_pixel_rate; ++ results->src_width[0] = mode_data->underlay_src_width; ++ results->src_width[1] = mode_data->underlay_src_width; ++ results->src_width[2] = mode_data->underlay_src_width; ++ results->src_width[3] = mode_data->underlay_src_width; ++ results->src_height[0] = mode_data->underlay_src_height; ++ results->src_height[1] = mode_data->underlay_src_height; ++ results->src_height[2] = mode_data->underlay_src_height; ++ results->src_height[3] = mode_data->underlay_src_height; ++ results->pitch_in_pixels[0] = mode_data->underlay_pitch_in_pixels; ++ results->pitch_in_pixels[1] = mode_data->underlay_pitch_in_pixels; ++ results->pitch_in_pixels[2] = mode_data->underlay_pitch_in_pixels; ++ results->pitch_in_pixels[3] = mode_data->underlay_pitch_in_pixels; ++ results->scale_ratio[0] = mode_data->d0_underlay_scale_ratio; ++ results->scale_ratio[1] = mode_data->d0_underlay_scale_ratio; ++ results->scale_ratio[2] = mode_data->d1_underlay_scale_ratio; ++ results->scale_ratio[3] = mode_data->d1_underlay_scale_ratio; ++ results->h_taps[0] = mode_data->underlay_htaps; ++ results->h_taps[1] = mode_data->underlay_htaps; ++ results->h_taps[2] = mode_data->underlay_htaps; ++ results->h_taps[3] = mode_data->underlay_htaps; ++ results->v_taps[0] = mode_data->underlay_vtaps; ++ results->v_taps[1] = mode_data->underlay_vtaps; ++ results->v_taps[2] = mode_data->underlay_vtaps; ++ results->v_taps[3] = mode_data->underlay_vtaps; ++ results->rotation_angle[0] = mode_data->underlay_rotation_angle; ++ results->rotation_angle[1] = mode_data->underlay_rotation_angle; ++ results->rotation_angle[2] = mode_data->underlay_rotation_angle; ++ results->rotation_angle[3] = mode_data->underlay_rotation_angle; ++ if (mode_data->underlay_tiling_mode == linear) { ++ tiling_mode[0] = def_linear; ++ tiling_mode[1] = def_linear; ++ tiling_mode[2] = def_linear; ++ tiling_mode[3] = def_linear; ++ } else { ++ tiling_mode[0] = def_landscape; ++ tiling_mode[1] = def_landscape; ++ tiling_mode[2] = def_landscape; ++ tiling_mode[3] = def_landscape; ++ } ++ stereo_mode[0] = mode_data->underlay_stereo_mode; ++ stereo_mode[1] = mode_data->underlay_stereo_mode; ++ stereo_mode[2] = mode_data->underlay_stereo_mode; ++ stereo_mode[3] = mode_data->underlay_stereo_mode; ++ results->lb_bpc[0] = mode_data->underlay_lb_bpc; ++ results->lb_bpc[1] = mode_data->underlay_lb_bpc; ++ results->lb_bpc[2] = mode_data->underlay_lb_bpc; ++ results->lb_bpc[3] = mode_data->underlay_lb_bpc; ++ results->compression_rate[0] = int_to_fixed(1); ++ results->compression_rate[1] = int_to_fixed(1); ++ results->compression_rate[2] = int_to_fixed(1); ++ results->compression_rate[3] = int_to_fixed(1); ++ results->access_one_channel_only[0] = false; ++ results->access_one_channel_only[1] = false; ++ results->access_one_channel_only[2] = false; ++ results->access_one_channel_only[3] = false; ++ results->cursor_width_pixels[0] = int_to_fixed(0); ++ results->cursor_width_pixels[1] = int_to_fixed(0); ++ results->cursor_width_pixels[2] = int_to_fixed(0); ++ results->cursor_width_pixels[3] = int_to_fixed(0); ++ for (i = 4; i <= maximum_number_of_surfaces - 3; i += 1) { ++ if (i < mode_data->number_of_displays + 4) { ++ if (i == 4 && mode_data->d0_underlay_mode == ul_only) { ++ results->enable[i] = false; ++ results->use_alpha[i] = false; ++ } else if (i == 4 ++ && mode_data->d0_underlay_mode == ul_blend) { ++ results->enable[i] = true; ++ results->use_alpha[i] = true; ++ } else if (i == 4) { ++ results->enable[i] = true; ++ results->use_alpha[i] = false; ++ } else if (i == 5 ++ && mode_data->d1_underlay_mode == ul_only) { ++ results->enable[i] = false; ++ results->use_alpha[i] = false; ++ } else if (i == 5 ++ && mode_data->d1_underlay_mode == ul_blend) { ++ results->enable[i] = true; ++ results->use_alpha[i] = true; ++ } else { ++ results->enable[i] = true; ++ results->use_alpha[i] = false; ++ } ++ } else { ++ results->enable[i] = false; ++ results->use_alpha[i] = false; ++ } ++ results->scatter_gather_enable_for_pipe[i] = ++ vbios->scatter_gather_enable; ++ surface_type[i] = def_graphics; ++ results->lb_size_per_component[i] = ++ dceip->lb_size_per_component444; ++ results->bytes_per_pixel[i] = ++ mode_data->graphics_bytes_per_pixel; ++ results->interlace_mode[i] = mode_data->graphics_interlace_mode; ++ results->h_taps[i] = mode_data->graphics_htaps; ++ results->v_taps[i] = mode_data->graphics_vtaps; ++ results->rotation_angle[i] = mode_data->graphics_rotation_angle; ++ if (mode_data->graphics_tiling_mode == linear) { ++ tiling_mode[i] = def_linear; ++ } else if (equ(mode_data->graphics_rotation_angle, ++ int_to_fixed(0)) ++ || equ(mode_data->graphics_rotation_angle, ++ int_to_fixed(180))) { ++ tiling_mode[i] = def_landscape; ++ } else { ++ tiling_mode[i] = def_portrait; ++ } ++ results->lb_bpc[i] = mode_data->graphics_lb_bpc; ++ if (i == 4) { ++ /* todo: check original d0_underlay_mode comparison, possible bug there*/ ++ if (mode_data->d0_fbc_enable ++ && (dceip->argb_compression_support ++ || mode_data->d0_underlay_mode ++ != ul_blend)) { ++ results->compression_rate[i] = ++ vbios->average_compression_rate; ++ results->access_one_channel_only[i] = ++ mode_data->d0_lpt_enable; ++ } else { ++ results->compression_rate[i] = int_to_fixed(1); ++ results->access_one_channel_only[i] = false; ++ } ++ results->h_total[i] = mode_data->d0_htotal; ++ results->pixel_rate[i] = mode_data->d0_pixel_rate; ++ results->src_width[i] = ++ mode_data->d0_graphics_src_width; ++ results->src_height[i] = ++ mode_data->d0_graphics_src_height; ++ results->pitch_in_pixels[i] = ++ mode_data->d0_graphics_src_width; ++ results->scale_ratio[i] = ++ mode_data->d0_graphics_scale_ratio; ++ stereo_mode[i] = mode_data->d0_graphics_stereo_mode; ++ } else if (i == 5) { ++ results->compression_rate[i] = int_to_fixed(1); ++ results->access_one_channel_only[i] = false; ++ results->h_total[i] = mode_data->d1_htotal; ++ results->pixel_rate[i] = mode_data->d1_pixel_rate; ++ results->src_width[i] = ++ mode_data->d1_graphics_src_width; ++ results->src_height[i] = ++ mode_data->d1_graphics_src_height; ++ results->pitch_in_pixels[i] = ++ mode_data->d1_graphics_src_width; ++ results->scale_ratio[i] = ++ mode_data->d1_graphics_scale_ratio; ++ stereo_mode[i] = mode_data->d1_graphics_stereo_mode; ++ } else { ++ results->compression_rate[i] = int_to_fixed(1); ++ results->access_one_channel_only[i] = false; ++ results->h_total[i] = mode_data->d2_htotal; ++ results->pixel_rate[i] = mode_data->d2_pixel_rate; ++ results->src_width[i] = ++ mode_data->d2_graphics_src_width; ++ results->src_height[i] = ++ mode_data->d2_graphics_src_height; ++ results->pitch_in_pixels[i] = ++ mode_data->d2_graphics_src_width; ++ results->scale_ratio[i] = ++ mode_data->d2_graphics_scale_ratio; ++ stereo_mode[i] = mode_data->d2_graphics_stereo_mode; ++ } ++ results->cursor_width_pixels[i] = vbios->cursor_width; ++ } ++ results->scatter_gather_enable_for_pipe[maximum_number_of_surfaces - 2] = ++ false; ++ results->scatter_gather_enable_for_pipe[maximum_number_of_surfaces - 1] = ++ false; ++ if (mode_data->d1_display_write_back_dwb_enable == true) { ++ results->enable[maximum_number_of_surfaces - 2] = true; ++ results->enable[maximum_number_of_surfaces - 1] = true; ++ } else { ++ results->enable[maximum_number_of_surfaces - 2] = false; ++ results->enable[maximum_number_of_surfaces - 1] = false; ++ } ++ surface_type[maximum_number_of_surfaces - 2] = ++ def_display_write_back420_luma; ++ surface_type[maximum_number_of_surfaces - 1] = ++ def_display_write_back420_chroma; ++ results->lb_size_per_component[maximum_number_of_surfaces - 2] = ++ dceip->underlay420_luma_lb_size_per_component; ++ results->lb_size_per_component[maximum_number_of_surfaces - 1] = ++ dceip->underlay420_chroma_lb_size_per_component; ++ results->bytes_per_pixel[maximum_number_of_surfaces - 2] = int_to_fixed( ++ 1); ++ results->bytes_per_pixel[maximum_number_of_surfaces - 1] = int_to_fixed( ++ 2); ++ results->interlace_mode[maximum_number_of_surfaces - 2] = ++ mode_data->graphics_interlace_mode; ++ results->interlace_mode[maximum_number_of_surfaces - 1] = ++ mode_data->graphics_interlace_mode; ++ results->h_taps[maximum_number_of_surfaces - 2] = int_to_fixed(1); ++ results->h_taps[maximum_number_of_surfaces - 1] = int_to_fixed(1); ++ results->v_taps[maximum_number_of_surfaces - 2] = int_to_fixed(1); ++ results->v_taps[maximum_number_of_surfaces - 1] = int_to_fixed(1); ++ results->rotation_angle[maximum_number_of_surfaces - 2] = int_to_fixed( ++ 0); ++ results->rotation_angle[maximum_number_of_surfaces - 1] = int_to_fixed( ++ 0); ++ tiling_mode[maximum_number_of_surfaces - 2] = def_linear; ++ tiling_mode[maximum_number_of_surfaces - 1] = def_linear; ++ results->lb_bpc[maximum_number_of_surfaces - 2] = int_to_fixed(8); ++ results->lb_bpc[maximum_number_of_surfaces - 1] = int_to_fixed(8); ++ results->compression_rate[maximum_number_of_surfaces - 2] = ++ int_to_fixed(1); ++ results->compression_rate[maximum_number_of_surfaces - 1] = ++ int_to_fixed(1); ++ results->access_one_channel_only[maximum_number_of_surfaces - 2] = ++ false; ++ results->access_one_channel_only[maximum_number_of_surfaces - 1] = ++ false; ++ results->h_total[maximum_number_of_surfaces - 2] = mode_data->d1_htotal; ++ results->h_total[maximum_number_of_surfaces - 1] = mode_data->d1_htotal; ++ results->pixel_rate[maximum_number_of_surfaces - 2] = ++ mode_data->d1_pixel_rate; ++ results->pixel_rate[maximum_number_of_surfaces - 1] = ++ mode_data->d1_pixel_rate; ++ results->src_width[maximum_number_of_surfaces - 2] = ++ mode_data->d1_graphics_src_width; ++ results->src_width[maximum_number_of_surfaces - 1] = ++ mode_data->d1_graphics_src_width; ++ results->src_height[maximum_number_of_surfaces - 2] = ++ mode_data->d1_graphics_src_height; ++ results->src_height[maximum_number_of_surfaces - 1] = ++ mode_data->d1_graphics_src_height; ++ results->pitch_in_pixels[maximum_number_of_surfaces - 2] = ++ mode_data->d1_graphics_src_width; ++ results->pitch_in_pixels[maximum_number_of_surfaces - 1] = ++ mode_data->d1_graphics_src_width; ++ results->scale_ratio[maximum_number_of_surfaces - 2] = int_to_fixed(1); ++ results->scale_ratio[maximum_number_of_surfaces - 1] = int_to_fixed(1); ++ stereo_mode[maximum_number_of_surfaces - 2] = mono; ++ stereo_mode[maximum_number_of_surfaces - 1] = mono; ++ results->cursor_width_pixels[maximum_number_of_surfaces - 2] = ++ int_to_fixed(0); ++ results->cursor_width_pixels[maximum_number_of_surfaces - 1] = ++ int_to_fixed(0); ++ results->use_alpha[maximum_number_of_surfaces - 2] = false; ++ results->use_alpha[maximum_number_of_surfaces - 1] = false; ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (equ(results->scale_ratio[i], int_to_fixed(1)) ++ && surface_type[i] == def_graphics ++ && stereo_mode[i] == mono ++ && results->interlace_mode[i] == false) { ++ results->h_taps[i] = int_to_fixed(1); ++ results->v_taps[i] = int_to_fixed(1); ++ } ++ if (surface_type[i] == def_display_write_back420_chroma ++ || surface_type[i] == def_underlay420_chroma) { ++ results->pitch_in_pixels_after_surface_type[i] = ++ bw_div(results->pitch_in_pixels[i], ++ int_to_fixed(2)); ++ results->src_width_after_surface_type = bw_div( ++ results->src_width[i], int_to_fixed(2)); ++ results->src_height_after_surface_type = bw_div( ++ results->src_height[i], ++ int_to_fixed(2)); ++ results->hsr_after_surface_type = bw_div( ++ results->scale_ratio[i], ++ int_to_fixed(2)); ++ results->vsr_after_surface_type = bw_div( ++ results->scale_ratio[i], ++ int_to_fixed(2)); ++ } else { ++ results->pitch_in_pixels_after_surface_type[i] = ++ results->pitch_in_pixels[i]; ++ results->src_width_after_surface_type = ++ results->src_width[i]; ++ results->src_height_after_surface_type = ++ results->src_height[i]; ++ results->hsr_after_surface_type = ++ results->scale_ratio[i]; ++ results->vsr_after_surface_type = ++ results->scale_ratio[i]; ++ } ++ if ((equ(results->rotation_angle[i], int_to_fixed(90)) ++ || equ(results->rotation_angle[i], ++ int_to_fixed(270))) ++ && surface_type[i] != def_graphics) { ++ results->src_width_after_rotation = ++ results->src_height_after_surface_type; ++ results->src_height_after_rotation = ++ results->src_width_after_surface_type; ++ results->hsr_after_rotation = ++ results->vsr_after_surface_type; ++ results->vsr_after_rotation = ++ results->hsr_after_surface_type; ++ } else { ++ results->src_width_after_rotation = ++ results->src_width_after_surface_type; ++ results->src_height_after_rotation = ++ results->src_height_after_surface_type; ++ results->hsr_after_rotation = ++ results->hsr_after_surface_type; ++ results->vsr_after_rotation = ++ results->vsr_after_surface_type; ++ } ++ if (stereo_mode[i] == top_bottom) { ++ results->source_width_pixels[i] = ++ results->src_width_after_rotation; ++ results->source_height_pixels = mul( ++ int_to_fixed(2), ++ results->src_height_after_rotation); ++ results->hsr_after_stereo = ++ results->hsr_after_rotation; ++ results->vsr_after_stereo = mul( ++ results->vsr_after_rotation, ++ int_to_fixed(1)); //todo: confirm correctness ++ } else if (stereo_mode[i] == side_by_side) { ++ results->source_width_pixels[i] = mul( ++ int_to_fixed(2), ++ results->src_width_after_rotation); ++ results->source_height_pixels = ++ results->src_height_after_rotation; ++ results->hsr_after_stereo = mul( ++ results->hsr_after_rotation, ++ int_to_fixed(1)); //todo: confirm correctness ++ results->vsr_after_stereo = ++ results->vsr_after_rotation; ++ } else { ++ results->source_width_pixels[i] = ++ results->src_width_after_rotation; ++ results->source_height_pixels = ++ results->src_height_after_rotation; ++ results->hsr_after_stereo = ++ results->hsr_after_rotation; ++ results->vsr_after_stereo = ++ results->vsr_after_rotation; ++ } ++ results->hsr[i] = results->hsr_after_stereo; ++ if (results->interlace_mode[i]) { ++ results->vsr[i] = mul(results->vsr_after_stereo, ++ int_to_fixed(2)); ++ } else { ++ results->vsr[i] = results->vsr_after_stereo; ++ } ++ if (mode_data->panning_and_bezel_adjustment != none) { ++ results->source_width_rounded_up_to_chunks[i] = ++ add( ++ bw_floor( ++ sub( ++ results->source_width_pixels[i], ++ int_to_fixed( ++ 1)), ++ int_to_fixed(128)), ++ int_to_fixed(256)); ++ } else { ++ results->source_width_rounded_up_to_chunks[i] = ++ bw_ceil(results->source_width_pixels[i], ++ int_to_fixed(128)); ++ } ++ results->source_height_rounded_up_to_chunks[i] = ++ results->source_height_pixels; ++ } ++ } ++ if (geq(dceip->number_of_graphics_pipes, ++ int_to_fixed(mode_data->number_of_displays)) ++ && geq(dceip->number_of_underlay_pipes, ++ results->number_of_underlay_surfaces) ++ && !(dceip->display_write_back_supported == false ++ && mode_data->d1_display_write_back_dwb_enable == true)) { ++ pipe_check = def_ok; ++ } else { ++ pipe_check = def_notok; ++ } ++ hsr_check = def_ok; ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (neq(results->hsr[i], int_to_fixed(1))) { ++ if (gtn(results->hsr[i], int_to_fixed(4))) { ++ hsr_check = def_hsr_more_than_4; ++ } else { ++ if (gtn(results->hsr[i], ++ results->h_taps[i])) { ++ hsr_check = ++ def_hsr_more_than_htaps; ++ } else { ++ if (dceip->pre_downscaler_enabled ++ == true ++ && gtn(results->hsr[i], ++ int_to_fixed(1)) ++ && leq(results->hsr[i], ++ bw_ceil( ++ bw_div( ++ results->h_taps[i], ++ int_to_fixed( ++ 4)), ++ int_to_fixed( ++ 1)))) { ++ hsr_check = ++ def_ceil_htaps_div_4_more_or_eq_hsr; ++ } ++ } ++ } ++ } ++ } ++ } ++ vsr_check = def_ok; ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (neq(results->vsr[i], int_to_fixed(1))) { ++ if (gtn(results->vsr[i], int_to_fixed(4))) { ++ vsr_check = def_vsr_more_than_4; ++ } else { ++ if (gtn(results->vsr[i], ++ results->v_taps[i])) { ++ vsr_check = ++ def_vsr_more_than_vtaps; ++ } ++ } ++ } ++ } ++ } ++ lb_size_check = def_ok; ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if ((dceip->pre_downscaler_enabled ++ && gtn(results->hsr[i], int_to_fixed(1)))) { ++ results->source_width_in_lb = bw_div( ++ results->source_width_pixels[i], ++ results->hsr[i]); ++ } else { ++ results->source_width_in_lb = ++ results->source_width_pixels[i]; ++ } ++ if (equ(results->lb_bpc[i], int_to_fixed(8))) { ++ results->lb_line_pitch = ++ bw_ceil( ++ mul(frc_to_fixed(24011, 3000), ++ bw_ceil( ++ results->source_width_in_lb, ++ int_to_fixed( ++ 8))), ++ int_to_fixed(48)); ++ } else if (equ(results->lb_bpc[i], int_to_fixed(10))) { ++ results->lb_line_pitch = ++ bw_ceil( ++ mul(frc_to_fixed(30023, 3000), ++ bw_ceil( ++ results->source_width_in_lb, ++ int_to_fixed( ++ 8))), ++ int_to_fixed(48)); ++ } else ++ // case else ++ { ++ results->lb_line_pitch = bw_ceil( ++ mul(results->source_width_in_lb, ++ results->lb_bpc[i]), ++ int_to_fixed(48)); ++ } ++ results->lb_partitions[i] = bw_floor( ++ bw_div(results->lb_size_per_component[i], ++ results->lb_line_pitch), ++ int_to_fixed(1)); ++ if ((surface_type[i] != def_graphics ++ || dceip->graphics_lb_nodownscaling_multi_line_prefetching ++ == true)) { ++ results->lb_partitions_max[i] = int_to_fixed( ++ 10); ++ } else { ++ results->lb_partitions_max[i] = int_to_fixed(7); ++ } ++ results->lb_partitions[i] = bw_min( ++ results->lb_partitions_max[i], ++ results->lb_partitions[i]); ++ if (gtn(add(results->v_taps[i], int_to_fixed(1)), ++ results->lb_partitions[i])) { ++ lb_size_check = def_notok; ++ } ++ } ++ } ++ if (mode_data->d0_fbc_enable ++ && (equ(mode_data->graphics_rotation_angle, int_to_fixed(90)) ++ || equ(mode_data->graphics_rotation_angle, ++ int_to_fixed(270)) ++ || mode_data->d0_graphics_stereo_mode != mono ++ || neq(mode_data->graphics_bytes_per_pixel, ++ int_to_fixed(4)))) { ++ fbc_check = def_invalid_rotation_or_bpp_or_stereo; ++ } else { ++ fbc_check = def_ok; ++ } ++ rotation_check = def_ok; ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if ((equ(results->rotation_angle[i], int_to_fixed(90)) ++ || equ(results->rotation_angle[i], ++ int_to_fixed(270))) ++ && (tiling_mode[i] == def_linear ++ || stereo_mode[i] != mono)) { ++ rotation_check = ++ def_invalid_linear_or_stereo_mode; ++ } ++ } ++ } ++ if (pipe_check == def_ok && hsr_check == def_ok && vsr_check == def_ok ++ && lb_size_check == def_ok && fbc_check == def_ok ++ && rotation_check == def_ok) { ++ mode_check = def_ok; ++ } else { ++ mode_check = def_notok; ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if ((equ(results->rotation_angle[i], int_to_fixed(90)) ++ || equ(results->rotation_angle[i], ++ int_to_fixed(270)))) { ++ if ((tiling_mode[i] == def_portrait)) { ++ results->orthogonal_rotation[i] = false; ++ } else { ++ results->orthogonal_rotation[i] = true; ++ } ++ } else { ++ if ((tiling_mode[i] == def_portrait)) { ++ results->orthogonal_rotation[i] = true; ++ } else { ++ results->orthogonal_rotation[i] = false; ++ } ++ } ++ if (equ(results->rotation_angle[i], int_to_fixed(90)) ++ || equ(results->rotation_angle[i], ++ int_to_fixed(270))) { ++ results->underlay_maximum_source_efficient_for_tiling = ++ dceip->underlay_maximum_height_efficient_for_tiling; ++ } else { ++ results->underlay_maximum_source_efficient_for_tiling = ++ dceip->underlay_maximum_width_efficient_for_tiling; ++ } ++ if (equ(dceip->de_tiling_buffer, int_to_fixed(0))) { ++ if (surface_type[i] ++ == def_display_write_back420_luma ++ || surface_type[i] ++ == def_display_write_back420_chroma) { ++ results->bytes_per_request[i] = ++ int_to_fixed(64); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed(64); ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(1); ++ results->latency_hiding_lines[i] = ++ int_to_fixed(1); ++ } else if (tiling_mode[i] == def_linear) { ++ results->bytes_per_request[i] = ++ int_to_fixed(64); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed(64); ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed(2); ++ } else { ++ if (surface_type[i] == def_graphics ++ || (gtn( ++ results->source_width_rounded_up_to_chunks[i], ++ bw_ceil( ++ results->underlay_maximum_source_efficient_for_tiling, ++ int_to_fixed( ++ 256))))) { ++ if (equ( ++ results->bytes_per_pixel[i], ++ int_to_fixed(8))) { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed(2); ++ if (results->orthogonal_rotation[i]) { ++ results->bytes_per_request[i] = ++ int_to_fixed( ++ 32); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed( ++ 32); ++ } else { ++ results->bytes_per_request[i] = ++ int_to_fixed( ++ 64); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed( ++ 64); ++ } ++ } else if (equ( ++ results->bytes_per_pixel[i], ++ int_to_fixed(4))) { ++ if (results->orthogonal_rotation[i]) { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed( ++ 2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed( ++ 2); ++ results->bytes_per_request[i] = ++ int_to_fixed( ++ 32); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed( ++ 16); ++ } else { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed( ++ 2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed( ++ 2); ++ results->bytes_per_request[i] = ++ int_to_fixed( ++ 64); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed( ++ 64); ++ } ++ } else if (equ( ++ results->bytes_per_pixel[i], ++ int_to_fixed(2))) { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed(2); ++ results->bytes_per_request[i] = ++ int_to_fixed( ++ 32); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed( ++ 32); ++ } else { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed(2); ++ results->bytes_per_request[i] = ++ int_to_fixed( ++ 32); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed( ++ 16); ++ } ++ } else { ++ results->bytes_per_request[i] = ++ int_to_fixed(64); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed(64); ++ if (results->orthogonal_rotation[i]) { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(8); ++ results->latency_hiding_lines[i] = ++ int_to_fixed(4); ++ } else { ++ if (equ( ++ results->bytes_per_pixel[i], ++ int_to_fixed( ++ 4))) { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed( ++ 2); ++ results->latency_hiding_lines[i] = ++ int_to_fixed( ++ 2); ++ } else if (equ( ++ results->bytes_per_pixel[i], ++ int_to_fixed( ++ 2))) { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed( ++ 4); ++ results->latency_hiding_lines[i] = ++ int_to_fixed( ++ 4); ++ } else { ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed( ++ 8); ++ results->latency_hiding_lines[i] = ++ int_to_fixed( ++ 4); ++ } ++ } ++ } ++ } ++ } else { ++ results->bytes_per_request[i] = int_to_fixed( ++ 256); ++ results->useful_bytes_per_request[i] = ++ int_to_fixed(256); ++ results->lines_interleaved_in_mem_access[i] = ++ int_to_fixed(4); ++ results->latency_hiding_lines[i] = int_to_fixed( ++ 4); ++ } ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->v_filter_init[i] = ++ bw_floor( ++ bw_div( ++ (add( ++ add( ++ add( ++ results->vsr[i], ++ results->v_taps[i]), ++ mul( ++ mul( ++ frc_to_fixed( ++ 1, ++ 2), ++ results->vsr[i]), ++ int_to_fixed( ++ results->interlace_mode[i]))), ++ int_to_fixed(1))), ++ int_to_fixed(2)), ++ int_to_fixed(1)); ++ if (mode_data->panning_and_bezel_adjustment ++ == any_lines) { ++ results->v_filter_init[i] = add( ++ results->v_filter_init[i], ++ int_to_fixed(1)); ++ } ++ if (stereo_mode[i] == top_bottom) { ++ v_filter_init_mode[i] = def_manual; ++ results->v_filter_init[i] = bw_min( ++ results->v_filter_init[i], ++ int_to_fixed(4)); ++ } else { ++ v_filter_init_mode[i] = def_auto; ++ } ++ if (stereo_mode[i] == top_bottom) { ++ results->num_lines_at_frame_start = ++ int_to_fixed(1); ++ } else { ++ results->num_lines_at_frame_start = ++ int_to_fixed(3); ++ } ++ if ((gtn(results->vsr[i], int_to_fixed(1)) ++ && surface_type[i] == def_graphics) ++ || mode_data->panning_and_bezel_adjustment ++ == any_lines) { ++ results->line_buffer_prefetch[i] = int_to_fixed( ++ 0); ++ } else if ((((dceip->underlay_downscale_prefetch_enabled ++ == true && surface_type[i] != def_graphics) ++ || surface_type[i] == def_graphics) ++ && (gtn(results->lb_partitions[i], ++ add(results->v_taps[i], ++ bw_ceil(results->vsr[i], ++ int_to_fixed(1))))))) { ++ results->line_buffer_prefetch[i] = int_to_fixed( ++ 1); ++ } else { ++ results->line_buffer_prefetch[i] = int_to_fixed( ++ 0); ++ } ++ results->lb_lines_in_per_line_out_in_beginning_of_frame[i] = ++ bw_div( ++ bw_ceil(results->v_filter_init[i], ++ dceip->lines_interleaved_into_lb), ++ results->num_lines_at_frame_start); ++ if (equ(results->line_buffer_prefetch[i], ++ int_to_fixed(1))) { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ bw_max(int_to_fixed(1), ++ results->vsr[i]); ++ } else if (leq(results->vsr[i], int_to_fixed(1))) { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ int_to_fixed(1); ++ } else if (leq(results->vsr[i], ++ bw_div(int_to_fixed(4), int_to_fixed(3)))) { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ bw_div(int_to_fixed(4), int_to_fixed(3)); ++ } else if (leq(results->vsr[i], ++ bw_div(int_to_fixed(6), int_to_fixed(4)))) { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ bw_div(int_to_fixed(6), int_to_fixed(4)); ++ } else if (leq(results->vsr[i], int_to_fixed(2))) { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ int_to_fixed(2); ++ } else if (leq(results->vsr[i], int_to_fixed(3))) { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ int_to_fixed(3); ++ } else { ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i] = ++ int_to_fixed(4); ++ } ++ if (equ(results->line_buffer_prefetch[i], ++ int_to_fixed(1)) ++ || equ( ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i], ++ int_to_fixed(2)) ++ || equ( ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i], ++ int_to_fixed(4))) { ++ results->horizontal_blank_and_chunk_granularity_factor[i] = ++ int_to_fixed(1); ++ } else { ++ results->horizontal_blank_and_chunk_granularity_factor[i] = ++ bw_div(results->h_total[i], ++ (bw_div( ++ (add( ++ results->h_total[i], ++ bw_div( ++ (sub( ++ results->source_width_pixels[i], ++ dceip->chunk_width)), ++ results->hsr[i]))), ++ int_to_fixed(2)))); ++ } ++ results->request_bandwidth[i] = ++ bw_div( ++ mul( ++ bw_div( ++ mul( ++ bw_div( ++ mul( ++ bw_max( ++ results->lb_lines_in_per_line_out_in_beginning_of_frame[i], ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i]), ++ results->source_width_rounded_up_to_chunks[i]), ++ (bw_div( ++ results->h_total[i], ++ results->pixel_rate[i]))), ++ results->bytes_per_pixel[i]), ++ results->useful_bytes_per_request[i]), ++ results->lines_interleaved_in_mem_access[i]), ++ results->latency_hiding_lines[i]); ++ results->display_bandwidth[i] = mul( ++ results->request_bandwidth[i], ++ results->bytes_per_request[i]); ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] == def_display_write_back420_luma) { ++ results->data_buffer_size[i] = ++ dceip->display_write_back420_luma_mcifwr_buffer_size; ++ } else if (surface_type[i] ++ == def_display_write_back420_chroma) { ++ results->data_buffer_size[i] = ++ dceip->display_write_back420_chroma_mcifwr_buffer_size; ++ } else if (surface_type[i] == def_underlay420_luma) { ++ results->data_buffer_size[i] = ++ dceip->underlay_luma_dmif_size; ++ } else if (surface_type[i] == def_underlay420_chroma) { ++ results->data_buffer_size[i] = bw_div( ++ dceip->underlay_chroma_dmif_size, ++ int_to_fixed(2)); ++ } else if (surface_type[i] == def_underlay422 ++ || surface_type[i] == def_underlay444) { ++ if (results->orthogonal_rotation[i] == false) { ++ results->data_buffer_size[i] = ++ dceip->underlay_luma_dmif_size; ++ } else { ++ results->data_buffer_size[i] = ++ add( ++ dceip->underlay_luma_dmif_size, ++ dceip->underlay_chroma_dmif_size); ++ } ++ } else { ++ if (mode_data->number_of_displays == 1 ++ && equ(dceip->de_tiling_buffer, ++ int_to_fixed(0))) { ++ if (mode_data->d0_fbc_enable) { ++ results->data_buffer_size[i] = ++ mul( ++ dceip->max_dmif_buffer_allocated, ++ dceip->graphics_dmif_size); ++ } else { ++ results->data_buffer_size[i] = ++ mul( ++ mul( ++ max_chunks_non_fbc_mode, ++ pixels_per_chunk), ++ results->bytes_per_pixel[i]); ++ } ++ } else { ++ results->data_buffer_size[i] = ++ dceip->graphics_dmif_size; ++ } ++ } ++ if (surface_type[i] == def_display_write_back420_luma ++ || surface_type[i] ++ == def_display_write_back420_chroma) { ++ results->memory_chunk_size_in_bytes[i] = ++ int_to_fixed(1024); ++ results->pipe_chunk_size_in_bytes[i] = ++ int_to_fixed(1024); ++ } else { ++ results->memory_chunk_size_in_bytes[i] = ++ mul( ++ mul(dceip->chunk_width, ++ results->lines_interleaved_in_mem_access[i]), ++ results->bytes_per_pixel[i]); ++ results->pipe_chunk_size_in_bytes[i] = ++ mul( ++ mul(dceip->chunk_width, ++ dceip->lines_interleaved_into_lb), ++ results->bytes_per_pixel[i]); ++ } ++ } ++ } ++ results->min_dmif_size_in_time = int_to_fixed(9999); ++ results->min_mcifwr_size_in_time = int_to_fixed(9999); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ if (ltn( ++ bw_div( ++ bw_div( ++ mul( ++ results->data_buffer_size[i], ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]), ++ results->display_bandwidth[i]), ++ results->min_dmif_size_in_time)) { ++ results->min_dmif_size_in_time = ++ bw_div( ++ bw_div( ++ mul( ++ results->data_buffer_size[i], ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]), ++ results->display_bandwidth[i]); ++ } ++ } else { ++ if (ltn( ++ bw_div( ++ bw_div( ++ mul( ++ results->data_buffer_size[i], ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]), ++ results->display_bandwidth[i]), ++ results->min_mcifwr_size_in_time)) { ++ results->min_mcifwr_size_in_time = ++ bw_div( ++ bw_div( ++ mul( ++ results->data_buffer_size[i], ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]), ++ results->display_bandwidth[i]); ++ } ++ } ++ } ++ } ++ results->total_requests_for_dmif_size = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i] ++ && surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->total_requests_for_dmif_size = add( ++ results->total_requests_for_dmif_size, ++ bw_div(results->data_buffer_size[i], ++ results->useful_bytes_per_request[i])); ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma ++ && dceip->limit_excessive_outstanding_dmif_requests ++ && (mode_data->number_of_displays > 1 ++ || gtn( ++ results->total_requests_for_dmif_size, ++ dceip->dmif_request_buffer_size))) { ++ results->adjusted_data_buffer_size[i] = ++ bw_min(results->data_buffer_size[i], ++ bw_ceil( ++ mul( ++ results->min_dmif_size_in_time, ++ results->display_bandwidth[i]), ++ results->memory_chunk_size_in_bytes[i])); ++ } else { ++ results->adjusted_data_buffer_size[i] = ++ results->data_buffer_size[i]; ++ } ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if ((mode_data->number_of_displays == 1 ++ && equ(results->number_of_underlay_surfaces, ++ int_to_fixed(0)))) { ++ results->outstanding_chunk_request_limit[i] = ++ int_to_fixed(255); ++ } else { ++ results->outstanding_chunk_request_limit[i] = ++ bw_ceil( ++ bw_div( ++ results->adjusted_data_buffer_size[i], ++ results->pipe_chunk_size_in_bytes[i]), ++ int_to_fixed(1)); ++ } ++ } ++ } ++ if (mode_data->number_of_displays > 1 ++ || (neq(mode_data->graphics_rotation_angle, int_to_fixed(0)) ++ && neq(mode_data->graphics_rotation_angle, ++ int_to_fixed(180)))) { ++ results->peak_pte_request_to_eviction_ratio_limiting = ++ dceip->peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display; ++ } else { ++ results->peak_pte_request_to_eviction_ratio_limiting = ++ dceip->peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation; ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i] ++ && results->scatter_gather_enable_for_pipe[i] == true) { ++ if (tiling_mode[i] == def_linear) { ++ results->useful_pte_per_pte_request = ++ int_to_fixed(8); ++ results->scatter_gather_page_width[i] = bw_div( ++ int_to_fixed(4096), ++ results->bytes_per_pixel[i]); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(1); ++ results->scatter_gather_pte_request_rows = ++ int_to_fixed(1); ++ results->scatter_gather_row_height = ++ dceip->scatter_gather_lines_of_pte_prefetching_in_linear_mode; ++ } else if (equ(results->rotation_angle[i], ++ int_to_fixed(0)) ++ || equ(results->rotation_angle[i], ++ int_to_fixed(180))) { ++ results->useful_pte_per_pte_request = ++ int_to_fixed(8); ++ if (equ(results->bytes_per_pixel[i], ++ int_to_fixed(4))) { ++ results->scatter_gather_page_width[i] = ++ int_to_fixed(32); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(32); ++ } else if (equ(results->bytes_per_pixel[i], ++ int_to_fixed(2))) { ++ results->scatter_gather_page_width[i] = ++ int_to_fixed(64); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(32); ++ } else { ++ results->scatter_gather_page_width[i] = ++ int_to_fixed(64); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(64); ++ } ++ results->scatter_gather_pte_request_rows = ++ dceip->scatter_gather_pte_request_rows_in_tiling_mode; ++ results->scatter_gather_row_height = ++ results->scatter_gather_page_height[i]; ++ } else { ++ results->useful_pte_per_pte_request = ++ int_to_fixed(1); ++ if (equ(results->bytes_per_pixel[i], ++ int_to_fixed(4))) { ++ results->scatter_gather_page_width[i] = ++ int_to_fixed(32); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(32); ++ } else if (equ(results->bytes_per_pixel[i], ++ int_to_fixed(2))) { ++ results->scatter_gather_page_width[i] = ++ int_to_fixed(32); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(64); ++ } else ++ // case else ++ { ++ results->scatter_gather_page_width[i] = ++ int_to_fixed(64); ++ results->scatter_gather_page_height[i] = ++ int_to_fixed(64); ++ } ++ results->scatter_gather_pte_request_rows = ++ dceip->scatter_gather_pte_request_rows_in_tiling_mode; ++ results->scatter_gather_row_height = ++ results->scatter_gather_page_height[i]; ++ } ++ results->pte_request_per_chunk[i] = bw_div( ++ bw_div(dceip->chunk_width, ++ results->scatter_gather_page_width[i]), ++ results->useful_pte_per_pte_request); ++ results->scatter_gather_pte_requests_in_row[i] = ++ bw_div( ++ mul(results->scatter_gather_row_height, ++ bw_ceil( ++ mul( ++ bw_div( ++ results->source_width_rounded_up_to_chunks[i], ++ dceip->chunk_width), ++ results->pte_request_per_chunk[i]), ++ int_to_fixed(1))), ++ results->scatter_gather_page_height[i]); ++ results->scatter_gather_pte_requests_in_vblank = mul( ++ results->scatter_gather_pte_request_rows, ++ results->scatter_gather_pte_requests_in_row[i]); ++ if (equ( ++ results->peak_pte_request_to_eviction_ratio_limiting, ++ int_to_fixed(0))) { ++ results->scatter_gather_pte_request_limit[i] = ++ results->scatter_gather_pte_requests_in_vblank; ++ } else { ++ results->scatter_gather_pte_request_limit[i] = ++ bw_max( ++ dceip->minimum_outstanding_pte_request_limit, ++ bw_min( ++ results->scatter_gather_pte_requests_in_vblank, ++ bw_ceil( ++ mul( ++ mul( ++ bw_div( ++ bw_ceil( ++ results->adjusted_data_buffer_size[i], ++ results->memory_chunk_size_in_bytes[i]), ++ results->memory_chunk_size_in_bytes[i]), ++ results->pte_request_per_chunk[i]), ++ results->peak_pte_request_to_eviction_ratio_limiting), ++ int_to_fixed( ++ 1)))); ++ } ++ } ++ } ++ results->inefficient_linear_pitch_in_bytes = mul( ++ mul(vbios->number_of_dram_banks, ++ vbios->number_of_dram_channels), int_to_fixed(256)); ++ if (mode_data->underlay_surface_type == yuv_420) { ++ results->inefficient_underlay_pitch_in_pixels = ++ results->inefficient_linear_pitch_in_bytes; ++ } else if (mode_data->underlay_surface_type == yuv_422) { ++ results->inefficient_underlay_pitch_in_pixels = bw_div( ++ results->inefficient_linear_pitch_in_bytes, ++ int_to_fixed(2)); ++ } else ++ // case else ++ { ++ results->inefficient_underlay_pitch_in_pixels = bw_div( ++ results->inefficient_linear_pitch_in_bytes, ++ int_to_fixed(4)); ++ } ++ if (mode_data->underlay_tiling_mode == linear ++ && vbios->scatter_gather_enable == true ++ && mode_data->underlay_pitch_in_pixels.value ++ % results->inefficient_underlay_pitch_in_pixels.value ++ == false) { ++ results->minimum_underlay_pitch_padding_recommended_for_efficiency = ++ int_to_fixed(256); ++ } else { ++ results->minimum_underlay_pitch_padding_recommended_for_efficiency = ++ int_to_fixed(0); ++ } ++ results->cursor_total_data = int_to_fixed(0); ++ results->cursor_total_request_groups = int_to_fixed(0); ++ results->scatter_gather_total_pte_requests = int_to_fixed(0); ++ results->scatter_gather_total_pte_request_groups = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->cursor_total_data = add( ++ results->cursor_total_data, ++ mul(results->cursor_width_pixels[i], ++ int_to_fixed(8))); ++ results->cursor_total_request_groups = add( ++ results->cursor_total_request_groups, ++ bw_ceil( ++ bw_div(results->cursor_width_pixels[i], ++ dceip->cursor_chunk_width), ++ int_to_fixed(1))); ++ if (results->scatter_gather_enable_for_pipe[i]) { ++ results->scatter_gather_total_pte_requests = ++ add( ++ results->scatter_gather_total_pte_requests, ++ results->scatter_gather_pte_request_limit[i]); ++ results->scatter_gather_total_pte_request_groups = ++ add( ++ results->scatter_gather_total_pte_request_groups, ++ bw_ceil( ++ bw_div( ++ results->scatter_gather_pte_request_limit[i], ++ bw_ceil( ++ results->pte_request_per_chunk[i], ++ int_to_fixed( ++ 1))), ++ int_to_fixed(1))); ++ } ++ } ++ } ++ results->tile_width_in_pixels = int_to_fixed(8); ++ results->dmif_total_number_of_data_request_page_close_open = ++ int_to_fixed(0); ++ results->mcifwr_total_number_of_data_request_page_close_open = ++ int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (results->scatter_gather_enable_for_pipe[i] == true ++ && tiling_mode[i] != def_linear) { ++ results->bytes_per_page_close_open = ++ mul( ++ results->lines_interleaved_in_mem_access[i], ++ bw_max( ++ mul( ++ mul( ++ mul( ++ results->bytes_per_pixel[i], ++ results->tile_width_in_pixels), ++ vbios->number_of_dram_banks), ++ vbios->number_of_dram_channels), ++ mul( ++ results->bytes_per_pixel[i], ++ results->scatter_gather_page_width[i]))); ++ } else if (results->scatter_gather_enable_for_pipe[i] ++ == true && tiling_mode[i] == def_linear ++ && (mul( ++ results->pitch_in_pixels_after_surface_type[i], ++ results->bytes_per_pixel[i])).value ++ % results->inefficient_linear_pitch_in_bytes.value ++ == false) { ++ results->bytes_per_page_close_open = ++ dceip->linear_mode_line_request_alternation_slice; ++ } else { ++ results->bytes_per_page_close_open = ++ results->memory_chunk_size_in_bytes[i]; ++ } ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->dmif_total_number_of_data_request_page_close_open = ++ add( ++ results->dmif_total_number_of_data_request_page_close_open, ++ bw_div( ++ bw_ceil( ++ results->adjusted_data_buffer_size[i], ++ results->memory_chunk_size_in_bytes[i]), ++ results->bytes_per_page_close_open)); ++ } else { ++ results->mcifwr_total_number_of_data_request_page_close_open = ++ add( ++ results->mcifwr_total_number_of_data_request_page_close_open, ++ bw_div( ++ bw_ceil( ++ results->adjusted_data_buffer_size[i], ++ results->memory_chunk_size_in_bytes[i]), ++ results->bytes_per_page_close_open)); ++ } ++ } ++ } ++ results->dmif_total_page_close_open_time = ++ bw_div( ++ mul( ++ (add( ++ add( ++ results->dmif_total_number_of_data_request_page_close_open, ++ results->scatter_gather_total_pte_request_groups), ++ results->cursor_total_request_groups)), ++ vbios->trc), int_to_fixed(1000)); ++ results->mcifwr_total_page_close_open_time = ++ bw_div( ++ mul( ++ results->mcifwr_total_number_of_data_request_page_close_open, ++ vbios->trc), int_to_fixed(1000)); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->adjusted_data_buffer_size_in_memory[i] = bw_div( ++ mul(results->adjusted_data_buffer_size[i], ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]); ++ } ++ } ++ results->total_requests_for_adjusted_dmif_size = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->total_requests_for_adjusted_dmif_size = ++ add( ++ results->total_requests_for_adjusted_dmif_size, ++ bw_div( ++ results->adjusted_data_buffer_size[i], ++ results->useful_bytes_per_request[i])); ++ } ++ } ++ } ++ if (equ(dceip->dcfclk_request_generation, int_to_fixed(1))) { ++ results->total_dmifmc_urgent_trips = int_to_fixed(1); ++ } else { ++ results->total_dmifmc_urgent_trips = ++ bw_ceil( ++ bw_div( ++ results->total_requests_for_adjusted_dmif_size, ++ (add(dceip->dmif_request_buffer_size, ++ mul( ++ vbios->number_of_request_slots_gmc_reserves_for_dmif_per_channel, ++ vbios->number_of_dram_channels)))), ++ int_to_fixed(1)); ++ } ++ results->total_dmifmc_urgent_latency = mul(vbios->dmifmc_urgent_latency, ++ results->total_dmifmc_urgent_trips); ++ results->total_display_reads_required_data = int_to_fixed(0); ++ results->total_display_reads_required_dram_access_data = int_to_fixed( ++ 0); ++ results->total_display_writes_required_data = int_to_fixed(0); ++ results->total_display_writes_required_dram_access_data = int_to_fixed( ++ 0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->display_reads_required_data = ++ results->adjusted_data_buffer_size_in_memory[i]; ++ results->display_reads_required_dram_access_data = ++ mul( ++ results->adjusted_data_buffer_size_in_memory[i], ++ bw_ceil( ++ bw_div( ++ vbios->dram_channel_width_in_bits, ++ results->bytes_per_request[i]), ++ int_to_fixed(1))); ++ if (results->access_one_channel_only[i]) { ++ results->display_reads_required_dram_access_data = ++ mul( ++ results->display_reads_required_dram_access_data, ++ vbios->number_of_dram_channels); ++ } ++ results->total_display_reads_required_data = ++ add( ++ results->total_display_reads_required_data, ++ results->display_reads_required_data); ++ results->total_display_reads_required_dram_access_data = ++ add( ++ results->total_display_reads_required_dram_access_data, ++ results->display_reads_required_dram_access_data); ++ } else { ++ results->total_display_writes_required_data = ++ add( ++ results->total_display_writes_required_data, ++ results->adjusted_data_buffer_size_in_memory[i]); ++ results->total_display_writes_required_dram_access_data = ++ add( ++ results->total_display_writes_required_dram_access_data, ++ mul( ++ results->adjusted_data_buffer_size_in_memory[i], ++ bw_ceil( ++ bw_div( ++ vbios->dram_channel_width_in_bits, ++ results->bytes_per_request[i]), ++ int_to_fixed( ++ 1)))); ++ } ++ } ++ } ++ results->total_display_reads_required_data = add( ++ add(results->total_display_reads_required_data, ++ results->cursor_total_data), ++ mul(results->scatter_gather_total_pte_requests, ++ int_to_fixed(64))); ++ results->total_display_reads_required_dram_access_data = add( ++ add(results->total_display_reads_required_dram_access_data, ++ results->cursor_total_data), ++ mul(results->scatter_gather_total_pte_requests, ++ int_to_fixed(64))); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (gtn(results->v_filter_init[i], int_to_fixed(4))) { ++ results->src_pixels_for_first_output_pixel[i] = ++ mul( ++ results->source_width_rounded_up_to_chunks[i], ++ int_to_fixed(4)); ++ } else { ++ if (gtn(results->v_filter_init[i], ++ int_to_fixed(2))) { ++ results->src_pixels_for_first_output_pixel[i] = ++ int_to_fixed(512); ++ } else { ++ results->src_pixels_for_first_output_pixel[i] = ++ int_to_fixed(0); ++ } ++ } ++ results->src_data_for_first_output_pixel[i] = ++ bw_div( ++ mul( ++ mul( ++ results->src_pixels_for_first_output_pixel[i], ++ results->bytes_per_pixel[i]), ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]); ++ results->src_pixels_for_last_output_pixel[i] = ++ mul( ++ results->source_width_rounded_up_to_chunks[i], ++ bw_max( ++ bw_ceil( ++ results->v_filter_init[i], ++ dceip->lines_interleaved_into_lb), ++ mul( ++ results->horizontal_blank_and_chunk_granularity_factor[i], ++ bw_ceil(results->vsr[i], ++ dceip->lines_interleaved_into_lb)))); ++ results->src_data_for_last_output_pixel[i] = ++ bw_div( ++ mul( ++ mul( ++ mul( ++ results->source_width_rounded_up_to_chunks[i], ++ bw_max( ++ bw_ceil( ++ results->v_filter_init[i], ++ dceip->lines_interleaved_into_lb), ++ results->lines_interleaved_in_mem_access[i])), ++ results->bytes_per_pixel[i]), ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]); ++ results->active_time[i] = ++ bw_div( ++ bw_div( ++ results->source_width_rounded_up_to_chunks[i], ++ results->hsr[i]), ++ results->pixel_rate[i]); ++ } ++ } ++ for (i = 0; i <= 2; i += 1) { ++ for (j = 0; j <= 2; j += 1) { ++ results->dmif_burst_time[i][j] = ++ bw_max3( ++ results->dmif_total_page_close_open_time, ++ bw_div( ++ results->total_display_reads_required_dram_access_data, ++ (mul( ++ bw_div( ++ mul(yclk[i], ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed( ++ 8)), ++ vbios->number_of_dram_channels))), ++ bw_div( ++ results->total_display_reads_required_data, ++ (mul(sclk[j], ++ vbios->data_return_bus_width)))); ++ if (mode_data->d1_display_write_back_dwb_enable ++ == true) { ++ results->mcifwr_burst_time[i][j] = ++ bw_max3( ++ results->mcifwr_total_page_close_open_time, ++ bw_div( ++ results->total_display_writes_required_dram_access_data, ++ (mul( ++ bw_div( ++ mul( ++ yclk[i], ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed( ++ 8)), ++ vbios->number_of_dram_channels))), ++ bw_div( ++ results->total_display_writes_required_data, ++ (mul(sclk[j], ++ vbios->data_return_bus_width)))); ++ } ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ for (j = 0; j <= 2; j += 1) { ++ for (k = 0; k <= 2; k += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] ++ != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->line_source_transfer_time[i][j][k] = ++ bw_max( ++ mul( ++ (add( ++ results->total_dmifmc_urgent_latency, ++ results->dmif_burst_time[j][k])), ++ bw_floor( ++ bw_div( ++ results->src_data_for_first_output_pixel[i], ++ results->adjusted_data_buffer_size_in_memory[i]), ++ int_to_fixed( ++ 1))), ++ sub( ++ mul( ++ (add( ++ results->total_dmifmc_urgent_latency, ++ results->dmif_burst_time[j][k])), ++ bw_floor( ++ bw_div( ++ results->src_data_for_last_output_pixel[i], ++ results->adjusted_data_buffer_size_in_memory[i]), ++ int_to_fixed( ++ 1))), ++ results->active_time[i])); ++ } else { ++ results->line_source_transfer_time[i][j][k] = ++ bw_max( ++ mul( ++ (add( ++ vbios->mcifwrmc_urgent_latency, ++ results->mcifwr_burst_time[j][k])), ++ bw_floor( ++ bw_div( ++ results->src_data_for_first_output_pixel[i], ++ results->adjusted_data_buffer_size_in_memory[i]), ++ int_to_fixed( ++ 1))), ++ sub( ++ mul( ++ (add( ++ vbios->mcifwrmc_urgent_latency, ++ results->mcifwr_burst_time[j][k])), ++ bw_floor( ++ bw_div( ++ results->src_data_for_last_output_pixel[i], ++ results->adjusted_data_buffer_size_in_memory[i]), ++ int_to_fixed( ++ 1))), ++ results->active_time[i])); ++ } ++ } ++ } ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (equ( ++ dceip->stutter_and_dram_clock_state_change_gated_before_cursor, ++ int_to_fixed(0)) ++ && gtn(results->cursor_width_pixels[i], ++ int_to_fixed(0))) { ++ if (ltn(results->vsr[i], int_to_fixed(2))) { ++ results->cursor_latency_hiding[i] = ++ bw_div( ++ bw_div( ++ mul( ++ (sub( ++ dceip->cursor_dcp_buffer_lines, ++ int_to_fixed( ++ 1))), ++ results->h_total[i]), ++ results->vsr[i]), ++ results->pixel_rate[i]); ++ } else { ++ results->cursor_latency_hiding[i] = ++ bw_div( ++ bw_div( ++ mul( ++ (sub( ++ dceip->cursor_dcp_buffer_lines, ++ int_to_fixed( ++ 3))), ++ results->h_total[i]), ++ results->vsr[i]), ++ results->pixel_rate[i]); ++ } ++ } else { ++ results->cursor_latency_hiding[i] = ++ int_to_fixed(9999); ++ } ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (dceip->graphics_lb_nodownscaling_multi_line_prefetching ++ == true ++ && (equ(results->vsr[i], int_to_fixed(1)) ++ || (leq(results->vsr[i], ++ frc_to_fixed(8, 10)) ++ && leq(results->v_taps[i], ++ int_to_fixed(2)) ++ && equ(results->lb_bpc[i], ++ int_to_fixed(8)))) ++ && surface_type[i] == def_graphics) { ++ results->minimum_latency_hiding[i] = ++ sub( ++ sub( ++ add( ++ bw_div( ++ mul( ++ bw_div( ++ (bw_div( ++ bw_div( ++ results->data_buffer_size[i], ++ results->bytes_per_pixel[i]), ++ results->source_width_pixels[i])), ++ results->vsr[i]), ++ results->h_total[i]), ++ results->pixel_rate[i]), ++ results->lb_partitions[i]), ++ int_to_fixed(1)), ++ results->total_dmifmc_urgent_latency); ++ } else { ++ results->minimum_latency_hiding[i] = ++ sub( ++ bw_div( ++ mul( ++ (add( ++ add( ++ results->line_buffer_prefetch[i], ++ int_to_fixed( ++ 1)), ++ bw_div( ++ bw_div( ++ bw_div( ++ results->data_buffer_size[i], ++ results->bytes_per_pixel[i]), ++ results->source_width_pixels[i]), ++ results->vsr[i]))), ++ results->h_total[i]), ++ results->pixel_rate[i]), ++ results->total_dmifmc_urgent_latency); ++ } ++ results->minimum_latency_hiding_with_cursor[i] = bw_min( ++ results->minimum_latency_hiding[i], ++ results->cursor_latency_hiding[i]); ++ } ++ } ++ for (i = 0; i <= 2; i += 1) { ++ for (j = 0; j <= 2; j += 1) { ++ results->blackout_duration_margin[i][j] = int_to_fixed( ++ 9999); ++ results->dispclk_required_for_blackout_duration[i][j] = ++ int_to_fixed(0); ++ results->dispclk_required_for_blackout_recovery[i][j] = ++ int_to_fixed(0); ++ for (k = 0; k <= maximum_number_of_surfaces - 1; k += ++ 1) { ++ if (results->enable[k] ++ && gtn(vbios->blackout_duration, ++ int_to_fixed(0))) { ++ if (surface_type[k] ++ != def_display_write_back420_luma ++ && surface_type[k] ++ != def_display_write_back420_chroma) { ++ results->blackout_duration_margin[i][j] = ++ bw_min( ++ results->blackout_duration_margin[i][j], ++ sub( ++ sub( ++ sub( ++ results->minimum_latency_hiding_with_cursor[k], ++ vbios->blackout_duration), ++ results->dmif_burst_time[i][j]), ++ results->line_source_transfer_time[k][i][j])); ++ results->dispclk_required_for_blackout_duration[i][j] = ++ bw_max3( ++ results->dispclk_required_for_blackout_duration[i][j], ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_first_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (sub( ++ sub( ++ results->minimum_latency_hiding_with_cursor[k], ++ vbios->blackout_duration), ++ results->dmif_burst_time[i][j]))), ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_last_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (add( ++ sub( ++ sub( ++ results->minimum_latency_hiding_with_cursor[k], ++ vbios->blackout_duration), ++ results->dmif_burst_time[i][j]), ++ results->active_time[k])))); ++ if (leq( ++ vbios->maximum_blackout_recovery_time, ++ add( ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->dmif_burst_time[i][j]))) { ++ results->dispclk_required_for_blackout_recovery[i][j] = ++ int_to_fixed( ++ 9999); ++ } else if (ltn( ++ results->adjusted_data_buffer_size[k], ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ (add( ++ add( ++ vbios->blackout_duration, ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2))), ++ results->dmif_burst_time[i][j]))))) { ++ results->dispclk_required_for_blackout_recovery[i][j] = ++ bw_max( ++ results->dispclk_required_for_blackout_recovery[i][j], ++ bw_div( ++ mul( ++ bw_div( ++ bw_div( ++ (sub( ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ (add( ++ vbios->blackout_duration, ++ vbios->maximum_blackout_recovery_time))), ++ results->adjusted_data_buffer_size[k])), ++ results->bytes_per_pixel[k]), ++ (sub( ++ sub( ++ vbios->maximum_blackout_recovery_time, ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2))), ++ results->dmif_burst_time[i][j]))), ++ results->latency_hiding_lines[k]), ++ results->lines_interleaved_in_mem_access[k])); ++ } ++ } else { ++ results->blackout_duration_margin[i][j] = ++ bw_min( ++ results->blackout_duration_margin[i][j], ++ sub( ++ sub( ++ sub( ++ sub( ++ results->minimum_latency_hiding_with_cursor[k], ++ vbios->blackout_duration), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]), ++ results->line_source_transfer_time[k][i][j])); ++ results->dispclk_required_for_blackout_duration[i][j] = ++ bw_max3( ++ results->dispclk_required_for_blackout_duration[i][j], ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_first_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (sub( ++ sub( ++ sub( ++ results->minimum_latency_hiding_with_cursor[k], ++ vbios->blackout_duration), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]))), ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_last_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (add( ++ sub( ++ sub( ++ sub( ++ results->minimum_latency_hiding_with_cursor[k], ++ vbios->blackout_duration), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]), ++ results->active_time[k])))); ++ if (ltn( ++ vbios->maximum_blackout_recovery_time, ++ add( ++ add( ++ mul( ++ vbios->mcifwrmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]))) { ++ results->dispclk_required_for_blackout_recovery[i][j] = ++ int_to_fixed( ++ 9999); ++ } else if (ltn( ++ results->adjusted_data_buffer_size[k], ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ (add( ++ add( ++ vbios->blackout_duration, ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2))), ++ results->dmif_burst_time[i][j]))))) { ++ results->dispclk_required_for_blackout_recovery[i][j] = ++ bw_max( ++ results->dispclk_required_for_blackout_recovery[i][j], ++ bw_div( ++ mul( ++ bw_div( ++ bw_div( ++ (sub( ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ (add( ++ vbios->blackout_duration, ++ vbios->maximum_blackout_recovery_time))), ++ results->adjusted_data_buffer_size[k])), ++ results->bytes_per_pixel[k]), ++ (sub( ++ vbios->maximum_blackout_recovery_time, ++ (add( ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->dmif_burst_time[i][j]))))), ++ results->latency_hiding_lines[k]), ++ results->lines_interleaved_in_mem_access[k])); ++ } ++ } ++ } ++ } ++ } ++ } ++ if (gtn(results->blackout_duration_margin[high][high], int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[high][high], ++ vbios->high_voltage_max_dispclk)) { ++ results->cpup_state_change_enable = true; ++ if (ltn( ++ results->dispclk_required_for_blackout_recovery[high][high], ++ vbios->high_voltage_max_dispclk)) { ++ results->cpuc_state_change_enable = true; ++ } else { ++ results->cpuc_state_change_enable = false; ++ } ++ } else { ++ results->cpup_state_change_enable = false; ++ results->cpuc_state_change_enable = false; ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (mode_data->number_of_displays <= 1 ++ || mode_data->display_synchronization_enabled ++ == true) { ++ results->maximum_latency_hiding[i] = ++ int_to_fixed(450); ++ } else { ++ results->maximum_latency_hiding[i] = ++ add( ++ add( ++ results->minimum_latency_hiding[i], ++ bw_div( ++ mul( ++ bw_div( ++ int_to_fixed( ++ 1), ++ results->vsr[i]), ++ results->h_total[i]), ++ results->pixel_rate[i])), ++ mul(frc_to_fixed(1, 2), ++ results->total_dmifmc_urgent_latency)); ++ } ++ results->maximum_latency_hiding_with_cursor[i] = bw_min( ++ results->maximum_latency_hiding[i], ++ results->cursor_latency_hiding[i]); ++ } ++ } ++ for (i = 0; i <= 2; i += 1) { ++ for (j = 0; j <= 2; j += 1) { ++ results->dram_speed_change_margin[i][j] = int_to_fixed( ++ 9999); ++ results->dispclk_required_for_dram_speed_change[i][j] = ++ int_to_fixed(0); ++ for (k = 0; k <= maximum_number_of_surfaces - 1; k += ++ 1) { ++ if (results->enable[k]) { ++ if (surface_type[k] ++ != def_display_write_back420_luma ++ && surface_type[k] ++ != def_display_write_back420_chroma) { ++ results->dram_speed_change_margin[i][j] = ++ bw_min( ++ results->dram_speed_change_margin[i][j], ++ sub( ++ sub( ++ sub( ++ results->maximum_latency_hiding_with_cursor[k], ++ vbios->nbp_state_change_latency), ++ results->dmif_burst_time[i][j]), ++ results->line_source_transfer_time[k][i][j])); ++ results->dispclk_required_for_dram_speed_change[i][j] = ++ bw_max3( ++ results->dispclk_required_for_dram_speed_change[i][j], ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_first_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (sub( ++ sub( ++ results->maximum_latency_hiding_with_cursor[k], ++ vbios->nbp_state_change_latency), ++ results->dmif_burst_time[i][j]))), ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_last_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (add( ++ sub( ++ sub( ++ results->maximum_latency_hiding_with_cursor[k], ++ vbios->nbp_state_change_latency), ++ results->dmif_burst_time[i][j]), ++ results->active_time[k])))); ++ } else { ++ results->dram_speed_change_margin[i][j] = ++ bw_min( ++ results->dram_speed_change_margin[i][j], ++ sub( ++ sub( ++ sub( ++ sub( ++ results->maximum_latency_hiding_with_cursor[k], ++ vbios->nbp_state_change_latency), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]), ++ results->line_source_transfer_time[k][i][j])); ++ results->dispclk_required_for_dram_speed_change[i][j] = ++ bw_max3( ++ results->dispclk_required_for_dram_speed_change[i][j], ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_first_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (sub( ++ sub( ++ sub( ++ results->maximum_latency_hiding_with_cursor[k], ++ vbios->nbp_state_change_latency), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]))), ++ bw_div( ++ bw_div( ++ mul( ++ results->src_pixels_for_last_output_pixel[k], ++ dceip->display_pipe_throughput_factor), ++ dceip->lb_write_pixels_per_dispclk), ++ (add( ++ sub( ++ sub( ++ sub( ++ results->maximum_latency_hiding_with_cursor[k], ++ vbios->nbp_state_change_latency), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[i][j]), ++ results->active_time[k])))); ++ } ++ } ++ } ++ } ++ } ++ if (gtn(results->dram_speed_change_margin[high][high], int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_dram_speed_change[high][high], ++ vbios->high_voltage_max_dispclk)) { ++ results->nbp_state_change_enable = true; ++ } else { ++ results->nbp_state_change_enable = false; ++ } ++ results->min_cursor_memory_interface_buffer_size_in_time = int_to_fixed( ++ 9999); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (gtn(results->cursor_width_pixels[i], ++ int_to_fixed(0))) { ++ results->min_cursor_memory_interface_buffer_size_in_time = ++ bw_min( ++ results->min_cursor_memory_interface_buffer_size_in_time, ++ bw_div( ++ mul( ++ bw_div( ++ bw_div( ++ dceip->cursor_memory_interface_buffer_pixels, ++ results->cursor_width_pixels[i]), ++ results->vsr[i]), ++ results->h_total[i]), ++ results->pixel_rate[i])); ++ } ++ } ++ } ++ results->min_read_buffer_size_in_time = bw_min( ++ results->min_cursor_memory_interface_buffer_size_in_time, ++ results->min_dmif_size_in_time); ++ results->display_reads_time_for_data_transfer = sub( ++ results->min_read_buffer_size_in_time, ++ results->total_dmifmc_urgent_latency); ++ results->display_writes_time_for_data_transfer = sub( ++ results->min_mcifwr_size_in_time, ++ vbios->mcifwrmc_urgent_latency); ++ results->dmif_required_dram_bandwidth = bw_div( ++ results->total_display_reads_required_dram_access_data, ++ results->display_reads_time_for_data_transfer); ++ results->mcifwr_required_dram_bandwidth = bw_div( ++ results->total_display_writes_required_dram_access_data, ++ results->display_writes_time_for_data_transfer); ++ results->required_dmifmc_urgent_latency_for_page_close_open = bw_div( ++ (sub(results->min_read_buffer_size_in_time, ++ results->dmif_total_page_close_open_time)), ++ results->total_dmifmc_urgent_trips); ++ results->required_mcifmcwr_urgent_latency = sub( ++ results->min_mcifwr_size_in_time, ++ results->mcifwr_total_page_close_open_time); ++ if (gtn(results->scatter_gather_total_pte_requests, ++ dceip->maximum_total_outstanding_pte_requests_allowed_by_saw)) { ++ results->required_dram_bandwidth_gbyte_per_second = ++ int_to_fixed(9999); ++ yclk_message = ++ def_exceeded_allowed_outstanding_pte_req_queue_size; ++ y_clk_level = high; ++ results->dram_bandwidth = mul( ++ bw_div( ++ mul(vbios->high_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels); ++ } else if (gtn(vbios->dmifmc_urgent_latency, ++ results->required_dmifmc_urgent_latency_for_page_close_open) ++ || gtn(vbios->mcifwrmc_urgent_latency, ++ results->required_mcifmcwr_urgent_latency)) { ++ results->required_dram_bandwidth_gbyte_per_second = ++ int_to_fixed(9999); ++ yclk_message = def_exceeded_allowed_page_close_open; ++ y_clk_level = high; ++ results->dram_bandwidth = mul( ++ bw_div( ++ mul(vbios->high_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels); ++ } else { ++ results->required_dram_bandwidth_gbyte_per_second = bw_div( ++ bw_max(results->dmif_required_dram_bandwidth, ++ results->mcifwr_required_dram_bandwidth), ++ int_to_fixed(1000)); ++ if (ltn( ++ mul(results->required_dram_bandwidth_gbyte_per_second, ++ int_to_fixed(1000)), ++ mul( ++ bw_div( ++ mul(vbios->low_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels)) ++ && (results->cpup_state_change_enable == false ++ || (gtn( ++ results->blackout_duration_margin[low][high], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[low][high], ++ vbios->high_voltage_max_dispclk))) ++ && (results->cpuc_state_change_enable == false ++ || (gtn( ++ results->blackout_duration_margin[low][high], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[low][high], ++ vbios->high_voltage_max_dispclk) ++ && ltn( ++ results->dispclk_required_for_blackout_recovery[low][high], ++ vbios->high_voltage_max_dispclk))) ++ && gtn(results->dram_speed_change_margin[low][high], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_dram_speed_change[low][high], ++ vbios->high_voltage_max_dispclk)) { ++ yclk_message = def_low; ++ y_clk_level = low; ++ results->dram_bandwidth = ++ mul( ++ bw_div( ++ mul(vbios->low_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels); ++ } else if (ltn( ++ mul(results->required_dram_bandwidth_gbyte_per_second, ++ int_to_fixed(1000)), ++ mul( ++ bw_div( ++ mul(vbios->high_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels))) { ++ yclk_message = def_high; ++ y_clk_level = high; ++ results->dram_bandwidth = ++ mul( ++ bw_div( ++ mul(vbios->high_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels); ++ } else { ++ yclk_message = def_exceeded_allowed_maximum_bw; ++ y_clk_level = high; ++ results->dram_bandwidth = ++ mul( ++ bw_div( ++ mul(vbios->high_yclk, ++ vbios->dram_channel_width_in_bits), ++ int_to_fixed(8)), ++ vbios->number_of_dram_channels); ++ } ++ } ++ results->dmif_required_sclk = bw_div( ++ bw_div(results->total_display_reads_required_data, ++ results->display_reads_time_for_data_transfer), ++ vbios->data_return_bus_width); ++ results->mcifwr_required_sclk = bw_div( ++ bw_div(results->total_display_writes_required_data, ++ results->display_writes_time_for_data_transfer), ++ vbios->data_return_bus_width); ++ if (gtn(results->scatter_gather_total_pte_requests, ++ dceip->maximum_total_outstanding_pte_requests_allowed_by_saw)) { ++ results->required_sclk = int_to_fixed(9999); ++ sclk_message = ++ def_exceeded_allowed_outstanding_pte_req_queue_size; ++ sclk_level = high; ++ } else if (gtn(vbios->dmifmc_urgent_latency, ++ results->required_dmifmc_urgent_latency_for_page_close_open) ++ || gtn(vbios->mcifwrmc_urgent_latency, ++ results->required_mcifmcwr_urgent_latency)) { ++ results->required_sclk = int_to_fixed(9999); ++ sclk_message = def_exceeded_allowed_page_close_open; ++ sclk_level = high; ++ } else { ++ results->required_sclk = bw_max(results->dmif_required_sclk, ++ results->mcifwr_required_sclk); ++ if (ltn(results->required_sclk, vbios->low_sclk) ++ && (results->cpup_state_change_enable == false ++ || (gtn( ++ results->blackout_duration_margin[y_clk_level][low], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[y_clk_level][low], ++ vbios->high_voltage_max_dispclk))) ++ && (results->cpuc_state_change_enable == false ++ || (gtn( ++ results->blackout_duration_margin[y_clk_level][low], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[y_clk_level][low], ++ vbios->high_voltage_max_dispclk) ++ && ltn( ++ results->dispclk_required_for_blackout_recovery[y_clk_level][low], ++ vbios->high_voltage_max_dispclk))) ++ && (results->nbp_state_change_enable == false ++ || (gtn( ++ results->dram_speed_change_margin[y_clk_level][low], ++ int_to_fixed(0)) ++ && leq( ++ results->dispclk_required_for_dram_speed_change[y_clk_level][low], ++ vbios->high_voltage_max_dispclk)))) { ++ sclk_message = def_low; ++ sclk_level = low; ++ } else if (ltn(results->required_sclk, vbios->mid_sclk) ++ && (results->cpup_state_change_enable == false ++ || (gtn( ++ results->blackout_duration_margin[y_clk_level][mid], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[y_clk_level][mid], ++ vbios->high_voltage_max_dispclk))) ++ && (results->cpuc_state_change_enable == false ++ || (gtn( ++ results->blackout_duration_margin[y_clk_level][mid], ++ int_to_fixed(0)) ++ && ltn( ++ results->dispclk_required_for_blackout_duration[y_clk_level][mid], ++ vbios->high_voltage_max_dispclk) ++ && ltn( ++ results->dispclk_required_for_blackout_recovery[y_clk_level][mid], ++ vbios->high_voltage_max_dispclk))) ++ && (results->nbp_state_change_enable == false ++ || (gtn( ++ results->dram_speed_change_margin[y_clk_level][mid], ++ int_to_fixed(0)) ++ && leq( ++ results->dispclk_required_for_dram_speed_change[y_clk_level][mid], ++ vbios->high_voltage_max_dispclk)))) { ++ sclk_message = def_mid; ++ sclk_level = mid; ++ } else if (ltn(results->required_sclk, vbios->high_sclk)) { ++ sclk_message = def_high; ++ sclk_level = high; ++ } else { ++ sclk_message = def_exceeded_allowed_maximum_sclk; ++ sclk_level = high; ++ } ++ } ++ results->downspread_factor = add( ++ bw_div(vbios->down_spread_percentage, int_to_fixed(100)), ++ int_to_fixed(1)); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] == def_graphics) { ++ if (equ(results->lb_bpc[i], int_to_fixed(6))) { ++ results->v_scaler_efficiency = ++ dceip->graphics_vscaler_efficiency6_bit_per_component; ++ } else if (equ(results->lb_bpc[i], ++ int_to_fixed(8))) { ++ results->v_scaler_efficiency = ++ dceip->graphics_vscaler_efficiency8_bit_per_component; ++ } else if (equ(results->lb_bpc[i], ++ int_to_fixed(10))) { ++ results->v_scaler_efficiency = ++ dceip->graphics_vscaler_efficiency10_bit_per_component; ++ } else { ++ results->v_scaler_efficiency = ++ dceip->graphics_vscaler_efficiency12_bit_per_component; ++ } ++ if (results->use_alpha[i] == true) { ++ results->v_scaler_efficiency = ++ bw_min( ++ results->v_scaler_efficiency, ++ dceip->alpha_vscaler_efficiency); ++ } ++ } else { ++ if (equ(results->lb_bpc[i], int_to_fixed(6))) { ++ results->v_scaler_efficiency = ++ dceip->underlay_vscaler_efficiency6_bit_per_component; ++ } else if (equ(results->lb_bpc[i], ++ int_to_fixed(8))) { ++ results->v_scaler_efficiency = ++ dceip->underlay_vscaler_efficiency8_bit_per_component; ++ } else if (equ(results->lb_bpc[i], ++ int_to_fixed(10))) { ++ results->v_scaler_efficiency = ++ dceip->underlay_vscaler_efficiency10_bit_per_component; ++ } else { ++ results->v_scaler_efficiency = ++ dceip->underlay_vscaler_efficiency12_bit_per_component; ++ } ++ } ++ if (dceip->pre_downscaler_enabled ++ && gtn(results->hsr[i], int_to_fixed(1))) { ++ results->scaler_limits_factor = ++ bw_max( ++ bw_div(results->v_taps[i], ++ results->v_scaler_efficiency), ++ bw_div( ++ results->source_width_rounded_up_to_chunks[i], ++ results->h_total[i])); ++ } else { ++ results->scaler_limits_factor = ++ bw_max3(int_to_fixed(1), ++ bw_ceil( ++ bw_div(results->h_taps[i], ++ int_to_fixed( ++ 4)), ++ int_to_fixed(1)), ++ mul(results->hsr[i], ++ bw_max( ++ bw_div( ++ results->v_taps[i], ++ results->v_scaler_efficiency), ++ int_to_fixed( ++ 1)))); ++ } ++ results->display_pipe_pixel_throughput = ++ bw_div( ++ bw_div( ++ mul( ++ bw_max( ++ results->lb_lines_in_per_line_out_in_beginning_of_frame[i], ++ mul( ++ results->lb_lines_in_per_line_out_in_middle_of_frame[i], ++ results->horizontal_blank_and_chunk_granularity_factor[i])), ++ results->source_width_rounded_up_to_chunks[i]), ++ (bw_div(results->h_total[i], ++ results->pixel_rate[i]))), ++ dceip->lb_write_pixels_per_dispclk); ++ results->dispclk_required_without_ramping[i] = ++ mul(results->downspread_factor, ++ bw_max( ++ mul(results->pixel_rate[i], ++ results->scaler_limits_factor), ++ mul( ++ dceip->display_pipe_throughput_factor, ++ results->display_pipe_pixel_throughput))); ++ results->dispclk_required_with_ramping[i] = ++ mul(dceip->dispclk_ramping_factor, ++ bw_max( ++ mul(results->pixel_rate[i], ++ results->scaler_limits_factor), ++ results->display_pipe_pixel_throughput)); ++ } ++ } ++ results->total_dispclk_required_with_ramping = int_to_fixed(0); ++ results->total_dispclk_required_without_ramping = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (ltn(results->total_dispclk_required_with_ramping, ++ results->dispclk_required_with_ramping[i])) { ++ results->total_dispclk_required_with_ramping = ++ results->dispclk_required_with_ramping[i]; ++ } ++ if (ltn(results->total_dispclk_required_without_ramping, ++ results->dispclk_required_without_ramping[i])) { ++ results->total_dispclk_required_without_ramping = ++ results->dispclk_required_without_ramping[i]; ++ } ++ } ++ } ++ results->total_read_request_bandwidth = int_to_fixed(0); ++ results->total_write_request_bandwidth = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->total_read_request_bandwidth = add( ++ results->total_read_request_bandwidth, ++ results->request_bandwidth[i]); ++ } else { ++ results->total_write_request_bandwidth = add( ++ results->total_write_request_bandwidth, ++ results->request_bandwidth[i]); ++ } ++ } ++ } ++ results->dispclk_required_for_total_read_request_bandwidth = bw_div( ++ mul(results->total_read_request_bandwidth, ++ dceip->dispclk_per_request), dceip->request_efficiency); ++ if (equ(dceip->dcfclk_request_generation, int_to_fixed(0))) { ++ results->total_dispclk_required_with_ramping_with_request_bandwidth = ++ bw_max(results->total_dispclk_required_with_ramping, ++ results->dispclk_required_for_total_read_request_bandwidth); ++ results->total_dispclk_required_without_ramping_with_request_bandwidth = ++ bw_max(results->total_dispclk_required_without_ramping, ++ results->dispclk_required_for_total_read_request_bandwidth); ++ } else { ++ results->total_dispclk_required_with_ramping_with_request_bandwidth = ++ results->total_dispclk_required_with_ramping; ++ results->total_dispclk_required_without_ramping_with_request_bandwidth = ++ results->total_dispclk_required_without_ramping; ++ } ++ if (results->cpuc_state_change_enable == true) { ++ results->total_dispclk_required_with_ramping_with_request_bandwidth = ++ bw_max3( ++ results->total_dispclk_required_with_ramping_with_request_bandwidth, ++ results->dispclk_required_for_blackout_duration[y_clk_level][sclk_level], ++ results->dispclk_required_for_blackout_recovery[y_clk_level][sclk_level]); ++ results->total_dispclk_required_without_ramping_with_request_bandwidth = ++ bw_max3( ++ results->total_dispclk_required_without_ramping_with_request_bandwidth, ++ results->dispclk_required_for_blackout_duration[y_clk_level][sclk_level], ++ results->dispclk_required_for_blackout_recovery[y_clk_level][sclk_level]); ++ } ++ if (results->cpup_state_change_enable == true) { ++ results->total_dispclk_required_with_ramping_with_request_bandwidth = ++ bw_max( ++ results->total_dispclk_required_with_ramping_with_request_bandwidth, ++ results->dispclk_required_for_blackout_duration[y_clk_level][sclk_level]); ++ results->total_dispclk_required_without_ramping_with_request_bandwidth = ++ bw_max( ++ results->total_dispclk_required_without_ramping_with_request_bandwidth, ++ results->dispclk_required_for_blackout_duration[y_clk_level][sclk_level]); ++ } ++ if (results->nbp_state_change_enable == true) { ++ results->total_dispclk_required_with_ramping_with_request_bandwidth = ++ bw_max( ++ results->total_dispclk_required_with_ramping_with_request_bandwidth, ++ results->dispclk_required_for_dram_speed_change[y_clk_level][sclk_level]); ++ results->total_dispclk_required_without_ramping_with_request_bandwidth = ++ bw_max( ++ results->total_dispclk_required_without_ramping_with_request_bandwidth, ++ results->dispclk_required_for_dram_speed_change[y_clk_level][sclk_level]); ++ } ++ if (ltn( ++ results->total_dispclk_required_with_ramping_with_request_bandwidth, ++ vbios->high_voltage_max_dispclk)) { ++ results->dispclk = ++ results->total_dispclk_required_with_ramping_with_request_bandwidth; ++ } else if (ltn( ++ results->total_dispclk_required_without_ramping_with_request_bandwidth, ++ vbios->high_voltage_max_dispclk)) { ++ results->dispclk = vbios->high_voltage_max_dispclk; ++ } else { ++ results->dispclk = ++ results->total_dispclk_required_without_ramping_with_request_bandwidth; ++ } ++ if (pipe_check == def_notok) { ++ voltage = def_na; ++ mode_background_color = def_na_color; ++ mode_font_color = def_vb_white; ++ } else if (mode_check == def_notok) { ++ voltage = def_notok; ++ mode_background_color = def_notok_color; ++ mode_font_color = def_vb_black; ++ } else if (yclk_message == def_low && sclk_message == def_low ++ && ltn(results->dispclk, vbios->low_voltage_max_dispclk)) { ++ voltage = def_low; ++ mode_background_color = def_low_color; ++ mode_font_color = def_vb_black; ++ } else if (yclk_message == def_low ++ && (sclk_message == def_low || sclk_message == def_mid) ++ && ltn(results->dispclk, vbios->mid_voltage_max_dispclk)) { ++ voltage = def_mid; ++ mode_background_color = def_mid_color; ++ mode_font_color = def_vb_black; ++ } else if ((yclk_message == def_low || yclk_message == def_high) ++ && (sclk_message == def_low || sclk_message == def_mid ++ || sclk_message == def_high) ++ && leq(results->dispclk, vbios->high_voltage_max_dispclk)) { ++ if (results->nbp_state_change_enable == true) { ++ voltage = def_high; ++ mode_background_color = def_high_color; ++ mode_font_color = def_vb_black; ++ } else { ++ voltage = def_high_no_nbp_state_change; ++ mode_background_color = ++ def_high_no_nbp_state_change_color; ++ mode_font_color = def_vb_black; ++ } ++ } else { ++ voltage = def_notok; ++ mode_background_color = def_notok_color; ++ mode_font_color = def_vb_black; ++ } ++ if (mode_background_color == def_na_color ++ || mode_background_color == def_notok_color) { ++ mode_pattern = def_xl_pattern_solid; ++ } else if (results->cpup_state_change_enable == false) { ++ mode_pattern = def_xl_pattern_checker; ++ } else if (results->cpuc_state_change_enable == false) { ++ mode_pattern = def_xl_pattern_light_horizontal; ++ } else { ++ mode_pattern = def_xl_pattern_solid; ++ } ++ results->blackout_recovery_time = int_to_fixed(0); ++ for (k = 0; k <= maximum_number_of_surfaces - 1; k += 1) { ++ if (results->enable[k] ++ && gtn(vbios->blackout_duration, int_to_fixed(0)) ++ && results->cpup_state_change_enable == true) { ++ if (surface_type[k] != def_display_write_back420_luma ++ && surface_type[k] ++ != def_display_write_back420_chroma) { ++ results->blackout_recovery_time = ++ bw_max(results->blackout_recovery_time, ++ add( ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->dmif_burst_time[y_clk_level][sclk_level])); ++ if (ltn(results->adjusted_data_buffer_size[k], ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ (add( ++ add( ++ vbios->blackout_duration, ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2))), ++ results->dmif_burst_time[y_clk_level][sclk_level]))))) { ++ results->blackout_recovery_time = ++ bw_max( ++ results->blackout_recovery_time, ++ bw_div( ++ (sub( ++ add( ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ vbios->blackout_duration), ++ bw_div( ++ mul( ++ mul( ++ mul( ++ (add( ++ mul( ++ results->total_dmifmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->dmif_burst_time[y_clk_level][sclk_level])), ++ results->dispclk), ++ results->bytes_per_pixel[k]), ++ results->lines_interleaved_in_mem_access[k]), ++ results->latency_hiding_lines[k])), ++ results->adjusted_data_buffer_size[k])), ++ (sub( ++ bw_div( ++ mul( ++ mul( ++ results->dispclk, ++ results->bytes_per_pixel[k]), ++ results->lines_interleaved_in_mem_access[k]), ++ results->latency_hiding_lines[k]), ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]))))); ++ } ++ } else { ++ results->blackout_recovery_time = ++ bw_max(results->blackout_recovery_time, ++ add( ++ mul( ++ vbios->mcifwrmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->mcifwr_burst_time[y_clk_level][sclk_level])); ++ if (ltn(results->adjusted_data_buffer_size[k], ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ (add( ++ add( ++ vbios->blackout_duration, ++ mul( ++ vbios->mcifwrmc_urgent_latency, ++ int_to_fixed( ++ 2))), ++ results->mcifwr_burst_time[y_clk_level][sclk_level]))))) { ++ results->blackout_recovery_time = ++ bw_max( ++ results->blackout_recovery_time, ++ bw_div( ++ (sub( ++ add( ++ mul( ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]), ++ vbios->blackout_duration), ++ bw_div( ++ mul( ++ mul( ++ mul( ++ (add( ++ add( ++ mul( ++ vbios->mcifwrmc_urgent_latency, ++ int_to_fixed( ++ 2)), ++ results->dmif_burst_time[i][j]), ++ results->mcifwr_burst_time[y_clk_level][sclk_level])), ++ results->dispclk), ++ results->bytes_per_pixel[k]), ++ results->lines_interleaved_in_mem_access[k]), ++ results->latency_hiding_lines[k])), ++ results->adjusted_data_buffer_size[k])), ++ (sub( ++ bw_div( ++ mul( ++ mul( ++ results->dispclk, ++ results->bytes_per_pixel[k]), ++ results->lines_interleaved_in_mem_access[k]), ++ results->latency_hiding_lines[k]), ++ bw_div( ++ mul( ++ results->display_bandwidth[k], ++ results->useful_bytes_per_request[k]), ++ results->bytes_per_request[k]))))); ++ } ++ } ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (surface_type[i] == def_display_write_back420_luma ++ || surface_type[i] ++ == def_display_write_back420_chroma) { ++ results->pixels_per_data_fifo_entry[i] = ++ int_to_fixed(16); ++ } else if (surface_type[i] == def_graphics) { ++ results->pixels_per_data_fifo_entry[i] = bw_div( ++ int_to_fixed(64), ++ results->bytes_per_pixel[i]); ++ } else if (results->orthogonal_rotation[i] == false) { ++ results->pixels_per_data_fifo_entry[i] = ++ int_to_fixed(16); ++ } else { ++ results->pixels_per_data_fifo_entry[i] = bw_div( ++ int_to_fixed(16), ++ results->bytes_per_pixel[i]); ++ } ++ } ++ } ++ results->min_pixels_per_data_fifo_entry = int_to_fixed(9999); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (gtn(results->min_pixels_per_data_fifo_entry, ++ results->pixels_per_data_fifo_entry[i])) { ++ results->min_pixels_per_data_fifo_entry = ++ results->pixels_per_data_fifo_entry[i]; ++ } ++ } ++ } ++ results->sclk_deep_sleep = bw_max( ++ bw_div(mul(results->dispclk, frc_to_fixed(115, 100)), ++ results->min_pixels_per_data_fifo_entry), ++ results->total_read_request_bandwidth); ++ results->chunk_request_time = int_to_fixed(0); ++ results->cursor_request_time = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->chunk_request_time = ++ add(results->chunk_request_time, ++ bw_div( ++ bw_div( ++ mul(pixels_per_chunk, ++ results->bytes_per_pixel[i]), ++ results->useful_bytes_per_request[i]), ++ bw_min(sclk[sclk_level], ++ bw_div(results->dispclk, ++ int_to_fixed( ++ 2))))); ++ } ++ } ++ results->cursor_request_time = (bw_div(results->cursor_total_data, ++ (mul(sclk[sclk_level], int_to_fixed(32))))); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->line_source_pixels_transfer_time = ++ bw_max( ++ bw_div( ++ bw_div( ++ results->src_pixels_for_first_output_pixel[i], ++ dceip->lb_write_pixels_per_dispclk), ++ (bw_div(results->dispclk, ++ dceip->display_pipe_throughput_factor))), ++ sub( ++ bw_div( ++ bw_div( ++ results->src_pixels_for_last_output_pixel[i], ++ dceip->lb_write_pixels_per_dispclk), ++ (bw_div(results->dispclk, ++ dceip->display_pipe_throughput_factor))), ++ results->active_time[i])); ++ if (surface_type[i] != def_display_write_back420_luma ++ && surface_type[i] ++ != def_display_write_back420_chroma) { ++ results->urgent_watermark[i] = ++ add( ++ add( ++ add( ++ add( ++ add( ++ results->total_dmifmc_urgent_latency, ++ results->dmif_burst_time[y_clk_level][sclk_level]), ++ bw_max( ++ results->line_source_pixels_transfer_time, ++ results->line_source_transfer_time[i][y_clk_level][sclk_level])), ++ vbios->blackout_duration), ++ results->chunk_request_time), ++ results->cursor_request_time); ++ results->stutter_exit_watermark[i] = ++ add( ++ sub( ++ vbios->stutter_self_refresh_exit_latency, ++ results->total_dmifmc_urgent_latency), ++ results->urgent_watermark[i]); ++ results->nbp_state_change_watermark[i] = ++ sub( ++ add( ++ sub( ++ vbios->nbp_state_change_latency, ++ results->total_dmifmc_urgent_latency), ++ results->urgent_watermark[i]), ++ vbios->blackout_duration); ++ } else { ++ results->urgent_watermark[i] = ++ add( ++ add( ++ add( ++ add( ++ add( ++ vbios->mcifwrmc_urgent_latency, ++ results->mcifwr_burst_time[y_clk_level][sclk_level]), ++ bw_max( ++ results->line_source_pixels_transfer_time, ++ results->line_source_transfer_time[i][y_clk_level][sclk_level])), ++ vbios->blackout_duration), ++ results->chunk_request_time), ++ results->cursor_request_time); ++ results->stutter_exit_watermark[i] = ++ int_to_fixed(0); ++ results->nbp_state_change_watermark[i] = ++ add( ++ sub( ++ add( ++ vbios->nbp_state_change_latency, ++ results->dmif_burst_time[y_clk_level][sclk_level]), ++ vbios->mcifwrmc_urgent_latency), ++ results->urgent_watermark[i]); ++ } ++ } ++ } ++ results->stutter_mode_enable = results->cpuc_state_change_enable; ++ if (mode_data->number_of_displays > 1) { ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (gtn(results->stutter_exit_watermark[i], ++ results->cursor_latency_hiding[i])) { ++ results->stutter_mode_enable = false; ++ } ++ } ++ } ++ } ++ results->dmifdram_access_efficiency = ++ bw_min( ++ bw_div( ++ bw_div( ++ results->total_display_reads_required_dram_access_data, ++ results->dram_bandwidth), ++ results->dmif_total_page_close_open_time), ++ int_to_fixed(1)); ++ if (gtn(results->total_display_writes_required_dram_access_data, ++ int_to_fixed(0))) { ++ results->mcifwrdram_access_efficiency = ++ bw_min( ++ bw_div( ++ bw_div( ++ results->total_display_writes_required_dram_access_data, ++ results->dram_bandwidth), ++ results->mcifwr_total_page_close_open_time), ++ int_to_fixed(1)); ++ } else { ++ results->mcifwrdram_access_efficiency = int_to_fixed(0); ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->average_bandwidth_no_compression[i] = ++ bw_div( ++ mul( ++ mul( ++ bw_div( ++ mul( ++ results->source_width_rounded_up_to_chunks[i], ++ results->bytes_per_pixel[i]), ++ (bw_div( ++ results->h_total[i], ++ results->pixel_rate[i]))), ++ results->vsr[i]), ++ results->bytes_per_request[i]), ++ results->useful_bytes_per_request[i]); ++ results->average_bandwidth[i] = bw_div( ++ results->average_bandwidth_no_compression[i], ++ results->compression_rate[i]); ++ } ++ } ++ results->total_average_bandwidth_no_compression = int_to_fixed(0); ++ results->total_average_bandwidth = int_to_fixed(0); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->total_average_bandwidth_no_compression = add( ++ results->total_average_bandwidth_no_compression, ++ results->average_bandwidth_no_compression[i]); ++ results->total_average_bandwidth = add( ++ results->total_average_bandwidth, ++ results->average_bandwidth[i]); ++ } ++ } ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->stutter_cycle_duration[i] = ++ sub( ++ mul( ++ bw_div( ++ bw_div( ++ mul( ++ bw_div( ++ bw_div( ++ results->adjusted_data_buffer_size[i], ++ results->bytes_per_pixel[i]), ++ results->source_width_rounded_up_to_chunks[i]), ++ results->h_total[i]), ++ results->vsr[i]), ++ results->pixel_rate[i]), ++ results->compression_rate[i]), ++ bw_max(int_to_fixed(0), ++ sub( ++ results->stutter_exit_watermark[i], ++ bw_div( ++ mul( ++ (add( ++ results->line_buffer_prefetch[i], ++ int_to_fixed( ++ 2))), ++ results->h_total[i]), ++ results->pixel_rate[i])))); ++ } ++ } ++ results->total_stutter_cycle_duration = int_to_fixed(9999); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ if (gtn(results->total_stutter_cycle_duration, ++ results->stutter_cycle_duration[i])) { ++ results->total_stutter_cycle_duration = ++ results->stutter_cycle_duration[i]; ++ } ++ } ++ } ++ results->stutter_burst_time = bw_div( ++ mul(results->total_stutter_cycle_duration, ++ results->total_average_bandwidth), ++ bw_min( ++ (mul(results->dram_bandwidth, ++ results->dmifdram_access_efficiency)), ++ mul(sclk[sclk_level], vbios->data_return_bus_width))); ++ results->time_in_self_refresh = sub( ++ sub(results->total_stutter_cycle_duration, ++ vbios->stutter_self_refresh_exit_latency), ++ results->stutter_burst_time); ++ if (mode_data->d1_display_write_back_dwb_enable == true) { ++ results->stutter_efficiency = int_to_fixed(0); ++ } else if (ltn(results->time_in_self_refresh, int_to_fixed(0))) { ++ results->stutter_efficiency = int_to_fixed(0); ++ } else { ++ results->stutter_efficiency = mul( ++ bw_div(results->time_in_self_refresh, ++ results->total_stutter_cycle_duration), ++ int_to_fixed(100)); ++ } ++ results->worst_number_of_trips_to_memory = int_to_fixed(1); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i] ++ && results->scatter_gather_enable_for_pipe[i] == true) { ++ results->number_of_trips_to_memory_for_getting_apte_row[i] = ++ bw_ceil( ++ bw_div( ++ results->scatter_gather_pte_requests_in_row[i], ++ results->scatter_gather_pte_request_limit[i]), ++ int_to_fixed(1)); ++ if (ltn(results->worst_number_of_trips_to_memory, ++ results->number_of_trips_to_memory_for_getting_apte_row[i])) { ++ results->worst_number_of_trips_to_memory = ++ results->number_of_trips_to_memory_for_getting_apte_row[i]; ++ } ++ } ++ } ++ results->immediate_flip_time = mul( ++ results->worst_number_of_trips_to_memory, ++ results->total_dmifmc_urgent_latency); ++ results->latency_for_non_dmif_clients = add( ++ results->total_dmifmc_urgent_latency, ++ results->dmif_burst_time[y_clk_level][sclk_level]); ++ if (mode_data->d1_display_write_back_dwb_enable == true) { ++ results->latency_for_non_mcifwr_clients = add( ++ vbios->mcifwrmc_urgent_latency, ++ dceip->mcifwr_all_surfaces_burst_time); ++ } else { ++ results->latency_for_non_mcifwr_clients = int_to_fixed(0); ++ } ++ results->dmifmc_urgent_latency_supported_in_high_sclk_and_yclk = bw_div( ++ (sub(results->min_read_buffer_size_in_time, ++ results->dmif_burst_time[high][high])), ++ results->total_dmifmc_urgent_trips); ++ results->nbp_state_dram_speed_change_margin = int_to_fixed(9999); ++ for (i = 0; i <= maximum_number_of_surfaces - 1; i += 1) { ++ if (results->enable[i]) { ++ results->nbp_state_dram_speed_change_margin = ++ bw_min( ++ results->nbp_state_dram_speed_change_margin, ++ sub( ++ results->maximum_latency_hiding_with_cursor[i], ++ results->nbp_state_change_watermark[i])); ++ } ++ } ++ for (i = 1; i <= 5; i += 1) { ++ results->display_reads_time_for_data_transfer_and_urgent_latency = ++ sub(results->min_read_buffer_size_in_time, ++ mul(results->total_dmifmc_urgent_trips, ++ int_to_fixed(i))); ++ if (pipe_check == def_ok ++ && (gtn( ++ results->display_reads_time_for_data_transfer_and_urgent_latency, ++ results->dmif_total_page_close_open_time))) { ++ results->dmif_required_sclk_for_urgent_latency[i] = ++ bw_div( ++ bw_div( ++ results->total_display_reads_required_data, ++ results->display_reads_time_for_data_transfer_and_urgent_latency), ++ vbios->data_return_bus_width); ++ } else { ++ results->dmif_required_sclk_for_urgent_latency[i] = ++ int_to_fixed(0); ++ } ++ } ++ ++} ++ ++/******************************************************************************* ++ * Public functions ++ ******************************************************************************/ ++ ++void bw_calcs_init(struct bw_calcs_input_dceip *bw_dceip, ++ struct bw_calcs_input_vbios *bw_vbios) ++{ ++ struct bw_calcs_input_dceip dceip; ++ struct bw_calcs_input_vbios vbios; ++ ++ vbios.number_of_dram_channels = int_to_fixed(2); ++ vbios.dram_channel_width_in_bits = int_to_fixed(64); ++ vbios.number_of_dram_banks = int_to_fixed(8); ++ vbios.high_yclk = int_to_fixed(1600); ++ vbios.low_yclk = frc_to_fixed(66666, 100); ++ vbios.low_sclk = int_to_fixed(200); ++ vbios.mid_sclk = int_to_fixed(300); ++ vbios.high_sclk = frc_to_fixed(62609, 100); ++ vbios.low_voltage_max_dispclk = int_to_fixed(352); ++ vbios.mid_voltage_max_dispclk = int_to_fixed(467); ++ vbios.high_voltage_max_dispclk = int_to_fixed(643); ++ vbios.data_return_bus_width = int_to_fixed(32); ++ vbios.trc = int_to_fixed(50); ++ vbios.dmifmc_urgent_latency = int_to_fixed(4); ++ vbios.stutter_self_refresh_exit_latency = frc_to_fixed(153, 10); ++ vbios.nbp_state_change_latency = frc_to_fixed(19649, 1000); ++ vbios.mcifwrmc_urgent_latency = int_to_fixed(10); ++ vbios.scatter_gather_enable = true; ++ vbios.down_spread_percentage = frc_to_fixed(5, 10); ++ vbios.cursor_width = int_to_fixed(32); ++ vbios.average_compression_rate = int_to_fixed(4); ++ vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = ++ int_to_fixed(256); ++ vbios.blackout_duration = int_to_fixed(18); /* us */ ++ vbios.maximum_blackout_recovery_time = int_to_fixed(20); ++ dceip.dmif_request_buffer_size = int_to_fixed(768); ++ dceip.de_tiling_buffer = int_to_fixed(0); ++ dceip.dcfclk_request_generation = int_to_fixed(0); ++ dceip.lines_interleaved_into_lb = int_to_fixed(2); ++ dceip.chunk_width = int_to_fixed(256); ++ dceip.number_of_graphics_pipes = int_to_fixed(3); ++ dceip.number_of_underlay_pipes = int_to_fixed(1); ++ dceip.display_write_back_supported = false; ++ dceip.argb_compression_support = false; ++ dceip.underlay_vscaler_efficiency6_bit_per_component = frc_to_fixed( ++ 35556, 10000); ++ dceip.underlay_vscaler_efficiency8_bit_per_component = frc_to_fixed( ++ 34286, 10000); ++ dceip.underlay_vscaler_efficiency10_bit_per_component = frc_to_fixed(32, ++ 10); ++ dceip.underlay_vscaler_efficiency12_bit_per_component = int_to_fixed(3); ++ dceip.graphics_vscaler_efficiency6_bit_per_component = frc_to_fixed(35, ++ 10); ++ dceip.graphics_vscaler_efficiency8_bit_per_component = frc_to_fixed( ++ 34286, 10000); ++ dceip.graphics_vscaler_efficiency10_bit_per_component = frc_to_fixed(32, ++ 10); ++ dceip.graphics_vscaler_efficiency12_bit_per_component = int_to_fixed(3); ++ dceip.alpha_vscaler_efficiency = int_to_fixed(3); ++ dceip.max_dmif_buffer_allocated = int_to_fixed(2); ++ dceip.graphics_dmif_size = int_to_fixed(12288); ++ dceip.underlay_luma_dmif_size = int_to_fixed(19456); ++ dceip.underlay_chroma_dmif_size = int_to_fixed(23552); ++ dceip.pre_downscaler_enabled = true; ++ dceip.underlay_downscale_prefetch_enabled = true; ++ dceip.lb_write_pixels_per_dispclk = int_to_fixed(1); ++ dceip.lb_size_per_component444 = int_to_fixed(82176); ++ dceip.graphics_lb_nodownscaling_multi_line_prefetching = false; ++ dceip.stutter_and_dram_clock_state_change_gated_before_cursor = ++ int_to_fixed(0); ++ dceip.underlay420_luma_lb_size_per_component = int_to_fixed(82176); ++ dceip.underlay420_chroma_lb_size_per_component = int_to_fixed(164352); ++ dceip.underlay422_lb_size_per_component = int_to_fixed(82176); ++ dceip.cursor_chunk_width = int_to_fixed(64); ++ dceip.cursor_dcp_buffer_lines = int_to_fixed(4); ++ dceip.cursor_memory_interface_buffer_pixels = int_to_fixed(64); ++ dceip.underlay_maximum_width_efficient_for_tiling = int_to_fixed(1920); ++ dceip.underlay_maximum_height_efficient_for_tiling = int_to_fixed(1080); ++ dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = ++ frc_to_fixed(3, 10); ++ dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = ++ int_to_fixed(25); ++ dceip.minimum_outstanding_pte_request_limit = int_to_fixed(2); ++ dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = ++ int_to_fixed(128); ++ dceip.limit_excessive_outstanding_dmif_requests = true; ++ dceip.linear_mode_line_request_alternation_slice = int_to_fixed(64); ++ dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = ++ int_to_fixed(32); ++ dceip.display_write_back420_luma_mcifwr_buffer_size = int_to_fixed( ++ 12288); ++ dceip.display_write_back420_chroma_mcifwr_buffer_size = int_to_fixed( ++ 8192); ++ dceip.request_efficiency = frc_to_fixed(8, 10); ++ dceip.dispclk_per_request = int_to_fixed(2); ++ dceip.dispclk_ramping_factor = frc_to_fixed(11, 10); ++ dceip.display_pipe_throughput_factor = frc_to_fixed(105, 100); ++ dceip.scatter_gather_pte_request_rows_in_tiling_mode = int_to_fixed(2); ++ dceip.mcifwr_all_surfaces_burst_time = int_to_fixed(0); /* todo: this is a bug*/ ++ ++ *bw_dceip = dceip; ++ *bw_vbios = vbios; ++} ++ ++/** ++ * Return: ++ * true - Display(s) configuration supported. ++ * In this case 'calcs_output' contains data for HW programming ++ * false - Display(s) configuration not supported (not enough bandwidth). ++ */ ++ ++bool bw_calcs(struct dc_context *ctx, const struct bw_calcs_input_dceip *dceip, ++ const struct bw_calcs_input_vbios *vbios, ++ const struct bw_calcs_input_mode_data *mode_data, ++ struct bw_calcs_output *calcs_output) ++{ ++ struct bw_results_internal *bw_results_internal = dc_service_alloc( ++ ctx, sizeof(struct bw_results_internal)); ++ struct bw_calcs_input_mode_data_internal *bw_data_internal = ++ dc_service_alloc( ++ ctx, sizeof(struct bw_calcs_input_mode_data_internal)); ++ switch (mode_data->number_of_displays) { ++ case (3): ++ bw_data_internal->d2_htotal = int_to_fixed( ++ mode_data->displays_data[2].h_total); ++ bw_data_internal->d2_pixel_rate = ++ mode_data->displays_data[2].pixel_rate; ++ bw_data_internal->d2_graphics_src_width = int_to_fixed( ++ mode_data->displays_data[2].graphics_src_width); ++ bw_data_internal->d2_graphics_src_height = int_to_fixed( ++ mode_data->displays_data[2].graphics_src_height); ++ bw_data_internal->d2_graphics_scale_ratio = ++ mode_data->displays_data[2].graphics_scale_ratio; ++ bw_data_internal->d2_graphics_stereo_mode = ++ mode_data->displays_data[2].graphics_stereo_mode; ++ case (2): ++ bw_data_internal->d1_display_write_back_dwb_enable = false; ++ bw_data_internal->d1_underlay_mode = ul_none; ++ bw_data_internal->d1_underlay_scale_ratio = int_to_fixed(0); ++ bw_data_internal->d1_htotal = int_to_fixed( ++ mode_data->displays_data[1].h_total); ++ bw_data_internal->d1_pixel_rate = ++ mode_data->displays_data[1].pixel_rate; ++ bw_data_internal->d1_graphics_src_width = int_to_fixed( ++ mode_data->displays_data[1].graphics_src_width); ++ bw_data_internal->d1_graphics_src_height = int_to_fixed( ++ mode_data->displays_data[1].graphics_src_height); ++ bw_data_internal->d1_graphics_scale_ratio = ++ mode_data->displays_data[1].graphics_scale_ratio; ++ bw_data_internal->d1_graphics_stereo_mode = ++ mode_data->displays_data[1].graphics_stereo_mode; ++ ++ case (1): ++ bw_data_internal->d0_fbc_enable = ++ mode_data->displays_data[0].fbc_enable; ++ bw_data_internal->d0_lpt_enable = ++ mode_data->displays_data[0].lpt_enable; ++ bw_data_internal->d0_underlay_mode = ++ mode_data->displays_data[0].underlay_mode; ++ bw_data_internal->d0_underlay_scale_ratio = int_to_fixed(0); ++ bw_data_internal->d0_htotal = int_to_fixed( ++ mode_data->displays_data[0].h_total); ++ bw_data_internal->d0_pixel_rate = ++ mode_data->displays_data[0].pixel_rate; ++ bw_data_internal->d0_graphics_src_width = int_to_fixed( ++ mode_data->displays_data[0].graphics_src_width); ++ bw_data_internal->d0_graphics_src_height = int_to_fixed( ++ mode_data->displays_data[0].graphics_src_height); ++ bw_data_internal->d0_graphics_scale_ratio = ++ mode_data->displays_data[0].graphics_scale_ratio; ++ bw_data_internal->d0_graphics_stereo_mode = ++ mode_data->displays_data[0].graphics_stereo_mode; ++ ++ default: ++ /* data for all displays */ ++ bw_data_internal->number_of_displays = ++ mode_data->number_of_displays; ++ bw_data_internal->graphics_rotation_angle = int_to_fixed( ++ mode_data->displays_data[0].graphics_rotation_angle); ++ bw_data_internal->underlay_rotation_angle = int_to_fixed( ++ mode_data->displays_data[0].underlay_rotation_angle); ++ bw_data_internal->underlay_surface_type = ++ mode_data->displays_data[0].underlay_surface_type; ++ bw_data_internal->panning_and_bezel_adjustment = ++ mode_data->displays_data[0].panning_and_bezel_adjustment; ++ bw_data_internal->graphics_tiling_mode = ++ mode_data->displays_data[0].graphics_tiling_mode; ++ bw_data_internal->graphics_interlace_mode = ++ mode_data->displays_data[0].graphics_interlace_mode; ++ bw_data_internal->graphics_bytes_per_pixel = int_to_fixed( ++ mode_data->displays_data[0].graphics_bytes_per_pixel); ++ bw_data_internal->graphics_htaps = int_to_fixed( ++ mode_data->displays_data[0].graphics_h_taps); ++ bw_data_internal->graphics_vtaps = int_to_fixed( ++ mode_data->displays_data[0].graphics_v_taps); ++ bw_data_internal->graphics_lb_bpc = int_to_fixed( ++ mode_data->displays_data[0].graphics_lb_bpc); ++ bw_data_internal->underlay_lb_bpc = int_to_fixed( ++ mode_data->displays_data[0].underlay_lb_bpc); ++ bw_data_internal->underlay_tiling_mode = ++ mode_data->displays_data[0].underlay_tiling_mode; ++ bw_data_internal->underlay_htaps = int_to_fixed( ++ mode_data->displays_data[0].underlay_h_taps); ++ bw_data_internal->underlay_vtaps = int_to_fixed( ++ mode_data->displays_data[0].underlay_v_taps); ++ bw_data_internal->underlay_src_width = int_to_fixed( ++ mode_data->displays_data[0].underlay_src_width); ++ bw_data_internal->underlay_src_height = int_to_fixed( ++ mode_data->displays_data[0].underlay_src_height); ++ bw_data_internal->underlay_pitch_in_pixels = int_to_fixed( ++ mode_data->displays_data[0].underlay_pitch_in_pixels); ++ bw_data_internal->underlay_stereo_mode = ++ mode_data->displays_data[0].underlay_stereo_mode; ++ bw_data_internal->display_synchronization_enabled = ++ mode_data->display_synchronization_enabled; ++ } ++ ++ if (bw_data_internal->number_of_displays != 0) { ++ struct bw_fixed high_sclk = vbios->high_sclk; ++ struct bw_fixed low_sclk = vbios->low_sclk; ++ struct bw_fixed high_yclk = vbios->high_yclk; ++ struct bw_fixed low_yclk = vbios->low_yclk; ++ ++ ((struct bw_calcs_input_vbios *)vbios)->low_yclk = low_yclk; ++ ((struct bw_calcs_input_vbios *)vbios)->high_yclk = low_yclk; ++ ((struct bw_calcs_input_vbios *)vbios)->low_sclk = low_sclk; ++ ((struct bw_calcs_input_vbios *)vbios)->mid_sclk = low_sclk; ++ ((struct bw_calcs_input_vbios *)vbios)->high_sclk = low_sclk; ++ calculate_bandwidth(dceip, vbios, bw_data_internal, ++ bw_results_internal); ++ ++ /* units: nanosecond, 16bit storage. */ ++ calcs_output->nbp_state_change_watermark[0].b_mark = ++ mul(bw_results_internal->nbp_state_change_watermark[4], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->nbp_state_change_watermark[1].b_mark = ++ mul(bw_results_internal->nbp_state_change_watermark[5], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->nbp_state_change_watermark[2].b_mark = ++ mul(bw_results_internal->nbp_state_change_watermark[6], ++ int_to_fixed(1000)).value >> 24; ++ ++ calcs_output->stutter_exit_watermark[0].b_mark = ++ mul(bw_results_internal->stutter_exit_watermark[4], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->stutter_exit_watermark[1].b_mark = ++ mul(bw_results_internal->stutter_exit_watermark[5], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->stutter_exit_watermark[2].b_mark = ++ mul(bw_results_internal->stutter_exit_watermark[6], ++ int_to_fixed(1000)).value >> 24; ++ ++ calcs_output->urgent_watermark[0].b_mark = ++ mul(bw_results_internal->urgent_watermark[4], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->urgent_watermark[1].b_mark = ++ mul(bw_results_internal->urgent_watermark[5], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->urgent_watermark[2].b_mark = ++ mul(bw_results_internal->urgent_watermark[6], ++ int_to_fixed(1000)).value >> 24; ++ ++ ((struct bw_calcs_input_vbios *)vbios)->low_yclk = high_yclk; ++ ((struct bw_calcs_input_vbios *)vbios)->high_yclk = high_yclk; ++ ((struct bw_calcs_input_vbios *)vbios)->low_sclk = high_sclk; ++ ((struct bw_calcs_input_vbios *)vbios)->mid_sclk = high_sclk; ++ ((struct bw_calcs_input_vbios *)vbios)->high_sclk = high_sclk; ++ ++ calculate_bandwidth(dceip, vbios, bw_data_internal, ++ bw_results_internal); ++ ++ calcs_output->nbp_state_change_watermark[0].a_mark = ++ mul(bw_results_internal->nbp_state_change_watermark[4], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->nbp_state_change_watermark[1].a_mark = ++ mul(bw_results_internal->nbp_state_change_watermark[5], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->nbp_state_change_watermark[2].a_mark = ++ mul(bw_results_internal->nbp_state_change_watermark[6], ++ int_to_fixed(1000)).value >> 24; ++ ++ calcs_output->stutter_exit_watermark[0].a_mark = ++ mul(bw_results_internal->stutter_exit_watermark[4], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->stutter_exit_watermark[1].a_mark = ++ mul(bw_results_internal->stutter_exit_watermark[5], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->stutter_exit_watermark[2].a_mark = ++ mul(bw_results_internal->stutter_exit_watermark[6], ++ int_to_fixed(1000)).value >> 24; ++ ++ calcs_output->urgent_watermark[0].a_mark = ++ mul(bw_results_internal->urgent_watermark[4], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->urgent_watermark[1].a_mark = ++ mul(bw_results_internal->urgent_watermark[5], ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->urgent_watermark[2].a_mark = ++ mul(bw_results_internal->urgent_watermark[6], ++ int_to_fixed(1000)).value >> 24; ++ ++ calcs_output->nbp_state_change_enable = ++ bw_results_internal->nbp_state_change_enable; ++ calcs_output->cpuc_state_change_enable = ++ bw_results_internal->cpuc_state_change_enable; ++ calcs_output->cpup_state_change_enable = ++ bw_results_internal->cpup_state_change_enable; ++ calcs_output->stutter_mode_enable = ++ bw_results_internal->stutter_mode_enable; ++ calcs_output->dispclk = ++ mul(bw_results_internal->dispclk, ++ int_to_fixed(1000)).value >> 24; ++ calcs_output->required_sclk = ++ mul(bw_results_internal->required_sclk, ++ int_to_fixed(1000)).value >> 24; ++ ++ ((struct bw_calcs_input_vbios *)vbios)->low_yclk = low_yclk; ++ ((struct bw_calcs_input_vbios *)vbios)->high_yclk = high_yclk; ++ ((struct bw_calcs_input_vbios *)vbios)->low_sclk = low_sclk; ++ ((struct bw_calcs_input_vbios *)vbios)->mid_sclk = high_sclk; ++ ((struct bw_calcs_input_vbios *)vbios)->high_sclk = high_sclk; ++ } else { ++ calcs_output->nbp_state_change_enable = true; ++ calcs_output->cpuc_state_change_enable = true; ++ calcs_output->cpup_state_change_enable = true; ++ calcs_output->stutter_mode_enable = true; ++ calcs_output->dispclk = 0; ++ calcs_output->required_sclk = 0; ++ } ++ ++ dc_service_free(ctx, bw_data_internal); ++ dc_service_free(ctx, bw_results_internal); ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/calcs/bw_fixed.c b/drivers/gpu/drm/amd/dal/dc/calcs/bw_fixed.c +new file mode 100644 +index 0000000..6bad7c6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/calcs/bw_fixed.c +@@ -0,0 +1,278 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++#include "bw_fixed.h" ++ ++ ++#define BITS_PER_FRACTIONAL_PART 24 ++ ++#define MIN_I32 \ ++ (long long)(-(1LL << (63 - BITS_PER_FRACTIONAL_PART))) ++ ++#define MAX_I32 \ ++ (long long)((1ULL << (63 - BITS_PER_FRACTIONAL_PART)) - 1) ++ ++#define MIN_I64 \ ++ (long long)(-(1LL << 63)) ++ ++#define MAX_I64 \ ++ (long long)((1ULL << 63) - 1) ++ ++ ++#define FRACTIONAL_PART_MASK \ ++ ((1ULL << BITS_PER_FRACTIONAL_PART) - 1) ++ ++#define GET_INTEGER_PART(x) \ ++ ((x) >> BITS_PER_FRACTIONAL_PART) ++ ++#define GET_FRACTIONAL_PART(x) \ ++ (FRACTIONAL_PART_MASK & (x)) ++ ++static unsigned long long abs_i64(long long arg) ++{ ++ if (arg >= 0) ++ return (unsigned long long)(arg); ++ else ++ return (unsigned long long)(-arg); ++} ++ ++struct bw_fixed bw_min3(struct bw_fixed v1, struct bw_fixed v2, struct bw_fixed v3) ++{ ++ return bw_min(bw_min(v1, v2), v3); ++} ++ ++struct bw_fixed bw_max3(struct bw_fixed v1, struct bw_fixed v2, struct bw_fixed v3) ++{ ++ return bw_max(bw_max(v1, v2), v3); ++} ++ ++struct bw_fixed int_to_fixed(long long value) ++{ ++ struct bw_fixed res; ++ ASSERT(value < MAX_I32 && value > MIN_I32); ++ res.value = value << BITS_PER_FRACTIONAL_PART; ++ return res; ++} ++ ++struct bw_fixed frc_to_fixed(long long numerator, long long denominator) ++{ ++ struct bw_fixed res; ++ bool arg1_negative = numerator < 0; ++ bool arg2_negative = denominator < 0; ++ unsigned long long arg1_value; ++ unsigned long long arg2_value; ++ unsigned long long remainder; ++ ++ /* determine integer part */ ++ unsigned long long res_value; ++ ++ ASSERT(denominator != 0); ++ ++ arg1_value = abs_i64(numerator); ++ arg2_value = abs_i64(denominator); ++ remainder = arg1_value % arg2_value; ++ res_value = arg1_value / arg2_value; ++ ++ ASSERT(res_value <= MAX_I32); ++ ++ /* determine fractional part */ ++ { ++ unsigned int i = BITS_PER_FRACTIONAL_PART; ++ ++ do ++ { ++ remainder <<= 1; ++ ++ res_value <<= 1; ++ ++ if (remainder >= arg2_value) ++ { ++ res_value |= 1; ++ remainder -= arg2_value; ++ } ++ } while (--i != 0); ++ } ++ ++ /* round up LSB */ ++ { ++ unsigned long long summand = (remainder << 1) >= arg2_value; ++ ++ ASSERT(res_value <= MAX_I64 - summand); ++ ++ res_value += summand; ++ } ++ ++ res.value = (signed long long)(res_value); ++ ++ if (arg1_negative ^ arg2_negative) ++ res.value = -res.value; ++ return res; ++} ++ ++struct bw_fixed bw_min(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return (arg1.value <= arg2.value) ? arg1 : arg2; ++} ++ ++struct bw_fixed bw_max(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return (arg2.value <= arg1.value) ? arg1 : arg2; ++} ++ ++struct bw_fixed bw_floor(const struct bw_fixed arg, const struct bw_fixed significance) ++{ ++ struct bw_fixed result; ++ signed long long multiplicand = arg.value / abs_i64(significance.value); ++ result.value = abs_i64(significance.value) * multiplicand; ++ ASSERT(abs_i64(result.value) <= abs_i64(arg.value)); ++ return result; ++} ++ ++struct bw_fixed bw_ceil(const struct bw_fixed arg, const struct bw_fixed significance) ++{ ++ struct bw_fixed result; ++ result.value = arg.value + arg.value % abs_i64(significance.value); ++ if (result.value < significance.value) ++ result.value = significance.value; ++ return result; ++} ++ ++struct bw_fixed add(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ struct bw_fixed res; ++ ++ res.value = arg1.value + arg2.value; ++ ++ return res; ++} ++ ++struct bw_fixed sub(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ struct bw_fixed res; ++ ++ res.value = arg1.value - arg2.value; ++ ++ return res; ++} ++ ++struct bw_fixed mul(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ struct bw_fixed res; ++ ++ bool arg1_negative = arg1.value < 0; ++ bool arg2_negative = arg2.value < 0; ++ ++ unsigned long long arg1_value = abs_i64(arg1.value); ++ unsigned long long arg2_value = abs_i64(arg2.value); ++ ++ unsigned long long arg1_int = GET_INTEGER_PART(arg1_value); ++ unsigned long long arg2_int = GET_INTEGER_PART(arg2_value); ++ ++ unsigned long long arg1_fra = GET_FRACTIONAL_PART(arg1_value); ++ unsigned long long arg2_fra = GET_FRACTIONAL_PART(arg2_value); ++ ++ unsigned long long tmp; ++ ++ res.value = arg1_int * arg2_int; ++ ++ ASSERT(res.value <= MAX_I32); ++ ++ res.value <<= BITS_PER_FRACTIONAL_PART; ++ ++ tmp = arg1_int * arg2_fra; ++ ++ ASSERT(tmp <= (unsigned long long)(MAX_I64 - res.value)); ++ ++ res.value += tmp; ++ ++ tmp = arg2_int * arg1_fra; ++ ++ ASSERT(tmp <= (unsigned long long)(MAX_I64 - res.value)); ++ ++ res.value += tmp; ++ ++ tmp = arg1_fra * arg2_fra; ++ ++ tmp = (tmp >> BITS_PER_FRACTIONAL_PART) + ++ (tmp >= (unsigned long long)(frc_to_fixed(1, 2).value)); ++ ++ ASSERT(tmp <= (unsigned long long)(MAX_I64 - res.value)); ++ ++ res.value += tmp; ++ ++ if (arg1_negative ^ arg2_negative) ++ res.value = -res.value; ++ return res; ++} ++ ++struct bw_fixed bw_div(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ struct bw_fixed res = frc_to_fixed(arg1.value, arg2.value); ++ return res; ++} ++ ++struct bw_fixed fixed31_32_to_bw_fixed(long long raw) ++{ ++ struct bw_fixed result = { 0 }; ++ ++ if (raw < 0) { ++ raw = -raw; ++ result.value = -(raw >> (32 - BITS_PER_FRACTIONAL_PART)); ++ } else { ++ result.value = raw >> (32 - BITS_PER_FRACTIONAL_PART); ++ } ++ ++ return result; ++} ++ ++bool equ(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return arg1.value == arg2.value; ++} ++ ++bool neq(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return arg1.value != arg2.value; ++} ++ ++bool leq(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return arg1.value <= arg2.value; ++} ++ ++bool geq(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return arg1.value >= arg2.value; ++} ++ ++bool ltn(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return arg1.value < arg2.value; ++} ++ ++bool gtn(const struct bw_fixed arg1, const struct bw_fixed arg2) ++{ ++ return arg1.value > arg2.value; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.c b/drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.c +new file mode 100644 +index 0000000..f8ee65e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.c +@@ -0,0 +1,1992 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/fixed31_32.h" ++ ++#include "scaler_filter.h" ++ ++enum { ++ DOWN_DB_SCALES = 8, ++ DOWN_DB_POINTS = 11, ++ ++ UP_DB_SCALES = 1, ++ UP_DB_POINTS = 7, ++ ++ MIN_SHARPNESS = -50, ++ MAX_SHARPNESS = 50, ++ ++ CONST_DIVIDER = 10000000, ++ ++ MAX_HOR_DOWNSCALE = 1666000, /* 1:6 */ ++ MAX_VER_DOWNSCALE = 1666000, /* 1:6 */ ++ ++ MAX_HOR_UPSCALE = 160000000, /* 16:1 */ ++ MAX_VER_UPSCALE = 160000000, /* 16:1 */ ++ ++ THRESHOLDRATIOLOW = 8000000, /* 0.8 */ ++ THRESHOLDRATIOUP = 10000000, /* 1.0 */ ++ ++ DOWN_DB_FUZZY = -120411996, /* -12.041200 */ ++ DOWN_DB_FLAT = -60205998, /* -6.020600 */ ++ DOWN_DB_SHARP = -10000000, /* -1.000000 */ ++ ++ UP_DB_FUZZY = -60205998, /* -6.020600 */ ++ UP_DB_FLAT = 0, ++ UP_DB_SHARP = 60205998 /* 6.020600 */ ++}; ++ ++static inline struct fixed31_32 max_hor_downscale(void) ++{ ++ return dal_fixed31_32_from_fraction(MAX_HOR_DOWNSCALE, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 max_ver_downscale(void) ++{ ++ return dal_fixed31_32_from_fraction(MAX_VER_DOWNSCALE, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 max_hor_upscale(void) ++{ ++ return dal_fixed31_32_from_fraction(MAX_HOR_UPSCALE, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 max_ver_upscale(void) ++{ ++ return dal_fixed31_32_from_fraction(MAX_VER_UPSCALE, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 threshold_ratio_low(void) ++{ ++ return dal_fixed31_32_from_fraction(THRESHOLDRATIOLOW, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 threshold_ratio_up(void) ++{ ++ return dal_fixed31_32_from_fraction(THRESHOLDRATIOUP, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 down_db_fuzzy(void) ++{ ++ return dal_fixed31_32_from_fraction(DOWN_DB_FUZZY, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 down_db_flat(void) ++{ ++ return dal_fixed31_32_from_fraction(DOWN_DB_FLAT, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 down_db_sharp(void) ++{ ++ return dal_fixed31_32_from_fraction(DOWN_DB_SHARP, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 up_db_fuzzy(void) ++{ ++ return dal_fixed31_32_from_fraction(UP_DB_FUZZY, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 up_db_flat(void) ++{ ++ return dal_fixed31_32_from_fraction(UP_DB_FLAT, CONST_DIVIDER); ++} ++ ++static inline struct fixed31_32 up_db_sharp(void) ++{ ++ return dal_fixed31_32_from_fraction(UP_DB_SHARP, CONST_DIVIDER); ++} ++ ++static const int32_t ++ downscaling_db_table[][DOWN_DB_SCALES + 1][DOWN_DB_POINTS] = { ++ /* 3 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 14302719, 14302719, 14302719, ++ 10000000, 99999, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14302339, 14302339, 14302339, ++ 10000000, 4452010, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14302760, 14302760, 14302760, ++ 10000000, 7826979, 5258399, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14302819, 14302819, 14302819, ++ 10000000, 8669400, 7414469, ++ 4422729, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14302730, 14302730, 12791190, ++ 10000000, 9045640, 8180170, ++ 6477950, 4599249, 2019010, ++ 99999, 99999 ++ }, ++ { ++ 14302699, 14302699, 12067849, ++ 10000000, 9236029, 8541280, ++ 7252740, 6021010, 4820120, ++ 3511950, 1769340 ++ }, ++ { ++ 14302710, 14302710, 11783510, ++ 10000000, 9325690, 8704419, ++ 7595670, 6583020, 5652850, ++ 4749999, 3847680 ++ }, ++ { ++ 14302920, 14302920, 11709250, ++ 10000000, 9345560, 8754609, ++ 7692559, 6738259, 5878239, ++ 5057529, 4264070 ++ } ++ }, ++ /* 4 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 14308999, 14308999, 14308999, ++ 10000000, 99999, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14308999, 14308999, 14308999, ++ 10000000, 6311039, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14308999, 14308999, 14308999, ++ 10000000, 8526669, 6832849, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14308999, 14308999, 12110630, ++ 10000000, 9117940, 8230940, ++ 6320130, 3719770, 99999, ++ 99999, 99999 ++ }, ++ { ++ 14308999, 14308999, 11474980, ++ 10000000, 9370139, 8771979, ++ 7601270, 6440780, 5249999, ++ 3887520, 2039040 ++ }, ++ { ++ 14308999, 13084859, 11179579, ++ 10000000, 9495180, 9016919, ++ 8134520, 7311699, 6560329, ++ 5845720, 5155519 ++ }, ++ { ++ 14308999, 12576600, 11048669, ++ 10000000, 9550499, 9132360, ++ 8368729, 7679399, 7073119, ++ 6520900, 6015530 ++ }, ++ { ++ 14308999, 12448530, 11007410, ++ 10000000, 9566799, 9165279, ++ 8435800, 7785279, 7215780, ++ 6701470, 6240640 ++ } ++ }, ++ /* 5 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 8971139, 8971139, 8971139, ++ 10000000, 99999, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 9466379, 9466379, 9466379, ++ 10000000, 5648760, 3834280, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 15000000, 15000000, 14550110, ++ 10000000, 7121120, 5994579, ++ 4314630, 2606149, 99999, ++ 99999, 99999 ++ }, ++ { ++ 15000000, 15000000, 13047469, ++ 10000000, 8368809, 7343569, ++ 5970299, 4924620, 4029389, ++ 3171139, 2276369 ++ }, ++ { ++ 15000000, 14157199, 11897679, ++ 10000000, 9166659, 8444600, ++ 7287240, 6374719, 5615460, ++ 4949580, 4350199 ++ }, ++ { ++ 15000000, 12877819, 11224579, ++ 10000000, 9488620, 9016109, ++ 8203780, 7500000, 6883730, ++ 6326839, 5818459 ++ }, ++ { ++ 14733040, 12233200, 10939040, ++ 10000000, 9608929, 9250000, ++ 8623390, 8076940, 7606369, ++ 7177749, 6785169 ++ }, ++ { ++ 14627330, 12046170, 10862360, ++ 10000000, 9639260, 9312710, ++ 8737679, 8242470, 7815709, ++ 7432209, 7082970 ++ } ++ }, ++ /* 6 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 8231559, 8231559, 8231559, ++ 10000000, 99999, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 8353310, 8353310, 8353310, ++ 10000000, 5504879, 4310710, ++ 870359, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 8643479, 8643479, 8643479, ++ 10000000, 6483510, 5768150, ++ 4630779, 3580690, 2501940, ++ 1015309, 99999 ++ }, ++ { ++ 15000000, 15000000, 13493930, ++ 10000000, 7516040, 6802409, ++ 5824409, 5080109, 4454280, ++ 3896749, 3386510 ++ }, ++ { ++ 15000000, 14055930, 12321079, ++ 10000000, 8872389, 8090410, ++ 7035570, 6281229, 5676810, ++ 5165010, 4717260 ++ }, ++ { ++ 15000000, 12915290, 11311399, ++ 10000000, 9460610, 8988440, ++ 8202149, 7548679, 6999999, ++ 6510639, 6065719 ++ }, ++ { ++ 14310129, 12140829, 10901659, ++ 10000000, 9635019, 9307180, ++ 8740929, 8263260, 7858849, ++ 7499330, 7170130 ++ }, ++ { ++ 13815449, 11911309, 10801299, ++ 10000000, 9669629, 9380580, ++ 8878319, 8452050, 8097199, ++ 7785030, 7504299 ++ } ++ }, ++ /* 7 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 10616660, 10616660, 10616660, ++ 10000000, 2646020, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 10099999, 10099999, 10099999, ++ 10000000, 4936839, 4112670, ++ 2729740, 896539, 99999, ++ 99999, 99999 ++ }, ++ { ++ 8345860, 8345860, 8345860, ++ 10000000, 6034079, 5371739, ++ 4466759, 3763799, 3155870, ++ 2588019, 2026730 ++ }, ++ { ++ 9298499, 9298499, 13768420, ++ 10000000, 7174239, 6524270, ++ 5670250, 5052099, 4549089, ++ 4115279, 3722150 ++ }, ++ { ++ 15000000, 14116940, 12563209, ++ 10000000, 8542140, 7782419, ++ 6865050, 6239479, 5758860, ++ 5351870, 4992800 ++ }, ++ { ++ 15000000, 12913750, 11306079, ++ 10000000, 9452580, 8969209, ++ 8168810, 7538409, 7029479, ++ 6603180, 6227809 ++ }, ++ { ++ 14390859, 11862809, 10757420, ++ 10000000, 9688709, 9404249, ++ 8904439, 8472480, 8099079, ++ 7765330, 7459110 ++ }, ++ { ++ 13752900, 11554559, 10637769, ++ 10000000, 9736120, 9499999, ++ 9079759, 8718389, 8408790, ++ 8134469, 7886120 ++ } ++ }, ++ /* 8 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 11277090, 11277090, 11277090, ++ 10000000, 2949059, 99999, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 11196039, 11196039, 11196039, ++ 10000000, 4627540, 4018869, ++ 3018769, 2000000, 250770, ++ 99999, 99999 ++ }, ++ { ++ 10878369, 10878369, 10878369, ++ 10000000, 5657230, 5118110, ++ 4372630, 3809120, 3337709, ++ 2919510, 2535369 ++ }, ++ { ++ 9090089, 9090089, 13961290, ++ 10000000, 6929969, 6334999, ++ 5569829, 5019649, 4584150, ++ 4208610, 3876540 ++ }, ++ { ++ 15000000, 14173229, 12732659, ++ 10000000, 8267070, 7575380, ++ 6764540, 6218209, 5803539, ++ 5454990, 5146239 ++ }, ++ { ++ 15000000, 12928279, 11292259, ++ 10000000, 9447429, 8954229, ++ 8141599, 7516989, 7039459, ++ 6649519, 6316819 ++ }, ++ { ++ 14661350, 11638879, 10665880, ++ 10000000, 9722669, 9464690, ++ 9013469, 8613470, 8266339, ++ 7949870, 7663450 ++ }, ++ { ++ 13861900, 11311980, 10543940, ++ 10000000, 9772019, 9565100, ++ 9198870, 8881340, 8609200, ++ 8365769, 8147500 ++ } ++ }, ++ /* 9 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { 10099999, 10099999, 10099999, ++ 10000000, 2939159, 1526470, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 11726609, 11726609, 11726609, ++ 10000000, 4329420, 3805609, ++ 3030480, 2363760, 1732099, ++ 980139, 99999 ++ }, ++ { ++ 10949269, 10949269, 10949269, ++ 10000000, 5452589, 4946640, ++ 4277969, 3790729, 3392640, ++ 3048950, 2750000 ++ }, ++ { ++ 8830279, 8830279, 14084529, ++ 10000000, 6743149, 6182519, ++ 5482980, 5000000, 4622060, ++ 4303340, 4022600 ++ }, ++ { ++ 9709150, 14111399, 12800760, ++ 10000000, 7989749, 7445629, ++ 6741260, 6241980, 5857459, ++ 5534989, 5255370 ++ }, ++ { ++ 15000000, 12830289, 11489900, ++ 10000000, 9302089, 8767340, ++ 8025540, 7500000, 7100800, ++ 6768149, 6481850 ++ }, ++ { ++ 14873609, 11576000, 10650579, ++ 10000000, 9731360, 9483649, ++ 9054650, 8680559, 8358049, ++ 8066400, 7802420 ++ }, ++ { ++ 12981410, 11185950, 10491620, ++ 10000000, 9795730, 9611030, ++ 9286710, 9007279, 8768100, ++ 8553469, 8361340 ++ } ++ }, ++ /* 10 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 8993279, 8993279, 8993279, ++ 10000000, 2921360, 1905619, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 9064850, 9064850, 9064850, ++ 10000000, 4095619, 3655839, ++ 3021000, 2500000, 2031680, ++ 1566990, 1055440 ++ }, ++ { ++ 11043460, 11043460, 11043460, ++ 10000000, 5287479, 4816150, ++ 4208439, 3769229, 3418970, ++ 3117449, 2850320 ++ }, ++ { ++ 8651900, 8651900, 14169909, ++ 10000000, 6596950, 6071490, ++ 5423219, 4980779, 4644620, ++ 4362219, 4114899 ++ }, ++ { ++ 9246050, 14055370, 12832759, ++ 10000000, 7831320, 7369570, ++ 6731680, 6262450, 5897690, ++ 5592269, 5328789 ++ }, ++ { ++ 15000000, 12770450, 11642129, ++ 10000000, 9120929, 8601920, ++ 7946630, 7490440, 7140589, ++ 6847490, 6593719 ++ }, ++ { ++ 14062479, 11541219, 10644329, ++ 10000000, 9736120, 9495139, ++ 9080520, 8724340, 8419489, ++ 8146359, 7899820 ++ }, ++ { ++ 12507469, 11102950, 10457479, ++ 10000000, 9811149, 9641249, ++ 9344969, 9090980, 8875219, ++ 8684499, 8513180 ++ } ++ }, ++ /* 11 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 10099509, 10099509, 10099509, ++ 10000000, 2788810, 2054850, ++ 99999, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 8872069, 8872069, 8872069, ++ 10000000, 3929649, 3522840, ++ 2963230, 2527720, 2157579, ++ 1823610, 1500000 ++ }, ++ { ++ 10099999, 10099999, 10099999, ++ 10000000, 5155599, 4712319, ++ 4154500, 3759450, 3448629, ++ 3183139, 2948490 ++ }, ++ { ++ 10511649, 10511649, 14216580, ++ 10000000, 6445930, 5988820, ++ 5401239, 4988409, 4673399, ++ 4410479, 4181599 ++ }, ++ { ++ 9170889, 14003310, 12949769, ++ 10000000, 7684900, 7250000, ++ 6670129, 6255810, 5934680, ++ 5664110, 5427970 ++ }, ++ { ++ 15000000, 12763030, 11734730, ++ 10000000, 8958870, 8478559, ++ 7893459, 7489529, 7179200, ++ 6917790, 6688359 ++ }, ++ { ++ 14634610, 11491880, 10619130, ++ 10000000, 9744859, 9509819, ++ 9102900, 8760340, 8463050, ++ 8202620, 7968729 ++ }, ++ { ++ 12415319, 10980290, 10405089, ++ 10000000, 9831910, 9680110, ++ 9413710, 9184579, 8987190, ++ 8813819, 8655819 ++ } ++ }, ++ /* 12 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 10832400, 10832400, 10832400, ++ 10000000, 2700819, 2115820, ++ 750000, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 10747549, 10747549, 10747549, ++ 10000000, 3781630, 3415020, ++ 2914879, 2537429, 2221180, ++ 1943199, 1688420 ++ }, ++ { ++ 11630790, 11630790, 11630790, ++ 10000000, 5047429, 4631519, ++ 4113860, 3750000, 3469760, ++ 3229379, 3016360 ++ }, ++ { ++ 10780229, 10780229, 10780229, ++ 10000000, 6340010, 5935009, ++ 5387600, 4995940, 4695929, ++ 4446829, 4231610 ++ }, ++ { ++ 9055669, 13968739, 13037070, ++ 10000000, 7556660, 7149490, ++ 6625509, 6250000, 5958870, ++ 5713790, 5500869 ++ }, ++ { ++ 14614900, 12760740, 11806739, ++ 10000000, 8824530, 8388419, ++ 7857400, 7489010, 7206150, ++ 6968010, 6759889 ++ }, ++ { ++ 14894100, 11451840, 10598870, ++ 10000000, 9750000, 9521099, ++ 9122239, 8784019, 8494700, ++ 8243309, 8018680 ++ }, ++ { ++ 12298769, 10886880, 10367530, ++ 10000000, 9846829, 9708030, ++ 9464049, 9252949, 9072539, ++ 8910980, 8766649 ++ } ++ }, ++ /* 13 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 10099999, 10099999, 10099999, ++ 10000000, 2574490, 2099110, ++ 1194889, 99999, 99999, ++ 99999, 99999 ++ }, ++ { ++ 10099999, 10099999, 10099999, ++ 10000000, 3679780, 3332070, ++ 2869139, 2530030, 2251899, ++ 2010450, 1793050 ++ }, ++ { ++ 9306690, 9306690, 9306690, ++ 10000000, 4964010, 4573009, ++ 4082309, 3742089, 3481810, ++ 3262990, 3070969 ++ }, ++ { ++ 10099999, 10099999, 10099999, ++ 10000000, 6217889, 5843269, ++ 5353810, 5000000, 4730190, ++ 4499999, 4301390 ++ }, ++ { ++ 8819990, 13964320, 13098440, ++ 10000000, 7454770, 7075160, ++ 6591439, 6250000, 5983970, ++ 5760229, 5564339 ++ }, ++ { ++ 14432849, 12727780, 11847709, ++ 10000000, 8695709, 8322049, ++ 7842620, 7500000, 7234820, ++ 7010849, 6814730 ++ }, ++ { ++ 15000000, 11440130, 10620100, ++ 10000000, 9742270, 9508739, ++ 9110010, 8782560, 8510140, ++ 8276290, 8069980 ++ }, ++ { ++ 12039999, 10825289, 10341939, ++ 10000000, 9858080, 9729740, ++ 9505100, 9310669, 9144560, ++ 8996559, 8862569 ++ } ++ }, ++ /* 14 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 9289590, 9289590, 9289590, ++ 10000000, 2485270, 2084970, ++ 1362659, 250000, 99999, ++ 99999, 99999 ++ }, ++ { ++ 9484500, 9484500, 9484500, ++ 10000000, 3593840, 3263100, ++ 2833609, 2519409, 2267650, ++ 2050379, 1856749 ++ }, ++ { ++ 9237130, 9237130, 9237130, ++ 10000000, 4898909, 4527629, ++ 4057880, 3734529, 3490320, ++ 3287230, 3111050 ++ }, ++ { ++ 9543399, 9543399, 9543399, ++ 10000000, 6110230, 5772359, ++ 5328080, 5007240, 4757330, ++ 4545379, 4359109 ++ }, ++ { ++ 9032660, 9032660, 9032660, ++ 10000000, 7373520, 7016940, ++ 6565740, 6250000, 6002650, ++ 5794939, 5610830 ++ }, ++ { ++ 14351329, 12697319, 11875350, ++ 10000000, 8606730, 8275989, ++ 7833449, 7510430, 7257339, ++ 7043970, 6857690 ++ }, ++ { ++ 13286800, 11436090, 10643019, ++ 10000000, 9729470, 9491149, ++ 9096930, 8778640, 8519319, ++ 8299450, 8104829 ++ }, ++ { ++ 11838380, 10778709, 10322740, ++ 10000000, 9866499, 9746059, ++ 9535790, 9354810, 9200339, ++ 9063839, 8940430 ++ } ++ }, ++ /* 15 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 9193199, 9193199, 9193199, ++ 10000000, 2400999, 2042409, ++ 1450179, 789309, 99999, ++ 99999, 99999 ++ }, ++ { ++ 10755189, 10755189, 10755189, ++ 10000000, 3532319, 3212479, ++ 2803660, 2510200, 2278629, ++ 2078720, 1899970 ++ }, ++ { ++ 8732669, 8732669, 8732669, ++ 10000000, 4821290, 4483030, ++ 4045079, 3737959, 3505080, ++ 3311960, 3143329 ++ }, ++ { ++ 9450280, 9450280, 9450280, ++ 10000000, 6040880, 5718960, ++ 5302609, 5004199, 4771710, ++ 4575310, 4404180 ++ }, ++ { ++ 10520930, 10520930, 10520930, ++ 10000000, 7298259, 6975160, ++ 6552690, 6250000, 6018469, ++ 5822089, 5648869 ++ }, ++ { ++ 14320160, 12683949, 11917040, ++ 10000000, 8541300, 8228710, ++ 7812070, 7509459, 7272909, ++ 7072560, 6895729 ++ }, ++ { ++ 15000000, 11434819, 10650700, ++ 10000000, 9723110, 9480339, ++ 9083300, 8771640, 8524850, ++ 8317480, 8135899 ++ }, ++ { ++ 11750520, 10722860, 10299190, ++ 10000000, 9875990, 9763770, ++ 9567070, 9397709, 9252669, ++ 9124029, 9008929 ++ } ++ }, ++ /* 16 tap downscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, ++ 0, -10000000, -20000000, ++ -40000000, -60209999, -80000000, ++ -100000000, -120410003 ++ }, ++ { ++ 10612260, 10612260, 10612260, ++ 10000000, 2308720, 1999289, ++ 1495770, 1009820, 315460, ++ 99999, 99999 ++ }, ++ { ++ 9394969, 9394969, 9394969, ++ 10000000, 3462660, 3162190, ++ 2780120, 2508420, 2295179, ++ 2109449, 1943989 ++ }, ++ { ++ 10609409, 10609409, 10609409, ++ 10000000, 4749999, 4447000, ++ 4039109, 3746300, 3522360, ++ 3336620, 3177059 ++ }, ++ { ++ 9435039, 9435039, 9435039, ++ 10000000, 5978109, 5675160, ++ 5282300, 5000000, 4782429, ++ 4598149, 4438050 ++ }, ++ { ++ 10592620, 10592620, 10592620, ++ 10000000, 7244589, 6940630, ++ 6537730, 6250000, 6027920, ++ 5842260, 5680159 ++ }, ++ { ++ 14282959, 12678509, 11963449, ++ 10000000, 8484349, 8181620, ++ 7785459, 7500000, 7281309, ++ 7095699, 6932809 ++ }, ++ { ++ 15000000, 11434919, 10673819, ++ 10000000, 9708179, 9456859, ++ 9060000, 8760929, 8529940, ++ 8338279, 8172209 ++ }, ++ { ++ 11690390, 10668220, 10277210, ++ 10000000, 9884750, 9780330, ++ 9597110, 9439319, 9304260, ++ 9183580, 9075019 ++ } ++ } ++}; ++ ++static const int32_t upscaling_db_table[][UP_DB_SCALES+1][UP_DB_POINTS] = { ++ /* 3 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 14302920, 14302920, 11709250, ++ 10000000, ++ 8754609, 7692559, 6738259 ++ } ++ }, ++ /* 4 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 14308999, 12448530, 11007410, ++ 10000000, 9165279, 8435800, ++ 7785279 ++ } ++ }, ++ /* 5 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 14627330, 12046170, 10862360, ++ 10000000, ++ 9312710, 8737679, 8242470 ++ } ++ }, ++ /* 6 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 13815449, 11911309, 10801299, ++ 10000000, ++ 9380580, 8878319, 8452050 ++ } ++ }, ++ /* 7 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 13752900, 11554559, 10637769, ++ 10000000, ++ 9499999, 9079759, 8718389 ++ } ++ }, ++ /* 8 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 13861900, 11311980, 10543940, ++ 10000000, ++ 9565100, 9198870, 8881340 ++ } ++ }, ++ /* 9 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 12981410, 11185950, 10491620, ++ 10000000, ++ 9611030, 9286710, 9007279 ++ } ++ }, ++ /* 10 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 12507469, 11102950, 10457479, ++ 10000000, ++ 9641249, 9344969, 9090980 ++ } ++ }, ++ /* 11 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 12415319, 10980290, 10405089, ++ 10000000, ++ 9680110, 9413710, 9184579 ++ } ++ }, ++ /* 12 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 12298769, 10886880, 10367530, ++ 10000000, ++ 9708030, 9464049, 9252949 ++ } ++ }, ++ /* 13 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 12039999, 10825289, 10341939, ++ 10000000, ++ 9729740, 9505100, 9310669 ++ } ++ }, ++ /* 14 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 11838380, 10778709, 10322740, ++ 10000000, ++ 9746059, 9535790, 9354810 ++ } ++ }, ++ /* 15 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 11750520, 10722860, 10299190, ++ 10000000, ++ 9763770, 9567070, 9397709 ++ } ++ }, ++ /* 16 tap upscaling */ ++ { ++ { ++ 60209999, 40000000, 20000000, 0, ++ -20000000, -40000000, -60209999 ++ }, ++ { ++ 11690390, 10668220, 10277210, ++ 10000000, ++ 9780330, 9597110, 9439319 ++ } ++ } ++}; ++ ++static bool allocate_3d_storage( ++ struct dc_context *ctx, ++ struct fixed31_32 ****ptr, ++ int32_t numberof_tables, ++ int32_t numberof_rows, ++ int32_t numberof_columns) ++{ ++ int32_t indexof_table = 0; ++ int32_t indexof_row = 0; ++ ++ struct fixed31_32 ***tables = dc_service_alloc( ++ ctx, ++ numberof_tables * sizeof(struct fixed31_32 **)); ++ ++ if (!tables) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ while (indexof_table != numberof_tables) { ++ struct fixed31_32 **rows = dc_service_alloc( ++ ctx, ++ numberof_rows * sizeof(struct fixed31_32 *)); ++ ++ if (!rows) { ++ BREAK_TO_DEBUGGER(); ++ --indexof_table; ++ goto failure; ++ } ++ ++ tables[indexof_table] = rows; ++ ++ while (indexof_row != numberof_rows) { ++ struct fixed31_32 *columns = dc_service_alloc( ++ ctx, ++ numberof_columns * sizeof(struct fixed31_32)); ++ ++ if (!columns) { ++ BREAK_TO_DEBUGGER(); ++ --indexof_row; ++ goto failure; ++ } ++ ++ rows[indexof_row] = columns; ++ ++ ++indexof_row; ++ } ++ ++ indexof_row = 0; ++ ++ ++indexof_table; ++ } ++ ++ *ptr = tables; ++ ++ return true; ++ ++failure: ++ ++ while (indexof_table >= 0) { ++ while (indexof_row >= 0) { ++ dc_service_free(ctx, tables[indexof_table][indexof_row]); ++ ++ --indexof_row; ++ } ++ ++ indexof_row = numberof_rows - 1; ++ ++ dc_service_free(ctx, tables[indexof_table]); ++ ++ --indexof_table; ++ } ++ ++ dc_service_free(ctx, tables); ++ ++ return false; ++} ++ ++static void destroy_3d_storage( ++ struct dc_context *ctx, ++ struct fixed31_32 ****ptr, ++ uint32_t numberof_tables, ++ uint32_t numberof_rows) ++{ ++ struct fixed31_32 ***tables = *ptr; ++ ++ uint32_t indexof_table = 0; ++ ++ if (!tables) ++ return; ++ ++ while (indexof_table != numberof_tables) { ++ uint32_t indexof_row = 0; ++ ++ while (indexof_row != numberof_rows) { ++ dc_service_free( ++ ctx, tables[indexof_table][indexof_row]); ++ ++ ++indexof_row; ++ }; ++ ++ dc_service_free(ctx, tables[indexof_table]); ++ ++ ++indexof_table; ++ }; ++ ++ dc_service_free(ctx, tables); ++ ++ *ptr = NULL; ++} ++ ++static bool create_downscaling_table( ++ struct scaler_filter *filter) ++{ ++ const int32_t numberof_tables = ++ ARRAY_SIZE(downscaling_db_table); ++ const int32_t numberof_rows = ++ ARRAY_SIZE(downscaling_db_table[0]); ++ const int32_t numberof_columns = ++ ARRAY_SIZE(downscaling_db_table[0][0]); ++ ++ int32_t indexof_table = 0; ++ ++ if (!allocate_3d_storage(filter->ctx, &filter->downscaling_table, ++ numberof_tables, numberof_rows, numberof_columns)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ while (indexof_table != numberof_tables) { ++ struct fixed31_32 **table = ++ filter->downscaling_table[indexof_table]; ++ ++ int32_t indexof_row = 0; ++ ++ while (indexof_row != numberof_rows) { ++ struct fixed31_32 *row = table[indexof_row]; ++ ++ int32_t indexof_column = 0; ++ ++ while (indexof_column != numberof_columns) { ++ row[indexof_column] = ++dal_fixed31_32_from_fraction( ++ downscaling_db_table[indexof_table][indexof_row][indexof_column], ++ CONST_DIVIDER); ++ ++ ++indexof_column; ++ } ++ ++ ++indexof_row; ++ } ++ ++ ++indexof_table; ++ } ++ ++ return true; ++} ++ ++static inline void destroy_downscaling_table( ++ struct scaler_filter *filter) ++{ ++ destroy_3d_storage( ++ filter->ctx, ++ &filter->downscaling_table, ++ ARRAY_SIZE(downscaling_db_table), ++ ARRAY_SIZE(downscaling_db_table[0])); ++} ++ ++static bool create_upscaling_table( ++ struct scaler_filter *filter) ++{ ++ const int32_t numberof_tables = ++ ARRAY_SIZE(upscaling_db_table); ++ const int32_t numberof_rows = ++ ARRAY_SIZE(upscaling_db_table[0]); ++ const int32_t numberof_columns = ++ ARRAY_SIZE(upscaling_db_table[0][0]); ++ ++ int32_t indexof_table = 0; ++ ++ if (!allocate_3d_storage(filter->ctx, &filter->upscaling_table, ++ numberof_tables, numberof_rows, numberof_columns)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ while (indexof_table != numberof_tables) { ++ struct fixed31_32 **table = ++ filter->upscaling_table[indexof_table]; ++ ++ int32_t indexof_row = 0; ++ ++ while (indexof_row != numberof_rows) { ++ struct fixed31_32 *row = table[indexof_row]; ++ ++ int32_t indexof_column = 0; ++ ++ while (indexof_column != numberof_columns) { ++ row[indexof_column] = ++dal_fixed31_32_from_fraction( ++ upscaling_db_table[indexof_table][indexof_row][indexof_column], ++ CONST_DIVIDER); ++ ++ ++indexof_column; ++ } ++ ++ ++indexof_row; ++ } ++ ++ ++indexof_table; ++ } ++ ++ return true; ++} ++ ++static inline void destroy_upscaling_table( ++ struct scaler_filter *filter) ++{ ++ destroy_3d_storage( ++ filter->ctx, ++ &filter->upscaling_table, ++ ARRAY_SIZE(upscaling_db_table), ++ ARRAY_SIZE(upscaling_db_table[0])); ++} ++ ++static bool same_filter_required( ++ struct scaler_filter *filter, ++ const struct scaler_filter_params *params, ++ uint32_t src_size, ++ uint32_t dst_size) ++{ ++ if (!filter->src_size) ++ return false; ++ if (!filter->dst_size) ++ return false; ++ if (filter->src_size != src_size) ++ return false; ++ if (filter->dst_size != dst_size) ++ return false; ++ if (filter->params.taps != params->taps) ++ return false; ++ if (filter->params.phases != params->phases) ++ return false; ++ if (filter->params.sharpness != params->sharpness) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * @brief ++ * (scale_max - scale_min) ++ * result = scale_min + (value - value_min) * ----------------------- ++ * (value_max - value_min) ++ */ ++ ++static struct fixed31_32 interpolate( ++ struct fixed31_32 value, ++ struct fixed31_32 value_min, ++ struct fixed31_32 value_max, ++ struct fixed31_32 scale_min, ++ struct fixed31_32 scale_max) ++{ ++ return dal_fixed31_32_add( ++ scale_min, ++ dal_fixed31_32_div( ++ dal_fixed31_32_mul( ++ dal_fixed31_32_sub( ++ value, ++ value_min), ++ dal_fixed31_32_sub( ++ scale_max, ++ scale_min)), ++ dal_fixed31_32_sub( ++ value_max, ++ value_min))); ++} ++ ++static bool map_sharpness( ++ struct scaler_filter *filter, ++ const struct scaler_filter_params *params, ++ uint32_t src_size, ++ uint32_t dst_size, ++ struct fixed31_32 *attenuation, ++ struct fixed31_32 *decibels_at_nyquist) ++{ ++ struct fixed31_32 ratio = dal_fixed31_32_from_fraction( ++ dst_size, ++ src_size); ++ ++ const struct fixed31_32 sharp_flat = ++ dal_fixed31_32_from_fraction(MIN_SHARPNESS + MAX_SHARPNESS, 2); ++ ++ struct fixed31_32 sharp_max = ++ dal_fixed31_32_from_int(MAX_SHARPNESS); ++ struct fixed31_32 sharp_min = ++ dal_fixed31_32_from_int(MIN_SHARPNESS); ++ ++ uint32_t index = params->taps - 3; ++ ++ struct fixed31_32 ratio_low; ++ struct fixed31_32 ratio_up; ++ ++ struct fixed31_32 db_min; ++ struct fixed31_32 db_flat; ++ struct fixed31_32 db_max; ++ struct fixed31_32 db_value; ++ ++ uint32_t i0; ++ uint32_t i1; ++ uint32_t row0; ++ uint32_t row1; ++ ++ int32_t sharp = params->sharpness; ++ ++ if (sharp < MIN_SHARPNESS) ++ sharp = MIN_SHARPNESS; ++ else if (sharp > MAX_SHARPNESS) ++ sharp = MAX_SHARPNESS; ++ ++ if (params->flags.bits.HORIZONTAL) { ++ if (dal_fixed31_32_lt(ratio, max_hor_downscale())) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } else if (dal_fixed31_32_lt( ++ max_hor_upscale(), ratio)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ } else { ++ if (dal_fixed31_32_lt(ratio, max_ver_downscale())) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } else if (dal_fixed31_32_lt( ++ max_ver_upscale(), ratio)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ } ++ ++ if (dst_size >= src_size) { ++ if (sharp < 0) { ++ db_max = up_db_flat(); ++ db_min = up_db_fuzzy(); ++ ++ sharp_max = sharp_flat; ++ } else { ++ db_max = up_db_sharp(); ++ db_min = up_db_flat(); ++ ++ sharp_min = sharp_flat; ++ } ++ ++ db_value = interpolate( ++ dal_fixed31_32_from_int(sharp), ++ sharp_min, sharp_max, ++ db_min, db_max); ++ ++ i0 = 0; ++ ++ while (dal_fixed31_32_lt( ++ db_value, filter->upscaling_table[index][0][i0]) && ++ (i0 < UP_DB_POINTS - 1)) ++ ++i0; ++ ++ i1 = i0 + 1; ++ ++ if (i0 == UP_DB_POINTS - 1) ++ i1 = i0--; ++ ++ sharp_max = filter->upscaling_table[index][1][i0]; ++ sharp_min = filter->upscaling_table[index][1][i1]; ++ ++ db_max = filter->upscaling_table[index][0][i0]; ++ db_min = filter->upscaling_table[index][0][i1]; ++ ++ *attenuation = interpolate( ++ db_value, ++ db_max, db_min, ++ sharp_max, sharp_min); ++ ++ *decibels_at_nyquist = db_value; ++ ++ return true; ++ } else if ((5 * dst_size) < (src_size << 2)) { ++ if (sharp < 0) { ++ db_max = down_db_flat(); ++ db_min = down_db_fuzzy(); ++ ++ sharp_max = sharp_flat; ++ } else { ++ db_max = down_db_sharp(); ++ db_min = down_db_flat(); ++ ++ sharp_min = sharp_flat; ++ } ++ ++ db_value = interpolate( ++ dal_fixed31_32_from_int(sharp), ++ sharp_min, sharp_max, ++ db_min, db_max); ++ } else { ++ struct fixed31_32 db_value_min = ++ filter->downscaling_table[index][0][0]; ++ ++ struct fixed31_32 db_value_max = ++ filter->downscaling_table[index][0][DOWN_DB_POINTS - 1]; ++ ++ db_min = interpolate( ++ ratio, ++ threshold_ratio_low(), threshold_ratio_up(), ++ down_db_fuzzy(), up_db_fuzzy()); ++ ++ db_flat = interpolate( ++ ratio, ++ threshold_ratio_low(), threshold_ratio_up(), ++ down_db_flat(), up_db_flat()); ++ ++ db_max = interpolate( ++ ratio, ++ threshold_ratio_low(), threshold_ratio_up(), ++ down_db_sharp(), up_db_sharp()); ++ ++ if (sharp < 0) { ++ db_max = db_flat; ++ ++ db_value = interpolate( ++ dal_fixed31_32_from_int(sharp), ++ sharp_min, dal_fixed31_32_zero, ++ db_min, db_max); ++ } else { ++ db_min = db_flat; ++ ++ db_value = interpolate( ++ dal_fixed31_32_from_int(sharp), ++ dal_fixed31_32_zero, sharp_max, ++ db_min, db_max); ++ } ++ ++ if (dal_fixed31_32_lt(db_value_min, db_value)) ++ db_value = db_value_min; ++ else if (dal_fixed31_32_lt(db_value, db_value_max)) ++ db_value = db_value_max; ++ } ++ ++ i1 = 0; ++ ++ while (dal_fixed31_32_lt(db_value, ++ filter->downscaling_table[index][0][i1]) && ++ (i1 < DOWN_DB_POINTS - 1)) ++ ++i1; ++ ++ if (i1 == 0) ++ i0 = i1++; ++ else ++ i0 = i1 - 1; ++ ++ row0 = dal_fixed31_32_round( ++ dal_fixed31_32_mul_int(ratio, DOWN_DB_SCALES)); ++ ++ if (dal_fixed31_32_lt( ++ dal_fixed31_32_from_fraction(row0, DOWN_DB_SCALES), ratio)) { ++ row1 = row0 + 1; ++ ++ if (row1 > DOWN_DB_SCALES) { ++ row1 = DOWN_DB_SCALES; ++ row0 = row1 - 1; ++ } ++ } else { ++ row1 = row0--; ++ ++ if (row0 < 1) { ++ row0 = 1; ++ row1 = 2; ++ } ++ } ++ ++ ratio_low = dal_fixed31_32_from_fraction(row0, DOWN_DB_SCALES); ++ ratio_up = dal_fixed31_32_from_fraction(row1, DOWN_DB_SCALES); ++ ++ sharp_max = interpolate( ++ ratio, ++ ratio_low, ratio_up, ++ filter->downscaling_table[index][row0][i0], ++ filter->downscaling_table[index][row1][i0]); ++ ++ sharp_min = interpolate( ++ ratio, ++ ratio_low, ratio_up, ++ filter->downscaling_table[index][row0][i1], ++ filter->downscaling_table[index][row1][i1]); ++ ++ db_max = filter->downscaling_table[index][0][i0]; ++ db_min = filter->downscaling_table[index][0][i1]; ++ ++ *attenuation = interpolate( ++ db_value, ++ db_max, db_min, ++ sharp_max, sharp_min); ++ ++ *decibels_at_nyquist = db_value; ++ ++ return true; ++} ++ ++static inline struct fixed31_32 lanczos( ++ struct fixed31_32 x, ++ struct fixed31_32 a2) ++{ ++ return dal_fixed31_32_mul( ++ dal_fixed31_32_sinc(x), ++ dal_fixed31_32_sinc( ++ dal_fixed31_32_mul(x, a2))); ++} ++ ++static bool generate_filter( ++ struct scaler_filter *filter, ++ const struct scaler_filter_params *params, ++ struct fixed31_32 attenuation, ++ struct fixed31_32 *ringing) ++{ ++ uint32_t n = params->phases * params->taps; ++ ++ uint32_t coefficients_quantity = n; ++ uint32_t coefficients_sum_quantity = params->phases; ++ ++ uint32_t i; ++ uint32_t i_limit; ++ uint32_t j; ++ uint32_t m; ++ ++ struct fixed31_32 attenby2; ++ ++ struct fixed31_32 a_max = dal_fixed31_32_zero; ++ struct fixed31_32 a_min = dal_fixed31_32_zero; ++ ++ if (filter->coefficients_quantity < coefficients_quantity) { ++ if (filter->coefficients) { ++ dc_service_free(filter->ctx, filter->coefficients); ++ ++ filter->coefficients = NULL; ++ filter->coefficients_quantity = 0; ++ } ++ ++ filter->coefficients = dc_service_alloc( ++ filter->ctx, ++ coefficients_quantity * sizeof(struct fixed31_32)); ++ ++ if (!filter->coefficients) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ filter->coefficients_quantity = coefficients_quantity; ++ } ++ ++ i = 0; ++ ++ while (i != filter->coefficients_quantity) { ++ filter->coefficients[i] = dal_fixed31_32_zero; ++ ++ ++i; ++ } ++ ++ if (filter->coefficients_sum_quantity < coefficients_sum_quantity) { ++ if (filter->coefficients_sum) { ++ dc_service_free(filter->ctx, filter->coefficients_sum); ++ ++ filter->coefficients_sum = NULL; ++ filter->coefficients_sum_quantity = 0; ++ } ++ ++ filter->coefficients_sum = dc_service_alloc( ++ filter->ctx, ++ coefficients_sum_quantity * sizeof(struct fixed31_32)); ++ ++ if (!filter->coefficients_sum) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ filter->coefficients_sum_quantity = coefficients_sum_quantity; ++ } ++ ++ i = 0; ++ ++ while (i != filter->coefficients_sum_quantity) { ++ filter->coefficients_sum[i] = dal_fixed31_32_zero; ++ ++ ++i; ++ } ++ ++ m = 0; ++ ++ attenby2 = dal_fixed31_32_div_int( ++ dal_fixed31_32_mul_int(attenuation, params->taps), 2); ++ ++ i = 1; ++ ++ while (i <= params->taps) { ++ j = 0; ++ ++ while (j != params->phases) { ++ struct fixed31_32 x = dal_fixed31_32_mul( ++ dal_fixed31_32_pi, ++ dal_fixed31_32_from_fraction( ++ (int64_t)(m << 1) - n, n)); ++ ++ uint32_t index = ++ (params->taps - i) * params->phases + j; ++ ++ filter->coefficients[index] = lanczos(x, attenby2); ++ ++ ++m; ++ ++ ++j; ++ } ++ ++ ++i; ++ } ++ ++ i = 0; ++ ++ while (i != params->phases) { ++ filter->coefficients_sum[i] = dal_fixed31_32_zero; ++ ++ m = i; ++ ++ j = 0; ++ ++ while (j != params->taps) { ++ filter->coefficients_sum[i] = ++ dal_fixed31_32_add( ++ filter->coefficients_sum[i], ++ filter->coefficients[m]); ++ ++ m += params->phases; ++ ++ ++j; ++ } ++ ++ ++i; ++ } ++ ++ i = 0; ++ ++ while (i != params->phases) { ++ m = i; ++ ++ j = 0; ++ ++ while (j != params->taps) { ++ filter->coefficients[m] = ++ dal_fixed31_32_div( ++ filter->coefficients[m], ++ filter->coefficients_sum[i]); ++ ++ m += params->phases; ++ ++ ++j; ++ } ++ ++ ++i; ++ } ++ ++ i = 0; ++ i_limit = (params->phases >> 1) + 1; ++ ++ while (i != i_limit) { ++ m = i; ++ ++ j = 0; ++ ++ while (j != params->taps) { ++ struct fixed31_32 tmp = filter->coefficients[m]; ++ ++ filter->filter[i * params->taps + j] = tmp; ++ ++ if (dal_fixed31_32_lt( ++ tmp, dal_fixed31_32_zero) && ++ dal_fixed31_32_lt(tmp, a_min)) ++ a_min = tmp; ++ else if (dal_fixed31_32_lt( ++ dal_fixed31_32_zero, tmp) && ++ dal_fixed31_32_lt(a_max, tmp)) ++ a_max = tmp; ++ ++ m += params->phases; ++ ++ ++j; ++ } ++ ++ ++i; ++ } ++ ++ if (dal_fixed31_32_eq(a_min, dal_fixed31_32_zero)) ++ *ringing = dal_fixed31_32_from_int(100); ++ else ++ *ringing = dal_fixed31_32_min( ++ dal_fixed31_32_abs( ++ dal_fixed31_32_div(a_max, a_min)), ++ dal_fixed31_32_from_int(100)); ++ ++ return true; ++} ++ ++static bool construct_scaler_filter( ++ struct dc_context *ctx, ++ struct scaler_filter *filter) ++{ ++ filter->src_size = 0; ++ filter->dst_size = 0; ++ filter->filter = NULL; ++ filter->integer_filter = NULL; ++ filter->filter_size_allocated = 0; ++ filter->filter_size_effective = 0; ++ filter->coefficients = NULL; ++ filter->coefficients_quantity = 0; ++ filter->coefficients_sum = NULL; ++ filter->coefficients_sum_quantity = 0; ++ filter->downscaling_table = NULL; ++ filter->upscaling_table = NULL; ++ filter->ctx = ctx; ++ ++ if (!create_downscaling_table(filter)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!create_upscaling_table(filter)) { ++ BREAK_TO_DEBUGGER(); ++ destroy_downscaling_table(filter); ++ return false; ++ } ++ ++ return true; ++} ++ ++static void destruct_scaler_filter( ++ struct scaler_filter *filter) ++{ ++ if (filter->coefficients_sum) ++ dc_service_free(filter->ctx, filter->coefficients_sum); ++ ++ if (filter->coefficients) ++ dc_service_free(filter->ctx, filter->coefficients); ++ ++ if (filter->integer_filter) ++ dc_service_free(filter->ctx, filter->integer_filter); ++ ++ if (filter->filter) ++ dc_service_free(filter->ctx, filter->filter); ++ ++ destroy_upscaling_table(filter); ++ ++ destroy_downscaling_table(filter); ++} ++ ++struct scaler_filter *dal_scaler_filter_create(struct dc_context *ctx) ++{ ++ struct scaler_filter *filter = ++ dc_service_alloc(ctx, sizeof(struct scaler_filter)); ++ ++ if (!filter) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ if (construct_scaler_filter(ctx, filter)) ++ return filter; ++ ++ BREAK_TO_DEBUGGER(); ++ ++ dc_service_free(ctx, filter); ++ ++ return NULL; ++} ++ ++bool dal_scaler_filter_generate( ++ struct scaler_filter *filter, ++ const struct scaler_filter_params *params, ++ uint32_t src_size, ++ uint32_t dst_size) ++{ ++ uint32_t filter_size_required; ++ ++ struct fixed31_32 attenuation; ++ struct fixed31_32 decibels_at_nyquist; ++ struct fixed31_32 ringing; ++ ++ if (!params) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if ((params->taps < 3) || (params->taps > 16)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!src_size || !dst_size) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (same_filter_required(filter, params, src_size, dst_size)) ++ return true; ++ ++ filter_size_required = ++ params->taps * ((params->phases >> 1) + 1); ++ ++ if (filter_size_required > filter->filter_size_allocated) { ++ if (filter->filter) { ++ dc_service_free(filter->ctx, filter->filter); ++ ++ filter->filter = 0; ++ filter->filter_size_allocated = 0; ++ } ++ ++ filter->filter = dc_service_alloc( ++ filter->ctx, ++ filter_size_required * sizeof(struct fixed31_32)); ++ ++ if (!filter->filter) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (filter->integer_filter) { ++ dc_service_free(filter->ctx, filter->integer_filter); ++ ++ filter->integer_filter = 0; ++ } ++ ++ filter->integer_filter = dc_service_alloc( ++ filter->ctx, ++ filter_size_required * sizeof(uint32_t)); ++ ++ if (!filter->integer_filter) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ filter->filter_size_allocated = filter_size_required; ++ } ++ ++ filter->filter_size_effective = filter_size_required; ++ ++ if (!map_sharpness(filter, params, src_size, dst_size, ++ &attenuation, &decibels_at_nyquist)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!generate_filter(filter, params, attenuation, &ringing)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ filter->params = *params; ++ filter->src_size = src_size; ++ filter->dst_size = dst_size; ++ ++ return true; ++} ++ ++const struct fixed31_32 *dal_scaler_filter_get( ++ const struct scaler_filter *filter, ++ uint32_t **data, ++ uint32_t *number) ++{ ++ if (!number) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ if (!data) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ *number = filter->filter_size_effective; ++ *data = filter->integer_filter; ++ ++ return filter->filter; ++} ++ ++void dal_scaler_filter_destroy( ++ struct scaler_filter **filter) ++{ ++ if (!filter || !*filter) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ destruct_scaler_filter(*filter); ++ ++ dc_service_free((*filter)->ctx, *filter); ++ ++ *filter = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.h b/drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.h +new file mode 100644 +index 0000000..668691d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/calcs/scaler_filter.h +@@ -0,0 +1,74 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_SCALER_FILTER_H__ ++#define __DAL_SCALER_FILTER_H__ ++ ++struct scaler_filter_params { ++ uint32_t taps; /* 3...16 */ ++ uint32_t phases; ++ int32_t sharpness; /* -50...50 */ ++ union { ++ struct { ++ uint32_t HORIZONTAL:1; ++ uint32_t RESERVED:31; ++ } bits; ++ uint32_t value; ++ } flags; ++}; ++ ++struct q31_32; ++ ++struct scaler_filter { ++ struct scaler_filter_params params; ++ uint32_t src_size; ++ uint32_t dst_size; ++ struct fixed31_32 *filter; ++ uint32_t *integer_filter; ++ uint32_t filter_size_allocated; ++ uint32_t filter_size_effective; ++ struct fixed31_32 *coefficients; ++ uint32_t coefficients_quantity; ++ struct fixed31_32 *coefficients_sum; ++ uint32_t coefficients_sum_quantity; ++ struct fixed31_32 ***downscaling_table; ++ struct fixed31_32 ***upscaling_table; ++ struct dc_context *ctx; ++}; ++ ++struct scaler_filter *dal_scaler_filter_create(struct dc_context *ctx); ++void dal_scaler_filter_destroy(struct scaler_filter **ptr); ++ ++bool dal_scaler_filter_generate( ++ struct scaler_filter *filter, ++ const struct scaler_filter_params *params, ++ uint32_t src_size, ++ uint32_t dst_size); ++ ++const struct fixed31_32 *dal_scaler_filter_get( ++ const struct scaler_filter *filter, ++ uint32_t **data, ++ uint32_t *number); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/connector/Makefile b/drivers/gpu/drm/amd/dal/dc/connector/Makefile +new file mode 100644 +index 0000000..ebd4115 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/connector/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the 'connector' sub-component of DAL. ++# It provides the control and status of HW connectors blocks. ++ ++ ++CONNECTOR = connector_base.o connector_signals.o ++ ++AMD_DAL_CONNECTOR = $(addprefix $(AMDDALPATH)/dc/connector/,$(CONNECTOR)) ++ ++AMD_DAL_FILES += $(AMD_DAL_CONNECTOR) +diff --git a/drivers/gpu/drm/amd/dal/dc/connector/connector.h b/drivers/gpu/drm/amd/dal/dc/connector/connector.h +new file mode 100644 +index 0000000..7d6057b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/connector/connector.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CONNECTOR_H__ ++#define __DAL_CONNECTOR_H__ ++ ++#include "include/connector_interface.h" ++ ++extern const uint32_t number_of_default_signals; ++extern const uint32_t number_of_signals; ++ ++/* Indexed by enum connector_id */ ++extern const struct connector_signals default_signals[]; ++/* Indexed by enum connector_id */ ++extern const struct connector_signals supported_signals[]; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/connector/connector_base.c b/drivers/gpu/drm/amd/dal/dc/connector/connector_base.c +new file mode 100644 +index 0000000..34005fd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/connector/connector_base.c +@@ -0,0 +1,421 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "connector.h" ++#include "include/irq_interface.h" ++#include "include/ddc_interface.h" ++#include "include/connector_interface.h" ++ ++struct connector { ++ struct graphics_object_id id; ++ uint32_t input_signals; ++ uint32_t output_signals; ++ struct adapter_service *as; ++ struct connector_feature_support features; ++ struct connector_signals default_signals; ++ struct dc_context *ctx; ++}; ++ ++static bool connector_construct( ++ struct connector *connector, ++ struct dc_context *ctx, ++ struct adapter_service *as, ++ struct graphics_object_id go_id) ++{ ++ bool hw_ddc_polling = false; ++ struct ddc *ddc; ++ struct irq *hpd; ++ enum connector_id connector_id; ++ uint32_t signals_vector = 0; ++ uint32_t signals_num = 0; ++ uint32_t i; ++ ++ if (!as) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ connector->as = as; ++ connector->id = go_id; ++ connector->features.ddc_line = CHANNEL_ID_UNKNOWN; ++ connector->features.hpd_line = HPD_SOURCEID_UNKNOWN; ++ connector->ctx = ctx; ++ ++ ddc = dal_adapter_service_obtain_ddc(as, connector->id); ++ hpd = dal_adapter_service_obtain_hpd_irq(as, connector->id); ++ ++ connector_id = dal_graphics_object_id_get_connector_id(go_id); ++ ++ /* Initialize DDC line */ ++ if (ddc) { ++ switch (dal_ddc_get_line(ddc)) { ++ case GPIO_DDC_LINE_DDC1: ++ connector->features.ddc_line = CHANNEL_ID_DDC1; ++ break; ++ case GPIO_DDC_LINE_DDC2: ++ connector->features.ddc_line = CHANNEL_ID_DDC2; ++ break; ++ case GPIO_DDC_LINE_DDC3: ++ connector->features.ddc_line = CHANNEL_ID_DDC3; ++ break; ++ case GPIO_DDC_LINE_DDC4: ++ connector->features.ddc_line = CHANNEL_ID_DDC4; ++ break; ++ case GPIO_DDC_LINE_DDC5: ++ connector->features.ddc_line = CHANNEL_ID_DDC5; ++ break; ++ case GPIO_DDC_LINE_DDC6: ++ connector->features.ddc_line = CHANNEL_ID_DDC6; ++ break; ++ case GPIO_DDC_LINE_DDC_VGA: ++ connector->features.ddc_line = CHANNEL_ID_DDC_VGA; ++ break; ++ case GPIO_DDC_LINE_I2C_PAD: ++ connector->features.ddc_line = CHANNEL_ID_I2C_PAD; ++ break; ++ default: ++ BREAK_TO_DEBUGGER(); ++ break; ++ } ++ ++ /* Initialize HW DDC polling support ++ * On DCE6.0 only DDC lines support HW polling (I2cPad does not) ++ */ ++ ++ if (dal_adapter_service_is_feature_supported( ++ FEATURE_ENABLE_HW_EDID_POLLING)) { ++ switch (dal_ddc_get_line(ddc)) { ++ case GPIO_DDC_LINE_DDC1: ++ case GPIO_DDC_LINE_DDC2: ++ case GPIO_DDC_LINE_DDC3: ++ case GPIO_DDC_LINE_DDC4: ++ case GPIO_DDC_LINE_DDC5: ++ case GPIO_DDC_LINE_DDC6: ++ case GPIO_DDC_LINE_DDC_VGA: ++ hw_ddc_polling = true; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ dal_adapter_service_release_ddc(as, ddc); ++ } ++ ++ /* Initialize HPD line */ ++ if (hpd) { ++ switch (dal_irq_get_source(hpd)) { ++ case DC_IRQ_SOURCE_HPD1: ++ connector->features.hpd_line = HPD_SOURCEID1; ++ break; ++ case DC_IRQ_SOURCE_HPD2: ++ connector->features.hpd_line = HPD_SOURCEID2; ++ break; ++ case DC_IRQ_SOURCE_HPD3: ++ connector->features.hpd_line = HPD_SOURCEID3; ++ break; ++ case DC_IRQ_SOURCE_HPD4: ++ connector->features.hpd_line = HPD_SOURCEID4; ++ break; ++ case DC_IRQ_SOURCE_HPD5: ++ connector->features.hpd_line = HPD_SOURCEID5; ++ break; ++ case DC_IRQ_SOURCE_HPD6: ++ connector->features.hpd_line = HPD_SOURCEID6; ++ break; ++ default: ++ BREAK_TO_DEBUGGER(); ++ break; ++ } ++ ++ dal_adapter_service_release_irq(as, hpd); ++ } ++ ++ if ((uint32_t)connector_id >= number_of_default_signals && ++ (uint32_t)connector_id >= number_of_signals) ++ return false; ++ ++ /* Initialize default signals */ ++ connector->default_signals = default_signals[connector_id]; ++ ++ /* Fill supported signals */ ++ signals_num = supported_signals[connector_id].number_of_signals; ++ for (i = 0; i < signals_num; i++) ++ signals_vector |= supported_signals[connector_id].signal[i]; ++ ++ /* Connector supports same set for input and output signals */ ++ connector->input_signals = signals_vector; ++ connector->output_signals = signals_vector; ++ ++ switch (connector_id) { ++ case CONNECTOR_ID_VGA: ++ if (hw_ddc_polling ++ && connector->features.ddc_line != CHANNEL_ID_UNKNOWN) ++ connector->features.HW_DDC_POLLING = true; ++ break; ++ case CONNECTOR_ID_SINGLE_LINK_DVII: ++ case CONNECTOR_ID_DUAL_LINK_DVII: ++ if (connector->features.hpd_line != HPD_SOURCEID_UNKNOWN) ++ connector->features.HPD_FILTERING = true; ++ if (hw_ddc_polling ++ && connector->features.ddc_line != CHANNEL_ID_UNKNOWN) ++ connector->features.HW_DDC_POLLING = true; ++ break; ++ case CONNECTOR_ID_SINGLE_LINK_DVID: ++ case CONNECTOR_ID_DUAL_LINK_DVID: ++ case CONNECTOR_ID_HDMI_TYPE_A: ++ case CONNECTOR_ID_LVDS: ++ case CONNECTOR_ID_DISPLAY_PORT: ++ case CONNECTOR_ID_EDP: ++ if (connector->features.hpd_line != HPD_SOURCEID_UNKNOWN) ++ connector->features.HPD_FILTERING = true; ++ break; ++ default: ++ connector->features.HPD_FILTERING = false; ++ connector->features.HW_DDC_POLLING = false; ++ break; ++ } ++ ++ return true; ++} ++ ++struct connector *dal_connector_create( ++ struct dc_context *ctx, ++ struct adapter_service *as, ++ struct graphics_object_id go_id) ++{ ++ struct connector *connector = NULL; ++ ++ connector = dc_service_alloc(ctx, sizeof(struct connector)); ++ ++ if (!connector) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ if (connector_construct(connector, ctx, as, go_id)) ++ return connector; ++ ++ BREAK_TO_DEBUGGER(); ++ ++ dc_service_free(ctx, connector); ++ ++ return NULL; ++} ++ ++void dal_connector_destroy(struct connector **connector) ++{ ++ if (!connector || !*connector) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ dc_service_free((*connector)->ctx, *connector); ++ ++ *connector = NULL; ++} ++ ++uint32_t dal_connector_enumerate_output_signals( ++ const struct connector *connector) ++{ ++ return connector->output_signals; ++} ++ ++uint32_t dal_connector_enumerate_input_signals( ++ const struct connector *connector) ++{ ++ return connector->input_signals; ++} ++ ++struct connector_signals dal_connector_get_default_signals( ++ const struct connector *connector) ++{ ++ return connector->default_signals; ++} ++ ++const struct graphics_object_id dal_connector_get_graphics_object_id( ++ const struct connector *connector) ++{ ++ return connector->id; ++} ++ ++/* ++ * Function: program_hpd_filter ++ * ++ * @brief ++ * Programs HPD filter on associated HPD line ++ * ++ * @param [in] delay_on_connect_in_ms: Connect filter timeout ++ * @param [in] delay_on_disconnect_in_ms: Disconnect filter timeout ++ * ++ * @return ++ * true on success, false otherwise ++ */ ++bool dal_connector_program_hpd_filter( ++ const struct connector *connector, ++ const uint32_t delay_on_connect_in_ms, ++ const uint32_t delay_on_disconnect_in_ms) ++{ ++ bool result = false; ++ ++ struct irq *hpd; ++ ++ /* Verify feature is supported */ ++ ++ if (!connector->features.HPD_FILTERING) ++ return result; ++ ++ /* Obtain HPD handle */ ++ ++ hpd = dal_adapter_service_obtain_hpd_irq( ++ connector->as, connector->id); ++ ++ if (!hpd) ++ return result; ++ ++ /* Setup HPD filtering */ ++ ++ if (GPIO_RESULT_OK == dal_irq_open(hpd)) { ++ struct gpio_hpd_config config; ++ ++ config.delay_on_connect = delay_on_connect_in_ms; ++ config.delay_on_disconnect = delay_on_disconnect_in_ms; ++ ++ dal_irq_setup_hpd_filter(hpd, &config); ++ ++ dal_irq_close(hpd); ++ ++ result = true; ++ } else { ++ ASSERT_CRITICAL(false); ++ } ++ ++ /* Release HPD handle */ ++ ++ dal_adapter_service_release_irq(connector->as, hpd); ++ ++ return result; ++} ++ ++/* ++ * Function: setup_ddc_polling ++ * ++ * @brief ++ * Enables/Disables HW polling on associated DDC line ++ * ++ * @param [in] ddc_config: Specifies polling mode ++ * ++ * @return ++ * true on success, false otherwise ++ */ ++static bool setup_ddc_polling( ++ const struct connector *connector, ++ enum gpio_ddc_config_type ddc_config) ++{ ++ bool result = false; ++ ++ struct ddc *ddc; ++ ++ /* Verify feature is supported */ ++ ++ if (!connector->features.HW_DDC_POLLING) ++ return result; ++ ++ /* Obtain DDC handle */ ++ ++ ddc = dal_adapter_service_obtain_ddc( ++ connector->as, connector->id); ++ ++ if (!ddc) { ++ BREAK_TO_DEBUGGER(); ++ return result; ++ } ++ ++ /* Setup DDC polling */ ++ ++ if (GPIO_RESULT_OK == dal_ddc_open(ddc, GPIO_MODE_HARDWARE, ++ GPIO_DDC_CONFIG_TYPE_MODE_I2C)) { ++ dal_ddc_set_config(ddc, ddc_config); ++ ++ dal_ddc_close(ddc); ++ ++ result = true; ++ } else { ++ BREAK_TO_DEBUGGER(); ++ } ++ ++ /* Release DDC handle */ ++ ++ dal_adapter_service_release_ddc(connector->as, ddc); ++ ++ return result; ++} ++ ++/* ++ * Function: enable_ddc_polling ++ * ++ * @brief ++ * Enables HW polling on associated DDC line ++ * ++ * @param [in] is_poll_for_connect: Specifies polling mode ++ * ++ * @return ++ * true on success, false otherwise ++ */ ++bool dal_connector_enable_ddc_polling( ++ const struct connector *connector, ++ const bool is_poll_for_connect) ++{ ++ enum gpio_ddc_config_type ddc_config = is_poll_for_connect ? ++ GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT : ++ GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT; ++ ++ return setup_ddc_polling(connector, ddc_config); ++} ++ ++/* ++ * Function: disable_ddc_polling ++ * ++ * @brief ++ * Disables HW polling on associated DDC line ++ * ++ * @return ++ * true on success, false otherwise ++ */ ++bool dal_connector_disable_ddc_polling(const struct connector *connector) ++{ ++ return setup_ddc_polling(connector, ++ GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING); ++} ++ ++void dal_connector_get_features( ++ const struct connector *con, ++ struct connector_feature_support *cfs) ++{ ++ dc_service_memmove(cfs, &con->features, ++ sizeof(struct connector_feature_support)); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/connector/connector_signals.c b/drivers/gpu/drm/amd/dal/dc/connector/connector_signals.c +new file mode 100644 +index 0000000..d1a289d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/connector/connector_signals.c +@@ -0,0 +1,204 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "connector.h" ++ ++static const enum signal_type signals_none[] = { ++ SIGNAL_TYPE_NONE ++}; ++ ++static const enum signal_type signals_single_link_dvii[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++ SIGNAL_TYPE_RGB ++}; ++ ++static const enum signal_type signals_dual_link_dvii[] = { ++ SIGNAL_TYPE_DVI_DUAL_LINK, ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++ SIGNAL_TYPE_RGB ++}; ++ ++static const enum signal_type signals_single_link_dvid[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK ++}; ++ ++static const enum signal_type signals_dual_link_dvid[] = { ++ SIGNAL_TYPE_DVI_DUAL_LINK, ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++}; ++ ++static const enum signal_type signals_vga[] = { ++ SIGNAL_TYPE_RGB ++}; ++ ++static const enum signal_type signals_hdmi_type_a[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++ SIGNAL_TYPE_HDMI_TYPE_A ++}; ++ ++static const enum signal_type signals_lvds[] = { ++ SIGNAL_TYPE_LVDS ++}; ++ ++static const enum signal_type signals_pcie[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++ SIGNAL_TYPE_HDMI_TYPE_A, ++ SIGNAL_TYPE_DISPLAY_PORT ++}; ++ ++static const enum signal_type signals_hardcode_dvi[] = { ++ SIGNAL_TYPE_NONE ++}; ++ ++static const enum signal_type signals_displayport[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++ SIGNAL_TYPE_HDMI_TYPE_A, ++ SIGNAL_TYPE_DISPLAY_PORT, ++ SIGNAL_TYPE_DISPLAY_PORT_MST ++}; ++ ++static const enum signal_type signals_edp[] = { ++ SIGNAL_TYPE_EDP ++}; ++ ++static const enum signal_type signals_wireless[] = { ++ SIGNAL_TYPE_WIRELESS ++}; ++ ++static const enum signal_type signals_miracast[] = { ++ SIGNAL_TYPE_WIRELESS ++}; ++ ++static const enum signal_type default_signals_none[] = { ++ SIGNAL_TYPE_NONE ++}; ++ ++static const enum signal_type default_signals_single_link_dvii[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK, ++ SIGNAL_TYPE_RGB ++}; ++ ++static const enum signal_type default_signals_dual_link_dvii[] = { ++ SIGNAL_TYPE_DVI_DUAL_LINK, ++ SIGNAL_TYPE_RGB ++}; ++ ++static const enum signal_type default_signals_single_link_dvid[] = { ++ SIGNAL_TYPE_DVI_SINGLE_LINK ++}; ++ ++static const enum signal_type default_signals_dual_link_dvid[] = { ++ SIGNAL_TYPE_DVI_DUAL_LINK, ++}; ++ ++static const enum signal_type default_signals_vga[] = { ++ SIGNAL_TYPE_RGB ++}; ++ ++static const enum signal_type default_signals_hdmi_type_a[] = { ++ SIGNAL_TYPE_HDMI_TYPE_A ++}; ++ ++static const enum signal_type default_signals_lvds[] = { ++ SIGNAL_TYPE_LVDS ++}; ++ ++static const enum signal_type default_signals_pcie[] = { ++ SIGNAL_TYPE_DISPLAY_PORT ++}; ++ ++static const enum signal_type default_signals_hardcode_dvi[] = { ++ SIGNAL_TYPE_NONE ++}; ++ ++static const enum signal_type default_signals_displayport[] = { ++ SIGNAL_TYPE_DISPLAY_PORT ++}; ++ ++static const enum signal_type default_signals_edp[] = { ++ SIGNAL_TYPE_EDP ++}; ++ ++static const enum signal_type default_signals_wireless[] = { ++ SIGNAL_TYPE_WIRELESS ++}; ++ ++static const enum signal_type default_signals_miracast[] = { ++ SIGNAL_TYPE_WIRELESS ++}; ++ ++/* ++ * Signal arrays ++ */ ++ ++#define SIGNALS_ARRAY_ELEM(a) {a, ARRAY_SIZE(a)} ++ ++/* Indexed by enum connector_id */ ++const struct connector_signals default_signals[] = { ++ SIGNALS_ARRAY_ELEM(default_signals_none), ++ SIGNALS_ARRAY_ELEM(default_signals_single_link_dvii), ++ SIGNALS_ARRAY_ELEM(default_signals_dual_link_dvii), ++ SIGNALS_ARRAY_ELEM(default_signals_single_link_dvid), ++ SIGNALS_ARRAY_ELEM(default_signals_dual_link_dvid), ++ SIGNALS_ARRAY_ELEM(default_signals_vga), ++ SIGNALS_ARRAY_ELEM(default_signals_hdmi_type_a), ++ SIGNALS_ARRAY_ELEM(default_signals_none), ++ SIGNALS_ARRAY_ELEM(default_signals_lvds), ++ SIGNALS_ARRAY_ELEM(default_signals_pcie), ++ SIGNALS_ARRAY_ELEM(default_signals_hardcode_dvi), ++ SIGNALS_ARRAY_ELEM(default_signals_displayport), ++ SIGNALS_ARRAY_ELEM(default_signals_edp), ++ /* MXM dummy connector */ ++ SIGNALS_ARRAY_ELEM(default_signals_none), ++ SIGNALS_ARRAY_ELEM(default_signals_wireless), ++ SIGNALS_ARRAY_ELEM(default_signals_miracast) ++}; ++ ++const uint32_t number_of_default_signals = ARRAY_SIZE(default_signals); ++ ++/* Indexed by enum connector_id */ ++const struct connector_signals supported_signals[] = { ++ SIGNALS_ARRAY_ELEM(signals_none), ++ SIGNALS_ARRAY_ELEM(signals_single_link_dvii), ++ SIGNALS_ARRAY_ELEM(signals_dual_link_dvii), ++ SIGNALS_ARRAY_ELEM(signals_single_link_dvid), ++ SIGNALS_ARRAY_ELEM(signals_dual_link_dvid), ++ SIGNALS_ARRAY_ELEM(signals_vga), ++ SIGNALS_ARRAY_ELEM(signals_hdmi_type_a), ++ SIGNALS_ARRAY_ELEM(signals_none), ++ SIGNALS_ARRAY_ELEM(signals_lvds), ++ SIGNALS_ARRAY_ELEM(signals_pcie), ++ SIGNALS_ARRAY_ELEM(signals_hardcode_dvi), ++ SIGNALS_ARRAY_ELEM(signals_displayport), ++ SIGNALS_ARRAY_ELEM(signals_edp), ++ /* MXM dummy connector */ ++ SIGNALS_ARRAY_ELEM(signals_none), ++ SIGNALS_ARRAY_ELEM(signals_wireless), ++ SIGNALS_ARRAY_ELEM(signals_miracast) ++}; ++ ++const uint32_t number_of_signals = ARRAY_SIZE(supported_signals); +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc.c b/drivers/gpu/drm/amd/dal/dc/core/dc.c +new file mode 100644 +index 0000000..e13ce4e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc.c +@@ -0,0 +1,849 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ */ ++ ++#include "dc_services.h" ++ ++#include "dc.h" ++ ++#include "core_status.h" ++#include "core_types.h" ++#include "hw_sequencer.h" ++ ++#include "resource.h" ++ ++#include "adapter_service_interface.h" ++#include "clock_source_interface.h" ++ ++#include "include/irq_service_interface.h" ++#include "bandwidth_calcs.h" ++#include "include/irq_service_interface.h" ++ ++#include "link_hwss.h" ++ ++/******************************************************************************* ++ * Private structures ++ ******************************************************************************/ ++ ++struct dc_target_sync_report { ++ uint32_t h_count; ++ uint32_t v_count; ++}; ++ ++struct dc_sync_report { ++ uint32_t targets_num; ++ struct dc_target_sync_report trg_reports[MAX_TARGET_NUM]; ++}; ++ ++/******************************************************************************* ++ * Private functions ++ ******************************************************************************/ ++static void destroy_links(struct dc *dc) ++{ ++ uint32_t i; ++ ++ for (i = 0; i < dc->link_count; i++) { ++ ++ if (NULL != dc->links[i]) ++ link_destroy(&dc->links[i]); ++ } ++} ++ ++ ++static bool create_links(struct dc *dc, const struct dc_init_data *init_params) ++{ ++ int i; ++ int connectors_num; ++ ++ dc->link_count = 0; ++ ++ connectors_num = dal_bios_parser_get_connectors_number( ++ dal_adapter_service_get_bios_parser( ++ init_params->adapter_srv)); ++ ++ if (0 == connectors_num || connectors_num > ENUM_ID_COUNT) { ++ dal_error("DC: Invalid number of connectors!\n"); ++ return false; ++ } ++ ++ dal_output_to_console("%s: connectors_num:%d\n", __func__, ++ connectors_num); ++ ++ dc->links = dc_service_alloc( ++ init_params->ctx, connectors_num * sizeof(struct core_link *)); ++ ++ if (NULL == dc->links) { ++ dal_error("DC: failed to allocate 'links' storage!\n"); ++ goto allocate_dc_links_storage_fail; ++ } ++ ++ for (i = 0; i < connectors_num; i++) { ++ struct link_init_data link_init_params = {0}; ++ struct core_link *link; ++ ++ link_init_params.ctx = init_params->ctx; ++ link_init_params.adapter_srv = init_params->adapter_srv; ++ link_init_params.connector_index = i; ++ link_init_params.link_index = dc->link_count; ++ link_init_params.dc = dc; ++ link = link_create(&link_init_params); ++ ++ if (link) { ++ dc->links[dc->link_count] = link; ++ link->dc = dc; ++ ++dc->link_count; ++ } ++ else { ++ dal_error("DC: failed to create link!\n"); ++ } ++ } ++ ++ if (!dc->link_count) { ++ dal_error("DC: no 'links' were created!\n"); ++ goto allocate_dc_links_storage_fail; ++ } ++ ++ return true; ++ ++allocate_dc_links_storage_fail: ++ return false; ++} ++ ++static void init_hw(struct dc *dc) ++{ ++ int i; ++ struct bios_parser *bp; ++ struct transform *xfm; ++ ++ bp = dal_adapter_service_get_bios_parser(dc->res_pool.adapter_srv); ++ for(i = 0; i < dc->res_pool.controller_count; i++) { ++ xfm = dc->res_pool.transforms[i]; ++ ++ dc->hwss.enable_display_power_gating( ++ dc->ctx, i, bp, ++ PIPE_GATING_CONTROL_INIT); ++ dc->hwss.enable_display_power_gating( ++ dc->ctx, i, bp, ++ PIPE_GATING_CONTROL_DISABLE); ++ ++ dc->hwss.transform_power_up(xfm); ++ dc->hwss.enable_display_pipe_clock_gating( ++ dc->ctx, ++ true); ++ } ++ ++ dc->hwss.clock_gating_power_up(dc->ctx, false); ++ dal_bios_parser_power_up(bp); ++ /***************************************/ ++ ++ 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 core_link *link = dc->links[i]; ++ if (dc->hwss.encoder_power_up(link->link_enc) != ENCODER_RESULT_OK) { ++ dal_error("Failed link encoder power up!\n"); ++ return; ++ } ++ } ++ ++ dal_bios_parser_set_scratch_acc_mode_change(bp); ++ ++ for(i = 0; i < dc->res_pool.controller_count; i++) { ++ struct timing_generator *tg = dc->res_pool.timing_generators[i]; ++ ++ dc->hwss.disable_vga(tg); ++ ++ /* Blank controller using driver code instead of ++ * command table. */ ++ dc->hwss.disable_memory_requests(tg); ++ } ++ ++ for(i = 0; i < dc->res_pool.audio_count; i++) { ++ struct audio *audio = dc->res_pool.audios[i]; ++ ++ if (dal_audio_power_up(audio) != AUDIO_RESULT_OK) ++ dal_error("Failed audio power up!\n"); ++ } ++ ++} ++ ++static struct adapter_service *create_as( ++ struct dc_init_data *dc_init_data, ++ const struct dal_init_data *init) ++{ ++ struct adapter_service *as = NULL; ++ struct as_init_data init_data; ++ ++ dc_service_memset(&init_data, 0, sizeof(init_data)); ++ ++ init_data.ctx = dc_init_data->ctx; ++ ++ /* BIOS parser init data */ ++ init_data.bp_init_data.ctx = dc_init_data->ctx; ++ init_data.bp_init_data.bios = init->asic_id.atombios_base_address; ++ ++ /* HW init data */ ++ init_data.hw_init_data.chip_id = init->asic_id.chip_id; ++ init_data.hw_init_data.chip_family = init->asic_id.chip_family; ++ init_data.hw_init_data.pci_revision_id = init->asic_id.pci_revision_id; ++ init_data.hw_init_data.fake_paths_num = init->asic_id.fake_paths_num; ++ init_data.hw_init_data.feature_flags = init->asic_id.feature_flags; ++ init_data.hw_init_data.hw_internal_rev = init->asic_id.hw_internal_rev; ++ init_data.hw_init_data.runtime_flags = init->asic_id.runtime_flags; ++ init_data.hw_init_data.vram_width = init->asic_id.vram_width; ++ init_data.hw_init_data.vram_type = init->asic_id.vram_type; ++ ++ /* bdf is BUS,DEVICE,FUNCTION*/ ++ init_data.bdf_info = init->bdf_info; ++ ++ init_data.display_param = &init->display_param; ++ ++ as = dal_adapter_service_create(&init_data); ++ ++ return as; ++} ++ ++static void bw_calcs_data_update_from_pplib(struct dc *dc) ++{ ++ struct dal_system_clock_range clk_range = { 0 }; ++ ++ dc_service_get_system_clocks_range(dc->ctx, &clk_range); ++ ++ /* on CZ Gardenia from PPLib we get: ++ * clk_range.max_mclk:80000 ++ * clk_range.min_mclk:80000 ++ * clk_range.max_sclk:80000 ++ * clk_range.min_sclk:30000 */ ++ ++ /* The values for calcs are stored in units of MHz, so for example ++ * 80000 will be stored as 800. */ ++ dc->bw_vbios.high_sclk = frc_to_fixed(clk_range.max_sclk, 100); ++ dc->bw_vbios.low_sclk = frc_to_fixed(clk_range.min_sclk, 100); ++ ++ dc->bw_vbios.high_yclk = frc_to_fixed(clk_range.max_mclk, 100); ++ dc->bw_vbios.low_yclk = frc_to_fixed(clk_range.min_mclk, 100); ++} ++ ++static bool construct(struct dc *dc, const struct dal_init_data *init_params) ++{ ++ struct dal_logger *logger; ++ /* Tempory code ++ * TODO: replace dal_init_data with dc_init_data when dal is removed ++ */ ++ struct dc_init_data dc_init_data = {0}; ++ ++ /* Create dc context */ ++ /* A temp dc context is used only to allocate the memory for actual ++ * dc context */ ++ struct dc_context ctx = {0}; ++ ctx.cgs_device = init_params->cgs_device; ++ ctx.dc = dc; ++ ++ dc_init_data.ctx = dc_service_alloc(&ctx, sizeof(*dc_init_data.ctx)); ++ if (!dc_init_data.ctx) { ++ dal_error("%s: failed to create ctx\n", __func__); ++ goto ctx_fail; ++ } ++ dc_init_data.ctx->driver_context = init_params->driver; ++ dc_init_data.ctx->cgs_device = init_params->cgs_device; ++ dc_init_data.ctx->dc = dc; ++ ++ /* Create logger */ ++ logger = dal_logger_create(dc_init_data.ctx); ++ ++ if (!logger) { ++ /* can *not* call logger. call base driver 'print error' */ ++ dal_error("%s: failed to create Logger!\n", __func__); ++ goto logger_fail; ++ } ++ dc_init_data.ctx->logger = logger; ++ ++ /* Create adapter service */ ++ dc_init_data.adapter_srv = create_as(&dc_init_data, init_params); ++ ++ if (!dc_init_data.adapter_srv) { ++ dal_error("%s: create_as() failed!\n", __func__); ++ goto as_fail; ++ } ++ ++ /* Initialize HW controlled by Adapter Service */ ++ if (false == dal_adapter_service_initialize_hw_data( ++ dc_init_data.adapter_srv)) { ++ dal_error("%s: dal_adapter_service_initialize_hw_data()"\ ++ " failed!\n", __func__); ++ /* Note that AS exist, so have to destroy it.*/ ++ goto as_fail; ++ } ++ ++ dc->ctx = dc_init_data.ctx; ++ ++ /* Create hardware sequencer */ ++ if (!dc_construct_hw_sequencer(dc_init_data.adapter_srv, dc)) ++ goto hwss_fail; ++ ++ ++ /* TODO: create all the sub-objects of DC. */ ++ if (false == create_links(dc, &dc_init_data)) ++ goto create_links_fail; ++ ++ if (!dc->hwss.construct_resource_pool( ++ dc_init_data.adapter_srv, ++ dc, ++ &dc->res_pool)) ++ goto construct_resource_fail; ++ ++ ++ bw_calcs_init(&dc->bw_dceip, &dc->bw_vbios); ++ ++ bw_calcs_data_update_from_pplib(dc); ++ ++ return true; ++ ++ /**** error handling here ****/ ++construct_resource_fail: ++create_links_fail: ++as_fail: ++ dal_logger_destroy(&dc_init_data.ctx->logger); ++logger_fail: ++hwss_fail: ++ dc_service_free(&ctx, dc_init_data.ctx); ++ctx_fail: ++ return false; ++} ++ ++static void destruct(struct dc *dc) ++{ ++ destroy_links(dc); ++ dc_service_free(dc->ctx, dc->links); ++ dc->hwss.destruct_resource_pool(&dc->res_pool); ++ dal_logger_destroy(&dc->ctx->logger); ++ dc_service_free(dc->ctx, dc->ctx); ++} ++ ++/******************************************************************************* ++ * Public functions ++ ******************************************************************************/ ++ ++struct dc *dc_create(const struct dal_init_data *init_params) ++ { ++ struct dc_context ctx = { ++ .driver_context = init_params->driver, ++ .cgs_device = init_params->cgs_device ++ }; ++ struct dc *dc = dc_service_alloc(&ctx, sizeof(*dc)); ++ ++ if (NULL == dc) ++ goto alloc_fail; ++ ++ ctx.dc = dc; ++ if (false == construct(dc, init_params)) ++ goto construct_fail; ++ ++ /*TODO: separate HW and SW initialization*/ ++ init_hw(dc); ++ ++ return dc; ++ ++construct_fail: ++ dc_service_free(&ctx, dc); ++ ++alloc_fail: ++ return NULL; ++} ++ ++void dc_destroy(struct dc **dc) ++{ ++ destruct(*dc); ++ dc_service_free((*dc)->ctx, *dc); ++ *dc = NULL; ++} ++ ++bool dc_validate_resources( ++ const struct dc *dc, ++ const struct dc_validation_set set[], ++ uint8_t set_count) ++{ ++ enum dc_status result = DC_ERROR_UNEXPECTED; ++ struct validate_context *context; ++ ++ context = dc_service_alloc(dc->ctx, sizeof(struct validate_context)); ++ if(context == NULL) ++ goto context_alloc_fail; ++ ++ result = dc->hwss.validate_with_context(dc, set, set_count, context); ++ ++ dc_service_free(dc->ctx, context); ++context_alloc_fail: ++ ++ return (result == DC_OK); ++ ++} ++ ++static void program_timing_sync( ++ struct dc_context *dc_ctx, ++ struct validate_context *ctx) ++{ ++ uint8_t i; ++ uint8_t j; ++ uint8_t group_size = 0; ++ uint8_t tg_count = ctx->res_ctx.pool.controller_count; ++ struct timing_generator *tg_set[3]; ++ ++ for (i = 0; i < tg_count; i++) { ++ if (!ctx->res_ctx.controller_ctx[i].stream) ++ continue; ++ ++ tg_set[0] = ctx->res_ctx.pool.timing_generators[i]; ++ group_size = 1; ++ ++ /* Add tg to the set, search rest of the tg's for ones with ++ * same timing, add all tgs with same timing to the group ++ */ ++ for (j = i + 1; j < tg_count; j++) { ++ if (!ctx->res_ctx.controller_ctx[j].stream) ++ continue; ++ ++ if (is_same_timing( ++ &ctx->res_ctx.controller_ctx[j].stream->public ++ .timing, ++ &ctx->res_ctx.controller_ctx[i].stream->public ++ .timing)) { ++ tg_set[group_size] = ++ ctx->res_ctx.pool.timing_generators[j]; ++ group_size++; ++ } ++ } ++ ++ /* Right now we limit to one timing sync group so if one is ++ * found we break. A group has to be more than one tg.*/ ++ if (group_size > 1) ++ break; ++ } ++ ++ if(group_size > 1) { ++ dc_ctx->dc->hwss.enable_timing_synchronization(dc_ctx, group_size, tg_set); ++ } ++} ++ ++static bool targets_changed( ++ struct dc *dc, ++ struct dc_target *targets[], ++ uint8_t target_count) ++{ ++ uint8_t i; ++ ++ if (target_count != dc->current_context.target_count) ++ return true; ++ ++ for (i = 0; i < dc->current_context.target_count; i++) { ++ if (&dc->current_context.targets[i]->public != targets[i]) ++ return true; ++ } ++ ++ return false; ++} ++ ++static void pplib_post_set_mode( ++ struct dc *dc, ++ const struct validate_context *context) ++{ ++ struct dc_pp_display_configuration pp_display_cfg = { 0 }; ++ ++ pp_display_cfg.nb_pstate_switch_disable = ++ context->bw_results.nbp_state_change_enable == false; ++ ++ pp_display_cfg.cpu_cc6_disable = ++ context->bw_results.cpuc_state_change_enable == false; ++ ++ pp_display_cfg.cpu_pstate_disable = ++ context->bw_results.cpup_state_change_enable == false; ++ ++ /* TODO: get cpu_pstate_separation_time from BW Calcs. */ ++ pp_display_cfg.cpu_pstate_separation_time = 0; ++ ++ dc_service_pp_post_dce_clock_change(dc->ctx, &pp_display_cfg); ++} ++ ++bool dc_commit_targets( ++ struct dc *dc, ++ struct dc_target *targets[], ++ uint8_t target_count) ++{ ++ enum dc_status result = DC_ERROR_UNEXPECTED; ++ struct validate_context *context; ++ struct dc_validation_set set[4]; ++ uint8_t i; ++ ++ if (false == targets_changed(dc, targets, target_count)) ++ return DC_OK; ++ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_INTERFACE_TRACE, ++ LOG_MINOR_COMPONENT_DC, ++ "%s: %d targets", ++ __func__, ++ target_count); ++ ++ for (i = 0; i < target_count; i++) { ++ struct dc_target *target = targets[i]; ++ ++ dc_target_log(target, ++ dc->ctx->logger, ++ LOG_MAJOR_INTERFACE_TRACE, ++ LOG_MINOR_COMPONENT_DC); ++ ++ set[i].target = targets[i]; ++ set[i].surface_count = 0; ++ ++ } ++ ++ context = dc_service_alloc(dc->ctx, sizeof(struct validate_context)); ++ if (context == NULL) ++ goto context_alloc_fail; ++ ++ result = dc->hwss.validate_with_context(dc, set, target_count, context); ++ if (result != DC_OK){ ++ BREAK_TO_DEBUGGER(); ++ goto fail; ++ } ++ ++ if (!dal_adapter_service_is_in_accelerated_mode( ++ dc->res_pool.adapter_srv)) { ++ dc->hwss.enable_accelerated_mode(context); ++ } ++ ++ for (i = 0; i < dc->current_context.target_count; i++) { ++ /*TODO: optimize this to happen only when necessary*/ ++ dc_target_disable_memory_requests( ++ &dc->current_context.targets[i]->public); ++ } ++ ++ if (result == DC_OK) { ++ dc->hwss.reset_hw_ctx(dc, context, target_count); ++ ++ if (context->target_count > 0) ++ result = dc->hwss.apply_ctx_to_hw(dc, context); ++ } ++ ++ for (i = 0; i < context->target_count; i++) { ++ struct dc_target *dc_target = &context->targets[i]->public; ++ if (context->targets[i]->status.surface_count > 0) ++ dc_target_enable_memory_requests(dc_target); ++ } ++ ++ /* Release old targets */ ++ for (i = 0; i < dc->current_context.target_count; i++) { ++ dc_target_release( ++ &dc->current_context.targets[i]->public); ++ dc->current_context.targets[i] = NULL; ++ } ++ /* Retain new targets*/ ++ for (i = 0; i < context->target_count; i++) { ++ dc_target_retain(&context->targets[i]->public); ++ } ++ ++ dc->current_context = *context; ++ ++ program_timing_sync(dc->ctx, context); ++ ++ pplib_post_set_mode(dc, context); ++ ++ /* TODO: disable unused plls*/ ++fail: ++ dc_service_free(dc->ctx, context); ++ ++context_alloc_fail: ++ return (result == DC_OK); ++} ++ ++uint8_t dc_get_current_target_count(const struct dc *dc) ++{ ++ return dc->current_context.target_count; ++} ++ ++struct dc_target *dc_get_target_at_index(const struct dc *dc, uint8_t i) ++{ ++ if (i < dc->current_context.target_count) ++ return &dc->current_context.targets[i]->public; ++ return NULL; ++} ++ ++const struct dc_link *dc_get_link_at_index(struct dc *dc, uint32_t link_index) ++{ ++ return &dc->links[link_index]->public; ++} ++ ++const struct graphics_object_id dc_get_link_id_at_index( ++ struct dc *dc, uint32_t link_index) ++{ ++ return dc->links[link_index]->link_id; ++} ++ ++const struct ddc_service *dc_get_ddc_at_index( ++ struct dc *dc, uint32_t link_index) ++{ ++ return dc->links[link_index]->ddc; ++} ++ ++const enum dc_irq_source dc_get_hpd_irq_source_at_index( ++ struct dc *dc, uint32_t link_index) ++{ ++ return dc->links[link_index]->public.irq_source_hpd; ++} ++ ++const struct audio **dc_get_audios(struct dc *dc) ++{ ++ return (const struct audio **)dc->res_pool.audios; ++} ++ ++void dc_get_caps(const struct dc *dc, struct dc_caps *caps) ++{ ++ caps->max_targets = dal_min(dc->res_pool.controller_count, dc->link_count); ++ caps->max_links = dc->link_count; ++ caps->max_audios = dc->res_pool.audio_count; ++} ++ ++void dc_flip_surface_addrs(struct dc* dc, ++ const struct dc_surface *const surfaces[], ++ struct dc_flip_addrs flip_addrs[], ++ uint32_t count) ++{ ++ uint8_t i; ++ for (i = 0; i < count; i++) { ++ struct core_surface *surface = DC_SURFACE_TO_CORE(surfaces[i]); ++ /* ++ * TODO figure out a good way to keep track of address. Until ++ * then we'll have to awkwardly bypass the "const" surface. ++ */ ++ surface->public.address = flip_addrs[i].address; ++ dc->hwss.update_plane_address( ++ surface, ++ DC_TARGET_TO_CORE(surface->status.dc_target)); ++ } ++} ++ ++enum dc_irq_source dc_interrupt_to_irq_source( ++ struct dc *dc, ++ uint32_t src_id, ++ uint32_t ext_id) ++{ ++ return dal_irq_service_to_irq_source(dc->res_pool.irqs, src_id, ext_id); ++} ++ ++ ++void dc_interrupt_set(const struct dc *dc, enum dc_irq_source src, bool enable) ++{ ++ dal_irq_service_set(dc->res_pool.irqs, src, enable); ++} ++ ++void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src) ++{ ++ dal_irq_service_ack(dc->res_pool.irqs, src); ++} ++ ++const struct dc_target *dc_get_target_on_irq_source( ++ const struct dc *dc, ++ enum dc_irq_source src) ++{ ++ uint8_t i, j; ++ uint8_t crtc_idx; ++ ++ switch (src) { ++ case DC_IRQ_SOURCE_VUPDATE1: ++ case DC_IRQ_SOURCE_VUPDATE2: ++ case DC_IRQ_SOURCE_VUPDATE3: ++ case DC_IRQ_SOURCE_VUPDATE4: ++ case DC_IRQ_SOURCE_VUPDATE5: ++ case DC_IRQ_SOURCE_VUPDATE6: ++ crtc_idx = src - DC_IRQ_SOURCE_VUPDATE1; ++ break; ++ case DC_IRQ_SOURCE_PFLIP1: ++ case DC_IRQ_SOURCE_PFLIP2: ++ case DC_IRQ_SOURCE_PFLIP3: ++ case DC_IRQ_SOURCE_PFLIP4: ++ case DC_IRQ_SOURCE_PFLIP5: ++ case DC_IRQ_SOURCE_PFLIP6: ++ case DC_IRQ_SOURCE_PFLIP_UNDERLAY0: ++ crtc_idx = src - DC_IRQ_SOURCE_PFLIP1; ++ break; ++ default: ++ dal_error("%s: invalid irq source: %d\n!",__func__, src); ++ goto fail; ++ } ++ ++ for (i = 0; i < dc->current_context.target_count; i++) { ++ const struct core_target *target = ++ dc->current_context.targets[i]; ++ ++ if (NULL == target) { ++ dal_error("%s: 'dc_target' is NULL for irq source: %d\n!", ++ __func__, src); ++ continue; ++ } ++ ++ for (j = 0; j < target->stream_count; j++) { ++ const uint8_t controller_idx = ++ target->streams[j]->controller_idx; ++ if (controller_idx == crtc_idx) ++ return &target->public; ++ } ++ } ++fail: ++ return NULL; ++} ++ ++void dc_set_power_state( ++ struct dc *dc, ++ enum dc_acpi_cm_power_state power_state, ++ enum dc_video_power_state video_power_state) ++{ ++ dc->previous_power_state = dc->current_power_state; ++ dc->current_power_state = video_power_state; ++ ++ switch (power_state) { ++ case DC_ACPI_CM_POWER_STATE_D0: ++ init_hw(dc); ++ break; ++ default: ++ /* NULL means "reset/release all DC targets" */ ++ dc_commit_targets(dc, NULL, 0); ++ ++ dc->hwss.power_down(&dc->current_context); ++ break; ++ } ++ ++} ++ ++void dc_resume(const struct dc *dc) ++{ ++ uint32_t i; ++ ++ for (i = 0; i < dc->link_count; i++) ++ core_link_resume(dc->links[i]); ++} ++ ++void dc_print_sync_report( ++ const struct dc *dc) ++{ ++ uint32_t i; ++ const struct core_target *core_target; ++ struct dc_context *dc_ctx = dc->ctx; ++ struct dc_target_sync_report *target_sync_report; ++ struct dc_sync_report sync_report = { 0 }; ++ ++ if (dc->current_context.target_count > MAX_TARGET_NUM) { ++ DC_ERROR("Target count: %d > %d!\n", ++ dc->current_context.target_count, ++ MAX_TARGET_NUM); ++ return; ++ } ++ ++ sync_report.targets_num = dc->current_context.target_count; ++ ++ /* Step 1: get data for sync validation */ ++ for (i = 0; i < dc->current_context.target_count; i++) { ++ ++ core_target = dc->current_context.targets[i]; ++ target_sync_report = &sync_report.trg_reports[i]; ++ ++ dc->hwss.get_crtc_positions( ++ core_target->streams[0]->tg, ++ &target_sync_report->h_count, ++ &target_sync_report->v_count); ++ ++ DC_SYNC_INFO("GSL:target[%d]: h: %d\t v: %d\n", ++ i, ++ target_sync_report->h_count, ++ target_sync_report->v_count); ++ } ++ ++ /* Step 2: validate that display pipes are synchronized (based on ++ * data from Step 1). */ ++} ++ ++bool dc_read_dpcd( ++ struct dc *dc, ++ uint32_t link_index, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size) ++{ ++ struct core_link *link = ++ DC_LINK_TO_LINK(dc_get_link_at_index(dc, link_index)); ++ enum dc_status r = core_link_read_dpcd(link, address, data, size); ++ ++ return r == DC_OK; ++} ++ ++bool dc_write_dpcd( ++ struct dc *dc, ++ uint32_t link_index, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size) ++{ ++ struct core_link *link = ++ DC_LINK_TO_LINK(dc_get_link_at_index(dc, link_index)); ++ enum dc_status r = core_link_write_dpcd(link, address, data, size); ++ ++ return r == DC_OK; ++} ++ ++bool dc_link_add_sink( ++ struct dc_link *link, ++ struct dc_sink *sink) ++{ ++ if (link->sink_count >= MAX_SINKS_PER_LINK) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ link->sink[link->sink_count] = sink; ++ link->sink_count++; ++ ++ return true; ++} ++ ++ ++void dc_link_remove_sink(struct dc_link *link, const struct dc_sink *sink) ++{ ++ int i; ++ ++ if (!link->sink_count) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ for (i = 0; i < link->sink_count; i++) { ++ if (link->sink[i] == sink) { ++ dc_sink_release(sink); ++ link->sink[i] = NULL; ++ link->sink_count--; ++ return; ++ } ++ } ++ ++ BREAK_TO_DEBUGGER(); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c +new file mode 100644 +index 0000000..b9e6ffd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c +@@ -0,0 +1,49 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++#include "core_types.h" ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/dce110_hw_sequencer.h" ++#endif ++ ++bool dc_construct_hw_sequencer( ++ struct adapter_service *adapter_serv, ++ struct dc *dc) ++{ ++ enum dce_version dce_ver = dal_adapter_service_get_dce_version(adapter_serv); ++ ++ switch (dce_ver) ++ { ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ return dce110_hw_sequencer_construct(dc); ++#endif ++ default: ++ break; ++ } ++ ++ return false; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link.c +new file mode 100644 +index 0000000..a0a131e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link.c +@@ -0,0 +1,1081 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dc_services.h" ++#include "dc_helpers.h" ++#include "dc.h" ++#include "core_dc.h" ++#include "adapter_service_interface.h" ++#include "grph_object_id.h" ++#include "connector_interface.h" ++#include "gpio_service_interface.h" ++#include "ddc_service_interface.h" ++#include "core_status.h" ++#include "dc_link_dp.h" ++#include "link_hwss.h" ++#include "stream_encoder_types.h" ++#include "link_encoder_types.h" ++#include "hw_sequencer.h" ++ ++ ++#define LINK_INFO(...) \ ++ dal_logger_write(dc_ctx->logger, \ ++ LOG_MAJOR_HW_TRACE, LOG_MINOR_HW_TRACE_HOTPLUG, \ ++ __VA_ARGS__) ++ ++/******************************************************************************* ++ * Private structures ++ ******************************************************************************/ ++ ++ ++/******************************************************************************* ++ * Private functions ++ ******************************************************************************/ ++static void destruct(struct core_link *link) ++{ ++ if (link->connector) ++ dal_connector_destroy(&link->connector); ++ ++ if (link->ddc) ++ dal_ddc_service_destroy(&link->ddc); ++ ++ if(link->link_enc) ++ link->ctx->dc->hwss.encoder_destroy(&link->link_enc); ++} ++ ++static bool detect_sink(struct core_link *link) ++{ ++ uint32_t is_hpd_high = 0; ++ struct irq *hpd_pin; ++ ++ /* todo: may need to lock gpio access */ ++ hpd_pin = dal_adapter_service_obtain_hpd_irq( ++ link->adapter_srv, ++ link->link_id); ++ if (hpd_pin == NULL) ++ goto hpd_gpio_failure; ++ ++ dal_irq_open(hpd_pin); ++ dal_irq_get_value(hpd_pin, &is_hpd_high); ++ dal_irq_close(hpd_pin); ++ dal_adapter_service_release_irq( ++ link->adapter_srv, ++ hpd_pin); ++ ++ if (is_hpd_high) { ++ link->public.type = dc_connection_single; ++ /* TODO: need to do the actual detection */ ++ } else { ++ link->public.type = dc_connection_none; ++ } ++ ++ return true; ++ ++hpd_gpio_failure: ++ return false; ++} ++ ++ ++enum ddc_transaction_type get_ddc_transaction_type( ++ enum signal_type sink_signal) ++{ ++ enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE; ++ ++ ++ switch (sink_signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ case SIGNAL_TYPE_LVDS: ++ case SIGNAL_TYPE_RGB: ++ transaction_type = DDC_TRANSACTION_TYPE_I2C; ++ break; ++ ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_EDP: ++ transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; ++ break; ++ ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ /* MST does not use I2COverAux, but there is the ++ * SPECIAL use case for "immediate dwnstrm device ++ * access" (EPR#370830). */ ++ transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; ++ break; ++ ++ default: ++ break; ++ } ++ ++ ++ return transaction_type; ++} ++ ++static enum signal_type get_basic_signal_type( ++ struct graphics_object_id encoder, ++ struct graphics_object_id downstream) ++{ ++ if (downstream.type == OBJECT_TYPE_CONNECTOR) { ++ switch (downstream.id) { ++ case CONNECTOR_ID_SINGLE_LINK_DVII: ++ switch (encoder.id) { ++ case ENCODER_ID_INTERNAL_DAC1: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC1: ++ case ENCODER_ID_INTERNAL_DAC2: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC2: ++ return SIGNAL_TYPE_RGB; ++ default: ++ return SIGNAL_TYPE_DVI_SINGLE_LINK; ++ } ++ break; ++ case CONNECTOR_ID_DUAL_LINK_DVII: ++ { ++ switch (encoder.id) { ++ case ENCODER_ID_INTERNAL_DAC1: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC1: ++ case ENCODER_ID_INTERNAL_DAC2: ++ case ENCODER_ID_INTERNAL_KLDSCP_DAC2: ++ return SIGNAL_TYPE_RGB; ++ default: ++ return SIGNAL_TYPE_DVI_DUAL_LINK; ++ } ++ } ++ break; ++ case CONNECTOR_ID_SINGLE_LINK_DVID: ++ return SIGNAL_TYPE_DVI_SINGLE_LINK; ++ case CONNECTOR_ID_DUAL_LINK_DVID: ++ return SIGNAL_TYPE_DVI_DUAL_LINK; ++ case CONNECTOR_ID_VGA: ++ return SIGNAL_TYPE_RGB; ++ case CONNECTOR_ID_HDMI_TYPE_A: ++ return SIGNAL_TYPE_HDMI_TYPE_A; ++ case CONNECTOR_ID_LVDS: ++ return SIGNAL_TYPE_LVDS; ++ case CONNECTOR_ID_DISPLAY_PORT: ++ return SIGNAL_TYPE_DISPLAY_PORT; ++ case CONNECTOR_ID_EDP: ++ return SIGNAL_TYPE_EDP; ++ default: ++ return SIGNAL_TYPE_NONE; ++ } ++ } else if (downstream.type == OBJECT_TYPE_ENCODER) { ++ switch (downstream.id) { ++ case ENCODER_ID_EXTERNAL_NUTMEG: ++ case ENCODER_ID_EXTERNAL_TRAVIS: ++ return SIGNAL_TYPE_DISPLAY_PORT; ++ default: ++ return SIGNAL_TYPE_NONE; ++ } ++ } ++ ++ return SIGNAL_TYPE_NONE; ++} ++ ++/* ++ * @brief ++ * Check whether there is a dongle on DP connector ++ */ ++static bool is_dp_sink_present(struct core_link *link) ++{ ++ enum gpio_result gpio_result; ++ uint32_t clock_pin = 0; ++ uint32_t data_pin = 0; ++ ++ struct ddc *ddc; ++ ++ enum connector_id connector_id = ++ dal_graphics_object_id_get_connector_id(link->link_id); ++ ++ bool present = ++ ((connector_id == CONNECTOR_ID_DISPLAY_PORT) || ++ (connector_id == CONNECTOR_ID_EDP)); ++ ++ ddc = dal_adapter_service_obtain_ddc(link->adapter_srv, link->link_id); ++ ++ if (!ddc) ++ return present; ++ ++ /* Open GPIO and set it to I2C mode */ ++ /* Note: this GpioMode_Input will be converted ++ * to GpioConfigType_I2cAuxDualMode in GPIO component, ++ * which indicates we need additional delay */ ++ ++ if (GPIO_RESULT_OK != dal_ddc_open( ++ ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) { ++ dal_adapter_service_release_ddc(link->adapter_srv, ddc); ++ ++ return present; ++ } ++ ++ /* Read GPIO: DP sink is present if both clock and data pins are zero */ ++ /* [anaumov] in DAL2, there was no check for GPIO failure */ ++ ++ gpio_result = dal_ddc_get_clock(ddc, &clock_pin); ++ ASSERT(gpio_result == GPIO_RESULT_OK); ++ ++ if (gpio_result == GPIO_RESULT_OK) ++ if (link->link_enc->features.flags.bits. ++ DP_SINK_DETECT_POLL_DATA_PIN) ++ gpio_result = dal_ddc_get_data(ddc, &data_pin); ++ ++ present = (gpio_result == GPIO_RESULT_OK) && !(clock_pin || data_pin); ++ ++ dal_ddc_close(ddc); ++ ++ dal_adapter_service_release_ddc(link->adapter_srv, ddc); ++ ++ return present; ++} ++ ++/* ++ * @brief ++ * Detect output sink type ++ */ ++static enum signal_type link_detect_sink(struct core_link *link) ++{ ++ enum signal_type result = get_basic_signal_type( ++ link->link_enc->id, link->link_id); ++ ++ /* Internal digital encoder will detect only dongles ++ * that require digital signal */ ++ ++ /* Detection mechanism is different ++ * for different native connectors. ++ * LVDS connector supports only LVDS signal; ++ * PCIE is a bus slot, the actual connector needs to be detected first; ++ * eDP connector supports only eDP signal; ++ * HDMI should check straps for audio */ ++ ++ /* PCIE detects the actual connector on add-on board */ ++ ++ if (link->link_id.id == CONNECTOR_ID_PCIE) { ++ /* ZAZTODO implement PCIE add-on card detection */ ++ } ++ ++ switch (link->link_id.id) { ++ case CONNECTOR_ID_HDMI_TYPE_A: { ++ /* check audio support: ++ * if native HDMI is not supported, switch to DVI */ ++ union audio_support audio_support = ++ dal_adapter_service_get_audio_support( ++ link->adapter_srv); ++ ++ if (!audio_support.bits.HDMI_AUDIO_NATIVE) ++ if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A) ++ result = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ } ++ break; ++ case CONNECTOR_ID_DISPLAY_PORT: { ++ ++ /* Check whether DP signal detected: if not - ++ * we assume signal is DVI; it could be corrected ++ * to HDMI after dongle detection */ ++ if (!is_dp_sink_present(link)) ++ result = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return result; ++} ++ ++static enum signal_type decide_signal_from_strap_and_dongle_type( ++ enum display_dongle_type dongle_type, ++ union audio_support *audio_support) ++{ ++ enum signal_type signal = SIGNAL_TYPE_NONE; ++ ++ switch (dongle_type) { ++ case DISPLAY_DONGLE_DP_HDMI_DONGLE: ++ if (audio_support->bits.HDMI_AUDIO_ON_DONGLE) ++ signal = SIGNAL_TYPE_HDMI_TYPE_A; ++ else ++ signal = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ break; ++ case DISPLAY_DONGLE_DP_DVI_DONGLE: ++ signal = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ break; ++ case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: ++ if (audio_support->bits.HDMI_AUDIO_NATIVE) ++ signal = SIGNAL_TYPE_HDMI_TYPE_A; ++ else ++ signal = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ break; ++ default: ++ signal = SIGNAL_TYPE_NONE; ++ break; ++ } ++ ++ return signal; ++} ++ ++static enum signal_type dp_passive_dongle_detection( ++ struct ddc_service *ddc, ++ struct display_sink_capability *sink_cap, ++ union audio_support *audio_support) ++{ ++ /* TODO:These 2 functions should be protected for upstreaming purposes ++ * in case hackers want to save 10 cents hdmi license fee ++ */ ++ dal_ddc_service_i2c_query_dp_dual_mode_adaptor( ++ ddc, sink_cap); ++ return decide_signal_from_strap_and_dongle_type( ++ sink_cap->dongle_type, ++ audio_support); ++} ++ ++static bool is_dp_active_dongle(enum display_dongle_type dongle_type) ++{ ++ return (dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER || ++ dongle_type == DISPLAY_DONGLE_DP_DVI_CONVERTER || ++ dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER); ++} ++ ++/* TODO: To beretired because this call is wrong with ++ * pluging in of active-dongle without display*/ ++static void link_unplug(struct core_link *link) ++{ ++ int i; ++ ++ for (i = 0; i < link->public.sink_count; i++) ++ dc_link_remove_sink(&link->public, link->public.sink[i]); ++} ++ ++static enum dc_edid_status read_edid(struct core_link *link) ++{ ++ uint32_t edid_retry = 3; ++ enum dc_edid_status edid_status; ++ const struct dc_sink *dc_sink = link->public.sink[0]; ++ struct core_sink *sink = DC_SINK_TO_CORE(dc_sink); ++ ++ if (link->public.sink[0]->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_DETECTION_EDID_PARSER, ++ "MST EDID read is not done here!\n"); ++ return EDID_BAD_INPUT; ++ } ++ ++ /* some dongles read edid incorrectly the first time, ++ * do check sum and retry to make sure read correct edid. ++ */ ++ do { ++ sink->public.dc_edid.length = ++ dal_ddc_service_edid_query(link->ddc); ++ ++ if (0 == sink->public.dc_edid.length) ++ return EDID_NO_RESPONSE; ++ ++ dal_ddc_service_get_edid_buf(link->ddc, ++ sink->public.dc_edid.raw_edid); ++ edid_status = dc_helpers_parse_edid_caps( ++ link->ctx, ++ &sink->public.dc_edid, ++ &sink->public.edid_caps); ++ --edid_retry; ++ if (edid_status == EDID_BAD_CHECKSUM) ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_DETECTION_EDID_PARSER, ++ "Bad EDID checksum, retry remain: %d\n", ++ edid_retry); ++ } while (edid_status == EDID_BAD_CHECKSUM && edid_retry > 0); ++ ++ return edid_status; ++} ++ ++void dc_link_detect(const struct dc_link *dc_link) ++{ ++ struct core_link *link = DC_LINK_TO_LINK(dc_link); ++ struct sink_init_data sink_init_data = { 0 }; ++ enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE; ++ struct display_sink_capability sink_caps = { 0 }; ++ uint8_t i; ++ enum signal_type signal = SIGNAL_TYPE_NONE; ++ bool converter_disable_audio = false; ++ union audio_support audio_support = ++ dal_adapter_service_get_audio_support( ++ link->adapter_srv); ++ enum dc_edid_status edid_status; ++ struct dc_context *dc_ctx = link->ctx; ++ struct dc_sink *dc_sink; ++ struct core_sink *sink = NULL; ++ ++ if (false == detect_sink(link)) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ if (link->public.type != dc_connection_none) { ++ /* From Disconnected-to-Connected. */ ++ switch (link->public.connector_signal) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: { ++ transaction_type = DDC_TRANSACTION_TYPE_I2C; ++ if (audio_support.bits.HDMI_AUDIO_NATIVE) ++ signal = SIGNAL_TYPE_HDMI_TYPE_A; ++ else ++ signal = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ break; ++ } ++ ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: { ++ transaction_type = DDC_TRANSACTION_TYPE_I2C; ++ signal = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ break; ++ } ++ ++ case SIGNAL_TYPE_DVI_DUAL_LINK: { ++ transaction_type = DDC_TRANSACTION_TYPE_I2C; ++ signal = SIGNAL_TYPE_DVI_DUAL_LINK; ++ break; ++ } ++ ++ case SIGNAL_TYPE_EDP: { ++ detect_dp_sink_caps(link); ++ transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; ++ signal = SIGNAL_TYPE_EDP; ++ break; ++ } ++ ++ case SIGNAL_TYPE_DISPLAY_PORT: { ++ signal = link_detect_sink(link); ++ transaction_type = get_ddc_transaction_type( ++ signal); ++ ++ if (transaction_type == ++ DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { ++ signal = ++ SIGNAL_TYPE_DISPLAY_PORT; ++ detect_dp_sink_caps(link); ++ ++ /* DP active dongles */ ++ if (is_dp_active_dongle( ++ link->dpcd_caps.dongle_type)) { ++ if (!link->dpcd_caps. ++ sink_count.bits.SINK_COUNT) { ++ link->public.type = ++ dc_connection_none; ++ /* active dongle unplug ++ * processing for short irq ++ */ ++ link_unplug(link); ++ return; ++ } ++ ++ if (link->dpcd_caps.dongle_type != ++ DISPLAY_DONGLE_DP_HDMI_CONVERTER) { ++ converter_disable_audio = true; ++ } ++ } ++ if (is_mst_supported(link)) { ++ signal = SIGNAL_TYPE_DISPLAY_PORT_MST; ++ ++ /* ++ * This call will initiate MST topology ++ * discovery. Which will detect ++ * MST ports and add new DRM connector ++ * DRM framework. Then read EDID via ++ * remote i2c over aux.In the end, will ++ * notify DRM detect result and save ++ * EDID into DRM framework. ++ * ++ * .detect is called by .fill_modes. ++ * .fill_modes is called by user mode ++ * ioctl DRM_IOCTL_MODE_GETCONNECTOR. ++ * ++ * .get_modes is called by .fill_modes. ++ * ++ * call .get_modes, AMDGPU DM ++ * implementation will create new ++ * dc_sink and add to dc_link. ++ * For long HPD plug in/out, MST has its ++ * own handle. ++ * ++ * Therefore, just after dc_create, ++ * link->sink is not created for MST ++ * until user mode app calls ++ * DRM_IOCTL_MODE_GETCONNECTOR. ++ * ++ * Need check ->sink usages in case ++ * ->sink = NULL ++ * TODO: s3 resume check*/ ++ ++ if (dc_helpers_dp_mst_start_top_mgr(link->ctx, &link->public)) { ++ return; ++ } else { ++ /* MST not supported */ ++ signal = SIGNAL_TYPE_DISPLAY_PORT; ++ } ++ } ++ } ++ else { ++ /* DP passive dongles */ ++ signal = dp_passive_dongle_detection(link->ddc, ++ &sink_caps, ++ &audio_support); ++ } ++ break; ++ } ++ ++ default: ++ DC_ERROR("Invalid connector type! signal:%d\n", ++ link->public.connector_signal); ++ return; ++ } /* switch() */ ++ ++ if (link->dpcd_caps.sink_count.bits.SINK_COUNT) ++ link->dpcd_sink_count = link->dpcd_caps.sink_count. ++ bits.SINK_COUNT; ++ else ++ link->dpcd_sink_count = 1; ++ ++ ++ dal_ddc_service_set_transaction_type( ++ link->ddc, ++ transaction_type); ++ ++ sink_init_data.link = &link->public; ++ sink_init_data.sink_signal = signal; ++ sink_init_data.dongle_max_pix_clk = ++ sink_caps.max_hdmi_pixel_clock; ++ sink_init_data.converter_disable_audio = ++ converter_disable_audio; ++ ++ dc_sink = sink_create(&sink_init_data); ++ if (!dc_sink) { ++ DC_ERROR("Failed to create sink!\n"); ++ return; ++ } ++ ++ sink = DC_SINK_TO_CORE(dc_sink); ++ ++ /*AG TODO handle failure */ ++ /*Only non MST case here */ ++ if (!dc_link_add_sink(&link->public, &sink->public)) ++ BREAK_TO_DEBUGGER(); ++ ++ edid_status = read_edid(link); ++ ++ switch (edid_status) { ++ case EDID_BAD_CHECKSUM: ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_DETECTION_EDID_PARSER, ++ "EDID checksum invalid.\n"); ++ break; ++ case EDID_NO_RESPONSE: ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_DETECTION_EDID_PARSER, ++ "No EDID read.\n"); ++ return; ++ ++ default: ++ break; ++ } ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_DETECTION, ++ LOG_MINOR_DETECTION_EDID_PARSER, ++ "%s: " ++ "manufacturer_id = %X, " ++ "product_id = %X, " ++ "serial_number = %X, " ++ "manufacture_week = %d, " ++ "manufacture_year = %d, " ++ "display_name = %s, " ++ "speaker_flag = %d, " ++ "audio_mode_count = %d\n", ++ __func__, ++ sink->public.edid_caps.manufacturer_id, ++ sink->public.edid_caps.product_id, ++ sink->public.edid_caps.serial_number, ++ sink->public.edid_caps.manufacture_week, ++ sink->public.edid_caps.manufacture_year, ++ sink->public.edid_caps.display_name, ++ sink->public.edid_caps.speaker_flags, ++ sink->public.edid_caps.audio_mode_count); ++ ++ for (i = 0; i < sink->public.edid_caps.audio_mode_count; i++) { ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_DETECTION, ++ LOG_MINOR_DETECTION_EDID_PARSER, ++ "%s: mode number = %d, " ++ "format_code = %d, " ++ "channel_count = %d, " ++ "sample_rate = %d, " ++ "sample_size = %d\n", ++ __func__, ++ i, ++ sink->public.edid_caps.audio_modes[i].format_code, ++ sink->public.edid_caps.audio_modes[i].channel_count, ++ sink->public.edid_caps.audio_modes[i].sample_rate, ++ sink->public.edid_caps.audio_modes[i].sample_size); ++ } ++ ++ } else { ++ /* From Connected-to-Disconnected. */ ++ switch (link->public.connector_signal) { ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ dc_helpers_dp_mst_stop_top_mgr(link->ctx, &link->public); ++ break; ++ default: ++ break; ++ } ++ link_unplug(link); ++ } ++ ++ LINK_INFO("link=%d, dc_sink_in=%p is now %s\n", ++ link->link_index, &sink->public, ++ (signal == SIGNAL_TYPE_NONE ? "Disconnected":"Connected")); ++ ++ /* TODO: */ ++ ++ return; ++} ++ ++static bool construct( ++ struct core_link *link, ++ const struct link_init_data *init_params) ++{ ++ struct irq *hpd_gpio = NULL; ++ struct ddc_service_init_data ddc_service_init_data = { 0 }; ++ struct dc_context *dc_ctx = init_params->ctx; ++ struct encoder_init_data enc_init_data = { 0 }; ++ struct connector_feature_support cfs = { 0 }; ++ ++ link->dc = init_params->dc; ++ link->adapter_srv = init_params->adapter_srv; ++ link->connector_index = init_params->connector_index; ++ link->ctx = dc_ctx; ++ link->link_index = init_params->link_index; ++ ++ link->link_id = dal_adapter_service_get_connector_obj_id( ++ init_params->adapter_srv, ++ init_params->connector_index); ++ ++ if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { ++ dal_error("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d!\n", ++ __func__, init_params->connector_index); ++ goto create_fail; ++ } ++ ++ switch (link->link_id.id) { ++ case CONNECTOR_ID_HDMI_TYPE_A: ++ link->public.connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; ++ break; ++ case CONNECTOR_ID_SINGLE_LINK_DVID: ++ case CONNECTOR_ID_SINGLE_LINK_DVII: ++ link->public.connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; ++ break; ++ case CONNECTOR_ID_DUAL_LINK_DVID: ++ case CONNECTOR_ID_DUAL_LINK_DVII: ++ link->public.connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK; ++ break; ++ case CONNECTOR_ID_DISPLAY_PORT: ++ link->public.connector_signal = SIGNAL_TYPE_DISPLAY_PORT; ++ hpd_gpio = dal_adapter_service_obtain_hpd_irq( ++ init_params->adapter_srv, ++ link->link_id); ++ ++ if (hpd_gpio != NULL) { ++ link->public.irq_source_hpd_rx = ++ dal_irq_get_rx_source(hpd_gpio); ++ dal_adapter_service_release_irq( ++ init_params->adapter_srv, hpd_gpio); ++ } ++ break; ++ case CONNECTOR_ID_EDP: ++ link->public.connector_signal = SIGNAL_TYPE_EDP; ++ hpd_gpio = dal_adapter_service_obtain_hpd_irq( ++ init_params->adapter_srv, ++ link->link_id); ++ ++ if (hpd_gpio != NULL) { ++ link->public.irq_source_hpd_rx = ++ dal_irq_get_rx_source(hpd_gpio); ++ dal_adapter_service_release_irq( ++ init_params->adapter_srv, hpd_gpio); ++ } ++ break; ++ default: ++ dal_logger_write(dc_ctx->logger, ++ LOG_MAJOR_WARNING, LOG_MINOR_TM_LINK_SRV, ++ "Unsupported Connector type:%d!\n", link->link_id.id); ++ goto create_fail; ++ } ++ ++ /* TODO: #DAL3 Implement id to str function.*/ ++ LINK_INFO("Connector[%d] description:\n", ++ init_params->connector_index); ++ ++ link->connector = dal_connector_create(dc_ctx, ++ init_params->adapter_srv, ++ link->link_id); ++ if (NULL == link->connector) { ++ DC_ERROR("Failed to create connector object!\n"); ++ goto create_fail; ++ } ++ ++ ++ hpd_gpio = dal_adapter_service_obtain_hpd_irq( ++ init_params->adapter_srv, ++ link->link_id); ++ ++ if (hpd_gpio != NULL) { ++ link->public.irq_source_hpd = dal_irq_get_source(hpd_gpio); ++ dal_adapter_service_release_irq( ++ init_params->adapter_srv, hpd_gpio); ++ } ++ ++ ddc_service_init_data.as = link->adapter_srv; ++ ddc_service_init_data.ctx = link->ctx; ++ ddc_service_init_data.id = link->link_id; ++ link->ddc = dal_ddc_service_create(&ddc_service_init_data); ++ ++ if (NULL == link->ddc) { ++ DC_ERROR("Failed to create ddc_service!\n"); ++ goto create_fail; ++ } ++ ++ dal_connector_get_features(link->connector, &cfs); ++ ++ enc_init_data.adapter_service = link->adapter_srv; ++ enc_init_data.ctx = dc_ctx; ++ enc_init_data.encoder = dal_adapter_service_get_src_obj( ++ link->adapter_srv, link->link_id, 0); ++ enc_init_data.connector = link->link_id; ++ enc_init_data.channel = cfs.ddc_line; ++ enc_init_data.hpd_source = cfs.hpd_line; ++ link->link_enc = dc_ctx->dc->hwss.encoder_create(&enc_init_data); ++ ++ if( link->link_enc == NULL) { ++ DC_ERROR("Failed to create link encoder!\n"); ++ goto create_fail; ++ } ++ ++ /* ++ * TODO check if GPIO programmed correctly ++ * ++ * If GPIO isn't programmed correctly HPD might not rise or drain ++ * fast enough, leading to bounces. ++ */ ++#define DELAY_ON_CONNECT_IN_MS 500 ++#define DELAY_ON_DISCONNECT_IN_MS 500 ++ ++ dal_connector_program_hpd_filter( ++ link->connector, ++ DELAY_ON_CONNECT_IN_MS, ++ DELAY_ON_DISCONNECT_IN_MS); ++ ++ return true; ++ ++create_fail: ++ return false; ++} ++ ++/******************************************************************************* ++ * Public functions ++ ******************************************************************************/ ++struct core_link *link_create(const struct link_init_data *init_params) ++{ ++ struct core_link *link = ++ dc_service_alloc(init_params->ctx, sizeof(*link)); ++ link->ctx = init_params->ctx; ++ ++ if (NULL == link) ++ goto alloc_fail; ++ ++ if (false == construct(link, init_params)) ++ goto construct_fail; ++ ++ return link; ++ ++construct_fail: ++ dc_service_free(init_params->ctx, link); ++ ++alloc_fail: ++ return NULL; ++} ++ ++void link_destroy(struct core_link **link) ++{ ++ destruct(*link); ++ dc_service_free((*link)->ctx, *link); ++ *link = NULL; ++} ++ ++static void dpcd_configure_panel_mode( ++ struct core_link *link, ++ enum dp_panel_mode panel_mode) ++{ ++ union dpcd_edp_config edp_config_set; ++ bool panel_mode_edp = false; ++ ++ dc_service_memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); ++ ++ if (DP_PANEL_MODE_DEFAULT != panel_mode) { ++ ++ switch (panel_mode) { ++ case DP_PANEL_MODE_EDP: ++ case DP_PANEL_MODE_SPECIAL: ++ panel_mode_edp = true; ++ break; ++ ++ default: ++ break; ++ } ++ ++ /*set edp panel mode in receiver*/ ++ core_link_read_dpcd( ++ link, ++ DPCD_ADDRESS_EDP_CONFIG_SET, ++ &edp_config_set.raw, ++ sizeof(edp_config_set.raw)); ++ ++ if (edp_config_set.bits.PANEL_MODE_EDP ++ != panel_mode_edp) { ++ enum ddc_result result = DDC_RESULT_UNKNOWN; ++ ++ edp_config_set.bits.PANEL_MODE_EDP = ++ panel_mode_edp; ++ result = core_link_write_dpcd( ++ link, ++ DPCD_ADDRESS_EDP_CONFIG_SET, ++ &edp_config_set.raw, ++ sizeof(edp_config_set.raw)); ++ ++ ASSERT(result == DDC_RESULT_SUCESSFULL); ++ } ++ } ++ dal_logger_write(link->ctx->logger, LOG_MAJOR_DETECTION, ++ LOG_MINOR_DETECTION_DP_CAPS, ++ "Connector: %d eDP panel mode supported: %d " ++ "eDP panel mode enabled: %d \n", ++ link->connector_index, ++ link->dpcd_caps.panel_mode_edp, ++ panel_mode_edp); ++} ++ ++static enum dc_status enable_link_dp(struct core_stream *stream) ++{ ++ enum dc_status status; ++ bool skip_video_pattern; ++ struct core_link *link = stream->sink->link; ++ struct link_settings link_settings = {0}; ++ enum dp_panel_mode panel_mode; ++ ++ /* get link settings for video mode timing */ ++ decide_link_settings(stream, &link_settings); ++ status = dp_enable_link_phy( ++ stream->sink->link, ++ stream->signal, ++ stream->stream_enc->id, ++ &link_settings); ++ ++ panel_mode = dp_get_panel_mode(link); ++ dpcd_configure_panel_mode(link, panel_mode); ++ ++ skip_video_pattern = true; ++ ++ if (link_settings.link_rate == LINK_RATE_LOW) ++ skip_video_pattern = false; ++ ++ if (perform_link_training(link, &link_settings, skip_video_pattern)) { ++ link->cur_link_settings = link_settings; ++ status = DC_OK; ++ } ++ else ++ status = DC_ERROR_UNEXPECTED; ++ ++ return status; ++} ++ ++static enum dc_status enable_link_hdmi(struct core_stream *stream) ++{ ++ struct core_link *link = stream->sink->link; ++ ++ /* TODO:Need to add missing use cases, reference ++ * dal_hw_sequencer_enable_link_base*/ ++ enum dc_status status = DC_OK; ++ ++ /* enable video output */ ++ /* here we need to specify that encoder output settings ++ * need to be calculated as for the set mode, ++ * it will lead to querying dynamic link capabilities ++ * which should be done before enable output */ ++ ++ uint32_t normalized_pix_clk = stream->public.timing.pix_clk_khz; ++ switch (stream->public.timing.display_color_depth) { ++ case COLOR_DEPTH_888: ++ break; ++ case COLOR_DEPTH_101010: ++ normalized_pix_clk = (normalized_pix_clk * 30) / 24; ++ break; ++ case COLOR_DEPTH_121212: ++ normalized_pix_clk = (normalized_pix_clk * 36) / 24; ++ break; ++ case COLOR_DEPTH_161616: ++ normalized_pix_clk = (normalized_pix_clk * 48) / 24; ++ break; ++ default: ++ break; ++ } ++ ++ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ dal_ddc_service_write_scdc_data( ++ stream->sink->link->ddc, ++ normalized_pix_clk, ++ stream->public.timing.flags.LTE_340MCSC_SCRAMBLE); ++ ++ stream->sink->link->cur_link_settings.lane_count = ++ (stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ++ ? LANE_COUNT_EIGHT : LANE_COUNT_FOUR; ++ ++ if (link->ctx->dc->hwss.encoder_enable_output( ++ stream->sink->link->link_enc, ++ &stream->sink->link->cur_link_settings, ++ stream->stream_enc->id, ++ dal_clock_source_get_id(stream->clock_source), ++ stream->signal, ++ stream->public.timing.display_color_depth, ++ stream->public.timing.pix_clk_khz) != ENCODER_RESULT_OK) ++ status = DC_ERROR_UNEXPECTED; ++ ++ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ dal_ddc_service_read_scdc_data(link->ddc); ++ ++ return status; ++} ++ ++/****************************enable_link***********************************/ ++enum dc_status core_link_enable(struct core_stream *stream) ++{ ++ enum dc_status status; ++ switch (stream->signal) { ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_EDP: ++ status = enable_link_dp(stream); ++ break; ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ status = enable_link_hdmi(stream); ++ break; ++ ++ default: ++ status = DC_ERROR_UNEXPECTED; ++ break; ++ } ++ ++ if (stream->audio) { ++ stream->ctx->dc->hwss.set_afmt_memory_power_state( ++ stream->ctx, stream->stream_enc->id, true); ++ /* notify audio driver for audio modes of monitor */ ++ dal_audio_enable_azalia_audio_jack_presence(stream->audio, ++ stream->stream_enc->id); ++ ++ /* un-mute audio */ ++ dal_audio_unmute(stream->audio, stream->stream_enc->id, ++ stream->signal); ++ } ++ ++ return status; ++} ++ ++enum dc_status core_link_disable(struct core_stream *stream) ++{ ++ /* TODO dp_set_hw_test_pattern */ ++ enum dc_status status = DC_OK; ++ struct dc *dc = stream->ctx->dc; ++ ++ /* here we need to specify that encoder output settings ++ * need to be calculated as for the set mode, ++ * it will lead to querying dynamic link capabilities ++ * which should be done before enable output */ ++ ++ if (dc_is_dp_signal(stream->signal)) ++ dp_disable_link_phy(stream->sink->link, stream->signal); ++ else if (ENCODER_RESULT_OK != dc->hwss.encoder_disable_output( ++ stream->sink->link->link_enc, stream->signal)) ++ status = DC_ERROR_UNEXPECTED; ++ ++ if (stream->audio) { ++ dc->hwss.set_afmt_memory_power_state( ++ stream->ctx, stream->stream_enc->id, false); ++ } ++ ++ return status; ++} ++ ++enum dc_status dc_link_validate_mode_timing( ++ const struct core_sink *sink, ++ struct core_link *link, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t max_pix_clk = sink->dongle_max_pix_clk; ++ ++ if (0 != max_pix_clk && timing->pix_clk_khz > max_pix_clk) ++ return DC_EXCEED_DONGLE_MAX_CLK; ++ ++ switch (sink->public.sink_signal) { ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ if(!dp_validate_mode_timing( ++ link, ++ timing)) ++ return DC_NO_DP_LINK_BANDWIDTH; ++ break; ++ ++ default: ++ break; ++ } ++ ++ return DC_OK; ++} ++ ++bool dc_link_set_backlight_level(const struct dc_link *public, uint32_t level) ++{ ++ struct core_link *protected = DC_LINK_TO_CORE(public); ++ struct dc_context *ctx = protected->ctx; ++ ++ dal_logger_write(ctx->logger, LOG_MAJOR_BACKLIGHT, ++ LOG_MINOR_BACKLIGHT_INTERFACE, ++ "New Backlight level: %d (0x%X)\n", level, level); ++ ++ ctx->dc->hwss.encoder_set_lcd_backlight_level(protected->link_enc, level); ++ ++ return true; ++} ++ ++void core_link_resume(struct core_link *link) ++{ ++ dal_connector_program_hpd_filter( ++ link->connector, ++ DELAY_ON_CONNECT_IN_MS, ++ DELAY_ON_DISCONNECT_IN_MS); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c +new file mode 100644 +index 0000000..9214aec +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c +@@ -0,0 +1,1689 @@ ++/* Copyright 2015 Advanced Micro Devices, Inc. */ ++#include "dc_services.h" ++#include "dc.h" ++#include "dc_helpers.h" ++#include "inc/core_types.h" ++#include "link_hwss.h" ++#include "ddc_service_interface.h" ++#include "connector_interface.h" ++#include "core_status.h" ++#include "dpcd_defs.h" ++ ++/* maximum pre emphasis level allowed for each voltage swing level*/ ++static const enum pre_emphasis voltage_swing_to_pre_emphasis[] = { ++ PRE_EMPHASIS_LEVEL3, ++ PRE_EMPHASIS_LEVEL2, ++ PRE_EMPHASIS_LEVEL1, ++ PRE_EMPHASIS_DISABLED }; ++ ++enum { ++ POST_LT_ADJ_REQ_LIMIT = 6, ++ POST_LT_ADJ_REQ_TIMEOUT = 200 ++}; ++ ++enum { ++ LINK_TRAINING_MAX_RETRY_COUNT = 5, ++ /* to avoid infinite loop where-in the receiver ++ * switches between different VS ++ */ ++ LINK_TRAINING_MAX_CR_RETRY = 100 ++}; ++ ++static const struct link_settings link_training_fallback_table[] = { ++/* 2160 Mbytes/sec*/ ++{ LANE_COUNT_FOUR, LINK_RATE_HIGH2, LINK_SPREAD_DISABLED }, ++/* 1080 Mbytes/sec*/ ++{ LANE_COUNT_FOUR, LINK_RATE_HIGH, LINK_SPREAD_DISABLED }, ++/* 648 Mbytes/sec*/ ++{ LANE_COUNT_FOUR, LINK_RATE_LOW, LINK_SPREAD_DISABLED }, ++/* 1080 Mbytes/sec*/ ++{ LANE_COUNT_TWO, LINK_RATE_HIGH2, LINK_SPREAD_DISABLED }, ++/* 540 Mbytes/sec*/ ++{ LANE_COUNT_TWO, LINK_RATE_HIGH, LINK_SPREAD_DISABLED }, ++/* 324 Mbytes/sec*/ ++{ LANE_COUNT_TWO, LINK_RATE_LOW, LINK_SPREAD_DISABLED }, ++/* 540 Mbytes/sec*/ ++{ LANE_COUNT_ONE, LINK_RATE_HIGH2, LINK_SPREAD_DISABLED }, ++/* 270 Mbytes/sec*/ ++{ LANE_COUNT_ONE, LINK_RATE_HIGH, LINK_SPREAD_DISABLED }, ++/* 162 Mbytes/sec*/ ++{ LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED } }; ++ ++static void wait_for_training_aux_rd_interval( ++ struct core_link* link, ++ uint32_t default_wait_in_micro_secs) ++{ ++ uint8_t training_rd_interval; ++ ++ /* overwrite the delay if rev > 1.1*/ ++ if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) { ++ /* DP 1.2 or later - retrieve delay through ++ * "DPCD_ADDR_TRAINING_AUX_RD_INTERVAL" register */ ++ core_link_read_dpcd( ++ link, ++ DPCD_ADDRESS_TRAINING_AUX_RD_INTERVAL, ++ &training_rd_interval, ++ sizeof(training_rd_interval)); ++ default_wait_in_micro_secs = training_rd_interval ? ++ (training_rd_interval * 4000) : ++ default_wait_in_micro_secs; ++ } ++ ++ dc_service_delay_in_microseconds(link->ctx, default_wait_in_micro_secs); ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s:\n wait = %d\n", ++ __func__, ++ default_wait_in_micro_secs); ++} ++ ++static void dpcd_set_training_pattern( ++ struct core_link* link, ++ union dpcd_training_pattern dpcd_pattern) ++{ ++ core_link_write_dpcd( ++ link, ++ DPCD_ADDRESS_TRAINING_PATTERN_SET, ++ &dpcd_pattern.raw, ++ 1); ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s\n %x pattern = %x\n", ++ __func__, ++ DPCD_ADDRESS_TRAINING_PATTERN_SET, ++ dpcd_pattern.bits.TRAINING_PATTERN_SET); ++} ++ ++static void dpcd_set_link_settings( ++ struct core_link* link, ++ const struct link_training_settings *lt_settings) ++{ ++ uint8_t rate = (uint8_t) ++ (lt_settings->link_settings.link_rate); ++ ++ union down_spread_ctrl downspread = {{0}}; ++ union lane_count_set lane_count_set = {{0}}; ++ uint8_t link_set_buffer[2]; ++ ++ ++ downspread.raw = (uint8_t) ++ (lt_settings->link_settings.link_spread); ++ ++ lane_count_set.bits.LANE_COUNT_SET = ++ lt_settings->link_settings.lane_count; ++ ++ lane_count_set.bits.ENHANCED_FRAMING = 1; ++ ++ lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = ++ link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED; ++ ++ link_set_buffer[0] = rate; ++ link_set_buffer[1] = lane_count_set.raw; ++ ++ core_link_write_dpcd(link, DPCD_ADDRESS_LINK_BW_SET, ++ link_set_buffer, 2); ++ core_link_write_dpcd(link, DPCD_ADDRESS_DOWNSPREAD_CNTL, ++ &downspread.raw, sizeof(downspread)); ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n", ++ __func__, ++ DPCD_ADDRESS_LINK_BW_SET, ++ lt_settings->link_settings.link_rate, ++ DPCD_ADDRESS_LANE_COUNT_SET, ++ lt_settings->link_settings.lane_count, ++ DPCD_ADDRESS_DOWNSPREAD_CNTL, ++ lt_settings->link_settings.link_spread); ++ ++} ++ ++static enum dpcd_training_patterns ++ hw_training_pattern_to_dpcd_training_pattern( ++ struct core_link* link, ++ enum hw_dp_training_pattern pattern) ++{ ++ enum dpcd_training_patterns dpcd_tr_pattern = ++ DPCD_TRAINING_PATTERN_VIDEOIDLE; ++ ++ switch (pattern) { ++ case HW_DP_TRAINING_PATTERN_1: ++ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1; ++ break; ++ case HW_DP_TRAINING_PATTERN_2: ++ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2; ++ break; ++ case HW_DP_TRAINING_PATTERN_3: ++ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3; ++ break; ++ default: ++ ASSERT(0); ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s: Invalid HW Training pattern: %d\n", ++ __func__, pattern); ++ break; ++ } ++ ++ return dpcd_tr_pattern; ++ ++} ++ ++static void dpcd_set_lt_pattern_and_lane_settings( ++ struct core_link* link, ++ const struct link_training_settings *lt_settings, ++ enum hw_dp_training_pattern pattern) ++{ ++ union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}}; ++ const uint32_t dpcd_base_lt_offset = ++ DPCD_ADDRESS_TRAINING_PATTERN_SET; ++ uint8_t dpcd_lt_buffer[5] = {0}; ++ union dpcd_training_pattern dpcd_pattern = {{0}}; ++ uint32_t lane; ++ uint32_t size_in_bytes; ++ bool edp_workaround = false; /* TODO link_prop.INTERNAL */ ++ ++ /***************************************************************** ++ * DpcdAddress_TrainingPatternSet ++ *****************************************************************/ ++ dpcd_pattern.bits.TRAINING_PATTERN_SET = ++ hw_training_pattern_to_dpcd_training_pattern(link, pattern); ++ ++ dpcd_lt_buffer[DPCD_ADDRESS_TRAINING_PATTERN_SET - dpcd_base_lt_offset] ++ = dpcd_pattern.raw; ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s\n %x pattern = %x\n", ++ __func__, ++ DPCD_ADDRESS_TRAINING_PATTERN_SET, ++ dpcd_pattern.bits.TRAINING_PATTERN_SET); ++ ++ ++ /***************************************************************** ++ * DpcdAddress_Lane0Set -> DpcdAddress_Lane3Set ++ *****************************************************************/ ++ for (lane = 0; lane < ++ (uint32_t)(lt_settings->link_settings.lane_count); lane++) { ++ ++ dpcd_lane[lane].bits.VOLTAGE_SWING_SET = ++ (uint8_t)(lt_settings->lane_settings[lane].VOLTAGE_SWING); ++ dpcd_lane[lane].bits.PRE_EMPHASIS_SET = ++ (uint8_t)(lt_settings->lane_settings[lane].PRE_EMPHASIS); ++ ++ dpcd_lane[lane].bits.MAX_SWING_REACHED = ++ (lt_settings->lane_settings[lane].VOLTAGE_SWING == ++ VOLTAGE_SWING_MAX_LEVEL ? 1 : 0); ++ dpcd_lane[lane].bits.MAX_PRE_EMPHASIS_REACHED = ++ (lt_settings->lane_settings[lane].PRE_EMPHASIS == ++ PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); ++ } ++ ++ /* concatinate everything into one buffer*/ ++ ++ size_in_bytes = lt_settings->link_settings.lane_count * sizeof(dpcd_lane[0]); ++ ++ // 0x00103 - 0x00102 ++ dc_service_memmove( ++ &dpcd_lt_buffer[DPCD_ADDRESS_LANE0_SET - dpcd_base_lt_offset], ++ dpcd_lane, ++ size_in_bytes); ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s:\n %x VS set = %x PE set = %x \ ++ max VS Reached = %x max PE Reached = %x\n", ++ __func__, ++ DPCD_ADDRESS_LANE0_SET, ++ dpcd_lane[0].bits.VOLTAGE_SWING_SET, ++ dpcd_lane[0].bits.PRE_EMPHASIS_SET, ++ dpcd_lane[0].bits.MAX_SWING_REACHED, ++ dpcd_lane[0].bits.MAX_PRE_EMPHASIS_REACHED); ++ ++ ++ if (edp_workaround) { ++ /* for eDP write in 2 parts because the 5-byte burst is ++ * causing issues on some eDP panels (EPR#366724) ++ */ ++ core_link_write_dpcd( ++ link, ++ DPCD_ADDRESS_TRAINING_PATTERN_SET, ++ &dpcd_pattern.raw, ++ sizeof(dpcd_pattern.raw) ); ++ ++ core_link_write_dpcd( ++ link, ++ DPCD_ADDRESS_LANE0_SET, ++ (uint8_t *)(dpcd_lane), ++ size_in_bytes); ++ ++ } else ++ /* write it all in (1 + number-of-lanes)-byte burst*/ ++ core_link_write_dpcd( ++ link, ++ dpcd_base_lt_offset, ++ dpcd_lt_buffer, ++ size_in_bytes + sizeof(dpcd_pattern.raw) ); ++ ++ link->ln_setting = lt_settings->lane_settings[0]; ++} ++ ++static bool is_cr_done(enum lane_count ln_count, ++ union lane_status *dpcd_lane_status) ++{ ++ bool done = true; ++ uint32_t lane; ++ /*LANEx_CR_DONE bits All 1's?*/ ++ for (lane = 0; lane < (uint32_t)(ln_count); lane++) { ++ if (!dpcd_lane_status[lane].bits.CR_DONE_0) ++ done = false; ++ } ++ return done; ++ ++} ++ ++static bool is_ch_eq_done(enum lane_count ln_count, ++ union lane_status *dpcd_lane_status, ++ union lane_align_status_updated *lane_status_updated) ++{ ++ bool done = true; ++ uint32_t lane; ++ if (!lane_status_updated->bits.INTERLANE_ALIGN_DONE) ++ done = false; ++ else { ++ for (lane = 0; lane < (uint32_t)(ln_count); lane++) { ++ if (!dpcd_lane_status[lane].bits.SYMBOL_LOCKED_0 || ++ !dpcd_lane_status[lane].bits.CHANNEL_EQ_DONE_0) ++ done = false; ++ } ++ } ++ return done; ++ ++} ++ ++static void update_drive_settings( ++ struct link_training_settings *dest, ++ struct link_training_settings src) ++{ ++ uint32_t lane; ++ for (lane = 0; lane < src.link_settings.lane_count; lane++) { ++ dest->lane_settings[lane].VOLTAGE_SWING = ++ src.lane_settings[lane].VOLTAGE_SWING; ++ dest->lane_settings[lane].PRE_EMPHASIS = ++ src.lane_settings[lane].PRE_EMPHASIS; ++ dest->lane_settings[lane].POST_CURSOR2 = ++ src.lane_settings[lane].POST_CURSOR2; ++ } ++} ++ ++static uint8_t get_nibble_at_index(const uint8_t *buf, ++ uint32_t index) ++{ ++ uint8_t nibble; ++ nibble = buf[index / 2]; ++ ++ if (index % 2) ++ nibble >>= 4; ++ else ++ nibble &= 0x0F; ++ ++ return nibble; ++} ++ ++static enum pre_emphasis get_max_pre_emphasis_for_voltage_swing( ++ enum voltage_swing voltage) ++{ ++ enum pre_emphasis pre_emphasis; ++ pre_emphasis = PRE_EMPHASIS_MAX_LEVEL; ++ ++ if (voltage <= VOLTAGE_SWING_MAX_LEVEL) ++ pre_emphasis = voltage_swing_to_pre_emphasis[voltage]; ++ ++ return pre_emphasis; ++ ++} ++ ++static void find_max_drive_settings( ++ const struct link_training_settings *link_training_setting, ++ struct link_training_settings *max_lt_setting) ++{ ++ uint32_t lane; ++ struct lane_settings max_requested; ++ ++ max_requested.VOLTAGE_SWING = ++ link_training_setting-> ++ lane_settings[0].VOLTAGE_SWING; ++ max_requested.PRE_EMPHASIS = ++ link_training_setting-> ++ lane_settings[0].PRE_EMPHASIS; ++ /*max_requested.postCursor2 = ++ * link_training_setting->laneSettings[0].postCursor2;*/ ++ ++ /* Determine what the maximum of the requested settings are*/ ++ for (lane = 1; lane < link_training_setting->link_settings.lane_count; ++ lane++) { ++ if (link_training_setting->lane_settings[lane].VOLTAGE_SWING > ++ max_requested.VOLTAGE_SWING) ++ ++ max_requested.VOLTAGE_SWING = ++ link_training_setting-> ++ lane_settings[lane].VOLTAGE_SWING; ++ ++ ++ if (link_training_setting->lane_settings[lane].PRE_EMPHASIS > ++ max_requested.PRE_EMPHASIS) ++ max_requested.PRE_EMPHASIS = ++ link_training_setting-> ++ lane_settings[lane].PRE_EMPHASIS; ++ ++ /* ++ if (link_training_setting->laneSettings[lane].postCursor2 > ++ max_requested.postCursor2) ++ { ++ max_requested.postCursor2 = ++ link_training_setting->laneSettings[lane].postCursor2; ++ } ++ */ ++ } ++ ++ /* make sure the requested settings are ++ * not higher than maximum settings*/ ++ if (max_requested.VOLTAGE_SWING > VOLTAGE_SWING_MAX_LEVEL) ++ max_requested.VOLTAGE_SWING = VOLTAGE_SWING_MAX_LEVEL; ++ ++ if (max_requested.PRE_EMPHASIS > PRE_EMPHASIS_MAX_LEVEL) ++ max_requested.PRE_EMPHASIS = PRE_EMPHASIS_MAX_LEVEL; ++ /* ++ if (max_requested.postCursor2 > PostCursor2_MaxLevel) ++ max_requested.postCursor2 = PostCursor2_MaxLevel; ++ */ ++ ++ /* make sure the pre-emphasis matches the voltage swing*/ ++ if (max_requested.PRE_EMPHASIS > ++ get_max_pre_emphasis_for_voltage_swing( ++ max_requested.VOLTAGE_SWING)) ++ max_requested.PRE_EMPHASIS = ++ get_max_pre_emphasis_for_voltage_swing( ++ max_requested.VOLTAGE_SWING); ++ ++ /* ++ * Post Cursor2 levels are completely independent from ++ * pre-emphasis (Post Cursor1) levels. But Post Cursor2 levels ++ * can only be applied to each allowable combination of voltage ++ * swing and pre-emphasis levels */ ++ /* if ( max_requested.postCursor2 > ++ * getMaxPostCursor2ForVoltageSwing(max_requested.voltageSwing)) ++ * max_requested.postCursor2 = ++ * getMaxPostCursor2ForVoltageSwing(max_requested.voltageSwing); ++ */ ++ ++ max_lt_setting->link_settings.link_rate = ++ link_training_setting->link_settings.link_rate; ++ max_lt_setting->link_settings.lane_count = ++ link_training_setting->link_settings.lane_count; ++ max_lt_setting->link_settings.link_spread = ++ link_training_setting->link_settings.link_spread; ++ ++ for (lane = 0; lane < ++ link_training_setting->link_settings.lane_count; ++ lane++) { ++ max_lt_setting->lane_settings[lane].VOLTAGE_SWING = ++ max_requested.VOLTAGE_SWING; ++ max_lt_setting->lane_settings[lane].PRE_EMPHASIS = ++ max_requested.PRE_EMPHASIS; ++ /*max_lt_setting->laneSettings[lane].postCursor2 = ++ * max_requested.postCursor2; ++ */ ++ } ++ ++} ++ ++static void get_lane_status_and_drive_settings( ++ struct core_link* link, ++ const struct link_training_settings *link_training_setting, ++ union lane_status *ln_status, ++ union lane_align_status_updated *ln_status_updated, ++ struct link_training_settings *req_settings) ++{ ++ uint8_t dpcd_buf[6] = {0}; ++ union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {{{0}}}; ++ struct link_training_settings request_settings = {{0}}; ++ uint32_t lane; ++ ++ dc_service_memset(req_settings, '\0', sizeof(struct link_training_settings)); ++ ++ core_link_read_dpcd( ++ link, ++ DPCD_ADDRESS_LANE_01_STATUS, ++ (uint8_t *)(dpcd_buf), ++ sizeof(dpcd_buf)); ++ ++ ++ for (lane = 0; lane < ++ (uint32_t)(link_training_setting->link_settings.lane_count); ++ lane++) { ++ ++ ln_status[lane].raw = ++ get_nibble_at_index(&dpcd_buf[0], lane); ++ dpcd_lane_adjust[lane].raw = ++ get_nibble_at_index(&dpcd_buf[4], lane); ++ } ++ ++ ln_status_updated->raw = dpcd_buf[2]; ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s:\n%x Lane01Status = %x\n %x Lane23Status = %x\n ", ++ __func__, ++ DPCD_ADDRESS_LANE_01_STATUS, dpcd_buf[0], ++ DPCD_ADDRESS_LANE_23_STATUS, dpcd_buf[1]); ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s:\n %x Lane01AdjustRequest = %x\n %x Lane23AdjustRequest = %x\n", ++ __func__, ++ DPCD_ADDRESS_ADJUST_REQUEST_LANE0_1, ++ dpcd_buf[4], ++ DPCD_ADDRESS_ADJUST_REQUEST_LANE2_3, ++ dpcd_buf[5]); ++ ++ /*copy to req_settings*/ ++ request_settings.link_settings.lane_count = ++ link_training_setting->link_settings.lane_count; ++ request_settings.link_settings.link_rate = ++ link_training_setting->link_settings.link_rate; ++ request_settings.link_settings.link_spread = ++ link_training_setting->link_settings.link_spread; ++ ++ for (lane = 0; lane < ++ (uint32_t)(link_training_setting->link_settings.lane_count); ++ lane++) { ++ ++ request_settings.lane_settings[lane].VOLTAGE_SWING = ++ (enum voltage_swing)(dpcd_lane_adjust[lane].bits. ++ VOLTAGE_SWING_LANE); ++ request_settings.lane_settings[lane].PRE_EMPHASIS = ++ (enum pre_emphasis)(dpcd_lane_adjust[lane].bits. ++ PRE_EMPHASIS_LANE); ++ } ++ ++ /*Note: for postcursor2, read adjusted ++ * postcursor2 settings from*/ ++ /*DpcdAddress_AdjustRequestPostCursor2 = ++ *0x020C (not implemented yet)*/ ++ ++ /* we find the maximum of the requested settings across all lanes*/ ++ /* and set this maximum for all lanes*/ ++ find_max_drive_settings(&request_settings, req_settings); ++ ++ /* if post cursor 2 is needed in the future, ++ * read DpcdAddress_AdjustRequestPostCursor2 = 0x020C ++ */ ++ ++} ++ ++static void dpcd_set_lane_settings( ++ struct core_link* link, ++ const struct link_training_settings *link_training_setting) ++{ ++ union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}}; ++ uint32_t lane; ++ ++ for (lane = 0; lane < ++ (uint32_t)(link_training_setting-> ++ link_settings.lane_count); ++ lane++) { ++ dpcd_lane[lane].bits.VOLTAGE_SWING_SET = ++ (uint8_t)(link_training_setting-> ++ lane_settings[lane].VOLTAGE_SWING); ++ dpcd_lane[lane].bits.PRE_EMPHASIS_SET = ++ (uint8_t)(link_training_setting-> ++ lane_settings[lane].PRE_EMPHASIS); ++ dpcd_lane[lane].bits.MAX_SWING_REACHED = ++ (link_training_setting-> ++ lane_settings[lane].VOLTAGE_SWING == ++ VOLTAGE_SWING_MAX_LEVEL ? 1 : 0); ++ dpcd_lane[lane].bits.MAX_PRE_EMPHASIS_REACHED = ++ (link_training_setting-> ++ lane_settings[lane].PRE_EMPHASIS == ++ PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); ++ } ++ ++ core_link_write_dpcd(link, ++ DPCD_ADDRESS_LANE0_SET, ++ (uint8_t *)(dpcd_lane), ++ link_training_setting->link_settings.lane_count); ++ ++ /* ++ if (LTSettings.link.rate == LinkRate_High2) ++ { ++ DpcdTrainingLaneSet2 dpcd_lane2[lane_count_DPMax] = {0}; ++ for ( uint32_t lane = 0; ++ lane < lane_count_DPMax; lane++) ++ { ++ dpcd_lane2[lane].bits.post_cursor2_set = ++ static_cast<unsigned char>( ++ LTSettings.laneSettings[lane].postCursor2); ++ dpcd_lane2[lane].bits.max_post_cursor2_reached = 0; ++ } ++ m_pDpcdAccessSrv->WriteDpcdData( ++ DpcdAddress_Lane0Set2, ++ reinterpret_cast<unsigned char*>(dpcd_lane2), ++ LTSettings.link.lanes); ++ } ++ */ ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ "%s\n %x VS set = %x PE set = %x \ ++ max VS Reached = %x max PE Reached = %x\n", ++ __func__, ++ DPCD_ADDRESS_LANE0_SET, ++ dpcd_lane[0].bits.VOLTAGE_SWING_SET, ++ dpcd_lane[0].bits.PRE_EMPHASIS_SET, ++ dpcd_lane[0].bits.MAX_SWING_REACHED, ++ dpcd_lane[0].bits.MAX_PRE_EMPHASIS_REACHED); ++ ++ link->ln_setting = link_training_setting->lane_settings[0]; ++ ++} ++ ++static bool is_max_vs_reached( ++ const struct link_training_settings *lt_settings) ++{ ++ uint32_t lane; ++ for (lane = 0; lane < ++ (uint32_t)(lt_settings->link_settings.lane_count); ++ lane++) { ++ if (lt_settings->lane_settings[lane].VOLTAGE_SWING ++ == VOLTAGE_SWING_MAX_LEVEL) ++ return true; ++ } ++ return false; ++ ++} ++ ++void set_drive_settings( ++ struct core_link *link, ++ struct link_training_settings *lt_settings) ++{ ++ /* program ASIC PHY settings*/ ++ dp_set_hw_lane_settings(link, lt_settings); ++ ++ /* Notify DP sink the PHY settings from source */ ++ dpcd_set_lane_settings(link, lt_settings); ++} ++ ++static bool perform_post_lt_adj_req_sequence( ++ struct core_link *link, ++ struct link_training_settings *lt_settings) ++{ ++ enum lane_count lane_count = ++ lt_settings->link_settings.lane_count; ++ ++ uint32_t adj_req_count; ++ uint32_t adj_req_timer; ++ bool req_drv_setting_changed; ++ uint32_t lane; ++ ++ req_drv_setting_changed = false; ++ for (adj_req_count = 0; adj_req_count < POST_LT_ADJ_REQ_LIMIT; ++ adj_req_count++) { ++ ++ req_drv_setting_changed = false; ++ ++ for (adj_req_timer = 0; ++ adj_req_timer < POST_LT_ADJ_REQ_TIMEOUT; ++ adj_req_timer++) { ++ ++ struct link_training_settings req_settings; ++ union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; ++ union lane_align_status_updated ++ dpcd_lane_status_updated; ++ ++ get_lane_status_and_drive_settings( ++ link, ++ lt_settings, ++ dpcd_lane_status, ++ &dpcd_lane_status_updated, ++ &req_settings); ++ ++ if (dpcd_lane_status_updated.bits. ++ POST_LT_ADJ_REQ_IN_PROGRESS == 0) ++ return true; ++ ++ if (!is_cr_done(lane_count, dpcd_lane_status)) ++ return false; ++ ++ if (!is_ch_eq_done( ++ lane_count, ++ dpcd_lane_status, ++ &dpcd_lane_status_updated)) ++ return false; ++ ++ for (lane = 0; lane < (uint32_t)(lane_count); lane++) { ++ ++ if (lt_settings-> ++ lane_settings[lane].VOLTAGE_SWING != ++ req_settings.lane_settings[lane]. ++ VOLTAGE_SWING || ++ lt_settings->lane_settings[lane].PRE_EMPHASIS != ++ req_settings.lane_settings[lane].PRE_EMPHASIS) { ++ ++ req_drv_setting_changed = true; ++ break; ++ } ++ } ++ ++ if (req_drv_setting_changed) { ++ update_drive_settings( ++ lt_settings,req_settings); ++ ++ set_drive_settings(link, lt_settings); ++ break; ++ } ++ ++ dc_service_sleep_in_milliseconds(link->ctx, 1); ++ } ++ ++ if (!req_drv_setting_changed) { ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_LINK_SERVICE, ++ "%s: Post Link Training Adjust Request Timed out\n", ++ __func__); ++ ++ ASSERT(0); ++ return true; ++ } ++ } ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_LINK_SERVICE, ++ "%s: Post Link Training Adjust Request limit reached\n", ++ __func__); ++ ++ ASSERT(0); ++ return true; ++ ++} ++ ++static bool perform_channel_equalization_sequence( ++ struct core_link *link, ++ struct link_training_settings *lt_settings) ++{ ++ struct link_training_settings req_settings; ++ enum hw_dp_training_pattern hw_tr_pattern; ++ uint32_t retries_ch_eq; ++ enum lane_count lane_count = lt_settings->link_settings.lane_count; ++ union lane_align_status_updated dpcd_lane_status_updated = {{0}}; ++ union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {{{0}}};; ++ ++ /*TODO hw_tr_pattern = HW_DP_TRAINING_PATTERN_3;*/ ++ hw_tr_pattern = HW_DP_TRAINING_PATTERN_2; ++ ++ dp_set_hw_training_pattern(link, hw_tr_pattern); ++ ++ for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; ++ retries_ch_eq++) { ++ ++ dp_set_hw_lane_settings(link, lt_settings); ++ ++ /* 2. update DPCD*/ ++ if (!retries_ch_eq) ++ /* EPR #361076 - write as a 5-byte burst, ++ * but only for the 1-st iteration*/ ++ dpcd_set_lt_pattern_and_lane_settings( ++ link, ++ lt_settings, ++ hw_tr_pattern); ++ else ++ dpcd_set_lane_settings(link, lt_settings); ++ ++ /* 3. wait for receiver to lock-on*/ ++ wait_for_training_aux_rd_interval(link, 400); ++ ++ /* 4. Read lane status and requested ++ * drive settings as set by the sink*/ ++ ++ get_lane_status_and_drive_settings( ++ link, ++ lt_settings, ++ dpcd_lane_status, ++ &dpcd_lane_status_updated, ++ &req_settings); ++ ++ /* 5. check CR done*/ ++ if (!is_cr_done(lane_count, dpcd_lane_status)) ++ return false; ++ ++ /* 6. check CHEQ done*/ ++ if (is_ch_eq_done(lane_count, ++ dpcd_lane_status, ++ &dpcd_lane_status_updated)) ++ return true; ++ ++ /* 7. update VS/PE/PC2 in lt_settings*/ ++ update_drive_settings(lt_settings, req_settings); ++ } ++ ++ return false; ++ ++} ++ ++static bool perform_clock_recovery_sequence( ++ struct core_link *link, ++ struct link_training_settings *lt_settings) ++{ ++ uint32_t retries_cr; ++ uint32_t retry_count; ++ uint32_t lane; ++ struct link_training_settings req_settings; ++ enum lane_count lane_count = ++ lt_settings->link_settings.lane_count; ++ enum hw_dp_training_pattern hw_tr_pattern = HW_DP_TRAINING_PATTERN_1; ++ union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; ++ union lane_align_status_updated dpcd_lane_status_updated; ++ ++ retries_cr = 0; ++ retry_count = 0; ++ /* initial drive setting (VS/PE/PC2)*/ ++ for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { ++ lt_settings->lane_settings[lane].VOLTAGE_SWING = ++ VOLTAGE_SWING_LEVEL0; ++ lt_settings->lane_settings[lane].PRE_EMPHASIS = ++ PRE_EMPHASIS_DISABLED; ++ lt_settings->lane_settings[lane].POST_CURSOR2 = ++ POST_CURSOR2_DISABLED; ++ } ++ ++ dp_set_hw_training_pattern(link, hw_tr_pattern); ++ ++ /* najeeb - The synaptics MST hub can put the LT in ++ * infinite loop by switching the VS ++ */ ++ /* between level 0 and level 1 continuously, here ++ * we try for CR lock for LinkTrainingMaxCRRetry count*/ ++ while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && ++ (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { ++ ++ dc_service_memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status)); ++ dc_service_memset(&dpcd_lane_status_updated, '\0', ++ sizeof(dpcd_lane_status_updated)); ++ ++ /* 1. call HWSS to set lane settings*/ ++ dp_set_hw_lane_settings( ++ link, ++ lt_settings); ++ ++ /* 2. update DPCD of the receiver*/ ++ if (!retries_cr) ++ /* EPR #361076 - write as a 5-byte burst, ++ * but only for the 1-st iteration.*/ ++ dpcd_set_lt_pattern_and_lane_settings( ++ link, ++ lt_settings, ++ hw_tr_pattern); ++ else ++ dpcd_set_lane_settings( ++ link, ++ lt_settings); ++ ++ ++ /* 3. wait receiver to lock-on*/ ++ wait_for_training_aux_rd_interval( ++ link, ++ 100); ++ ++ /* 4. Read lane status and requested drive ++ * settings as set by the sink ++ */ ++ get_lane_status_and_drive_settings( ++ link, ++ lt_settings, ++ dpcd_lane_status, ++ &dpcd_lane_status_updated, ++ &req_settings); ++ ++ ++ /* 5. check CR done*/ ++ if (is_cr_done(lane_count, dpcd_lane_status)) ++ return true; ++ ++ /* 6. max VS reached*/ ++ if (is_max_vs_reached(lt_settings)) ++ return false; ++ ++ /* 7. same voltage*/ ++ /* Note: VS same for all lanes, ++ * so comparing first lane is sufficient*/ ++ if (lt_settings->lane_settings[0].VOLTAGE_SWING == ++ req_settings.lane_settings[0].VOLTAGE_SWING) ++ retries_cr++; ++ else ++ retries_cr = 0; ++ ++ ++ /* 8. update VS/PE/PC2 in lt_settings*/ ++ update_drive_settings(lt_settings, req_settings); ++ ++ retry_count++; ++ } ++ ++ if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) { ++ ASSERT(0); ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_LINK_SERVICE, ++ "%s: Link Training Error, could not \ ++ get CR after %d tries. \ ++ Possibly voltage swing issue", __func__, ++ LINK_TRAINING_MAX_CR_RETRY); ++ ++ } ++ ++ return false; ++} ++ ++ bool perform_link_training( ++ struct core_link *link, ++ const struct link_settings *link_setting, ++ bool skip_video_pattern) ++{ ++ bool status; ++ union dpcd_training_pattern dpcd_pattern = {{0}}; ++ union lane_count_set lane_count_set = {{0}}; ++ const int8_t *link_rate = "Unknown"; ++ struct link_training_settings lt_settings; ++ ++ status = false; ++ dc_service_memset(<_settings, '\0', sizeof(lt_settings)); ++ ++ lt_settings.link_settings.link_rate = link_setting->link_rate; ++ lt_settings.link_settings.lane_count = link_setting->lane_count; ++ ++ /*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/ ++ ++ /* TODO hard coded to SS for now ++ * lt_settings.link_settings.link_spread = ++ * dal_display_path_is_ss_supported( ++ * path_mode->display_path) ? ++ * LINK_SPREAD_05_DOWNSPREAD_30KHZ : ++ * LINK_SPREAD_DISABLED; ++ */ ++ lt_settings.link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; ++ ++ /* 1. set link rate, lane count and spread*/ ++ dpcd_set_link_settings(link, <_settings); ++ ++ /* 2. perform link training (set link training done ++ * to false is done as well)*/ ++ if (perform_clock_recovery_sequence(link, <_settings)) { ++ ++ if (perform_channel_equalization_sequence(link, <_settings)) ++ status = true; ++ } ++ ++ if (status || !skip_video_pattern) { ++ ++ /* 3. set training not in progress*/ ++ dpcd_pattern.bits.TRAINING_PATTERN_SET = ++ DPCD_TRAINING_PATTERN_VIDEOIDLE; ++ dpcd_set_training_pattern(link, dpcd_pattern); ++ ++ /* 4. mainlink output idle pattern*/ ++ dp_set_hw_test_pattern(link, DP_TEST_PATTERN_VIDEO_MODE); ++ ++ /* 5. post training adjust if required*/ ++ if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED ++ == 1) { ++ if (status == true) { ++ if (perform_post_lt_adj_req_sequence( ++ link, <_settings) == false) ++ status = false; ++ } ++ ++ lane_count_set.bits.LANE_COUNT_SET = ++ lt_settings.link_settings.lane_count; ++ lane_count_set.bits.ENHANCED_FRAMING = 1; ++ lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; ++ ++ core_link_write_dpcd( ++ link, ++ DPCD_ADDRESS_LANE_COUNT_SET, ++ &lane_count_set.raw, ++ sizeof(lane_count_set)); ++ } ++ } ++ ++ /* 6. print status message*/ ++ switch (lt_settings.link_settings.link_rate) { ++ ++ case LINK_RATE_LOW: ++ link_rate = "Low"; ++ break; ++ case LINK_RATE_HIGH: ++ link_rate = "High"; ++ break; ++ case LINK_RATE_HIGH2: ++ link_rate = "High2"; ++ break; ++ case LINK_RATE_RBR2: ++ link_rate = "RBR2"; ++ break; ++ default: ++ break; ++ } ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_MST, ++ LOG_MINOR_MST_PROGRAMMING, ++ "Link training for %d lanes at %s rate %s\n", ++ lt_settings.link_settings.lane_count, ++ link_rate, ++ status ? "succeeded" : "failed"); ++ ++ return status; ++} ++ ++/*TODO add more check to see if link support request link configuration */ ++static bool is_link_setting_supported( ++ const struct link_settings *link_setting, ++ const struct link_settings *max_link_setting) ++{ ++ if (link_setting->lane_count > max_link_setting->lane_count || ++ link_setting->link_rate > max_link_setting->link_rate) ++ return false; ++ return true; ++} ++ ++static const uint32_t get_link_training_fallback_table_len( ++ struct core_link *link) ++{ ++ return ARRAY_SIZE(link_training_fallback_table); ++} ++ ++static const struct link_settings *get_link_training_fallback_table( ++ struct core_link *link, uint32_t i) ++{ ++ return &link_training_fallback_table[i]; ++} ++ ++static bool exceeded_limit_link_setting(const struct link_settings *link_setting, ++ const struct link_settings *limit_link_setting) ++{ ++ return (link_setting->lane_count * link_setting->link_rate ++ > limit_link_setting->lane_count * limit_link_setting->link_rate ? ++ true : false); ++} ++ ++ ++bool dp_hbr_verify_link_cap( ++ struct core_link *link, ++ struct link_settings *known_limit_link_setting) ++{ ++ struct link_settings max_link_cap = {0}; ++ bool success; ++ bool skip_link_training; ++ const struct link_settings *cur; ++ bool skip_video_pattern; ++ uint32_t i; ++ ++ success = false; ++ skip_link_training = false; ++ ++ /* TODO confirm this is correct for cz */ ++ max_link_cap.lane_count = LANE_COUNT_FOUR; ++ max_link_cap.link_rate = LINK_RATE_HIGH2; ++ max_link_cap.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; ++ ++ /* TODO implement override and monitor patch later */ ++ ++ /* try to train the link from high to low to ++ * find the physical link capability ++ */ ++ /* disable PHY done possible by BIOS, will be done by driver itself */ ++ dp_disable_link_phy(link, link->public.connector_signal); ++ ++ for (i = 0; i < get_link_training_fallback_table_len(link) && ++ !success; i++) { ++ cur = get_link_training_fallback_table(link, i); ++ ++ if (known_limit_link_setting->lane_count != LANE_COUNT_UNKNOWN && ++ exceeded_limit_link_setting(cur, ++ known_limit_link_setting)) ++ continue; ++ ++ if (!is_link_setting_supported(cur, &max_link_cap)) ++ continue; ++ ++ skip_video_pattern = true; ++ if (cur->link_rate == LINK_RATE_LOW) ++ skip_video_pattern = false; ++ if (dp_enable_link_phy( ++ link, ++ link->public.connector_signal, ++ ENGINE_ID_UNKNOWN, ++ cur)) { ++ if (skip_link_training) ++ success = true; ++ else { ++ uint8_t num_retries = 3; ++ uint8_t j; ++ uint8_t delay_between_retries = 10; ++ for (j = 0; j < num_retries; ++j) { ++ success = perform_link_training( ++ link, ++ cur, ++ skip_video_pattern); ++ ++ if (success) ++ break; ++ ++ dc_service_sleep_in_milliseconds( ++ link->ctx, ++ delay_between_retries); ++ ++ delay_between_retries += 10; ++ } ++ } ++ } ++ ++ if (success) ++ link->verified_link_cap = *cur; ++ ++ /* always disable the link before trying another ++ * setting or before returning we'll enable it later ++ * based on the actual mode we're driving ++ */ ++ dp_disable_link_phy(link, link->public.connector_signal); ++ } ++ ++ /* Link Training failed for all Link Settings ++ * (Lane Count is still unknown) ++ */ ++ if (!success) { ++ /* If all LT fails for all settings, ++ * set verified = failed safe (1 lane low) ++ */ ++ link->verified_link_cap.lane_count = LANE_COUNT_ONE; ++ link->verified_link_cap.link_rate = LINK_RATE_LOW; ++ ++ link->verified_link_cap.link_spread = ++ LINK_SPREAD_DISABLED; ++ } ++ ++ link->max_link_setting = link->verified_link_cap; ++ ++ return success; ++} ++ ++static uint32_t bandwidth_in_kbps_from_timing( ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t bits_per_channel = 0; ++ uint32_t kbps; ++ switch (timing->display_color_depth) { ++ ++ case COLOR_DEPTH_666: ++ bits_per_channel = 6; ++ break; ++ case COLOR_DEPTH_888: ++ bits_per_channel = 8; ++ break; ++ case COLOR_DEPTH_101010: ++ bits_per_channel = 10; ++ break; ++ case COLOR_DEPTH_121212: ++ bits_per_channel = 12; ++ break; ++ case COLOR_DEPTH_141414: ++ bits_per_channel = 14; ++ break; ++ case COLOR_DEPTH_161616: ++ bits_per_channel = 16; ++ break; ++ default: ++ break; ++ } ++ ASSERT(bits_per_channel != 0); ++ ++ kbps = timing->pix_clk_khz; ++ kbps *= bits_per_channel; ++ ++ if (timing->flags.Y_ONLY != 1) ++ /*Only YOnly make reduce bandwidth by 1/3 compares to RGB*/ ++ kbps *= 3; ++ ++ return kbps; ++ ++} ++ ++static uint32_t bandwidth_in_kbps_from_link_settings( ++ const struct link_settings *link_setting) ++{ ++ uint32_t link_rate_in_kbps = link_setting->link_rate * ++ LINK_RATE_REF_FREQ_IN_KHZ; ++ ++ uint32_t lane_count = link_setting->lane_count; ++ uint32_t kbps = link_rate_in_kbps; ++ kbps *= lane_count; ++ kbps *= 8; /* 8 bits per byte*/ ++ ++ return kbps; ++ ++} ++ ++bool dp_validate_mode_timing( ++ struct core_link *link, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t req_bw; ++ uint32_t max_bw; ++ ++ const struct link_settings *link_setting; ++ ++ /*always DP fail safe mode*/ ++ if (timing->pix_clk_khz == (uint32_t)25175 && ++ timing->h_addressable == (uint32_t)640 && ++ timing->v_addressable == (uint32_t)480) ++ return true; ++ ++ /* For static validation we always use reported ++ * link settings for other cases, when no modelist ++ * changed we can use verified link setting*/ ++ link_setting = &link->reported_link_cap; ++ ++ /* TODO: DYNAMIC_VALIDATION needs to be implemented */ ++ /*if (flags.DYNAMIC_VALIDATION == 1 && ++ link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN) ++ link_setting = &link->verified_link_cap; ++ */ ++ ++ req_bw = bandwidth_in_kbps_from_timing(timing); ++ max_bw = bandwidth_in_kbps_from_link_settings(link_setting); ++ ++ if (req_bw < max_bw) { ++ /* remember the biggest mode here, during ++ * initial link training (to get ++ * verified_link_cap), LS sends event about ++ * cannot train at reported cap to upper ++ * layer and upper layer will re-enumerate modes. ++ * this is not necessary if the lower ++ * verified_link_cap is enough to drive ++ * all the modes */ ++ ++ /* TODO: DYNAMIC_VALIDATION needs to be implemented */ ++ /* if (flags.DYNAMIC_VALIDATION == 1) ++ dpsst->max_req_bw_for_verified_linkcap = dal_max( ++ dpsst->max_req_bw_for_verified_linkcap, req_bw); */ ++ return true; ++ } else ++ return false; ++} ++ ++void decide_link_settings(struct core_stream *stream, ++ struct link_settings *link_setting) ++{ ++ ++ const struct link_settings *cur_ls; ++ struct core_link* link; ++ uint32_t req_bw; ++ uint32_t link_bw; ++ uint32_t i; ++ ++ req_bw = bandwidth_in_kbps_from_timing( ++ &stream->public.timing); ++ ++ /* if preferred is specified through AMDDP, use it, if it's enough ++ * to drive the mode ++ */ ++ link = stream->sink->link; ++ ++ if ((link->reported_link_cap.lane_count != LANE_COUNT_UNKNOWN) && ++ (link->reported_link_cap.link_rate <= ++ link->verified_link_cap.link_rate)) { ++ ++ link_bw = bandwidth_in_kbps_from_link_settings( ++ &link->reported_link_cap); ++ ++ if (req_bw < link_bw) { ++ *link_setting = link->reported_link_cap; ++ return; ++ } ++ } ++ ++ /* search for first suitable setting for the requested ++ * bandwidth ++ */ ++ for (i = 0; i < get_link_training_fallback_table_len(link); i++) { ++ ++ cur_ls = get_link_training_fallback_table(link, i); ++ ++ link_bw = ++ bandwidth_in_kbps_from_link_settings( ++ cur_ls); ++ ++ if (req_bw < link_bw) { ++ if (is_link_setting_supported( ++ cur_ls, ++ &link->max_link_setting)) { ++ *link_setting = *cur_ls; ++ return; ++ } ++ } ++ } ++ ++ BREAK_TO_DEBUGGER(); ++ ASSERT(link->verified_link_cap.lane_count != ++ LANE_COUNT_UNKNOWN); ++ ++ *link_setting = link->verified_link_cap; ++} ++ ++/*************************Short Pulse IRQ***************************/ ++ ++static bool hpd_rx_irq_check_link_loss_status( ++ struct core_link *link, ++ union hpd_irq_data *hpd_irq_dpcd_data) ++{ ++ uint8_t irq_reg_rx_power_state; ++ enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; ++ union lane_status lane_status; ++ uint32_t lane; ++ bool sink_status_changed; ++ bool return_code; ++ ++ sink_status_changed = false; ++ return_code = false; ++ ++ if (link->cur_link_settings.lane_count == 0) ++ return return_code; ++ /*1. Check that we can handle interrupt: Not in FS DOS, ++ * Not in "Display Timeout" state, Link is trained. ++ */ ++ ++ dpcd_result = core_link_read_dpcd(link, ++ DPCD_ADDRESS_POWER_STATE, ++ &irq_reg_rx_power_state, ++ sizeof(irq_reg_rx_power_state)); ++ ++ if (dpcd_result != DC_OK) { ++ irq_reg_rx_power_state = DP_PWR_STATE_D0; ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_HPD_IRQ, ++ "%s: DPCD read failed to obtain power state.\n", ++ __func__); ++ } ++ ++ if (irq_reg_rx_power_state == DP_PWR_STATE_D0) { ++ ++ /*2. Check that Link Status changed, before re-training.*/ ++ ++ /*parse lane status*/ ++ for (lane = 0; lane < ++ (uint32_t)(link->cur_link_settings.lane_count) && ++ !sink_status_changed; lane++) { ++ ++ /* check status of lanes 0,1 ++ * changed DpcdAddress_Lane01Status (0x202)*/ ++ lane_status.raw = get_nibble_at_index( ++ &hpd_irq_dpcd_data->bytes.lane01_status.raw, ++ lane); ++ ++ if (!lane_status.bits.CHANNEL_EQ_DONE_0 || ++ !lane_status.bits.CR_DONE_0 || ++ !lane_status.bits.SYMBOL_LOCKED_0) { ++ /* if one of the channel equalization, clock ++ * recovery or symbol lock is dropped ++ * consider it as (link has been ++ * dropped) dp sink status has changed*/ ++ sink_status_changed = true; ++ break; ++ } ++ ++ } ++ ++ /* Check interlane align.*/ ++ if (sink_status_changed || ++ !hpd_irq_dpcd_data->bytes.lane_status_updated.bits. ++ INTERLANE_ALIGN_DONE) { ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_HPD_IRQ, ++ "%s: Link Status changed.\n", ++ __func__); ++ ++ return_code = true; ++ } ++ } ++ ++ return return_code; ++} ++ ++static enum dc_status read_hpd_rx_irq_data( ++ struct core_link *link, ++ union hpd_irq_data *irq_data) ++{ ++ /* The HW reads 16 bytes from 200h on HPD, ++ * but if we get an AUX_DEFER, the HW cannot retry ++ * and this causes the CTS tests 4.3.2.1 - 3.2.4 to ++ * fail, so we now explicitly read 6 bytes which is ++ * the req from the above mentioned test cases. ++ */ ++ return core_link_read_dpcd( ++ link, ++ DPCD_ADDRESS_SINK_COUNT, ++ irq_data->raw, ++ sizeof(union hpd_irq_data)); ++} ++ ++bool dc_link_handle_hpd_rx_irq(const struct dc_link *dc_link) ++{ ++ struct core_link *link = DC_LINK_TO_LINK(dc_link); ++ union hpd_irq_data hpd_irq_dpcd_data = {{{{0}}}}; ++ enum dc_status result = DDC_RESULT_UNKNOWN; ++ bool status = false; ++ /* For use cases related to down stream connection status change, ++ * PSR and device auto test, refer to function handle_sst_hpd_irq ++ * in DAL2.1*/ ++ ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_HPD_IRQ, ++ "%s: Got short pulse HPD on connector %d\n", ++ __func__, link->connector_index); ++ ++ /* All the "handle_hpd_irq_xxx()" methods ++ * should be called only after ++ * dal_dpsst_ls_read_hpd_irq_data ++ * Order of calls is important too ++ */ ++ result = read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data); ++ ++ if (result != DC_OK) { ++ dal_logger_write(link->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_HPD_IRQ, ++ "%s: DPCD read failed to obtain irq data\n", ++ __func__); ++ return false; ++ } ++ ++ /* check if we have MST msg and return since we poll for it */ ++ if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY || ++ hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) ++ return false; ++ ++ ++ /* For now we only handle 'Downstream port status' case. */ ++ /* If we got sink count changed it means Downstream port status changed, ++ * then DM should call DC to do the detection. */ ++ if (hpd_rx_irq_check_link_loss_status( ++ link, ++ &hpd_irq_dpcd_data)) { ++ perform_link_training(link, &link->cur_link_settings, true); ++ status = false; ++ } ++ ++ if (hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT ++ != link->dpcd_sink_count) ++ status = true; ++ ++ /* reasons for HPD RX: ++ * 1. Link Loss - ie Re-train the Link ++ * 2. MST sideband message ++ * 3. Automated Test - ie. Internal Commit ++ * 4. CP (copy protection) - (not interesting for DM???) ++ * 5. DRR ++ * 6. Downstream Port status changed -ie. Detect - this the only one ++ * which is interesting for DM because it must call dc_link_detect. ++ */ ++ return status; ++} ++ ++/*query dpcd for version and mst cap addresses*/ ++bool is_mst_supported(struct core_link *link) ++{ ++ bool mst = false; ++ enum dc_status st = DC_OK; ++ union dpcd_rev rev; ++ union mstm_cap cap; ++ ++ rev.raw = 0; ++ cap.raw = 0; ++ ++ st = core_link_read_dpcd(link, DPCD_ADDRESS_DPCD_REV, &rev.raw, ++ sizeof(rev)); ++ ++ if (st == DC_OK && rev.raw >= DPCD_REV_12) { ++ ++ st = core_link_read_dpcd(link, DPCD_ADDRESS_MSTM_CAP, ++ &cap.raw, sizeof(cap)); ++ if (st == DC_OK && cap.bits.MST_CAP == 1) ++ mst = true; ++ } ++ return mst; ++ ++} ++ ++static void get_active_converter_info( ++ uint8_t data, struct core_link *link) ++{ ++ union dp_downstream_port_present ds_port = { .byte = data }; ++ ++ /* decode converter info*/ ++ if (!ds_port.fields.PORT_PRESENT) { ++ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE; ++ ddc_service_set_dongle_type(link->ddc, ++ link->dpcd_caps.dongle_type); ++ return; ++ } ++ ++ switch (ds_port.fields.PORT_TYPE) { ++ case DOWNSTREAM_VGA: ++ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_VGA_CONVERTER; ++ break; ++ case DOWNSTREAM_DVI_HDMI: ++ /* At this point we don't know is it DVI or HDMI, ++ * assume DVI.*/ ++ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_CONVERTER; ++ break; ++ default: ++ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE; ++ break; ++ } ++ ++ if (link->dpcd_caps.dpcd_rev.raw >= DCS_DPCD_REV_11) { ++ uint8_t det_caps[4]; ++ union dwnstream_port_caps_byte0 *port_caps = ++ (union dwnstream_port_caps_byte0 *)det_caps; ++ core_link_read_dpcd(link, DPCD_ADDRESS_DWN_STRM_PORT0_CAPS, ++ det_caps, sizeof(det_caps)); ++ ++ switch (port_caps->bits.DWN_STRM_PORTX_TYPE) { ++ case DOWN_STREAM_DETAILED_VGA: ++ link->dpcd_caps.dongle_type = ++ DISPLAY_DONGLE_DP_VGA_CONVERTER; ++ break; ++ case DOWN_STREAM_DETAILED_DVI: ++ link->dpcd_caps.dongle_type = ++ DISPLAY_DONGLE_DP_DVI_CONVERTER; ++ break; ++ case DOWN_STREAM_DETAILED_HDMI: ++ link->dpcd_caps.dongle_type = ++ DISPLAY_DONGLE_DP_HDMI_CONVERTER; ++ ++ if (ds_port.fields.DETAILED_CAPS) { ++ ++ union dwnstream_port_caps_byte3_hdmi ++ hdmi_caps = {.raw = det_caps[3] }; ++ ++ link->dpcd_caps.is_dp_hdmi_s3d_converter = ++ hdmi_caps.bits.FRAME_SEQ_TO_FRAME_PACK; ++ } ++ break; ++ } ++ } ++ ddc_service_set_dongle_type(link->ddc, ++ link->dpcd_caps.dongle_type); ++} ++ ++static void dp_wa_power_up_0010FA(struct core_link *link, uint8_t *dpcd_data, ++ int length) ++{ ++ int retry = 0; ++ struct dp_device_vendor_id dp_id; ++ union dp_downstream_port_present ds_port = { 0 }; ++ ++ if (!link->dpcd_caps.dpcd_rev.raw) { ++ do { ++ dp_receiver_power_ctrl(link, true); ++ core_link_read_dpcd(link, DPCD_ADDRESS_DPCD_REV, ++ dpcd_data, length); ++ link->dpcd_caps.dpcd_rev.raw = dpcd_data[ ++ DPCD_ADDRESS_DPCD_REV - ++ DPCD_ADDRESS_DPCD_REV]; ++ } while (retry++ < 4 && !link->dpcd_caps.dpcd_rev.raw); ++ } ++ ++ ds_port.byte = dpcd_data[DPCD_ADDRESS_DOWNSTREAM_PORT_PRESENT - ++ DPCD_ADDRESS_DPCD_REV]; ++ ++ get_active_converter_info(ds_port.byte, link); ++ ++ /* read IEEE branch device id */ ++ core_link_read_dpcd(link, DPCD_ADDRESS_BRANCH_DEVICE_ID_START, ++ (uint8_t *)&dp_id, sizeof(dp_id)); ++ link->dpcd_caps.branch_dev_id = ++ (dp_id.ieee_oui[0] << 16) + ++ (dp_id.ieee_oui[1] << 8) + ++ dp_id.ieee_oui[2]; ++ ++ if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) { ++ switch (link->dpcd_caps.branch_dev_id) { ++ /* Some active dongles (DP-VGA, DP-DLDVI converters) power down ++ * all internal circuits including AUX communication preventing ++ * reading DPCD table and EDID (spec violation). ++ * Encoder will skip DP RX power down on disable_output to ++ * keep receiver powered all the time.*/ ++ case DP_BRANCH_DEVICE_ID_1: ++ case DP_BRANCH_DEVICE_ID_4: ++ link->dp_wa.bits.KEEP_RECEIVER_POWERED = 1; ++ break; ++ ++ /* TODO: May need work around for other dongles. */ ++ default: ++ link->dp_wa.bits.KEEP_RECEIVER_POWERED = 0; ++ break; ++ } ++ } else ++ link->dp_wa.bits.KEEP_RECEIVER_POWERED = 0; ++} ++ ++static void retrieve_link_cap(struct core_link *link) ++{ ++ uint8_t dpcd_data[ ++ DPCD_ADDRESS_EDP_CONFIG_CAP - ++ DPCD_ADDRESS_DPCD_REV + 1]; ++ ++ union down_stream_port_count down_strm_port_count; ++ union edp_configuration_cap edp_config_cap; ++ union max_down_spread max_down_spread; ++ union dp_downstream_port_present ds_port = { 0 }; ++ ++ dc_service_memset(dpcd_data, '\0', sizeof(dpcd_data)); ++ dc_service_memset(&down_strm_port_count, ++ '\0', sizeof(union down_stream_port_count)); ++ dc_service_memset(&edp_config_cap, '\0', ++ sizeof(union edp_configuration_cap)); ++ dc_service_memset(&max_down_spread, '\0', ++ sizeof(union max_down_spread)); ++ ++ core_link_read_dpcd(link, DPCD_ADDRESS_DPCD_REV, ++ dpcd_data, sizeof(dpcd_data)); ++ link->dpcd_caps.dpcd_rev.raw = dpcd_data[ ++ DPCD_ADDRESS_DPCD_REV - ++ DPCD_ADDRESS_DPCD_REV]; ++ ++ ds_port.byte = dpcd_data[DPCD_ADDRESS_DOWNSTREAM_PORT_PRESENT - ++ DPCD_ADDRESS_DPCD_REV]; ++ ++ get_active_converter_info(ds_port.byte, link); ++ ++ dp_wa_power_up_0010FA(link, dpcd_data, sizeof(dpcd_data)); ++ ++ link->dpcd_caps.allow_invalid_MSA_timing_param = ++ down_strm_port_count.bits.IGNORE_MSA_TIMING_PARAM; ++ ++ link->dpcd_caps.max_ln_count.raw = dpcd_data[ ++ DPCD_ADDRESS_MAX_LANE_COUNT - DPCD_ADDRESS_DPCD_REV]; ++ ++ max_down_spread.raw = dpcd_data[ ++ DPCD_ADDRESS_MAX_DOWNSPREAD - DPCD_ADDRESS_DPCD_REV]; ++ ++ link->reported_link_cap.lane_count = ++ link->dpcd_caps.max_ln_count.bits.MAX_LANE_COUNT; ++ link->reported_link_cap.link_rate = dpcd_data[ ++ DPCD_ADDRESS_MAX_LINK_RATE - DPCD_ADDRESS_DPCD_REV]; ++ link->reported_link_cap.link_spread = ++ max_down_spread.bits.MAX_DOWN_SPREAD ? ++ LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED; ++ ++ edp_config_cap.raw = dpcd_data[ ++ DPCD_ADDRESS_EDP_CONFIG_CAP - DPCD_ADDRESS_DPCD_REV]; ++ link->dpcd_caps.panel_mode_edp = ++ edp_config_cap.bits.ALT_SCRAMBLER_RESET; ++ ++ link->edp_revision = DPCD_EDP_REVISION_EDP_UNKNOWN; ++ ++ /* read sink count */ ++ core_link_read_dpcd(link, ++ DPCD_ADDRESS_SINK_COUNT, ++ &link->dpcd_caps.sink_count.raw, ++ sizeof(link->dpcd_caps.sink_count.raw)); ++ ++ /* Display control registers starting at DPCD 700h are only valid and ++ * enabled if this eDP config cap bit is set. */ ++ if (edp_config_cap.bits.DPCD_DISPLAY_CONTROL_CAPABLE) { ++ /* Read the Panel's eDP revision at DPCD 700h. */ ++ core_link_read_dpcd(link, ++ DPCD_ADDRESS_EDP_REV, ++ (uint8_t *)(&link->edp_revision), ++ sizeof(link->edp_revision)); ++ } ++ /* TODO: Confirm if need retrieve_psr_link_cap */ ++} ++ ++void detect_dp_sink_caps(struct core_link *link) ++{ ++ retrieve_link_cap(link); ++ ++ /* dc init_hw has power encoder using default ++ * signal for connector. For native DP, no ++ * need to power up encoder again. If not native ++ * DP, hw_init may need check signal or power up ++ * encoder here. ++ */ ++ ++ if (is_mst_supported(link)) { ++ link->verified_link_cap = link->reported_link_cap; ++ } else { ++ dp_hbr_verify_link_cap(link, ++ &link->reported_link_cap); ++ } ++ /* TODO save sink caps in link->sink */ ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c +new file mode 100644 +index 0000000..164cdeb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c +@@ -0,0 +1,188 @@ ++/* Copyright 2015 Advanced Micro Devices, Inc. */ ++ ++#include "dc_services.h" ++#include "dc.h" ++#include "inc/core_dc.h" ++#include "include/ddc_service_types.h" ++#include "include/i2caux_interface.h" ++#include "link_hwss.h" ++#include "include/connector_interface.h" ++#include "hw_sequencer.h" ++#include "include/ddc_service_interface.h" ++ ++enum dc_status core_link_read_dpcd( ++ struct core_link* link, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size) ++{ ++ if (dal_ddc_service_read_dpcd_data(link->ddc, address, data, size) ++ != DDC_RESULT_SUCESSFULL) ++ return DC_ERROR_UNEXPECTED; ++ ++ return DC_OK; ++} ++ ++enum dc_status core_link_write_dpcd( ++ struct core_link* link, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t size) ++{ ++ if (dal_ddc_service_write_dpcd_data(link->ddc, address, data, size) ++ != DDC_RESULT_SUCESSFULL) ++ return DC_ERROR_UNEXPECTED; ++ ++ return DC_OK; ++} ++ ++void dp_receiver_power_ctrl(struct core_link *link, bool on) ++{ ++ uint8_t state; ++ ++ state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3; ++ ++ core_link_write_dpcd(link, DPCD_ADDRESS_POWER_STATE, &state, ++ sizeof(state)); ++} ++ ++ ++/* TODO: HBR2 need raise clock for DP link training */ ++enum dc_status dp_enable_link_phy( ++ struct core_link *link, ++ enum signal_type signal, ++ enum engine_id engine, ++ const struct link_settings *link_settings) ++{ ++ enum dc_status status = DC_OK; ++ ++ if (link->dc->hwss.encoder_enable_output( ++ link->link_enc, ++ link_settings, ++ engine, ++ CLOCK_SOURCE_ID_EXTERNAL, ++ signal, ++ COLOR_DEPTH_UNDEFINED, ++ 0) != ENCODER_RESULT_OK) ++ status = DC_ERROR_UNEXPECTED; ++ ++ dp_receiver_power_ctrl(link, true); ++ ++ return status; ++} ++ ++void dp_disable_link_phy(struct core_link *link, enum signal_type signal) ++{ ++ if(!link) ++ return; ++ ++ if (!link->dp_wa.bits.KEEP_RECEIVER_POWERED) ++ dp_receiver_power_ctrl(link, false); ++ ++ link->dc->hwss.encoder_disable_output(link->link_enc, signal); ++ ++ /* Clear current link setting.*/ ++ dc_service_memset(&link->cur_link_settings, 0, ++ sizeof(link->cur_link_settings)); ++} ++ ++bool dp_set_hw_training_pattern( ++ struct core_link *link, ++ enum hw_dp_training_pattern pattern) ++{ ++ enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; ++ struct encoder_set_dp_phy_pattern_param pattern_param = {0}; ++ struct link_encoder *encoder = link->link_enc; ++ ++ switch (pattern) { ++ case HW_DP_TRAINING_PATTERN_1: ++ test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN1; ++ break; ++ case HW_DP_TRAINING_PATTERN_2: ++ test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN2; ++ break; ++ case HW_DP_TRAINING_PATTERN_3: ++ test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN3; ++ break; ++ default: ++ break; ++ } ++ ++ pattern_param.dp_phy_pattern = test_pattern; ++ pattern_param.custom_pattern = NULL; ++ pattern_param.custom_pattern_size = 0; ++ pattern_param.dp_panel_mode = dp_get_panel_mode(link); ++ ++ link->ctx->dc->hwss.encoder_set_dp_phy_pattern(encoder, &pattern_param); ++ ++ return true; ++} ++ ++ ++bool dp_set_hw_lane_settings( ++ struct core_link *link, ++ const struct link_training_settings *link_settings) ++{ ++ struct link_encoder *encoder = link->link_enc; ++ ++ /* call Encoder to set lane settings */ ++ link->ctx->dc->hwss.encoder_dp_set_lane_settings(encoder, link_settings); ++ ++ return true; ++} ++ ++enum dp_panel_mode dp_get_panel_mode(struct core_link *link) ++{ ++ /* We need to explicitly check that connector ++ * is not DP. Some Travis_VGA get reported ++ * by video bios as DP. ++ */ ++ if (link->public.connector_signal != SIGNAL_TYPE_DISPLAY_PORT) { ++ ++ switch (link->dpcd_caps.branch_dev_id) { ++ case DP_BRANCH_DEVICE_ID_2: ++ if (strncmp( ++ link->dpcd_caps.branch_dev_name, ++ DP_VGA_LVDS_CONVERTER_ID_2, ++ sizeof( ++ link->dpcd_caps. ++ branch_dev_name)) == 0) { ++ return DP_PANEL_MODE_SPECIAL; ++ } ++ break; ++ case DP_BRANCH_DEVICE_ID_3: ++ if (strncmp(link->dpcd_caps.branch_dev_name, ++ DP_VGA_LVDS_CONVERTER_ID_3, ++ sizeof( ++ link->dpcd_caps. ++ branch_dev_name)) == 0) { ++ return DP_PANEL_MODE_SPECIAL; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (link->dpcd_caps.panel_mode_edp) { ++ return DP_PANEL_MODE_EDP; ++ } ++ } ++ ++ return DP_PANEL_MODE_DEFAULT; ++} ++ ++void dp_set_hw_test_pattern( ++ struct core_link *link, ++ enum dp_test_pattern test_pattern) ++{ ++ struct encoder_set_dp_phy_pattern_param pattern_param = {0}; ++ struct link_encoder *encoder = link->link_enc; ++ ++ pattern_param.dp_phy_pattern = test_pattern; ++ pattern_param.custom_pattern = NULL; ++ pattern_param.custom_pattern_size = 0; ++ pattern_param.dp_panel_mode = dp_get_panel_mode(link); ++ ++ link->ctx->dc->hwss.encoder_set_dp_phy_pattern(encoder, &pattern_param); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c +new file mode 100644 +index 0000000..5803e22 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c +@@ -0,0 +1,378 @@ ++/* ++* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++ ++#include "resource.h" ++#include "include/irq_service_interface.h" ++#include "link_encoder_types.h" ++#include "stream_encoder_types.h" ++ ++ ++void unreference_clock_source( ++ struct resource_context *res_ctx, ++ struct clock_source *clock_source) ++{ ++ int i; ++ for (i = 0; i < res_ctx->pool.clk_src_count; i++) { ++ if (res_ctx->pool.clock_sources[i] == clock_source) { ++ res_ctx->clock_source_ref_count[i]--; ++ } ++ } ++} ++ ++void reference_clock_source( ++ struct resource_context *res_ctx, ++ struct clock_source *clock_source) ++{ ++ int i; ++ for (i = 0; i < res_ctx->pool.clk_src_count; i++) { ++ if (res_ctx->pool.clock_sources[i] == clock_source) { ++ res_ctx->clock_source_ref_count[i]++; ++ } ++ } ++} ++ ++bool is_same_timing( ++ const struct dc_crtc_timing *timing1, ++ const struct dc_crtc_timing *timing2) ++{ ++ return dal_memcmp(timing1, timing2, sizeof(struct dc_crtc_timing)) == 0; ++} ++ ++static bool is_sharable_clk_src( ++ const struct core_stream *stream_with_clk_src, ++ const struct core_stream *stream) ++{ ++ enum clock_source_id id = dal_clock_source_get_id( ++ stream_with_clk_src->clock_source); ++ ++ if (stream_with_clk_src->clock_source == NULL) ++ return false; ++ ++ if (!dc_is_dp_signal(stream->signal) && id == CLOCK_SOURCE_ID_EXTERNAL) ++ return false; ++ ++ if(!is_same_timing( ++ &stream_with_clk_src->public.timing, &stream->public.timing)) ++ return false; ++ ++ return true; ++} ++ ++struct clock_source *find_used_clk_src_for_sharing( ++ struct validate_context *context, ++ struct core_stream *stream) ++{ ++ uint8_t i, j; ++ for (i = 0; i < context->target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ for (j = 0; j < target->stream_count; j++) ++ { ++ if (target->streams[j]->clock_source == NULL) ++ continue; ++ if (is_sharable_clk_src(target->streams[j], stream)) ++ return target->streams[j]->clock_source; ++ } ++ } ++ ++ return NULL; ++} ++ ++static enum pixel_format convert_pixel_format_to_dalsurface( ++ enum surface_pixel_format surface_pixel_format) ++{ ++ enum pixel_format dal_pixel_format = PIXEL_FORMAT_UNKNOWN; ++ ++ switch (surface_pixel_format) { ++ case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: ++ dal_pixel_format = PIXEL_FORMAT_INDEX8; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: ++ dal_pixel_format = PIXEL_FORMAT_RGB565; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_RGB565: ++ dal_pixel_format = PIXEL_FORMAT_RGB565; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: ++ dal_pixel_format = PIXEL_FORMAT_ARGB8888; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: ++ dal_pixel_format = PIXEL_FORMAT_ARGB8888; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: ++ dal_pixel_format = PIXEL_FORMAT_ARGB2101010; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: ++ dal_pixel_format = PIXEL_FORMAT_ARGB2101010; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: ++ dal_pixel_format = PIXEL_FORMAT_ARGB2101010_XRBIAS; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: ++ dal_pixel_format = PIXEL_FORMAT_FP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: ++ dal_pixel_format = PIXEL_FORMAT_FP16; ++ break; ++ ++ ++ case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: ++ dal_pixel_format = PIXEL_FORMAT_420BPP12; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: ++ dal_pixel_format = PIXEL_FORMAT_420BPP12; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_422_YCb: ++ dal_pixel_format = PIXEL_FORMAT_422BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_422_YCr: ++ dal_pixel_format = PIXEL_FORMAT_422BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_422_CbY: ++ dal_pixel_format = PIXEL_FORMAT_422BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_422_CrY: ++ dal_pixel_format = PIXEL_FORMAT_422BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb1555: ++ dal_pixel_format = PIXEL_FORMAT_444BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_CrYCb565: ++ dal_pixel_format = PIXEL_FORMAT_444BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb4444: ++ dal_pixel_format = PIXEL_FORMAT_444BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_CbYCrA5551: ++ dal_pixel_format = PIXEL_FORMAT_444BPP16; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb8888: ++ dal_pixel_format = PIXEL_FORMAT_444BPP32; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb2101010: ++ dal_pixel_format = PIXEL_FORMAT_444BPP32; ++ break; ++ case SURFACE_PIXEL_FORMAT_VIDEO_444_CbYCrA1010102: ++ dal_pixel_format = PIXEL_FORMAT_444BPP32; ++ break; ++ default: ++ dal_pixel_format = PIXEL_FORMAT_UNKNOWN; ++ break; ++ } ++ return dal_pixel_format; ++} ++ ++static void calculate_viewport( ++ const struct dc_surface *surface, ++ struct core_stream *stream) ++{ ++ const struct rect src = surface->src_rect; ++ const struct rect clip = surface->clip_rect; ++ const struct rect dst = surface->dst_rect; ++ ++ /* offset = src.ofs + (clip.ofs - dst.ofs) * scl_ratio ++ * num_pixels = clip.num_pix * scl_ratio ++ */ ++ stream->viewport.x = src.x + (clip.x - dst.x) * src.width / dst.width; ++ stream->viewport.width = clip.width * src.width / dst.width; ++ ++ stream->viewport.y = src.y + (clip.y - dst.y) * src.height / dst.height; ++ stream->viewport.height = clip.height * src.height / dst.height; ++ ++ /* Minimum viewport such that 420/422 chroma vp is non 0 */ ++ if (stream->viewport.width < 2) ++ { ++ stream->viewport.width = 2; ++ } ++ if (stream->viewport.height < 2) ++ { ++ stream->viewport.height = 2; ++ } ++} ++ ++static void calculate_overscan( ++ const struct dc_surface *surface, ++ struct core_stream *stream) ++{ ++ stream->overscan.left = stream->public.dst.x; ++ if (stream->public.src.x < surface->clip_rect.x) ++ stream->overscan.left += (surface->clip_rect.x ++ - stream->public.src.x) * stream->public.dst.width ++ / stream->public.src.width; ++ ++ stream->overscan.right = stream->public.timing.h_addressable ++ - stream->public.dst.x - stream->public.dst.width; ++ if (stream->public.src.x + stream->public.src.width ++ > surface->clip_rect.x + surface->clip_rect.width) ++ stream->overscan.right = stream->public.timing.h_addressable - ++ dal_fixed31_32_floor(dal_fixed31_32_div( ++ dal_fixed31_32_from_int( ++ stream->viewport.width), ++ stream->ratios.horz)) - ++ stream->overscan.left; ++ ++ ++ stream->overscan.top = stream->public.dst.y; ++ if (stream->public.src.y < surface->clip_rect.y) ++ stream->overscan.top += (surface->clip_rect.y ++ - stream->public.src.y) * stream->public.dst.height ++ / stream->public.src.height; ++ ++ stream->overscan.bottom = stream->public.timing.v_addressable ++ - stream->public.dst.y - stream->public.dst.height; ++ if (stream->public.src.y + stream->public.src.height ++ > surface->clip_rect.y + surface->clip_rect.height) ++ stream->overscan.bottom = stream->public.timing.v_addressable - ++ dal_fixed31_32_floor(dal_fixed31_32_div( ++ dal_fixed31_32_from_int( ++ stream->viewport.height), ++ stream->ratios.vert)) - ++ stream->overscan.top; ++ ++ ++ /* TODO: Add timing overscan to finalize overscan calculation*/ ++} ++ ++static void calculate_scaling_ratios( ++ const struct dc_surface *surface, ++ struct core_stream *stream) ++{ ++ const uint32_t in_w = stream->public.src.width; ++ const uint32_t in_h = stream->public.src.height; ++ const uint32_t out_w = stream->public.dst.width; ++ const uint32_t out_h = stream->public.dst.height; ++ ++ stream->ratios.horz = dal_fixed31_32_from_fraction( ++ surface->src_rect.width, ++ surface->dst_rect.width); ++ stream->ratios.vert = dal_fixed31_32_from_fraction( ++ surface->src_rect.height, ++ surface->dst_rect.height); ++ ++ if (surface->stereo_format == PLANE_STEREO_FORMAT_SIDE_BY_SIDE) ++ stream->ratios.horz.value *= 2; ++ else if (surface->stereo_format ++ == PLANE_STEREO_FORMAT_TOP_AND_BOTTOM) ++ stream->ratios.vert.value *= 2; ++ ++ stream->ratios.vert.value = stream->ratios.vert.value * in_h / out_h; ++ stream->ratios.horz.value = stream->ratios.horz.value * in_w / out_w; ++ ++ stream->ratios.horz_c = stream->ratios.horz; ++ stream->ratios.vert_c = stream->ratios.vert; ++ ++ if (stream->format == PIXEL_FORMAT_420BPP12) { ++ stream->ratios.horz_c.value /= 2; ++ stream->ratios.vert_c.value /= 2; ++ } else if (stream->format == PIXEL_FORMAT_422BPP16) { ++ stream->ratios.horz_c.value /= 2; ++ } ++} ++ ++/*TODO: per pipe not per stream*/ ++void build_scaling_params( ++ const struct dc_surface *surface, ++ struct core_stream *stream) ++{ ++ /* Important: scaling ratio calculation requires pixel format, ++ * overscan calculation requires scaling ratios and viewport ++ * and lb depth/taps calculation requires overscan. Call sequence ++ * is therefore important */ ++ stream->format = convert_pixel_format_to_dalsurface(surface->format); ++ ++ calculate_viewport(surface, stream); ++ ++ calculate_scaling_ratios(surface, stream); ++ ++ calculate_overscan(surface, stream); ++ ++ /* Check if scaling is required update taps if not */ ++ if (dal_fixed31_32_u2d19(stream->ratios.horz) == 1 << 19) ++ stream->taps.h_taps = 1; ++ else ++ stream->taps.h_taps = surface->scaling_quality.h_taps; ++ ++ if (dal_fixed31_32_u2d19(stream->ratios.horz_c) == 1 << 19) ++ stream->taps.h_taps_c = 1; ++ else ++ stream->taps.h_taps_c = surface->scaling_quality.h_taps_c; ++ ++ if (dal_fixed31_32_u2d19(stream->ratios.vert) == 1 << 19) ++ stream->taps.v_taps = 1; ++ else ++ stream->taps.v_taps = surface->scaling_quality.v_taps; ++ ++ if (dal_fixed31_32_u2d19(stream->ratios.vert_c) == 1 << 19) ++ stream->taps.v_taps_c = 1; ++ else ++ stream->taps.v_taps_c = surface->scaling_quality.v_taps_c; ++} ++ ++void build_scaling_params_for_context( ++ const struct dc *dc, ++ struct validate_context *context) ++{ ++ uint8_t i, j, k; ++ for (i = 0; i < context->target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ if (context->target_flags[i].unchanged) ++ continue; ++ for (j = 0; j < target->status.surface_count; j++) { ++ const struct dc_surface *surface = target->status.surfaces[j]; ++ for (k = 0; k < target->stream_count; k++) { ++ struct core_stream *stream = target->streams[k]; ++ ++ build_scaling_params(surface, stream); ++ } ++ } ++ } ++} ++ ++bool logical_attach_surfaces_to_target( ++ struct dc_surface *surfaces[], ++ uint8_t surface_count, ++ struct dc_target *dc_target) ++{ ++ uint8_t i; ++ struct core_target *target = DC_TARGET_TO_CORE(dc_target); ++ ++ if (target->status.surface_count >= MAX_SURFACE_NUM) { ++ dal_error("Surface: this target has too many surfaces!\n"); ++ return false; ++ } ++ ++ for (i = 0; i < target->status.surface_count; i++) ++ dc_surface_release(target->status.surfaces[i]); ++ ++ for (i = 0; i < surface_count; i++) { ++ struct core_surface *surface = DC_SURFACE_TO_CORE(surfaces[i]); ++ surface->status.dc_target = &target->public; ++ target->status.surfaces[i] = surfaces[i]; ++ dc_surface_retain(target->status.surfaces[i]); ++ } ++ target->status.surface_count = surface_count; ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_sink.c b/drivers/gpu/drm/amd/dal/dc/core/dc_sink.c +new file mode 100644 +index 0000000..3d537d5 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_sink.c +@@ -0,0 +1,118 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dc_services.h" ++#include "dc_helpers.h" ++#include "core_types.h" ++ ++/******************************************************************************* ++ * Private definitions ++ ******************************************************************************/ ++ ++struct sink { ++ struct core_sink protected; ++ int ref_count; ++}; ++ ++#define DC_SINK_TO_SINK(dc_sink) \ ++ container_of(dc_sink, struct sink, protected.public) ++ ++/******************************************************************************* ++ * Private functions ++ ******************************************************************************/ ++ ++static void destruct(struct sink *sink) ++{ ++ ++} ++ ++static bool construct(struct sink *sink, const struct sink_init_data *init_params) ++{ ++ ++ struct core_link *core_link = DC_LINK_TO_LINK(init_params->link); ++ ++ sink->protected.public.sink_signal = init_params->sink_signal; ++ sink->protected.link = core_link; ++ sink->protected.ctx = core_link->ctx; ++ sink->protected.dongle_max_pix_clk = init_params->dongle_max_pix_clk; ++ sink->protected.converter_disable_audio = ++ init_params->converter_disable_audio; ++ ++ return true; ++} ++ ++/******************************************************************************* ++ * Public functions ++ ******************************************************************************/ ++ ++void dc_sink_retain(const struct dc_sink *dc_sink) ++{ ++ struct sink *sink = DC_SINK_TO_SINK(dc_sink); ++ ++ ++sink->ref_count; ++} ++ ++void dc_sink_release(const struct dc_sink *dc_sink) ++{ ++ struct core_sink *core_sink = DC_SINK_TO_CORE(dc_sink); ++ struct sink *sink = DC_SINK_TO_SINK(dc_sink); ++ ++ --sink->ref_count; ++ ++ if (sink->ref_count == 0) { ++ destruct(sink); ++ dc_service_free(core_sink->ctx, sink); ++ } ++} ++ ++ ++/******************************************************************************* ++ * Protected functions - visible only inside of DC (not visible in DM) ++ ******************************************************************************/ ++ ++struct dc_sink *sink_create(const struct sink_init_data *init_params) ++{ ++ struct core_link *core_link = DC_LINK_TO_LINK(init_params->link); ++ ++ struct sink *sink = dc_service_alloc(core_link->ctx, sizeof(*sink)); ++ ++ if (NULL == sink) ++ goto alloc_fail; ++ ++ if (false == construct(sink, init_params)) ++ goto construct_fail; ++ ++ /* TODO should we move this outside to where the assignment actually happens? */ ++ dc_sink_retain(&sink->protected.public); ++ ++ return &sink->protected.public; ++ ++construct_fail: ++ dc_service_free(core_link->ctx, sink); ++ ++alloc_fail: ++ return NULL; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_stream.c b/drivers/gpu/drm/amd/dal/dc/core/dc_stream.c +new file mode 100644 +index 0000000..1a7bf50 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_stream.c +@@ -0,0 +1,172 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++#include "dc.h" ++#include "core_types.h" ++#include "resource.h" ++ ++/******************************************************************************* ++ * Private definitions ++ ******************************************************************************/ ++ ++struct stream { ++ struct core_stream protected; ++ int ref_count; ++}; ++ ++#define DC_STREAM_TO_STREAM(dc_stream) container_of(dc_stream, struct stream, protected.public) ++ ++/******************************************************************************* ++ * Private functions ++ ******************************************************************************/ ++ ++static void build_bit_depth_reduction_params( ++ struct bit_depth_reduction_params *fmt_bit_depth) ++{ ++ /*TODO: Need to un-hardcode, refer to function with same name ++ * in dal2 hw_sequencer*/ ++ ++ fmt_bit_depth->flags.TRUNCATE_ENABLED = 0; ++ fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 0; ++ fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 0; ++ fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 1; ++ ++ fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; ++ /* frame random is on by default */ ++ fmt_bit_depth->flags.FRAME_RANDOM = 1; ++ /* apply RGB dithering */ ++ fmt_bit_depth->flags.RGB_RANDOM = true; ++ ++ return; ++ ++} ++ ++static void setup_pixel_encoding( ++ struct clamping_and_pixel_encoding_params *clamping) ++{ ++ /*TODO: Need to un-hardcode, refer to function with same name ++ * in dal2 hw_sequencer*/ ++ ++ clamping->pixel_encoding = PIXEL_ENCODING_RGB; ++ ++ return; ++} ++ ++static bool construct(struct core_stream *stream, ++ const struct dc_sink *dc_sink_data) ++{ ++ uint32_t i = 0; ++ ++ stream->sink = DC_SINK_TO_CORE(dc_sink_data); ++ stream->ctx = stream->sink->ctx; ++ stream->public.sink = dc_sink_data; ++ ++ dc_sink_retain(dc_sink_data); ++ ++ build_bit_depth_reduction_params(&stream->fmt_bit_depth); ++ setup_pixel_encoding(&stream->clamping); ++ ++ /* Copy audio modes */ ++ /* TODO - Remove this translation */ ++ for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++) ++ { ++ stream->public.audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count; ++ stream->public.audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code; ++ stream->public.audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate; ++ stream->public.audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size; ++ } ++ stream->public.audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count; ++ stream->public.audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency; ++ stream->public.audio_info.video_latency = dc_sink_data->edid_caps.video_latency; ++ dc_service_memmove( ++ stream->public.audio_info.display_name, ++ dc_sink_data->edid_caps.display_name, ++ AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); ++ stream->public.audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id; ++ stream->public.audio_info.product_id = dc_sink_data->edid_caps.product_id; ++ stream->public.audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags; ++ ++ /* TODO - Unhardcode port_id */ ++ stream->public.audio_info.port_id[0] = 0x5558859e; ++ stream->public.audio_info.port_id[1] = 0xd989449; ++ ++ /* EDID CAP translation for HDMI 2.0 */ ++ stream->public.timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble; ++ return true; ++} ++ ++static void destruct(struct core_stream *stream) ++{ ++ dc_sink_release(&stream->sink->public); ++} ++ ++void dc_stream_retain(struct dc_stream *dc_stream) ++{ ++ struct stream *stream = DC_STREAM_TO_STREAM(dc_stream); ++ stream->ref_count++; ++} ++ ++void dc_stream_release(struct dc_stream *public) ++{ ++ struct stream *stream = DC_STREAM_TO_STREAM(public); ++ struct core_stream *protected = DC_STREAM_TO_CORE(public); ++ struct dc_context *ctx = protected->ctx; ++ stream->ref_count--; ++ ++ if (stream->ref_count == 0) { ++ destruct(protected); ++ dc_service_free(ctx, stream); ++ } ++} ++ ++struct dc_stream *dc_create_stream_for_sink(const struct dc_sink *dc_sink) ++{ ++ struct core_sink *sink = DC_SINK_TO_CORE(dc_sink); ++ struct stream *stream; ++ ++ if (sink == NULL) ++ goto alloc_fail; ++ ++ stream = dc_service_alloc(sink->ctx, sizeof(struct stream)); ++ ++ if (NULL == stream) ++ goto alloc_fail; ++ ++ if (false == construct(&stream->protected, dc_sink)) ++ goto construct_fail; ++ ++ dc_stream_retain(&stream->protected.public); ++ ++ return &stream->protected.public; ++ ++construct_fail: ++ dc_service_free(sink->ctx, stream); ++ ++alloc_fail: ++ return NULL; ++} ++ ++ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_surface.c b/drivers/gpu/drm/amd/dal/dc/core/dc_surface.c +new file mode 100644 +index 0000000..41a5feb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_surface.c +@@ -0,0 +1,124 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/* DC interface (public) */ ++#include "dc_services.h" ++#include "dc.h" ++ ++/* DC core (private) */ ++#include "core_dc.h" ++#include "adjustment_types.h" ++ ++ ++/******************************************************************************* ++ * Private structures ++ ******************************************************************************/ ++struct surface { ++ struct core_surface protected; ++ enum dc_irq_source irq_source; ++ int ref_count; ++}; ++ ++#define DC_SURFACE_TO_SURFACE(dc_surface) container_of(dc_surface, struct surface, protected.public) ++#define CORE_SURFACE_TO_SURFACE(core_surface) container_of(core_surface, struct surface, protected) ++ ++/******************************************************************************* ++ * Private functions ++ ******************************************************************************/ ++static bool construct(struct dc_context *ctx, struct surface *surface) ++{ ++ uint32_t i; ++ struct gamma_ramp *gamma = ++ &surface->protected.public.gamma_correction; ++ ++ /* construct gamma default value. */ ++ for (i = 0; i < NUM_OF_RAW_GAMMA_RAMP_RGB_256; i++) { ++ gamma->gamma_ramp_rgb256x3x16.red[i] = ++ (unsigned short) (i << 8); ++ gamma->gamma_ramp_rgb256x3x16.green[i] = ++ (unsigned short) (i << 8); ++ gamma->gamma_ramp_rgb256x3x16.blue[i] = ++ (unsigned short) (i << 8); ++ } ++ gamma->type = GAMMA_RAMP_TYPE_RGB256; ++ gamma->size = sizeof(gamma->gamma_ramp_rgb256x3x16); ++ ++ surface->protected.ctx = ctx; ++ return true; ++} ++ ++static void destruct(struct surface *surface) ++{ ++} ++ ++/******************************************************************************* ++ * Public functions ++ ******************************************************************************/ ++void enable_surface_flip_reporting(struct dc_surface *dc_surface, ++ uint32_t controller_id) ++{ ++ struct surface *surface = DC_SURFACE_TO_SURFACE(dc_surface); ++ surface->irq_source = controller_id + DC_IRQ_SOURCE_PFLIP1 - 1; ++ /*register_flip_interrupt(surface);*/ ++} ++ ++struct dc_surface *dc_create_surface(const struct dc *dc) ++{ ++ struct surface *surface = dc_service_alloc(dc->ctx, sizeof(*surface)); ++ ++ if (NULL == surface) ++ goto alloc_fail; ++ ++ if (false == construct(dc->ctx, surface)) ++ goto construct_fail; ++ ++ dc_surface_retain(&surface->protected.public); ++ ++ return &surface->protected.public; ++ ++construct_fail: ++ dc_service_free(dc->ctx, surface); ++ ++alloc_fail: ++ return NULL; ++} ++ ++void dc_surface_retain(const struct dc_surface *dc_surface) ++{ ++ struct surface *surface = DC_SURFACE_TO_SURFACE(dc_surface); ++ ++ ++surface->ref_count; ++} ++ ++void dc_surface_release(const struct dc_surface *dc_surface) ++{ ++ struct surface *surface = DC_SURFACE_TO_SURFACE(dc_surface); ++ --surface->ref_count; ++ ++ if (surface->ref_count == 0) { ++ destruct(surface); ++ dc_service_free(surface->protected.ctx, surface); ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_target.c b/drivers/gpu/drm/amd/dal/dc/core/dc_target.c +new file mode 100644 +index 0000000..9243c01 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_target.c +@@ -0,0 +1,473 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++#include "core_types.h" ++#include "hw_sequencer.h" ++#include "resource.h" ++ ++#define COEFF_RANGE 3 ++#define REGAMMA_COEFF_A0 31308 ++#define REGAMMA_COEFF_A1 12920 ++#define REGAMMA_COEFF_A2 55 ++#define REGAMMA_COEFF_A3 55 ++#define REGAMMA_COEFF_GAMMA 2400 ++ ++struct target { ++ struct core_target protected; ++ int ref_count; ++}; ++ ++#define DC_TARGET_TO_TARGET(dc_target) \ ++ container_of(dc_target, struct target, protected.public) ++#define CORE_TARGET_TO_TARGET(core_target) \ ++ container_of(core_target, struct target, protected) ++ ++static void construct( ++ struct core_target *target, ++ struct dc_context *ctx, ++ struct dc_stream *dc_streams[], ++ uint8_t stream_count) ++{ ++ uint8_t i; ++ for (i = 0; i < stream_count; i++) { ++ target->streams[i] = DC_STREAM_TO_CORE(dc_streams[i]); ++ target->public.streams[i] = dc_streams[i]; ++ dc_stream_retain(dc_streams[i]); ++ } ++ ++ target->ctx = ctx; ++ target->stream_count = stream_count; ++} ++ ++static void destruct(struct core_target *core_target) ++{ ++ int i; ++ ++ for (i = 0; i < core_target->status.surface_count; i++) { ++ dc_surface_release(core_target->status.surfaces[i]); ++ core_target->status.surfaces[i] = 0; ++ } ++ for (i = 0; i < core_target->stream_count; i++) { ++ dc_stream_release(&core_target->streams[i]->public); ++ core_target->streams[i] = 0; ++ } ++} ++ ++void dc_target_retain(struct dc_target *dc_target) ++{ ++ struct target *target = DC_TARGET_TO_TARGET(dc_target); ++ ++ target->ref_count++; ++} ++ ++void dc_target_release(struct dc_target *dc_target) ++{ ++ struct target *target = DC_TARGET_TO_TARGET(dc_target); ++ struct core_target *protected = DC_TARGET_TO_CORE(dc_target); ++ ++ ASSERT(target->ref_count > 0); ++ target->ref_count--; ++ if (target->ref_count == 0) { ++ destruct(protected); ++ dc_service_free(protected->ctx, target); ++ } ++} ++ ++const struct dc_target_status *dc_target_get_status( ++ const struct dc_target* dc_target) ++{ ++ struct core_target* target = DC_TARGET_TO_CORE(dc_target); ++ return &target->status; ++} ++ ++struct dc_target *dc_create_target_for_streams( ++ struct dc_stream *dc_streams[], ++ uint8_t stream_count) ++{ ++ struct core_stream *stream; ++ struct target *target; ++ ++ if (0 == stream_count) ++ goto target_alloc_fail; ++ ++ stream = DC_STREAM_TO_CORE(dc_streams[0]); ++ ++ target = dc_service_alloc(stream->ctx, sizeof(struct target)); ++ ++ if (NULL == target) ++ goto target_alloc_fail; ++ ++ construct(&target->protected, stream->ctx, dc_streams, stream_count); ++ ++ dc_target_retain(&target->protected.public); ++ ++ return &target->protected.public; ++ ++ ++target_alloc_fail: ++ return NULL; ++} ++ ++static void build_gamma_params( ++ enum pixel_format pixel_format, ++ struct gamma_parameters *gamma_param) ++{ ++ uint32_t i; ++ ++ /* translate parameters */ ++ gamma_param->surface_pixel_format = pixel_format; ++ ++ gamma_param->regamma_adjust_type = GRAPHICS_REGAMMA_ADJUST_SW; ++ gamma_param->degamma_adjust_type = GRAPHICS_REGAMMA_ADJUST_SW; ++ ++ gamma_param->selected_gamma_lut = GRAPHICS_GAMMA_LUT_LEGACY; ++ ++ /* TODO support non-legacy gamma */ ++ gamma_param->disable_adjustments = false; ++ gamma_param->flag.bits.config_is_changed = 0; ++ gamma_param->flag.bits.regamma_update = 1; ++ gamma_param->flag.bits.gamma_update = 1; ++ ++ /* Set regamma */ ++ gamma_param->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB = 0; ++ gamma_param->regamma.features.bits.OVERLAY_DEGAMMA_SRGB = 0; ++ gamma_param->regamma.features.bits.GAMMA_RAMP_ARRAY = 0; ++ gamma_param->regamma.features.bits.APPLY_DEGAMMA = 0; ++ ++ for (i = 0; i < COEFF_RANGE; i++) { ++ gamma_param->regamma.gamma_coeff.a0[i] = REGAMMA_COEFF_A0; ++ gamma_param->regamma.gamma_coeff.a1[i] = REGAMMA_COEFF_A1; ++ gamma_param->regamma.gamma_coeff.a2[i] = REGAMMA_COEFF_A2; ++ gamma_param->regamma.gamma_coeff.a3[i] = REGAMMA_COEFF_A3; ++ gamma_param->regamma.gamma_coeff.gamma[i] = REGAMMA_COEFF_GAMMA; ++ } ++} ++ ++ ++static bool program_gamma( ++ struct dc_context *ctx, ++ struct dc_surface *surface, ++ struct input_pixel_processor *ipp, ++ struct output_pixel_processor *opp) ++{ ++ struct gamma_parameters *gamma_param; ++ bool result= false; ++ ++ gamma_param = dc_service_alloc(ctx, sizeof(struct gamma_parameters)); ++ ++ if (!gamma_param) ++ goto gamma_param_fail; ++ ++ build_gamma_params(surface->format, gamma_param); ++ ++ result = ctx->dc->hwss.set_gamma_ramp(ipp, opp, ++ &surface->gamma_correction, ++ gamma_param); ++ ++ dc_service_free(ctx, gamma_param); ++ ++gamma_param_fail: ++ return result; ++} ++ ++bool dc_commit_surfaces_to_target( ++ struct dc *dc, ++ struct dc_surface *surfaces[], ++ uint8_t surface_count, ++ struct dc_target *dc_target) ++ ++{ ++ uint8_t i, j; ++ struct core_target *target = DC_TARGET_TO_CORE(dc_target); ++ bool need_blanking = (target->status.surface_count == 0); ++ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_INTERFACE_TRACE, ++ LOG_MINOR_COMPONENT_DC, ++ "%s: commit %d surfaces to target 0x%x", ++ __func__, ++ surface_count, ++ dc_target); ++ ++ ++ if (!logical_attach_surfaces_to_target( ++ surfaces, ++ surface_count, ++ dc_target)) { ++ BREAK_TO_DEBUGGER(); ++ goto unexpected_fail; ++ } ++ ++ for (i = 0; i < surface_count; i++) ++ for (j = 0; j < target->stream_count; j++) ++ build_scaling_params(surfaces[i], target->streams[j]); ++ ++ if (dc->hwss.validate_bandwidth(dc, &dc->current_context) != DC_OK) { ++ BREAK_TO_DEBUGGER(); ++ goto unexpected_fail; ++ } ++ ++ dc->hwss.program_bw(dc, &dc->current_context); ++ ++ if (need_blanking) ++ dc_target_disable_memory_requests(dc_target); ++ ++ for (i = 0; i < surface_count; i++) { ++ struct dc_surface *surface = surfaces[i]; ++ struct core_surface *core_surface = DC_SURFACE_TO_CORE(surface); ++ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_INTERFACE_TRACE, ++ LOG_MINOR_COMPONENT_DC, ++ "0x%x:", ++ surface); ++ dc_surface_retain(surface); ++ ++ program_gamma(dc->ctx, surface, ++ target->streams[0]->ipp, ++ target->streams[0]->opp); ++ ++ dc->hwss.set_plane_config( ++ core_surface, ++ target); ++ ++ dc->hwss.update_plane_address(core_surface, target); ++ } ++ ++ if (surface_count > 0 && need_blanking) ++ dc_target_enable_memory_requests(dc_target); ++ ++ return true; ++ ++unexpected_fail: ++ for (i = 0; i < surface_count; i++) { ++ target->status.surfaces[i] = NULL; ++ } ++ target->status.surface_count = 0; ++ ++ return false; ++} ++ ++bool dc_target_is_connected_to_sink( ++ const struct dc_target * dc_target, ++ const struct dc_sink *dc_sink) ++{ ++ struct core_target *target = DC_TARGET_TO_CORE(dc_target); ++ uint8_t i; ++ for (i = 0; i < target->stream_count; i++) { ++ if (&target->streams[i]->sink->public == dc_sink) ++ return true; ++ } ++ return false; ++} ++ ++void dc_target_enable_memory_requests(struct dc_target *target) ++{ ++ uint8_t i; ++ struct core_target *core_target = DC_TARGET_TO_CORE(target); ++ for (i = 0; i < core_target->stream_count; i++) { ++ struct timing_generator *tg = core_target->streams[i]->tg; ++ if (false == core_target->ctx->dc->hwss.enable_memory_requests(tg)) { ++ dal_error("DC: failed to unblank crtc!\n"); ++ BREAK_TO_DEBUGGER(); ++ } ++ } ++} ++ ++void dc_target_disable_memory_requests(struct dc_target *target) ++{ ++ uint8_t i; ++ struct core_target *core_target = DC_TARGET_TO_CORE(target); ++ for (i = 0; i < core_target->stream_count; i++) { ++ struct timing_generator *tg = core_target->streams[i]->tg; ++ ++ if (NULL == tg) { ++ dal_error("DC: timing generator is NULL!\n"); ++ BREAK_TO_DEBUGGER(); ++ continue; ++ } ++ ++ if (false == core_target->ctx->dc->hwss.disable_memory_requests(tg)) { ++ dal_error("DC: failed to blank crtc!\n"); ++ BREAK_TO_DEBUGGER(); ++ } ++ } ++} ++ ++/** ++ * Update the cursor attributes and set cursor surface address ++ */ ++bool dc_target_set_cursor_attributes( ++ struct dc_target *dc_target, ++ const struct dc_cursor_attributes *attributes) ++{ ++ struct core_target *core_target; ++ struct input_pixel_processor *ipp; ++ ++ if (NULL == dc_target) { ++ dal_error("DC: dc_target is NULL!\n"); ++ return false; ++ ++ } ++ ++ core_target = DC_TARGET_TO_CORE(dc_target); ++ ipp = core_target->streams[0]->ipp; ++ ++ if (NULL == ipp) { ++ dal_error("DC: input pixel processor is NULL!\n"); ++ return false; ++ } ++ ++ if (true == core_target->ctx->dc->hwss.cursor_set_attributes(ipp, attributes)) ++ return true; ++ ++ return false; ++} ++ ++bool dc_target_set_cursor_position( ++ struct dc_target *dc_target, ++ const struct dc_cursor_position *position) ++{ ++ struct core_target *core_target; ++ struct input_pixel_processor *ipp; ++ ++ if (NULL == dc_target) { ++ dal_error("DC: dc_target is NULL!\n"); ++ return false; ++ } ++ ++ if (NULL == position) { ++ dal_error("DC: cursor position is NULL!\n"); ++ return false; ++ } ++ ++ core_target = DC_TARGET_TO_CORE(dc_target); ++ ipp = core_target->streams[0]->ipp; ++ ++ if (NULL == ipp) { ++ dal_error("DC: input pixel processor is NULL!\n"); ++ return false; ++ } ++ ++ ++ if (true == core_target->ctx->dc->hwss.cursor_set_position(ipp, position)) ++ return true; ++ ++ return false; ++} ++ ++/* TODO: #flip temporary to make flip work */ ++uint8_t dc_target_get_link_index(const struct dc_target *dc_target) ++{ ++ const struct core_target *target = CONST_DC_TARGET_TO_CORE(dc_target); ++ ++ return target->streams[0]->sink->link->link_index; ++} ++ ++uint32_t dc_target_get_vblank_counter(const struct dc_target *dc_target) ++{ ++ struct core_target *core_target = DC_TARGET_TO_CORE(dc_target); ++ struct timing_generator *tg = core_target->streams[0]->tg; ++ ++ return core_target->ctx->dc->hwss.get_vblank_counter(tg); ++} ++ ++enum dc_irq_source dc_target_get_irq_src( ++ const struct dc_target *dc_target, const enum irq_type irq_type) ++{ ++ struct core_target *core_target = DC_TARGET_TO_CORE(dc_target); ++ ++ /* #TODO - Remove the assumption that the controller is always in the ++ * first stream of a core target */ ++ uint8_t controller_idx = core_target->streams[0]->controller_idx; ++ ++ /* Get controller id */ ++ enum controller_id crtc_id = controller_idx + 1; ++ ++ /* Calculate controller offset */ ++ unsigned int offset = crtc_id - CONTROLLER_ID_D0; ++ unsigned int base = irq_type; ++ ++ /* Calculate irq source */ ++ enum dc_irq_source src = base + offset; ++ ++ return src; ++} ++ ++void dc_target_log( ++ const struct dc_target *dc_target, ++ struct dal_logger *dal_logger, ++ enum log_major log_major, ++ enum log_minor log_minor) ++{ ++ int i; ++ ++ const struct core_target *core_target = ++ CONST_DC_TARGET_TO_CORE(dc_target); ++ ++ dal_logger_write(dal_logger, ++ log_major, ++ log_minor, ++ "core_target 0x%x: surface_count=%d, stream_count=%d", ++ core_target, ++ core_target->status.surface_count, ++ core_target->stream_count); ++ ++ for (i = 0; i < core_target->stream_count; i++) { ++ const struct core_stream *core_stream = core_target->streams[i]; ++ ++ dal_logger_write(dal_logger, ++ log_major, ++ log_minor, ++ "core_stream 0x%x: src: %d, %d, %d, %d; dst: %d, %d, %d, %d;", ++ core_stream, ++ core_stream->public.src.x, ++ core_stream->public.src.y, ++ core_stream->public.src.width, ++ core_stream->public.src.height, ++ core_stream->public.dst.x, ++ core_stream->public.dst.y, ++ core_stream->public.dst.width, ++ core_stream->public.dst.height); ++ dal_logger_write(dal_logger, ++ log_major, ++ log_minor, ++ "\tpix_clk_khz: %d, h_total: %d, v_total: %d", ++ core_stream->public.timing.pix_clk_khz, ++ core_stream->public.timing.h_total, ++ core_stream->public.timing.v_total); ++ dal_logger_write(dal_logger, ++ log_major, ++ log_minor, ++ "\tsink name: %s, serial: %d", ++ core_stream->sink->public.edid_caps.display_name, ++ core_stream->sink->public.edid_caps.serial_number); ++ dal_logger_write(dal_logger, ++ log_major, ++ log_minor, ++ "\tconnector: %d", ++ core_stream->sink->link->connector_index); ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dc.h b/drivers/gpu/drm/amd/dal/dc/dc.h +new file mode 100644 +index 0000000..1db9395 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dc.h +@@ -0,0 +1,440 @@ ++/* ++ * Copyright 2012-14 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef DC_INTERFACE_H_ ++#define DC_INTERFACE_H_ ++ ++#include "dc_types.h" ++/* TODO: We should not include audio_interface.h here. Maybe just define ++ * struct audio_info here */ ++#include "audio_interface.h" ++#include "logger_types.h" ++ ++#define MAX_SINKS_PER_LINK 4 ++ ++/******************************************************************************* ++ * Display Core Interfaces ++ ******************************************************************************/ ++struct dc_init_data { ++ struct dc_context *ctx; ++ struct adapter_service *adapter_srv; ++}; ++ ++struct dc_caps { ++ uint32_t max_targets; ++ uint32_t max_links; ++ uint32_t max_audios; ++}; ++ ++void dc_get_caps(const struct dc *dc, struct dc_caps *caps); ++ ++struct dc *dc_create(const struct dal_init_data *init_params); ++void dc_destroy(struct dc **dc); ++ ++/******************************************************************************* ++ * Surface Interfaces ++ ******************************************************************************/ ++ ++struct dc_surface { ++ bool enabled; ++ bool flip_immediate; ++ struct dc_plane_address address; ++ ++ struct scaling_taps scaling_quality; ++ struct rect src_rect; ++ struct rect dst_rect; ++ struct rect clip_rect; ++ ++ union plane_size plane_size; ++ union plane_tiling_info tiling_info; ++ struct plane_colorimetry colorimetry; ++ ++ enum surface_pixel_format format; ++ enum dc_rotation_angle rotation; ++ enum plane_stereo_format stereo_format; ++ ++ struct gamma_ramp gamma_correction; ++}; ++ ++/* ++ * This structure is filled in by dc_surface_get_status and contains ++ * the last requested address and the currently active address so the called ++ * can determine if there are any outstanding flips ++ */ ++struct dc_surface_status { ++ struct dc_plane_address requested_address; ++ struct dc_plane_address current_address; ++ const struct dc_target *dc_target; ++}; ++ ++/* ++ * Create a new surface with default parameters; ++ */ ++struct dc_surface *dc_create_surface(const struct dc *dc); ++const struct dc_surface_status* dc_surface_get_status( ++ struct dc_surface *dc_surface); ++ ++void dc_surface_retain(const struct dc_surface *dc_surface); ++void dc_surface_release(const struct dc_surface *dc_surface); ++ ++/* ++ * This structure holds a surface address. There could be multiple addresses ++ * in cases such as Stereo 3D, Planar YUV, etc. Other per-flip attributes such ++ * as frame durations and DCC format can also be set. ++ */ ++struct dc_flip_addrs { ++ struct dc_plane_address address; ++ ++ /* TODO: DCC format info */ ++ /* TODO: add flip duration for FreeSync */ ++}; ++ ++/* ++ * Optimized flip address update function. ++ * ++ * After this call: ++ * Surface addresses and flip attributes are programmed. ++ * Surface flip occur at next configured time (h_sync or v_sync flip) ++ */ ++void dc_flip_surface_addrs(struct dc* dc, ++ const struct dc_surface *const surfaces[], ++ struct dc_flip_addrs flip_addrs[], ++ uint32_t count); ++ ++/* ++ * Set up surface attributes and associate to a target ++ * The surfaces parameter is an absolute set of all surface active for the target. ++ * If no surfaces are provided, the target will be blanked; no memory read. ++ * Any flip related attribute changes must be done through this interface. ++ * ++ * After this call: ++ * Surfaces attributes are programmed and configured to be composed into target. ++ * This does not trigger a flip. No surface address is programmed. ++ */ ++bool dc_commit_surfaces_to_target( ++ struct dc *dc, ++ struct dc_surface *dc_surfaces[], ++ uint8_t surface_count, ++ struct dc_target *dc_target); ++ ++/******************************************************************************* ++ * Target Interfaces ++ ******************************************************************************/ ++#define MAX_STREAM_NUM 1 ++ ++struct dc_target { ++ uint32_t temp; ++ const struct dc_stream *streams[MAX_STREAM_NUM]; ++}; ++ ++/* ++ * Target status is returned from dc_target_get_status in order to get the ++ * the IRQ source, current frame counter and currently attached surfaces. ++ */ ++struct dc_target_status { ++ enum dc_irq_source page_flip_src; ++ enum dc_irq_source v_update_src; ++ uint32_t cur_frame_count; ++ const struct dc_surface *surfaces[MAX_SURFACE_NUM]; ++ uint8_t surface_count; ++}; ++ ++struct dc_target *dc_create_target_for_streams( ++ struct dc_stream *dc_streams[], ++ uint8_t stream_count); ++ ++/* ++ * Get the current target status. ++ */ ++const struct dc_target_status *dc_target_get_status( ++ const struct dc_target* dc_target); ++ ++void dc_target_retain(struct dc_target *dc_target); ++void dc_target_release(struct dc_target *dc_target); ++void dc_target_log( ++ const struct dc_target *dc_target, ++ struct dal_logger *dal_logger, ++ enum log_major log_major, ++ enum log_minor log_minor); ++ ++uint8_t dc_get_current_target_count(const struct dc *dc); ++struct dc_target *dc_get_target_at_index(const struct dc *dc, uint8_t i); ++ ++bool dc_target_is_connected_to_sink( ++ const struct dc_target *dc_target, ++ const struct dc_sink *dc_sink); ++ ++uint8_t dc_target_get_link_index(const struct dc_target *dc_target); ++uint8_t dc_target_get_controller_id(const struct dc_target *dc_target); ++ ++uint32_t dc_target_get_vblank_counter(const struct dc_target *dc_target); ++enum dc_irq_source dc_target_get_irq_src( ++ const struct dc_target *dc_target, const enum irq_type irq_type); ++ ++void dc_target_enable_memory_requests(struct dc_target *target); ++void dc_target_disable_memory_requests(struct dc_target *target); ++ ++/* ++ * Structure to store surface/target associations for validation ++ */ ++struct dc_validation_set { ++ const struct dc_target *target; ++ const struct dc_surface *surfaces[4]; ++ uint8_t surface_count; ++}; ++ ++/* ++ * This function takes a set of resources and checks that they are cofunctional. ++ * ++ * After this call: ++ * No hardware is programmed for call. Only validation is done. ++ */ ++bool dc_validate_resources( ++ const struct dc *dc, ++ const struct dc_validation_set set[], ++ uint8_t set_count); ++ ++/* ++ * Set up streams and links associated to targets to drive sinks ++ * The targets parameter is an absolute set of all active targets. ++ * ++ * After this call: ++ * Phy, Encoder, Timing Generator are programmed and enabled. ++ * New targets are enabled with blank stream; no memory read. ++ */ ++bool dc_commit_targets( ++ struct dc *dc, ++ struct dc_target *targets[], ++ uint8_t target_count); ++ ++/******************************************************************************* ++ * Stream Interfaces ++ ******************************************************************************/ ++struct dc_stream { ++ const struct dc_sink *sink; ++ struct dc_crtc_timing timing; ++ ++ struct rect src; /* viewport in target space*/ ++ struct rect dst; /* stream addressable area */ ++ ++ struct audio_info audio_info; ++ ++ /* TODO: dithering */ ++ /* TODO: transfer function (CSC/regamma/gamut remap) */ ++ /* TODO: custom INFO packets */ ++ /* TODO: DRR/Freesync parameters */ ++ /* TODO: ABM info (DMCU) */ ++ /* TODO: PSR info */ ++ /* TODO: CEA VIC */ ++}; ++ ++/** ++ * Create a new default stream for the requested sink ++ */ ++struct dc_stream *dc_create_stream_for_sink(const struct dc_sink *dc_sink); ++ ++void dc_stream_retain(struct dc_stream *dc_stream); ++void dc_stream_release(struct dc_stream *dc_stream); ++ ++/******************************************************************************* ++ * Link Interfaces ++ ******************************************************************************/ ++ ++/* ++ * A link contains one or more sinks and their connected status. ++ * The currently active signal type (HDMI, DP-SST, DP-MST) is also reported. ++ */ ++struct dc_link { ++ const struct dc_sink *sink[MAX_SINKS_PER_LINK]; /* TODO: multiple sink support for MST */ ++ unsigned int sink_count; ++ enum dc_connection_type type; ++ enum signal_type connector_signal; ++ enum dc_irq_source irq_source_hpd; ++ enum dc_irq_source irq_source_hpd_rx;/* aka DP Short Pulse */ ++}; ++ ++/* ++ * Return an enumerated dc_link. dc_link order is constant and determined at ++ * boot time. They cannot be created or destroyed. ++ * Use dc_get_caps() to get number of links. ++ */ ++const struct dc_link *dc_get_link_at_index(struct dc *dc, uint32_t link_index); ++ ++/* Return id of physical connector represented by a dc_link at link_index.*/ ++const struct graphics_object_id dc_get_link_id_at_index( ++ struct dc *dc, uint32_t link_index); ++ ++/* Set backlight level of an embedded panel (eDP, LVDS). */ ++bool dc_link_set_backlight_level(const struct dc_link *dc_link, uint32_t level); ++ ++/* Request DC to detect if there is a Panel connected. */ ++void dc_link_detect(const struct dc_link *dc_link); ++ ++/* Notify DC about DP RX Interrupt (aka Short Pulse Interrupt). ++ * Return: ++ * true - Downstream port status changed. DM should call DC to do the ++ * detection. ++ * false - no change in Downstream port status. No further action required ++ * from DM. */ ++bool dc_link_handle_hpd_rx_irq(const struct dc_link *dc_link); ++ ++bool dc_link_add_sink( ++ struct dc_link *link, ++ struct dc_sink *sink ++ ); ++ ++void dc_link_remove_sink(struct dc_link *link, const struct dc_sink *sink); ++ ++/******************************************************************************* ++ * Sink Interfaces - A sink corresponds to a display output device ++ ******************************************************************************/ ++ ++/* ++ * The sink structure contains EDID and other display device properties ++ */ ++struct dc_sink { ++ enum signal_type sink_signal; ++ struct dc_edid dc_edid; /* raw edid */ ++ struct dc_edid_caps edid_caps; /* parse display caps */ ++}; ++ ++void dc_sink_retain(const struct dc_sink *sink); ++void dc_sink_release(const struct dc_sink *sink); ++ ++const struct audio **dc_get_audios(struct dc *dc); ++ ++struct sink_init_data { ++ enum signal_type sink_signal; ++ struct dc_link *link; ++ uint32_t dongle_max_pix_clk; ++ bool converter_disable_audio; ++}; ++ ++struct dc_sink *sink_create(const struct sink_init_data *init_params); ++ ++ ++/******************************************************************************* ++ * Cursor interfaces - To manages the cursor within a target ++ ******************************************************************************/ ++struct dc_cursor { ++ struct dc_plane_address address; ++ struct dc_cursor_attributes attributes; ++}; ++ ++/* ++ * Create a new cursor with default values for a given target. ++ */ ++struct dc_cursor *dc_create_cursor_for_target( ++ const struct dc *dc, ++ struct dc_target *dc_target); ++ ++/** ++ * Commit cursor attribute changes such as pixel format and dimensions and ++ * surface address. ++ * ++ * After this call: ++ * Cursor address and format is programmed to the new values. ++ * Cursor position is unmodified. ++ */ ++bool dc_commit_cursor( ++ const struct dc *dc, ++ struct dc_cursor *cursor); ++ ++/* ++ * Optimized cursor position update ++ * ++ * After this call: ++ * Cursor position will be programmed as well as enable/disable bit. ++ */ ++bool dc_set_cursor_position( ++ const struct dc *dc, ++ struct dc_cursor *cursor, ++ struct dc_cursor_position *pos); ++ ++ ++ ++/******************************************************************************* ++ * Interrupt interfaces ++ ******************************************************************************/ ++enum dc_irq_source dc_interrupt_to_irq_source( ++ struct dc *dc, ++ uint32_t src_id, ++ uint32_t ext_id); ++void dc_interrupt_set(const struct dc *dc, enum dc_irq_source src, bool enable); ++void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src); ++const enum dc_irq_source dc_get_hpd_irq_source_at_index( ++ struct dc *dc, uint32_t link_index); ++const struct dc_target *dc_get_target_on_irq_source( ++ const struct dc *dc, ++ enum dc_irq_source src); ++ ++ ++/******************************************************************************* ++ * Power Interfaces ++ ******************************************************************************/ ++ ++void dc_set_power_state( ++ struct dc *dc, ++ enum dc_acpi_cm_power_state power_state, ++ enum dc_video_power_state video_power_state); ++void dc_resume(const struct dc *dc); ++ ++/******************************************************************************* ++ * DDC Interfaces ++ ******************************************************************************/ ++ ++const struct ddc_service *dc_get_ddc_at_index( ++ struct dc *dc, uint32_t link_index); ++const struct dc_ddc* dc_get_ddc_from_sink(const struct dc_sink* sink); ++const struct dc_ddc* dc_get_ddc_from_link(const struct dc_link* link); ++bool dc_ddc_query_i2c(const struct dc_ddc* ddc, ++ uint32_t address, ++ uint8_t* write_buf, ++ uint32_t write_size, ++ uint8_t* read_buf, ++ uint32_t read_size); ++bool dc_ddc_dpcd_read(const struct dc_ddc* ddc, uint32_t address, ++ uint8_t* data, uint32_t len); ++bool dc_ddc_dpcd_write(const struct dc_ddc* ddc, uint32_t address, ++ const uint8_t* data, uint32_t len); ++ ++ ++ ++bool dc_read_dpcd( ++ struct dc *dc, ++ uint32_t link_index, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size); ++ ++bool dc_write_dpcd( ++ struct dc *dc, ++ uint32_t link_index, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size); ++ ++ ++#endif /* DC_INTERFACE_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dc_helpers.h b/drivers/gpu/drm/amd/dal/dc/dc_helpers.h +new file mode 100644 +index 0000000..c06eb8c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dc_helpers.h +@@ -0,0 +1,75 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/** ++ * This file defines helper functions provided by the Display Manager to ++ * Display Core. ++ */ ++#ifndef __DC_HELPERS__ ++#define __DC_HELPERS__ ++ ++#include "dc_types.h" ++#include "dc.h" ++ ++enum dc_edid_status dc_helpers_parse_edid_caps( ++ struct dc_context *ctx, ++ const struct dc_edid *edid, ++ struct dc_edid_caps *edid_caps); ++ ++/* ++ * Writes payload allocation table in immediate downstream device. ++ */ ++bool dc_helpers_dp_mst_write_payload_allocation_table( ++ struct dc_context *ctx, ++ const struct dc_sink *sink, ++ struct dp_mst_stream_allocation *alloc_entity, ++ bool enable); ++ ++/* ++ * Polls for ACT (allocation change trigger) handled and ++ */ ++bool dc_helpers_dp_mst_poll_for_allocation_change_trigger( ++ struct dc_context *ctx, ++ const struct dc_sink *sink); ++/* ++ * Sends ALLOCATE_PAYLOAD message. ++ */ ++bool dc_helpers_dp_mst_send_payload_allocation( ++ struct dc_context *ctx, ++ const struct dc_sink *sink, ++ bool enable); ++ ++void dc_helpers_dp_mst_handle_mst_hpd_rx_irq( ++ void *param); ++ ++bool dc_helpers_dp_mst_start_top_mgr( ++ struct dc_context *ctx, ++ const struct dc_link *link); ++ ++void dc_helpers_dp_mst_stop_top_mgr( ++ struct dc_context *ctx, ++ const struct dc_link *link); ++ ++#endif /* __DC_HELPERS__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dc_services.h b/drivers/gpu/drm/amd/dal/dc/dc_services.h +new file mode 100644 +index 0000000..f430864 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dc_services.h +@@ -0,0 +1,174 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/** ++ * This file defines external dependencies of Display Core. ++ */ ++ ++#ifndef __DC_SERVICES_H__ ++#define __DC_SERVICES_H__ ++ ++/* TODO: remove when DC is complete. */ ++#include "dal_services_types.h" ++#include "include/dal_types.h" ++#include "logger_interface.h" ++#include "irq_types.h" ++#include "dal_power_interface_types.h" ++ ++ ++/* if the pointer is not NULL, the allocated memory is zeroed */ ++void *dc_service_alloc(struct dc_context *ctx, uint32_t size); ++ ++void dc_service_free(struct dc_context *ctx, void *p); ++ ++void dc_service_memset(void *p, int32_t c, uint32_t count); ++ ++void dc_service_memmove(void *dst, const void *src, uint32_t size); ++ ++/* TODO: rename to dc_memcmp*/ ++int32_t dal_memcmp(const void *p1, const void *p2, uint32_t count); ++ ++/* TODO: remove when windows_dm will start registering for IRQs */ ++irq_handler_idx dc_service_register_interrupt( ++ struct dc_context *ctx, ++ struct dc_interrupt_params *int_params, ++ interrupt_handler ih, ++ void *handler_args); ++ ++/* TODO: remove when windows_dm will start registering for IRQs */ ++void dc_service_unregister_interrupt( ++ struct dc_context *ctx, ++ enum dc_irq_source irq_source, ++ irq_handler_idx handler_idx); ++ ++/************************************** ++ * Calls to Power Play (PP) component ++ **************************************/ ++ ++/* DAL calls this function to notify PP about clocks it needs for the Mode Set. ++ * This is done *before* it changes DCE clock. ++ * ++ * If required clock is higher than current, then PP will increase the voltage. ++ * ++ * If required clock is lower than current, then PP will defer reduction of ++ * voltage until the call to dc_service_pp_post_dce_clock_change(). ++ * ++ * \input - Contains clocks needed for Mode Set. ++ * ++ * \output - Contains clocks adjusted by PP which DAL should use for Mode Set. ++ * Valid only if function returns zero. ++ * ++ * \returns true - call is successful ++ * false - call failed ++ */ ++bool dc_service_pp_pre_dce_clock_change( ++ struct dc_context *ctx, ++ struct dal_to_power_info *input, ++ struct power_to_dal_info *output); ++ ++struct dc_pp_display_configuration { ++ bool nb_pstate_switch_disable;/* controls NB PState switch */ ++ bool cpu_cc6_disable; /* controls CPU CState switch ( on or off) */ ++ bool cpu_pstate_disable; ++ uint32_t cpu_pstate_separation_time; ++}; ++ ++/* DAL calls this function to notify PP about completion of Mode Set. ++ * For PP it means that current DCE clocks are those which were returned ++ * by dc_service_pp_pre_dce_clock_change(), in the 'output' parameter. ++ * ++ * If the clocks are higher than before, then PP does nothing. ++ * ++ * If the clocks are lower than before, then PP reduces the voltage. ++ * ++ * \returns true - call is successful ++ * false - call failed ++ */ ++bool dc_service_pp_post_dce_clock_change( ++ struct dc_context *ctx, ++ const struct dc_pp_display_configuration *pp_display_cfg); ++ ++/* The returned clocks range are 'static' system clocks which will be used for ++ * mode validation purposes. ++ * ++ * \returns true - call is successful ++ * false - call failed ++ */ ++bool dc_service_get_system_clocks_range( ++ struct dc_context *ctx, ++ struct dal_system_clock_range *sys_clks); ++ ++/* for future use */ ++bool dc_service_pp_set_display_clock( ++ struct dc_context *ctx, ++ struct dal_to_power_dclk *dclk); ++ ++void dc_service_sleep_in_milliseconds(struct dc_context *ctx, uint32_t milliseconds); ++ ++/* end of power component calls */ ++ ++void dc_service_delay_in_microseconds(struct dc_context *ctx, uint32_t microseconds); ++ ++/* ++ * ++ * general debug capabilities ++ * ++ */ ++#if defined(CONFIG_DEBUG_KERNEL) || defined(CONFIG_DEBUG_DRIVER) ++ ++#if defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB) ++#define ASSERT_CRITICAL(expr) do { \ ++ if (WARN_ON(!(expr))) { \ ++ kgdb_breakpoint(); \ ++ } \ ++} while (0) ++#else ++#define ASSERT_CRITICAL(expr) do { \ ++ if (WARN_ON(!(expr))) { \ ++ ; \ ++ } \ ++} while (0) ++#endif ++ ++#if defined(CONFIG_DEBUG_KERNEL_DAL) ++#define ASSERT(expr) ASSERT_CRITICAL(expr) ++ ++#else ++#define ASSERT(expr) WARN_ON(!(expr)) ++#endif ++ ++#define BREAK_TO_DEBUGGER() ASSERT(0) ++ ++#else ++ ++#define ASSERT_CRITICAL(expr) do {if (expr)/* Do nothing */; } while (0) ++ ++#define ASSERT(expr) do {if (expr)/* Do nothing */; } while (0) ++ ++#define BREAK_TO_DEBUGGER() do {} while (0) ++ ++#endif /* CONFIG_DEBUG_KERNEL || CONFIG_DEBUG_DRIVER */ ++ ++#endif /* __DC_SERVICES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dc_temp.h b/drivers/gpu/drm/amd/dal/dc/dc_temp.h +new file mode 100644 +index 0000000..b609deb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dc_temp.h +@@ -0,0 +1,508 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef DC_TEMP_H_ ++#define DC_TEMP_H_ ++ ++#include "dc_types.h" ++ ++#define MAX_SURFACE_NUM 2 ++ ++enum clamping_range { ++ CLAMPING_FULL_RANGE = 0, /* No Clamping */ ++ CLAMPING_LIMITED_RANGE_8BPC, /* 8 bpc: Clamping 1 to FE */ ++ CLAMPING_LIMITED_RANGE_10BPC, /* 10 bpc: Clamping 4 to 3FB */ ++ CLAMPING_LIMITED_RANGE_12BPC, /* 12 bpc: Clamping 10 to FEF */ ++ /* Use programmable clampping value on FMT_CLAMP_COMPONENT_R/G/B. */ ++ CLAMPING_LIMITED_RANGE_PROGRAMMABLE ++}; ++ ++struct clamping_and_pixel_encoding_params { ++ enum dc_pixel_encoding pixel_encoding; /* Pixel Encoding */ ++ enum clamping_range clamping_level; /* Clamping identifier */ ++ enum dc_color_depth c_depth; /* Deep color use. */ ++}; ++ ++struct bit_depth_reduction_params { ++ struct { ++ /* truncate/round */ ++ /* trunc/round enabled*/ ++ uint32_t TRUNCATE_ENABLED:1; ++ /* 2 bits: 0=6 bpc, 1=8 bpc, 2 = 10bpc*/ ++ uint32_t TRUNCATE_DEPTH:2; ++ /* truncate or round*/ ++ uint32_t TRUNCATE_MODE:1; ++ ++ /* spatial dither */ ++ /* Spatial Bit Depth Reduction enabled*/ ++ uint32_t SPATIAL_DITHER_ENABLED:1; ++ /* 2 bits: 0=6 bpc, 1 = 8 bpc, 2 = 10bpc*/ ++ uint32_t SPATIAL_DITHER_DEPTH:2; ++ /* 0-3 to select patterns*/ ++ uint32_t SPATIAL_DITHER_MODE:2; ++ /* Enable RGB random dithering*/ ++ uint32_t RGB_RANDOM:1; ++ /* Enable Frame random dithering*/ ++ uint32_t FRAME_RANDOM:1; ++ /* Enable HighPass random dithering*/ ++ uint32_t HIGHPASS_RANDOM:1; ++ ++ /* temporal dither*/ ++ /* frame modulation enabled*/ ++ uint32_t FRAME_MODULATION_ENABLED:1; ++ /* same as for trunc/spatial*/ ++ uint32_t FRAME_MODULATION_DEPTH:2; ++ /* 2/4 gray levels*/ ++ uint32_t TEMPORAL_LEVEL:1; ++ uint32_t FRC25:2; ++ uint32_t FRC50:2; ++ uint32_t FRC75:2; ++ } flags; ++ ++ uint32_t r_seed_value; ++ uint32_t b_seed_value; ++ uint32_t g_seed_value; ++}; ++ ++enum pipe_gating_control { ++ PIPE_GATING_CONTROL_DISABLE = 0, ++ PIPE_GATING_CONTROL_ENABLE, ++ PIPE_GATING_CONTROL_INIT ++}; ++ ++enum surface_color_space { ++ SURFACE_COLOR_SPACE_SRGB = 0x0000, ++ SURFACE_COLOR_SPACE_BT601 = 0x0001, ++ SURFACE_COLOR_SPACE_BT709 = 0x0002, ++ SURFACE_COLOR_SPACE_XVYCC_BT601 = 0x0004, ++ SURFACE_COLOR_SPACE_XVYCC_BT709 = 0x0008, ++ SURFACE_COLOR_SPACE_XRRGB = 0x0010 ++}; ++ ++enum { ++ MAX_LANES = 2, ++ MAX_COFUNC_PATH = 6, ++ LAYER_INDEX_PRIMARY = -1, ++}; ++ ++/* Scaling format */ ++enum scaling_transformation { ++ SCALING_TRANSFORMATION_UNINITIALIZED, ++ SCALING_TRANSFORMATION_IDENTITY = 0x0001, ++ SCALING_TRANSFORMATION_CENTER_TIMING = 0x0002, ++ SCALING_TRANSFORMATION_FULL_SCREEN_SCALE = 0x0004, ++ SCALING_TRANSFORMATION_PRESERVE_ASPECT_RATIO_SCALE = 0x0008, ++ SCALING_TRANSFORMATION_DAL_DECIDE = 0x0010, ++ SCALING_TRANSFORMATION_INVALID = 0x80000000, ++ ++ /* Flag the first and last */ ++ SCALING_TRANSFORMATION_BEGING = SCALING_TRANSFORMATION_IDENTITY, ++ SCALING_TRANSFORMATION_END = ++ SCALING_TRANSFORMATION_PRESERVE_ASPECT_RATIO_SCALE ++}; ++ ++struct view_stereo_3d_support { ++ enum view_3d_format format; ++ struct { ++ uint32_t CLONE_MODE:1; ++ uint32_t SCALING:1; ++ uint32_t SINGLE_FRAME_SW_PACKED:1; ++ } features; ++}; ++ ++struct plane_colorimetry { ++ enum surface_color_space color_space; ++ bool limited_range; ++}; ++ ++enum tiling_mode { ++ TILING_MODE_INVALID, ++ TILING_MODE_LINEAR, ++ TILING_MODE_TILED, ++ TILING_MODE_COUNT ++}; ++ ++struct view_position { ++ uint32_t x; ++ uint32_t y; ++}; ++ ++union plane_tiling_info { ++ ++ struct { ++ /* Specifies the number of memory banks for tiling ++ * purposes. ++ * Only applies to 2D and 3D tiling modes. ++ * POSSIBLE VALUES: 2,4,8,16 ++ */ ++ uint32_t NUM_BANKS:5; ++ /* Specifies the number of tiles in the x direction ++ * to be incorporated into the same bank. ++ * Only applies to 2D and 3D tiling modes. ++ * POSSIBLE VALUES: 1,2,4,8 ++ */ ++ uint32_t BANK_WIDTH:4; ++ /* Specifies the number of tiles in the y direction to ++ * be incorporated into the same bank. ++ * Only applies to 2D and 3D tiling modes. ++ * POSSIBLE VALUES: 1,2,4,8 ++ */ ++ uint32_t BANK_HEIGHT:4; ++ /* Specifies the macro tile aspect ratio. Only applies ++ * to 2D and 3D tiling modes. ++ */ ++ uint32_t TILE_ASPECT:3; ++ /* Specifies the number of bytes that will be stored ++ * contiguously for each tile. ++ * If the tile data requires more storage than this ++ * amount, it is split into multiple slices. ++ * This field must not be larger than ++ * GB_ADDR_CONFIG.DRAM_ROW_SIZE. ++ * Only applies to 2D and 3D tiling modes. ++ * For color render targets, TILE_SPLIT >= 256B. ++ */ ++ uint32_t TILE_SPLIT:3; ++ /* Specifies the addressing within a tile. ++ * 0x0 - DISPLAY_MICRO_TILING ++ * 0x1 - THIN_MICRO_TILING ++ * 0x2 - DEPTH_MICRO_TILING ++ * 0x3 - ROTATED_MICRO_TILING ++ */ ++ uint32_t TILE_MODE:2; ++ /* Specifies the number of pipes and how they are ++ * interleaved in the surface. ++ * Refer to memory addressing document for complete ++ * details and constraints. ++ */ ++ uint32_t PIPE_CONFIG:5; ++ /* Specifies the tiling mode of the surface. ++ * THIN tiles use an 8x8x1 tile size. ++ * THICK tiles use an 8x8x4 tile size. ++ * 2D tiling modes rotate banks for successive Z slices ++ * 3D tiling modes rotate pipes and banks for Z slices ++ * Refer to memory addressing document for complete ++ * details and constraints. ++ */ ++ uint32_t ARRAY_MODE:4; ++ } grph; ++ ++ ++ struct { ++ /*possible values: 2,4,8,16*/ ++ uint32_t NUM_BANKS:5; ++ /*must use enum video_array_mode*/ ++ uint32_t ARRAY_MODE:4; ++ /*must use enum addr_pipe_config*/ ++ uint32_t PIPE_CONFIG:5; ++ /*possible values 1,2,4,8 */ ++ uint32_t BANK_WIDTH_LUMA:4; ++ /*possible values 1,2,4,8 */ ++ uint32_t BANK_HEIGHT_LUMA:4; ++ /*must use enum macro_tile_aspect*/ ++ uint32_t TILE_ASPECT_LUMA:3; ++ /*must use enum tile_split*/ ++ uint32_t TILE_SPLIT_LUMA:3; ++ /*must use micro_tile_mode */ ++ uint32_t TILE_MODE_LUMA:2; ++ /*possible values: 1,2,4,8*/ ++ uint32_t BANK_WIDTH_CHROMA:4; ++ /*possible values: 1,2,4,8*/ ++ uint32_t BANK_HEIGHT_CHROMA:4; ++ /*must use enum macro_tile_aspect*/ ++ uint32_t TILE_ASPECT_CHROMA:3; ++ /*must use enum tile_split*/ ++ uint32_t TILE_SPLIT_CHROMA:3; ++ /*must use enum micro_tile_mode*/ ++ uint32_t TILE_MODE_CHROMA:2; ++ ++ } video; ++ ++ uint64_t value; ++}; ++ ++union plane_size { ++ /* Grph or Video will be selected ++ * based on format above: ++ * Use Video structure if ++ * format >= DalPixelFormat_VideoBegin ++ * else use Grph structure ++ */ ++ struct { ++ struct rect surface_size; ++ /* Graphic surface pitch in pixels. ++ * In LINEAR_GENERAL mode, pitch ++ * is 32 pixel aligned. ++ */ ++ uint32_t surface_pitch; ++ } grph; ++ ++ struct { ++ struct rect luma_size; ++ /* Graphic surface pitch in pixels. ++ * In LINEAR_GENERAL mode, pitch is ++ * 32 pixel aligned. ++ */ ++ uint32_t luma_pitch; ++ ++ struct rect chroma_size; ++ /* Graphic surface pitch in pixels. ++ * In LINEAR_GENERAL mode, pitch is ++ * 32 pixel aligned. ++ */ ++ uint32_t chroma_pitch; ++ } video; ++}; ++ ++/* Windows only */ ++enum dc_scaling_transform { ++ SCL_TRANS_CENTERED = 0, ++ SCL_TRANS_ASPECT_RATIO, ++ SCL_TRANS_FULL ++}; ++ ++struct dev_c_lut { ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++}; ++ ++struct dev_c_lut16 { ++ uint16_t red; ++ uint16_t green; ++ uint16_t blue; ++}; ++ ++enum gamma_ramp_type { ++ GAMMA_RAMP_UNINITIALIZED = 0, ++ GAMMA_RAMP_DEFAULT, ++ GAMMA_RAMP_RBG256X3X16, ++ GAMMA_RAMP_DXGI_1, ++}; ++ ++enum surface_type { ++ OVERLAY_SURFACE = 1, GRAPHIC_SURFACE ++}; ++ ++#define CONST_RGB_GAMMA_VALUE 2400 ++ ++enum { ++ RGB_256X3X16 = 256, DX_GAMMA_RAMP_MAX = 1025 ++}; ++ ++struct gamma_ramp_rgb256x3x16 { ++ uint16_t red[RGB_256X3X16]; ++ uint16_t green[RGB_256X3X16]; ++ uint16_t blue[RGB_256X3X16]; ++}; ++ ++struct dxgi_rgb { ++ struct fixed32_32 red; ++ struct fixed32_32 green; ++ struct fixed32_32 blue; ++}; ++ ++struct gamma_ramp_dxgi_1 { ++ struct dxgi_rgb scale; ++ struct dxgi_rgb offset; ++ struct dxgi_rgb gamma_curve[DX_GAMMA_RAMP_MAX]; ++}; ++ ++struct gamma_ramp { ++ enum gamma_ramp_type type; ++ union { ++ struct gamma_ramp_rgb256x3x16 gamma_ramp_rgb256x3x16; ++ struct gamma_ramp_dxgi_1 gamma_ramp_dxgi1; ++ }; ++ uint32_t size; ++}; ++ ++struct regamma_ramp { ++ uint16_t gamma[RGB_256X3X16 * 3]; ++}; ++ ++/* used by Graphics and Overlay gamma */ ++struct gamma_coeff { ++ int32_t gamma[3]; ++ int32_t a0[3]; /* index 0 for red, 1 for green, 2 for blue */ ++ int32_t a1[3]; ++ int32_t a2[3]; ++ int32_t a3[3]; ++}; ++ ++struct regamma_lut { ++ union { ++ struct { ++ uint32_t GRAPHICS_DEGAMMA_SRGB :1; ++ uint32_t OVERLAY_DEGAMMA_SRGB :1; ++ uint32_t GAMMA_RAMP_ARRAY :1; ++ uint32_t APPLY_DEGAMMA :1; ++ uint32_t RESERVED :28; ++ } bits; ++ uint32_t value; ++ } features; ++ ++ union { ++ struct regamma_ramp regamma_ramp; ++ struct gamma_coeff gamma_coeff; ++ }; ++}; ++ ++union gamma_flag { ++ struct { ++ uint32_t config_is_changed :1; ++ uint32_t both_pipe_req :1; ++ uint32_t regamma_update :1; ++ uint32_t gamma_update :1; ++ uint32_t reserved :28; ++ } bits; ++ uint32_t u_all; ++}; ++ ++enum graphics_regamma_adjust { ++ GRAPHICS_REGAMMA_ADJUST_BYPASS = 0, GRAPHICS_REGAMMA_ADJUST_HW, /* without adjustments */ ++ GRAPHICS_REGAMMA_ADJUST_SW /* use adjustments */ ++}; ++ ++enum graphics_gamma_lut { ++ GRAPHICS_GAMMA_LUT_LEGACY = 0, /* use only legacy LUT */ ++ GRAPHICS_GAMMA_LUT_REGAMMA, /* use only regamma LUT */ ++ GRAPHICS_GAMMA_LUT_LEGACY_AND_REGAMMA /* use legacy & regamma LUT's */ ++}; ++ ++enum graphics_degamma_adjust { ++ GRAPHICS_DEGAMMA_ADJUST_BYPASS = 0, GRAPHICS_DEGAMMA_ADJUST_HW, /*without adjustments */ ++ GRAPHICS_DEGAMMA_ADJUST_SW /* use adjustments */ ++}; ++ ++struct gamma_parameters { ++ union gamma_flag flag; ++ enum pixel_format surface_pixel_format; /*OS surface pixel format*/ ++ struct regamma_lut regamma; ++ ++ enum graphics_regamma_adjust regamma_adjust_type; ++ enum graphics_degamma_adjust degamma_adjust_type; ++ ++ enum graphics_gamma_lut selected_gamma_lut; ++ ++ bool disable_adjustments; ++ ++ /* here we grow with parameters if necessary */ ++}; ++ ++struct pixel_format_support { ++ bool INDEX8 :1; ++ bool RGB565 :1; ++ bool ARGB8888 :1; ++ bool ARGB2101010 :1; ++ bool ARGB2101010_XRBIAS :1; ++ bool FP16 :1; ++}; ++ ++struct render_mode { ++ struct view view; ++ enum pixel_format pixel_format; ++}; ++ ++struct refresh_rate { ++ uint32_t field_rate; ++ bool INTERLACED :1; ++ bool VIDEO_OPTIMIZED_RATE :1; ++}; ++ ++struct stereo_3d_view { ++ enum view_3d_format view_3d_format; ++ union { ++ uint32_t raw; ++ struct /*stereo_3d_view_flags*/ ++ { ++ bool SINGLE_FRAME_SW_PACKED :1; ++ bool EXCLUSIVE_3D :1; ++ } bits; ++ } flags; ++}; ++ ++enum solution_importance { ++ SOLUTION_IMPORTANCE_PREFERRED = 1, ++ /* Means we want to use this solution ++ * even in wide topology configurations*/ ++ SOLUTION_IMPORTANCE_SAFE, ++ SOLUTION_IMPORTANCE_UNSAFE, ++ SOLUTION_IMPORTANCE_DEFAULT ++/* Temporary state , means Solution object ++ * should define importance by itself ++ */ ++}; ++ ++struct solution { ++ const struct dc_mode_timing *dc_mode_timing; ++ enum solution_importance importance; ++ bool is_custom_mode; ++ uint32_t scl_support[NUM_PIXEL_FORMATS]; ++ /* bit vector of the scaling that can be supported on the timing */ ++ uint32_t scl_support_guaranteed[NUM_PIXEL_FORMATS]; ++ /* subset of m_sclSupport that can be guaranteed supported */ ++}; ++ ++enum timing_select { ++ TIMING_SELECT_DEFAULT, ++ TIMING_SELECT_NATIVE_ONLY, ++ TIMING_SELECT_PRESERVE_ASPECT ++}; ++ ++enum downscale_state { ++ DOWNSCALESTATE_DEFAULT, // Disabled, but not user selected ++ DOWNSCALESTATE_DISABLED, // User disabled through CCC ++ DOWNSCALESTATE_ENABLED // User enabled through CCC ++}; ++struct scaling_support { ++ bool IDENTITY :1; ++ bool FULL_SCREEN_SCALE :1; ++ bool PRESERVE_ASPECT_RATIO_SCALE :1; ++ bool CENTER_TIMING :1; ++}; ++ ++ ++/* TODO: combine the two cursor functions into one to make cursor ++ * programming resistant to changes in OS call sequence. */ ++bool dc_target_set_cursor_attributes( ++ struct dc_target *dc_target, ++ const struct dc_cursor_attributes *attributes); ++ ++bool dc_target_set_cursor_position( ++ struct dc_target *dc_target, ++ const struct dc_cursor_position *position); ++ ++/****************************************************************************** ++ * TODO: these definitions only for Timing Sync feature bring-up. Remove ++ * when the feature is complete. ++ *****************************************************************************/ ++ ++#define MAX_TARGET_NUM 6 ++ ++void dc_print_sync_report( ++ const struct dc *dc); ++ ++/******************************************************************************/ ++ ++#endif /* DC_TEMP_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dc_types.h b/drivers/gpu/drm/amd/dal/dc/dc_types.h +new file mode 100644 +index 0000000..b6526e9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dc_types.h +@@ -0,0 +1,677 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef DC_TYPES_H_ ++#define DC_TYPES_H_ ++ ++#include "fixed32_32.h" ++#include "fixed31_32.h" ++#include "irq_types.h" ++ ++/* forward declarations */ ++struct dc; ++struct dc_surface; ++struct dc_target; ++struct dc_stream; ++struct dc_link; ++struct dc_sink; ++struct dal; ++ ++#define MAX_EDID_BUFFER_SIZE 512 ++ ++/*Displayable pixel format in fb*/ ++enum surface_pixel_format { ++ SURFACE_PIXEL_FORMAT_GRPH_BEGIN = 0, ++ /*TOBE REMOVED paletta 256 colors*/ ++ SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS = ++ SURFACE_PIXEL_FORMAT_GRPH_BEGIN, ++ /*16 bpp*/ ++ SURFACE_PIXEL_FORMAT_GRPH_ARGB1555, ++ /*16 bpp*/ ++ SURFACE_PIXEL_FORMAT_GRPH_RGB565, ++ /*32 bpp*/ ++ SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, ++ /*32 bpp swaped*/ ++ SURFACE_PIXEL_FORMAT_GRPH_BGRA8888, ++ ++ SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010, ++ /*swaped*/ ++ SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010, ++ /*TOBE REMOVED swaped, XR_BIAS has no differance ++ * for pixel layout than previous and we can ++ * delete this after discusion*/ ++ SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS, ++ /*64 bpp */ ++ SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616, ++ /*swaped & float*/ ++ SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F, ++ /*grow graphics here if necessary */ ++ ++ SURFACE_PIXEL_FORMAT_VIDEO_BEGIN, ++ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr = ++ SURFACE_PIXEL_FORMAT_VIDEO_BEGIN, ++ SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb, ++ SURFACE_PIXEL_FORMAT_VIDEO_422_YCb, ++ SURFACE_PIXEL_FORMAT_VIDEO_422_YCr, ++ SURFACE_PIXEL_FORMAT_VIDEO_422_CbY, ++ SURFACE_PIXEL_FORMAT_VIDEO_422_CrY, ++ /*grow 422/420 video here if necessary */ ++ SURFACE_PIXEL_FORMAT_VIDEO_444_BEGIN, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb1555 = ++ SURFACE_PIXEL_FORMAT_VIDEO_444_BEGIN, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_CrYCb565, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb4444, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_CbYCrA5551, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb8888, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb2101010, ++ SURFACE_PIXEL_FORMAT_VIDEO_444_CbYCrA1010102 ++ /*grow 444 video here if necessary */ ++}; ++ ++ ++/* Pixel format */ ++enum pixel_format { ++ /*graph*/ ++ PIXEL_FORMAT_UNINITIALIZED, ++ PIXEL_FORMAT_INDEX8, ++ PIXEL_FORMAT_RGB565, ++ PIXEL_FORMAT_ARGB8888, ++ PIXEL_FORMAT_ARGB2101010, ++ PIXEL_FORMAT_ARGB2101010_XRBIAS, ++ PIXEL_FORMAT_FP16, ++ /*video*/ ++ PIXEL_FORMAT_420BPP12, ++ PIXEL_FORMAT_422BPP16, ++ PIXEL_FORMAT_444BPP16, ++ PIXEL_FORMAT_444BPP32, ++ /*end of pixel format definition*/ ++ PIXEL_FORMAT_INVALID, ++ ++ PIXEL_FORMAT_GRPH_BEGIN = PIXEL_FORMAT_INDEX8, ++ PIXEL_FORMAT_GRPH_END = PIXEL_FORMAT_FP16, ++ PIXEL_FORMAT_VIDEO_BEGIN = PIXEL_FORMAT_420BPP12, ++ PIXEL_FORMAT_VIDEO_END = PIXEL_FORMAT_444BPP32, ++ PIXEL_FORMAT_UNKNOWN ++}; ++ ++enum plane_stereo_format { ++ PLANE_STEREO_FORMAT_NONE = 0, ++ PLANE_STEREO_FORMAT_SIDE_BY_SIDE = 1, ++ PLANE_STEREO_FORMAT_TOP_AND_BOTTOM = 2, ++ PLANE_STEREO_FORMAT_FRAME_ALTERNATE = 3, ++ PLANE_STEREO_FORMAT_ROW_INTERLEAVED = 5, ++ PLANE_STEREO_FORMAT_COLUMN_INTERLEAVED = 6, ++ PLANE_STEREO_FORMAT_CHECKER_BOARD = 7 ++}; ++ ++/* 3D format for view, typically define how L/R eye surface is arranged within ++ * frames ++ */ ++enum view_3d_format { ++ VIEW_3D_FORMAT_NONE = 0, ++ VIEW_3D_FORMAT_FRAME_SEQUENTIAL, ++ VIEW_3D_FORMAT_SIDE_BY_SIDE, ++ VIEW_3D_FORMAT_TOP_AND_BOTTOM, ++ VIEW_3D_FORMAT_COUNT, ++ VIEW_3D_FORMAT_FIRST = VIEW_3D_FORMAT_FRAME_SEQUENTIAL ++}; ++ ++enum dc_pixel_encoding { ++ PIXEL_ENCODING_UNDEFINED, ++ PIXEL_ENCODING_RGB, ++ PIXEL_ENCODING_YCBCR422, ++ PIXEL_ENCODING_YCBCR444, ++ PIXEL_ENCODING_YCBCR420, ++ PIXEL_ENCODING_COUNT ++}; ++ ++/* TODO: Find way to calculate number of bits ++ * Please increase if pixel_format enum increases ++ * num from PIXEL_FORMAT_INDEX8 to PIXEL_FORMAT_444BPP32 ++ */ ++#define NUM_PIXEL_FORMATS 10 ++ ++ ++ ++union large_integer { ++ struct { ++ uint32_t low_part; ++ int32_t high_part; ++ }; ++ ++ struct { ++ uint32_t low_part; ++ int32_t high_part; ++ } u; ++ ++ int64_t quad_part; ++}; ++ ++#define PHYSICAL_ADDRESS_LOC union large_integer ++ ++enum dc_edid_connector_type { ++ EDID_CONNECTOR_UNKNOWN = 0, ++ EDID_CONNECTOR_ANALOG = 1, ++ EDID_CONNECTOR_DIGITAL = 10, ++ EDID_CONNECTOR_DVI = 11, ++ EDID_CONNECTOR_HDMIA = 12, ++ EDID_CONNECTOR_MDDI = 14, ++ EDID_CONNECTOR_DISPLAYPORT = 15 ++}; ++ ++enum dc_edid_status { ++ EDID_OK, ++ EDID_BAD_INPUT, ++ EDID_NO_RESPONSE, ++ EDID_BAD_CHECKSUM, ++}; ++ ++/* audio capability from EDID*/ ++struct dc_cea_audio_mode { ++ uint8_t format_code; /* ucData[0] [6:3]*/ ++ uint8_t channel_count; /* ucData[0] [2:0]*/ ++ uint8_t sample_rate; /* ucData[1]*/ ++ union { ++ uint8_t sample_size; /* for LPCM*/ ++ /* for Audio Formats 2-8 (Max bit rate divided by 8 kHz)*/ ++ uint8_t max_bit_rate; ++ uint8_t audio_codec_vendor_specific; /* for Audio Formats 9-15*/ ++ }; ++}; ++ ++struct dc_edid { ++ uint32_t length; ++ uint8_t raw_edid[MAX_EDID_BUFFER_SIZE]; ++}; ++ ++/* When speaker location data block is not available, DEFAULT_SPEAKER_LOCATION ++ * is used. In this case we assume speaker location are: front left, front ++ * right and front center. */ ++#define DEFAULT_SPEAKER_LOCATION 5 ++ ++#define DC_MAX_AUDIO_DESC_COUNT 16 ++ ++#define AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS 20 ++ ++struct dc_edid_caps { ++ /* sink identification */ ++ uint16_t manufacturer_id; ++ uint16_t product_id; ++ uint32_t serial_number; ++ uint8_t manufacture_week; ++ uint8_t manufacture_year; ++ uint8_t display_name[AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS]; ++ ++ /* audio caps */ ++ uint8_t speaker_flags; ++ uint32_t audio_mode_count; ++ struct dc_cea_audio_mode audio_modes[DC_MAX_AUDIO_DESC_COUNT]; ++ uint32_t audio_latency; ++ uint32_t video_latency; ++ ++ /*HDMI 2.0 caps*/ ++ uint8_t lte_340mcsc_scramble; ++}; ++ ++struct scaling_taps { ++ uint32_t v_taps; ++ uint32_t h_taps; ++ uint32_t v_taps_c; ++ uint32_t h_taps_c; ++}; ++ ++struct scaling_ratios { ++ struct fixed31_32 horz; ++ struct fixed31_32 vert; ++ struct fixed31_32 horz_c; ++ struct fixed31_32 vert_c; ++}; ++ ++struct rect { ++ uint32_t x; ++ uint32_t y; ++ uint32_t width; ++ uint32_t height; ++}; ++ ++struct view { ++ uint32_t width; ++ uint32_t height; ++}; ++ ++struct dc_resolution { ++ uint32_t width; ++ uint32_t height; ++}; ++ ++ ++struct dc_mode_flags { ++ /* note: part of refresh rate flag*/ ++ uint32_t INTERLACE :1; ++ /* native display timing*/ ++ uint32_t NATIVE :1; ++ /* preferred is the recommended mode, one per display */ ++ uint32_t PREFERRED :1; ++ /* true if this mode should use reduced blanking timings ++ *_not_ related to the Reduced Blanking adjustment*/ ++ uint32_t REDUCED_BLANKING :1; ++ /* note: part of refreshrate flag*/ ++ uint32_t VIDEO_OPTIMIZED_RATE :1; ++ /* should be reported to upper layers as mode_flags*/ ++ uint32_t PACKED_PIXEL_FORMAT :1; ++ /*< preferred view*/ ++ uint32_t PREFERRED_VIEW :1; ++ /* this timing should be used only in tiled mode*/ ++ uint32_t TILED_MODE :1; ++ uint32_t DSE_MODE :1; ++ /* Refresh rate divider when Miracast sink is using a ++ different rate than the output display device ++ Must be zero for wired displays and non-zero for ++ Miracast displays*/ ++ uint32_t MIRACAST_REFRESH_DIVIDER; ++}; ++ ++struct dc_crtc_timing_flags { ++ uint32_t INTERLACE :1; ++ uint32_t HSYNC_POSITIVE_POLARITY :1; /* when set to 1, ++ it is positive polarity --reversed with dal1 or video bios define*/ ++ uint32_t VSYNC_POSITIVE_POLARITY :1; /* when set to 1, ++ it is positive polarity --reversed with dal1 or video bios define*/ ++ ++ uint32_t HORZ_COUNT_BY_TWO:1; ++ ++ uint32_t EXCLUSIVE_3D :1; /* if this bit set, ++ timing can be driven in 3D format only ++ and there is no corresponding 2D timing*/ ++ uint32_t RIGHT_EYE_3D_POLARITY :1; /* 1 - means right eye polarity ++ (right eye = '1', left eye = '0') */ ++ uint32_t SUB_SAMPLE_3D :1; /* 1 - means left/right images subsampled ++ when mixed into 3D image. 0 - means summation (3D timing is doubled)*/ ++ uint32_t USE_IN_3D_VIEW_ONLY :1; /* Do not use this timing in 2D View, ++ because corresponding 2D timing also present in the list*/ ++ uint32_t STEREO_3D_PREFERENCE :1; /* Means this is 2D timing ++ and we want to match priority of corresponding 3D timing*/ ++ uint32_t Y_ONLY :1; ++ ++ uint32_t YCBCR420 :1; /* TODO: shouldn't need this flag, should be a separate pixel format */ ++ uint32_t DTD_COUNTER :5; /* values 1 to 16 */ ++ ++ /* HDMI 2.0 - Support scrambling for TMDS character ++ * rates less than or equal to 340Mcsc */ ++ uint32_t LTE_340MCSC_SCRAMBLE:1; ++ ++}; ++ ++enum dc_timing_standard { ++ TIMING_STANDARD_UNDEFINED, ++ TIMING_STANDARD_DMT, ++ TIMING_STANDARD_GTF, ++ TIMING_STANDARD_CVT, ++ TIMING_STANDARD_CVT_RB, ++ TIMING_STANDARD_CEA770, ++ TIMING_STANDARD_CEA861, ++ TIMING_STANDARD_HDMI, ++ TIMING_STANDARD_TV_NTSC, ++ TIMING_STANDARD_TV_NTSC_J, ++ TIMING_STANDARD_TV_PAL, ++ TIMING_STANDARD_TV_PAL_M, ++ TIMING_STANDARD_TV_PAL_CN, ++ TIMING_STANDARD_TV_SECAM, ++ TIMING_STANDARD_EXPLICIT, ++ /*!< For explicit timings from EDID, VBIOS, etc.*/ ++ TIMING_STANDARD_USER_OVERRIDE, ++ /*!< For mode timing override by user*/ ++ TIMING_STANDARD_MAX ++}; ++ ++enum dc_aspect_ratio { ++ ASPECT_RATIO_NO_DATA, ++ ASPECT_RATIO_4_3, ++ ASPECT_RATIO_16_9, ++ ASPECT_RATIO_64_27, ++ ASPECT_RATIO_256_135, ++ ASPECT_RATIO_FUTURE ++}; ++ ++enum dc_color_depth { ++ COLOR_DEPTH_UNDEFINED, ++ COLOR_DEPTH_666, ++ COLOR_DEPTH_888, ++ COLOR_DEPTH_101010, ++ COLOR_DEPTH_121212, ++ COLOR_DEPTH_141414, ++ COLOR_DEPTH_161616, ++ COLOR_DEPTH_COUNT ++}; ++ ++enum dc_timing_3d_format { ++ TIMING_3D_FORMAT_NONE, ++ TIMING_3D_FORMAT_FRAME_ALTERNATE, /* No stereosync at all*/ ++ TIMING_3D_FORMAT_INBAND_FA, /* Inband Frame Alternate (DVI/DP)*/ ++ TIMING_3D_FORMAT_DP_HDMI_INBAND_FA, /* Inband FA to HDMI Frame Pack*/ ++ /* for active DP-HDMI dongle*/ ++ TIMING_3D_FORMAT_SIDEBAND_FA, /* Sideband Frame Alternate (eDP)*/ ++ TIMING_3D_FORMAT_HW_FRAME_PACKING, ++ TIMING_3D_FORMAT_SW_FRAME_PACKING, ++ TIMING_3D_FORMAT_ROW_INTERLEAVE, ++ TIMING_3D_FORMAT_COLUMN_INTERLEAVE, ++ TIMING_3D_FORMAT_PIXEL_INTERLEAVE, ++ TIMING_3D_FORMAT_SIDE_BY_SIDE, ++ TIMING_3D_FORMAT_TOP_AND_BOTTOM, ++ TIMING_3D_FORMAT_SBS_SW_PACKED, ++ /* Side-by-side, packed by application/driver into 2D frame*/ ++ TIMING_3D_FORMAT_TB_SW_PACKED, ++ /* Top-and-bottom, packed by application/driver into 2D frame*/ ++ ++ TIMING_3D_FORMAT_MAX, ++}; ++ ++enum dc_timing_source { ++ TIMING_SOURCE_UNDEFINED, ++ ++ /* explicitly specifed by user, most important*/ ++ TIMING_SOURCE_USER_FORCED, ++ TIMING_SOURCE_USER_OVERRIDE, ++ TIMING_SOURCE_CUSTOM, ++ TIMING_SOURCE_EXPLICIT, ++ ++ /* explicitly specified by the display device, more important*/ ++ TIMING_SOURCE_EDID_CEA_SVD_3D, ++ TIMING_SOURCE_EDID_CEA_SVD_PREFERRED, ++ TIMING_SOURCE_EDID_CEA_SVD_420, ++ TIMING_SOURCE_EDID_DETAILED, ++ TIMING_SOURCE_EDID_ESTABLISHED, ++ TIMING_SOURCE_EDID_STANDARD, ++ TIMING_SOURCE_EDID_CEA_SVD, ++ TIMING_SOURCE_EDID_CVT_3BYTE, ++ TIMING_SOURCE_EDID_4BYTE, ++ TIMING_SOURCE_VBIOS, ++ TIMING_SOURCE_CV, ++ TIMING_SOURCE_TV, ++ TIMING_SOURCE_HDMI_VIC, ++ ++ /* implicitly specified by display device, still safe but less important*/ ++ TIMING_SOURCE_DEFAULT, ++ ++ /* only used for custom base modes */ ++ TIMING_SOURCE_CUSTOM_BASE, ++ ++ /* these timing might not work, least important*/ ++ TIMING_SOURCE_RANGELIMIT, ++ TIMING_SOURCE_OS_FORCED, ++ TIMING_SOURCE_IMPLICIT, ++ ++ /* only used by default mode list*/ ++ TIMING_SOURCE_BASICMODE, ++ ++ TIMING_SOURCE_COUNT ++}; ++ ++enum dc_timing_support_method { ++ TIMING_SUPPORT_METHOD_UNDEFINED, ++ TIMING_SUPPORT_METHOD_EXPLICIT, ++ TIMING_SUPPORT_METHOD_IMPLICIT, ++ TIMING_SUPPORT_METHOD_NATIVE ++}; ++ ++struct dc_mode_info { ++ uint32_t pixel_width; ++ uint32_t pixel_height; ++ uint32_t field_rate; ++ /* Vertical refresh rate for progressive modes. ++ * Field rate for interlaced modes.*/ ++ ++ enum dc_timing_standard timing_standard; ++ enum dc_timing_source timing_source; ++ struct dc_mode_flags flags; ++}; ++ ++/* TODO: assess necessity*/ ++/*scanning type*/ ++enum scanning_type { ++ SCANNING_TYPE_NODATA = 0, ++ SCANNING_TYPE_OVERSCAN, ++ SCANNING_TYPE_UNDERSCAN, ++ SCANNING_TYPE_FUTURE, ++ SCANNING_TYPE_UNDEFINED ++}; ++ ++struct dc_crtc_timing { ++ uint32_t h_total; ++ uint32_t h_border_left; ++ uint32_t h_addressable; ++ uint32_t h_border_right; ++ uint32_t h_front_porch; ++ uint32_t h_sync_width; ++ ++ uint32_t v_total; ++ uint32_t v_border_top; ++ uint32_t v_addressable; ++ uint32_t v_border_bottom; ++ uint32_t v_front_porch; ++ uint32_t v_sync_width; ++ ++ uint32_t pix_clk_khz; ++ ++ uint32_t vic; ++ uint32_t hdmi_vic; ++ enum dc_timing_standard timing_standard; ++ enum dc_timing_3d_format timing_3d_format; ++ enum dc_color_depth display_color_depth; ++ enum dc_pixel_encoding pixel_encoding; ++ enum dc_aspect_ratio aspect_ratio; ++ enum scanning_type scan_type; ++ ++ struct dc_crtc_timing_flags flags; ++}; ++ ++struct dc_mode_timing { ++ struct dc_mode_info mode_info; ++ struct dc_crtc_timing crtc_timing; ++}; ++ ++/* Rotation angle */ ++enum dc_rotation_angle { ++ ROTATION_ANGLE_0 = 0, ++ ROTATION_ANGLE_90, ++ ROTATION_ANGLE_180, ++ ROTATION_ANGLE_270, ++ ROTATION_ANGLE_COUNT ++}; ++ ++struct dc_cursor_position { ++ uint32_t x; ++ uint32_t y; ++ ++ uint32_t x_origin; ++ uint32_t y_origin; ++ ++ /* ++ * This parameter indicates whether HW cursor should be enabled ++ */ ++ bool enable; ++ ++ /* ++ * This parameter indicates whether cursor hot spot should be ++ * programmed ++ */ ++ bool hot_spot_enable; ++}; ++ ++/* This enum is for programming CURSOR_MODE register field. */ ++/* What this register should be programmed to depends on */ ++/* OS requested cursor shape flags */ ++/* and what we stored in the cursor surface. */ ++enum dc_cursor_color_format { ++ CURSOR_MODE_MONO, ++ CURSOR_MODE_COLOR_1BIT_AND, ++ CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA, ++ CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA ++}; ++ ++union dc_cursor_attribute_flags { ++ struct { ++ uint32_t ENABLE_MAGNIFICATION:1; ++ uint32_t INVERSE_TRANSPARENT_CLAMPING:1; ++ uint32_t HORIZONTAL_MIRROR:1; ++ uint32_t VERTICAL_MIRROR:1; ++ uint32_t RESERVED:28; ++ } bits; ++ uint32_t value; ++}; ++ ++/* This is all the parameters required by DAL in order to */ ++/* update the cursor attributes, */ ++/* including the new cursor image surface address, size, */ ++/* hotspot location, color format, etc. */ ++struct dc_cursor_attributes { ++ PHYSICAL_ADDRESS_LOC address; ++ ++ /* Width and height should correspond to cursor surface width x heigh */ ++ uint32_t width; ++ uint32_t height; ++ uint32_t x_hot; ++ uint32_t y_hot; ++ ++ enum dc_cursor_color_format color_format; ++ ++ /* In case we support HW Cursor rotation in the future */ ++ enum dc_rotation_angle rotation_angle; ++ ++ union dc_cursor_attribute_flags attribute_flags; ++ ++}; ++ ++ ++enum dc_plane_addr_type { ++ PLN_ADDR_TYPE_GRAPHICS = 0, ++ PLN_ADDR_TYPE_GRPH_STEREO, ++ PLN_ADDR_TYPE_VIDEO_PROGRESSIVE, ++ PLN_ADDR_TYPE_VIDEO_INTERLACED, ++ PLN_ADDR_TYPE_VIDEO_PROGRESSIVE_STEREO, ++ PLN_ADDR_TYPE_VIDEO_INTERLACED_STEREO ++}; ++ ++struct dc_plane_address { ++ enum dc_plane_addr_type type; ++ union { ++ struct{ ++ PHYSICAL_ADDRESS_LOC addr; ++ } grph; ++ ++ /*stereo*/ ++ struct { ++ PHYSICAL_ADDRESS_LOC left_addr; ++ PHYSICAL_ADDRESS_LOC right_addr; ++ } grph_stereo; ++ ++ /*video progressive*/ ++ struct { ++ PHYSICAL_ADDRESS_LOC chroma_addr; ++ PHYSICAL_ADDRESS_LOC luma_addr; ++ } video_progressive; ++ ++ /*video interlaced*/ ++ struct { ++ PHYSICAL_ADDRESS_LOC chroma_addr; ++ PHYSICAL_ADDRESS_LOC luma_addr; ++ PHYSICAL_ADDRESS_LOC chroma_bottom_addr; ++ PHYSICAL_ADDRESS_LOC luma_bottom_addr; ++ } video_interlaced; ++ ++ /*video Progressive Stereo*/ ++ struct { ++ PHYSICAL_ADDRESS_LOC left_chroma_addr; ++ PHYSICAL_ADDRESS_LOC left_luma_addr; ++ PHYSICAL_ADDRESS_LOC right_chroma_addr; ++ PHYSICAL_ADDRESS_LOC right_luma_addr; ++ } video_progressive_stereo; ++ ++ /*video interlaced stereo*/ ++ struct { ++ PHYSICAL_ADDRESS_LOC left_chroma_addr; ++ PHYSICAL_ADDRESS_LOC left_luma_addr; ++ PHYSICAL_ADDRESS_LOC left_chroma_bottom_addr; ++ PHYSICAL_ADDRESS_LOC left_luma_bottom_addr; ++ ++ PHYSICAL_ADDRESS_LOC right_chroma_addr; ++ PHYSICAL_ADDRESS_LOC right_luma_addr; ++ PHYSICAL_ADDRESS_LOC right_chroma_bottom_addr; ++ PHYSICAL_ADDRESS_LOC right_luma_bottom_addr; ++ } video_interlaced_stereo; ++ }; ++}; ++ ++enum dc_power_state { ++ DC_POWER_STATE_ON = 1, ++ DC_POWER_STATE_STANDBY, ++ DC_POWER_STATE_SUSPEND, ++ DC_POWER_STATE_OFF ++}; ++ ++/* DC PowerStates */ ++enum dc_video_power_state { ++ DC_VIDEO_POWER_UNSPECIFIED = 0, ++ DC_VIDEO_POWER_ON = 1, ++ DC_VIDEO_POWER_STANDBY, ++ DC_VIDEO_POWER_SUSPEND, ++ DC_VIDEO_POWER_OFF, ++ DC_VIDEO_POWER_HIBERNATE, ++ DC_VIDEO_POWER_SHUTDOWN, ++ DC_VIDEO_POWER_ULPS, /* BACO or Ultra-Light-Power-State */ ++ DC_VIDEO_POWER_AFTER_RESET, ++ DC_VIDEO_POWER_MAXIMUM ++}; ++ ++enum dc_acpi_cm_power_state { ++ DC_ACPI_CM_POWER_STATE_D0 = 1, ++ DC_ACPI_CM_POWER_STATE_D1 = 2, ++ DC_ACPI_CM_POWER_STATE_D2 = 4, ++ DC_ACPI_CM_POWER_STATE_D3 = 8 ++}; ++ ++struct view_port_alignment { ++ uint8_t x_width_size_alignment; ++ uint8_t y_height_size_alignment; ++ uint8_t x_start_alignment; ++ uint8_t y_start_alignment; ++}; ++ ++enum dc_connection_type { ++ dc_connection_none, ++ dc_connection_single, ++ dc_connection_mst_branch, ++ dc_connection_active_dongle ++}; ++ ++struct dc_csc_adjustments { ++ struct fixed31_32 contrast; ++ struct fixed31_32 saturation; ++ struct fixed31_32 brightness; ++ struct fixed31_32 hue; ++}; ++ ++#include "dc_temp.h" ++ ++#endif /* DC_TYPES_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/Makefile b/drivers/gpu/drm/amd/dal/dc/dce110/Makefile +new file mode 100644 +index 0000000..5bf9b56 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/Makefile +@@ -0,0 +1,33 @@ ++# ++# Makefile for the 'controller' sub-component of DAL. ++# It provides the control and status of HW CRTC block. ++ ++DCE110 = dce110_ipp.o dce110_ipp_cursor.o \ ++dce110_ipp_gamma.o dce110_link_encoder.o dce110_opp.o \ ++dce110_opp_formatter.o dce110_opp_regamma.o dce110_stream_encoder.o \ ++dce110_timing_generator.o dce110_transform.o dce110_transform_gamut.o \ ++dce110_transform_scl.o dce110_transform_sclv.o dce110_opp_csc.o\ ++dce110_compressor.o dce110_mem_input.o dce110_hw_sequencer.o \ ++dce110_resource.o dce110_transform_bit_depth.o ++ ++AMD_DAL_DCE110 = $(addprefix $(AMDDALPATH)/dc/dce110/,$(DCE110)) ++ ++AMD_DAL_FILES += $(AMD_DAL_DCE110) ++ ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ifdef 0#CONFIG_DRM_AMD_DAL_DCE11_0 ++TG_DCE110 = dce110_ipp.o dce110_ipp_cursor.o \ ++dce110_ipp_gamma.o dce110_timing_generator.o dce110_link_encoder.o \ ++dce110_opp.o dce110_opp_regamma.o dce110_opp_formatter.o dce110_opp_csc.o \ ++dce110_transform.o dce110_transform_gamut.o dce110_transform_bit_depth.o \ ++dce110_compressor.o dce110_mem_input.o dce110_hw_sequencer.o dce110_resource.o ++ ++AMD_DAL_TG_DCE110 = $(addprefix \ ++ $(AMDDALPATH)/dc/dce110/,$(TG_DCE110)) ++ ++AMD_DAL_FILES += $(AMD_DAL_TG_DCE110) ++endif ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.c +new file mode 100644 +index 0000000..7abb790 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.c +@@ -0,0 +1,886 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "gmc/gmc_8_2_sh_mask.h" ++#include "gmc/gmc_8_2_d.h" ++ ++#include "include/logger_interface.h" ++#include "include/adapter_service_interface.h" ++ ++#include "dce110_compressor.h" ++ ++#define DCP_REG(reg)\ ++ (reg + cp110->offsets.dcp_offset) ++#define DMIF_REG(reg)\ ++ (reg + cp110->offsets.dmif_offset) ++ ++static const struct dce110_compressor_reg_offsets reg_offsets[] = { ++{ ++ .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .dmif_offset = ++ (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL ++ - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .dmif_offset = ++ (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL ++ - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .dmif_offset = ++ (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL ++ - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), ++} ++}; ++ ++static const uint32_t dce11_one_lpt_channel_max_resolution = 2560 * 1600; ++ ++enum fbc_idle_force { ++ /* Bit 0 - Display registers updated */ ++ FBC_IDLE_FORCE_DISPLAY_REGISTER_UPDATE = 0x00000001, ++ ++ /* Bit 2 - FBC_GRPH_COMP_EN register updated */ ++ FBC_IDLE_FORCE_GRPH_COMP_EN = 0x00000002, ++ /* Bit 3 - FBC_SRC_SEL register updated */ ++ FBC_IDLE_FORCE_SRC_SEL_CHANGE = 0x00000004, ++ /* Bit 4 - FBC_MIN_COMPRESSION register updated */ ++ FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE = 0x00000008, ++ /* Bit 5 - FBC_ALPHA_COMP_EN register updated */ ++ FBC_IDLE_FORCE_ALPHA_COMP_EN = 0x00000010, ++ /* Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated */ ++ FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN = 0x00000020, ++ /* Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated */ ++ FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF = 0x00000040, ++ ++ /* Bit 24 - Memory write to region 0 defined by MC registers. */ ++ FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION0 = 0x01000000, ++ /* Bit 25 - Memory write to region 1 defined by MC registers */ ++ FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION1 = 0x02000000, ++ /* Bit 26 - Memory write to region 2 defined by MC registers */ ++ FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION2 = 0x04000000, ++ /* Bit 27 - Memory write to region 3 defined by MC registers. */ ++ FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION3 = 0x08000000, ++ ++ /* Bit 28 - Memory write from any client other than MCIF */ ++ FBC_IDLE_FORCE_MEMORY_WRITE_OTHER_THAN_MCIF = 0x10000000, ++ /* Bit 29 - CG statics screen signal is inactive */ ++ FBC_IDLE_FORCE_CG_STATIC_SCREEN_IS_INACTIVE = 0x20000000, ++}; ++ ++static uint32_t lpt_size_alignment(struct dce110_compressor *cp110) ++{ ++ /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ ++ return cp110->base.raw_size * cp110->base.banks_num * ++ cp110->base.dram_channels_num; ++} ++ ++static uint32_t lpt_memory_control_config(struct dce110_compressor *cp110, ++ uint32_t lpt_control) ++{ ++ /*LPT MC Config */ ++ if (cp110->base.options.bits.LPT_MC_CONFIG == 1) { ++ /* POSSIBLE VALUES for LPT NUM_PIPES (DRAM CHANNELS): ++ * 00 - 1 CHANNEL ++ * 01 - 2 CHANNELS ++ * 02 - 4 OR 6 CHANNELS ++ * (Only for discrete GPU, N/A for CZ) ++ * 03 - 8 OR 12 CHANNELS ++ * (Only for discrete GPU, N/A for CZ) */ ++ switch (cp110->base.dram_channels_num) { ++ case 2: ++ set_reg_field_value( ++ lpt_control, ++ 1, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_NUM_PIPES); ++ break; ++ case 1: ++ set_reg_field_value( ++ lpt_control, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_NUM_PIPES); ++ break; ++ default: ++ dal_logger_write( ++ cp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: Invalid LPT NUM_PIPES!!!", ++ __func__); ++ break; ++ } ++ ++ /* The mapping for LPT NUM_BANKS is in ++ * GRPH_CONTROL.GRPH_NUM_BANKS register field ++ * Specifies the number of memory banks for tiling ++ * purposes. Only applies to 2D and 3D tiling modes. ++ * POSSIBLE VALUES: ++ * 00 - DCP_GRPH_NUM_BANKS_2BANK: ADDR_SURF_2_BANK ++ * 01 - DCP_GRPH_NUM_BANKS_4BANK: ADDR_SURF_4_BANK ++ * 02 - DCP_GRPH_NUM_BANKS_8BANK: ADDR_SURF_8_BANK ++ * 03 - DCP_GRPH_NUM_BANKS_16BANK: ADDR_SURF_16_BANK */ ++ switch (cp110->base.banks_num) { ++ case 16: ++ set_reg_field_value( ++ lpt_control, ++ 3, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_NUM_BANKS); ++ break; ++ case 8: ++ set_reg_field_value( ++ lpt_control, ++ 2, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_NUM_BANKS); ++ break; ++ case 4: ++ set_reg_field_value( ++ lpt_control, ++ 1, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_NUM_BANKS); ++ break; ++ case 2: ++ set_reg_field_value( ++ lpt_control, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_NUM_BANKS); ++ break; ++ default: ++ dal_logger_write( ++ cp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: Invalid LPT NUM_BANKS!!!", ++ __func__); ++ break; ++ } ++ ++ /* The mapping is in DMIF_ADDR_CALC. ++ * ADDR_CONFIG_PIPE_INTERLEAVE_SIZE register field for ++ * Carrizo specifies the memory interleave per pipe. ++ * It effectively specifies the location of pipe bits in ++ * the memory address. ++ * POSSIBLE VALUES: ++ * 00 - ADDR_CONFIG_PIPE_INTERLEAVE_256B: 256 byte ++ * interleave ++ * 01 - ADDR_CONFIG_PIPE_INTERLEAVE_512B: 512 byte ++ * interleave ++ */ ++ switch (cp110->base.channel_interleave_size) { ++ case 256: /*256B */ ++ set_reg_field_value( ++ lpt_control, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); ++ break; ++ case 512: /*512B */ ++ set_reg_field_value( ++ lpt_control, ++ 1, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); ++ break; ++ default: ++ dal_logger_write( ++ cp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: Invalid LPT INTERLEAVE_SIZE!!!", ++ __func__); ++ break; ++ } ++ ++ /* The mapping for LOW_POWER_TILING_ROW_SIZE is in ++ * DMIF_ADDR_CALC.ADDR_CONFIG_ROW_SIZE register field ++ * for Carrizo. Specifies the size of dram row in bytes. ++ * This should match up with NOOFCOLS field in ++ * MC_ARB_RAMCFG (ROW_SIZE = 4 * 2 ^^ columns). ++ * This register DMIF_ADDR_CALC is not used by the ++ * hardware as it is only used for addrlib assertions. ++ * POSSIBLE VALUES: ++ * 00 - ADDR_CONFIG_1KB_ROW: Treat 1KB as DRAM row ++ * boundary ++ * 01 - ADDR_CONFIG_2KB_ROW: Treat 2KB as DRAM row ++ * boundary ++ * 02 - ADDR_CONFIG_4KB_ROW: Treat 4KB as DRAM row ++ * boundary */ ++ switch (cp110->base.raw_size) { ++ case 4096: /*4 KB */ ++ set_reg_field_value( ++ lpt_control, ++ 2, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ROW_SIZE); ++ break; ++ case 2048: ++ set_reg_field_value( ++ lpt_control, ++ 1, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ROW_SIZE); ++ break; ++ case 1024: ++ set_reg_field_value( ++ lpt_control, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ROW_SIZE); ++ break; ++ default: ++ dal_logger_write( ++ cp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: Invalid LPT ROW_SIZE!!!", ++ __func__); ++ break; ++ } ++ } else { ++ dal_logger_write( ++ cp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: LPT MC Configuration is not provided", ++ __func__); ++ } ++ ++ return lpt_control; ++} ++ ++ ++ ++static bool is_source_bigger_than_epanel_size( ++ struct dce110_compressor *cp110, ++ uint32_t source_view_width, ++ uint32_t source_view_height) ++{ ++ if (cp110->base.embedded_panel_h_size != 0 && ++ cp110->base.embedded_panel_v_size != 0 && ++ ((source_view_width * source_view_height) > ++ (cp110->base.embedded_panel_h_size * ++ cp110->base.embedded_panel_v_size))) ++ return true; ++ ++ return false; ++} ++ ++static uint32_t align_to_chunks_number_per_line( ++ struct dce110_compressor *cp110, ++ uint32_t pixels) ++{ ++ return 256 * ((pixels + 255) / 256); ++} ++ ++static void wait_for_fbc_state_changed( ++ struct dce110_compressor *cp110, ++ bool enabled) ++{ ++ uint8_t counter = 0; ++ uint32_t addr = mmFBC_STATUS; ++ uint32_t value; ++ ++ while (counter < 10) { ++ value = dal_read_reg(cp110->base.ctx, addr); ++ if (get_reg_field_value( ++ value, ++ FBC_STATUS, ++ FBC_ENABLE_STATUS) == enabled) ++ break; ++ dc_service_delay_in_microseconds(cp110->base.ctx, 10); ++ counter++; ++ } ++ ++ if (counter == 10) { ++ dal_logger_write( ++ cp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: wait counter exceeded, changes to HW not applied", ++ __func__); ++ } ++} ++ ++void dce110_compressor_power_up_fbc(struct compressor *compressor) ++{ ++ uint32_t value; ++ uint32_t addr; ++ ++ addr = mmFBC_CNTL; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); ++ set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); ++ set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); ++ if (compressor->options.bits.CLK_GATING_DISABLED == 1) { ++ /* HW needs to do power measurement comparison. */ ++ set_reg_field_value( ++ value, ++ 0, ++ FBC_CNTL, ++ FBC_COMP_CLK_GATE_EN); ++ } ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ addr = mmFBC_COMP_MODE; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); ++ set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); ++ set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ addr = mmFBC_COMP_CNTL; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); ++ dal_write_reg(compressor->ctx, addr, value); ++ /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ ++ /* 1 ==> 4:1 */ ++ /* 2 ==> 8:1 */ ++ /* 0xF ==> 1:1 */ ++ set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); ++ dal_write_reg(compressor->ctx, addr, value); ++ compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; ++ ++ value = 0; ++ dal_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); ++ ++ value = 0xFFFFFF; ++ dal_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); ++} ++ ++void dce110_compressor_enable_fbc( ++ struct compressor *compressor, ++ uint32_t paths_num, ++ struct compr_addr_and_pitch_params *params) ++{ ++ struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); ++ ++ if (compressor->options.bits.FBC_SUPPORT && ++ (compressor->options.bits.DUMMY_BACKEND == 0) && ++ (!dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) && ++ (!is_source_bigger_than_epanel_size( ++ cp110, ++ params->source_view_width, ++ params->source_view_height))) { ++ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* Before enabling FBC first need to enable LPT if applicable ++ * LPT state should always be changed (enable/disable) while FBC ++ * is disabled */ ++ if (compressor->options.bits.LPT_SUPPORT && (paths_num < 2) && ++ (params->source_view_width * ++ params->source_view_height <= ++ dce11_one_lpt_channel_max_resolution)) { ++ dce110_compressor_enable_lpt(compressor); ++ } ++ ++ addr = mmFBC_CNTL; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); ++ set_reg_field_value( ++ value, ++ params->inst, ++ FBC_CNTL, FBC_SRC_SEL); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ /* Keep track of enum controller_id FBC is attached to */ ++ compressor->is_enabled = true; ++ compressor->attached_inst = params->inst; ++ cp110->offsets = reg_offsets[params->inst - 1]; ++ ++ /*Toggle it as there is bug in HW */ ++ set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); ++ dal_write_reg(compressor->ctx, addr, value); ++ set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ wait_for_fbc_state_changed(cp110, true); ++ } ++} ++ ++void dce110_compressor_disable_fbc(struct compressor *compressor) ++{ ++ struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); ++ ++ if (compressor->options.bits.FBC_SUPPORT && ++ dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { ++ uint32_t reg_data; ++ /* Turn off compression */ ++ reg_data = dal_read_reg(compressor->ctx, mmFBC_CNTL); ++ set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); ++ dal_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); ++ ++ /* Reset enum controller_id to undefined */ ++ compressor->attached_inst = 0; ++ compressor->is_enabled = false; ++ ++ /* Whenever disabling FBC make sure LPT is disabled if LPT ++ * supported */ ++ if (compressor->options.bits.LPT_SUPPORT) ++ dce110_compressor_disable_lpt(compressor); ++ ++ wait_for_fbc_state_changed(cp110, false); ++ } ++} ++ ++bool dce110_compressor_is_fbc_enabled_in_hw( ++ struct compressor *compressor, ++ uint32_t *inst) ++{ ++ /* Check the hardware register */ ++ uint32_t value; ++ ++ value = dal_read_reg(compressor->ctx, mmFBC_STATUS); ++ if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { ++ if (inst != NULL) ++ *inst = compressor->attached_inst; ++ return true; ++ } ++ ++ value = dal_read_reg(compressor->ctx, mmFBC_MISC); ++ if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { ++ value = dal_read_reg(compressor->ctx, mmFBC_CNTL); ++ ++ if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { ++ if (inst != NULL) ++ *inst = ++ compressor->attached_inst; ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool dce110_compressor_is_lpt_enabled_in_hw(struct compressor *compressor) ++{ ++ /* Check the hardware register */ ++ uint32_t value = dal_read_reg(compressor->ctx, ++ mmLOW_POWER_TILING_CONTROL); ++ ++ return get_reg_field_value( ++ value, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ENABLE); ++} ++ ++void dce110_compressor_program_compressed_surface_address_and_pitch( ++ struct compressor *compressor, ++ struct compr_addr_and_pitch_params *params) ++{ ++ struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); ++ uint32_t value = 0; ++ uint32_t fbc_pitch = 0; ++ uint32_t compressed_surf_address_low_part = ++ compressor->compr_surface_address.addr.low_part; ++ ++ /* Clear content first. */ ++ dal_write_reg( ++ compressor->ctx, ++ DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), ++ 0); ++ dal_write_reg(compressor->ctx, ++ DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); ++ ++ if (compressor->options.bits.LPT_SUPPORT) { ++ uint32_t lpt_alignment = lpt_size_alignment(cp110); ++ ++ if (lpt_alignment != 0) { ++ compressed_surf_address_low_part = ++ ((compressed_surf_address_low_part ++ + (lpt_alignment - 1)) / lpt_alignment) ++ * lpt_alignment; ++ } ++ } ++ ++ /* Write address, HIGH has to be first. */ ++ dal_write_reg(compressor->ctx, ++ DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), ++ compressor->compr_surface_address.addr.high_part); ++ dal_write_reg(compressor->ctx, ++ DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), ++ compressed_surf_address_low_part); ++ ++ fbc_pitch = align_to_chunks_number_per_line( ++ cp110, ++ params->source_view_width); ++ ++ if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) ++ fbc_pitch = fbc_pitch / 8; ++ else ++ dal_logger_write( ++ compressor->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: Unexpected DCE11 compression ratio", ++ __func__); ++ ++ /* Clear content first. */ ++ dal_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); ++ ++ /* Write FBC Pitch. */ ++ set_reg_field_value( ++ value, ++ fbc_pitch, ++ GRPH_COMPRESS_PITCH, ++ GRPH_COMPRESS_PITCH); ++ dal_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); ++ ++} ++ ++void dce110_compressor_disable_lpt(struct compressor *compressor) ++{ ++ struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); ++ uint32_t value; ++ uint32_t addr; ++ uint32_t inx; ++ ++ /* Disable all pipes LPT Stutter */ ++ for (inx = 0; inx < 3; inx++) { ++ value = ++ dal_read_reg( ++ compressor->ctx, ++ DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); ++ set_reg_field_value( ++ value, ++ 0, ++ DPG_PIPE_STUTTER_CONTROL_NONLPTCH, ++ STUTTER_ENABLE_NONLPTCH); ++ dal_write_reg( ++ compressor->ctx, ++ DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), ++ value); ++ } ++ /* Disable Underlay pipe LPT Stutter */ ++ addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 0, ++ DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, ++ STUTTER_ENABLE_NONLPTCH); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ /* Disable LPT */ ++ addr = mmLOW_POWER_TILING_CONTROL; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ENABLE); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ /* Clear selection of Channel(s) containing Compressed Surface */ ++ addr = mmGMCON_LPT_TARGET; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 0xFFFFFFFF, ++ GMCON_LPT_TARGET, ++ STCTRL_LPT_TARGET); ++ dal_write_reg(compressor->ctx, mmGMCON_LPT_TARGET, value); ++} ++ ++void dce110_compressor_enable_lpt(struct compressor *compressor) ++{ ++ struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); ++ uint32_t value; ++ uint32_t addr; ++ uint32_t value_control; ++ uint32_t channels; ++ ++ /* Enable LPT Stutter from Display pipe */ ++ value = dal_read_reg(compressor->ctx, ++ DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_STUTTER_CONTROL_NONLPTCH, ++ STUTTER_ENABLE_NONLPTCH); ++ dal_write_reg(compressor->ctx, ++ DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), value); ++ ++ /* Enable Underlay pipe LPT Stutter */ ++ addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, ++ STUTTER_ENABLE_NONLPTCH); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ /* Selection of Channel(s) containing Compressed Surface: 0xfffffff ++ * will disable LPT. ++ * STCTRL_LPT_TARGETn corresponds to channel n. */ ++ addr = mmLOW_POWER_TILING_CONTROL; ++ value_control = dal_read_reg(compressor->ctx, addr); ++ channels = get_reg_field_value(value_control, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_MODE); ++ ++ addr = mmGMCON_LPT_TARGET; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ channels + 1, /* not mentioned in programming guide, ++ but follow DCE8.1 */ ++ GMCON_LPT_TARGET, ++ STCTRL_LPT_TARGET); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ /* Enable LPT */ ++ addr = mmLOW_POWER_TILING_CONTROL; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ENABLE); ++ dal_write_reg(compressor->ctx, addr, value); ++} ++ ++void dce110_compressor_program_lpt_control( ++ struct compressor *compressor, ++ struct compr_addr_and_pitch_params *params) ++{ ++ struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); ++ uint32_t rows_per_channel; ++ uint32_t lpt_alignment; ++ uint32_t source_view_width; ++ uint32_t source_view_height; ++ uint32_t lpt_control = 0; ++ ++ if (!compressor->options.bits.LPT_SUPPORT) ++ return; ++ ++ lpt_control = dal_read_reg(compressor->ctx, ++ mmLOW_POWER_TILING_CONTROL); ++ ++ /* POSSIBLE VALUES for Low Power Tiling Mode: ++ * 00 - Use channel 0 ++ * 01 - Use Channel 0 and 1 ++ * 02 - Use Channel 0,1,2,3 ++ * 03 - reserved */ ++ switch (compressor->lpt_channels_num) { ++ /* case 2: ++ * Use Channel 0 & 1 / Not used for DCE 11 */ ++ case 1: ++ /*Use Channel 0 for LPT for DCE 11 */ ++ set_reg_field_value( ++ lpt_control, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_MODE); ++ break; ++ default: ++ dal_logger_write( ++ compressor->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: Invalid selected DRAM channels for LPT!!!", ++ __func__); ++ break; ++ } ++ ++ lpt_control = lpt_memory_control_config(cp110, lpt_control); ++ ++ /* Program LOW_POWER_TILING_ROWS_PER_CHAN field which depends on ++ * FBC compressed surface pitch. ++ * LOW_POWER_TILING_ROWS_PER_CHAN = Roundup ((Surface Height * ++ * Surface Pitch) / (Row Size * Number of Channels * ++ * Number of Banks)). */ ++ rows_per_channel = 0; ++ lpt_alignment = lpt_size_alignment(cp110); ++ source_view_width = ++ align_to_chunks_number_per_line( ++ cp110, ++ params->source_view_width); ++ source_view_height = (params->source_view_height + 1) & (~0x1); ++ ++ if (lpt_alignment != 0) { ++ rows_per_channel = source_view_width * source_view_height * 4; ++ rows_per_channel = ++ (rows_per_channel % lpt_alignment) ? ++ (rows_per_channel / lpt_alignment + 1) : ++ rows_per_channel / lpt_alignment; ++ } ++ ++ set_reg_field_value( ++ lpt_control, ++ rows_per_channel, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ROWS_PER_CHAN); ++ ++ dal_write_reg(compressor->ctx, ++ mmLOW_POWER_TILING_CONTROL, lpt_control); ++} ++ ++/* ++ * DCE 11 Frame Buffer Compression Implementation ++ */ ++ ++ ++void dce110_compressor_set_fbc_invalidation_triggers( ++ struct compressor *compressor, ++ uint32_t fbc_trigger) ++{ ++ /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) ++ * for DCE 11 regions cannot be used - does not work with S/G ++ */ ++ uint32_t addr = mmFBC_CLIENT_REGION_MASK; ++ uint32_t value = dal_read_reg(compressor->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ FBC_CLIENT_REGION_MASK, ++ FBC_MEMORY_REGION_MASK); ++ dal_write_reg(compressor->ctx, addr, value); ++ ++ /* Setup events when to clear all CSM entries (effectively marking ++ * current compressed data invalid) ++ * For DCE 11 CSM metadata 11111 means - "Not Compressed" ++ * Used as the initial value of the metadata sent to the compressor ++ * after invalidation, to indicate that the compressor should attempt ++ * to compress all chunks on the current pass. Also used when the chunk ++ * is not successfully written to memory. ++ * When this CSM value is detected, FBC reads from the uncompressed ++ * buffer. Set events according to passed in value, these events are ++ * valid for DCE11: ++ * - bit 0 - display register updated ++ * - bit 28 - memory write from any client except from MCIF ++ * - bit 29 - CG static screen signal is inactive ++ * In addition, DCE11.1 also needs to set new DCE11.1 specific events ++ * that are used to trigger invalidation on certain register changes, ++ * for example enabling of Alpha Compression may trigger invalidation of ++ * FBC once bit is set. These events are as follows: ++ * - Bit 2 - FBC_GRPH_COMP_EN register updated ++ * - Bit 3 - FBC_SRC_SEL register updated ++ * - Bit 4 - FBC_MIN_COMPRESSION register updated ++ * - Bit 5 - FBC_ALPHA_COMP_EN register updated ++ * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated ++ * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated ++ */ ++ addr = mmFBC_IDLE_FORCE_CLEAR_MASK; ++ value = dal_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ fbc_trigger | ++ FBC_IDLE_FORCE_GRPH_COMP_EN | ++ FBC_IDLE_FORCE_SRC_SEL_CHANGE | ++ FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE | ++ FBC_IDLE_FORCE_ALPHA_COMP_EN | ++ FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN | ++ FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF, ++ FBC_IDLE_FORCE_CLEAR_MASK, ++ FBC_IDLE_FORCE_CLEAR_MASK); ++ dal_write_reg(compressor->ctx, addr, value); ++} ++ ++bool dce110_compressor_construct(struct dce110_compressor *compressor, ++ struct dc_context *ctx, struct adapter_service *as) ++{ ++ struct embedded_panel_info panel_info; ++ ++ compressor->base.options.bits.FBC_SUPPORT = true; ++ if (!(dal_adapter_service_is_feature_supported( ++ FEATURE_DISABLE_LPT_SUPPORT))) ++ compressor->base.options.bits.LPT_SUPPORT = true; ++ /* For DCE 11 always use one DRAM channel for LPT */ ++ compressor->base.lpt_channels_num = 1; ++ ++ if (dal_adapter_service_is_feature_supported(FEATURE_DUMMY_FBC_BACKEND)) ++ compressor->base.options.bits.DUMMY_BACKEND = true; ++ ++ /* Check if this system has more than 1 DRAM channel; if only 1 then LPT ++ * should not be supported */ ++ if (compressor->base.memory_bus_width == 64) ++ compressor->base.options.bits.LPT_SUPPORT = false; ++ ++ if (dal_adapter_service_is_feature_supported( ++ FEATURE_DISABLE_FBC_COMP_CLK_GATE)) ++ compressor->base.options.bits.CLK_GATING_DISABLED = true; ++ ++ compressor->base.ctx = ctx; ++ compressor->base.embedded_panel_h_size = 0; ++ compressor->base.embedded_panel_v_size = 0; ++ compressor->base.memory_bus_width = ++ dal_adapter_service_get_asic_vram_bit_width(as); ++ compressor->base.allocated_size = 0; ++ compressor->base.preferred_requested_size = 0; ++ compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; ++ compressor->base.options.raw = 0; ++ compressor->base.banks_num = 0; ++ compressor->base.raw_size = 0; ++ compressor->base.channel_interleave_size = 0; ++ compressor->base.dram_channels_num = 0; ++ compressor->base.lpt_channels_num = 0; ++ compressor->base.attached_inst = 0; ++ compressor->base.is_enabled = false; ++ ++ if (dal_adapter_service_get_embedded_panel_info(as, ++ &panel_info)) { ++ compressor->base.embedded_panel_h_size = ++ panel_info.lcd_timing.horizontal_addressable; ++ compressor->base.embedded_panel_v_size = ++ panel_info.lcd_timing.vertical_addressable; ++ } ++ return true; ++} ++ ++struct compressor *dce110_compressor_create(struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ struct dce110_compressor *cp110 = ++ dc_service_alloc(ctx, sizeof(struct dce110_compressor)); ++ ++ if (!cp110) ++ return NULL; ++ ++ if (dce110_compressor_construct(cp110, ctx, as)) ++ return &cp110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, cp110); ++ return NULL; ++} ++ ++void dce110_compressor_destroy(struct compressor **compressor) ++{ ++ dc_service_free((*compressor)->ctx, TO_DCE110_COMPRESSOR(*compressor)); ++ *compressor = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.h +new file mode 100644 +index 0000000..0beef22 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_compressor.h +@@ -0,0 +1,84 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_COMPRESSOR_DCE110_H__ ++#define __DC_COMPRESSOR_DCE110_H__ ++ ++#include "../inc/compressor.h" ++ ++#define TO_DCE110_COMPRESSOR(compressor)\ ++ container_of(compressor, struct dce110_compressor, base) ++ ++struct dce110_compressor_reg_offsets { ++ uint32_t dcp_offset; ++ uint32_t dmif_offset; ++}; ++ ++struct dce110_compressor { ++ struct compressor base; ++ struct dce110_compressor_reg_offsets offsets; ++}; ++ ++struct compressor *dce110_compressor_create(struct dc_context *ctx, ++ struct adapter_service *as); ++ ++bool dce110_compressor_construct(struct dce110_compressor *cp110, ++ struct dc_context *ctx, struct adapter_service *as); ++ ++void dce110_compressor_destroy(struct compressor **cp); ++ ++/* FBC RELATED */ ++void dce110_compressor_power_up_fbc(struct compressor *cp); ++ ++void dce110_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, ++ struct compr_addr_and_pitch_params *params); ++ ++void dce110_compressor_disable_fbc(struct compressor *cp); ++ ++void dce110_compressor_set_fbc_invalidation_triggers(struct compressor *cp, ++ uint32_t fbc_trigger); ++ ++void dce110_compressor_program_compressed_surface_address_and_pitch( ++ struct compressor *cp, ++ struct compr_addr_and_pitch_params *params); ++ ++bool dce110_compressor_get_required_compressed_surface_size( ++ struct compressor *cp, ++ struct fbc_input_info *input_info, ++ struct fbc_requested_compressed_size *size); ++ ++bool dce110_compressor_is_fbc_enabled_in_hw(struct compressor *cp, ++ uint32_t *fbc_mapped_crtc_id); ++ ++/* LPT RELATED */ ++void dce110_compressor_enable_lpt(struct compressor *cp); ++ ++void dce110_compressor_disable_lpt(struct compressor *cp); ++ ++void dce110_compressor_program_lpt_control(struct compressor *cp, ++ struct compr_addr_and_pitch_params *params); ++ ++bool dce110_compressor_is_lpt_enabled_in_hw(struct compressor *cp); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c +new file mode 100644 +index 0000000..74294cb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c +@@ -0,0 +1,1825 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++#include "dc.h" ++#include "core_types.h" ++#include "core_status.h" ++#include "resource.h" ++#include "hw_sequencer.h" ++#include "dc_helpers.h" ++ ++#include "dce110/dce110_resource.h" ++#include "dce110/dce110_timing_generator.h" ++#include "dce110/dce110_link_encoder.h" ++#include "dce110/dce110_stream_encoder.h" ++#include "stream_encoder_types.h" ++#include "link_encoder_types.h" ++#include "dce110/dce110_mem_input.h" ++#include "dce110/dce110_ipp.h" ++#include "dce110/dce110_transform.h" ++#include "dce110/dce110_opp.h" ++#include "gpu/dce110/dc_clock_gating_dce110.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++struct dce110_hw_seq_reg_offsets { ++ uint32_t dcfe_offset; ++ uint32_t blnd_offset; ++ uint32_t crtc_offset; ++ uint32_t dcp_offset; ++}; ++ ++enum crtc_stereo_mixer_mode { ++ HW_STEREO_MIXER_MODE_INACTIVE, ++ HW_STEREO_MIXER_MODE_ROW_INTERLEAVE, ++ HW_STEREO_MIXER_MODE_COLUMN_INTERLEAVE, ++ HW_STEREO_MIXER_MODE_PIXEL_INTERLEAVE, ++ HW_STEREO_MIXER_MODE_BLENDER ++}; ++ ++struct crtc_mixer_params { ++ bool sub_sampling; ++ enum crtc_stereo_mixer_mode mode; ++}; ++ ++enum pipe_lock_control { ++ PIPE_LOCK_CONTROL_GRAPHICS = 1 << 0, ++ PIPE_LOCK_CONTROL_BLENDER = 1 << 1, ++ PIPE_LOCK_CONTROL_SCL = 1 << 2, ++ PIPE_LOCK_CONTROL_SURFACE = 1 << 3, ++ PIPE_LOCK_CONTROL_MODE = 1 << 4 ++}; ++ ++enum blender_mode { ++ BLENDER_MODE_CURRENT_PIPE = 0,/* Data from current pipe only */ ++ BLENDER_MODE_OTHER_PIPE, /* Data from other pipe only */ ++ BLENDER_MODE_BLENDING,/* Alpha blending - blend 'current' and 'other' */ ++ BLENDER_MODE_STEREO ++}; ++ ++enum blender_type { ++ BLENDER_TYPE_NON_SINGLE_PIPE = 0, ++ BLENDER_TYPE_SB_SINGLE_PIPE, ++ BLENDER_TYPE_TB_SINGLE_PIPE ++}; ++ ++enum dc_memory_sleep_state { ++ DC_MEMORY_SLEEP_DISABLE = 0, ++ DC_MEMORY_LIGHT_SLEEP, ++ DC_MEMORY_DEEP_SLEEP, ++ DC_MEMORY_SHUTDOWN ++}; ++enum { ++ DCE110_PIPE_UPDATE_PENDING_DELAY = 1000, ++ DCE110_PIPE_UPDATE_PENDING_CHECKCOUNT = 5000 ++}; ++ ++static const struct dce110_hw_seq_reg_offsets reg_offsets[] = { ++{ ++ .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .blnd_offset = (mmBLND0_BLND_CONTROL - mmBLND0_BLND_CONTROL), ++ .crtc_offset = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), ++ .dcp_offset = (mmDCP0_DVMM_PTE_CONTROL - mmDCP0_DVMM_PTE_CONTROL), ++}, ++{ ++ .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .blnd_offset = (mmBLND1_BLND_CONTROL - mmBLND0_BLND_CONTROL), ++ .crtc_offset = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), ++ .dcp_offset = (mmDCP1_DVMM_PTE_CONTROL - mmDCP0_DVMM_PTE_CONTROL), ++}, ++{ ++ .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .blnd_offset = (mmBLND2_BLND_CONTROL - mmBLND0_BLND_CONTROL), ++ .crtc_offset = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), ++ .dcp_offset = (mmDCP2_DVMM_PTE_CONTROL - mmDCP0_DVMM_PTE_CONTROL), ++} ++}; ++ ++#define HW_REG_DCFE(reg, id)\ ++ (reg + reg_offsets[id].dcfe_offset) ++ ++#define HW_REG_BLND(reg, id)\ ++ (reg + reg_offsets[id].blnd_offset) ++ ++#define HW_REG_CRTC(reg, id)\ ++ (reg + reg_offsets[id].crtc_offset) ++ ++#define HW_REG_DCP(reg, id)\ ++ (reg + reg_offsets[id].dcp_offset) ++ ++ ++static void init_pte(struct dc_context *ctx); ++ ++/******************************************************************************* ++ * Private definitions ++ ******************************************************************************/ ++ ++static void dce110_enable_display_pipe_clock_gating( ++ struct dc_context *ctx, ++ bool clock_gating) ++{ ++ /*TODO*/ ++} ++ ++static bool dce110_enable_display_power_gating( ++ struct dc_context *ctx, ++ uint8_t controller_id, ++ struct bios_parser *bp, ++ enum pipe_gating_control power_gating) ++{ ++ enum bp_result bp_result = BP_RESULT_OK; ++ enum bp_pipe_control_action cntl; ++ ++ if (power_gating == PIPE_GATING_CONTROL_INIT) ++ cntl = ASIC_PIPE_INIT; ++ else if (power_gating == PIPE_GATING_CONTROL_ENABLE) ++ cntl = ASIC_PIPE_ENABLE; ++ else ++ cntl = ASIC_PIPE_DISABLE; ++ ++ if (!(power_gating == PIPE_GATING_CONTROL_INIT && ++ (controller_id + 1) != CONTROLLER_ID_D0)) ++ bp_result = dal_bios_parser_enable_disp_power_gating( ++ bp, controller_id + 1, cntl); ++ ++ if (power_gating != PIPE_GATING_CONTROL_ENABLE) ++ init_pte(ctx); ++ ++ if (bp_result == BP_RESULT_OK) ++ return true; ++ else ++ return false; ++} ++ ++ ++static bool set_gamma_ramp( ++ struct input_pixel_processor *ipp, ++ struct output_pixel_processor *opp, ++ const struct gamma_ramp *ramp, ++ const struct gamma_parameters *params) ++{ ++ /*Power on LUT memory*/ ++ dce110_opp_power_on_regamma_lut(opp, true); ++ ++ if (params->surface_pixel_format == PIXEL_FORMAT_INDEX8 || ++ params->selected_gamma_lut == GRAPHICS_GAMMA_LUT_LEGACY) { ++ /* do legacy DCP for 256 colors if we are requested to do so */ ++ dce110_ipp_set_legacy_input_gamma_ramp( ++ ipp, ramp, params); ++ ++ dce110_ipp_set_legacy_input_gamma_mode(ipp, true); ++ ++ /* set bypass */ ++ dce110_ipp_program_prescale(ipp, PIXEL_FORMAT_UNINITIALIZED); ++ ++ dce110_ipp_set_degamma(ipp, params, true); ++ ++ dce110_opp_set_regamma(opp, ramp, params, true); ++ } else if (params->selected_gamma_lut == ++ GRAPHICS_GAMMA_LUT_LEGACY_AND_REGAMMA) { ++ if (!dce110_opp_map_legacy_and_regamma_hw_to_x_user( ++ opp, ramp, params)) { ++ BREAK_TO_DEBUGGER(); ++ /* invalid parameters or bug */ ++ return false; ++ } ++ ++ /* do legacy DCP for 256 colors if we are requested to do so */ ++ dce110_ipp_set_legacy_input_gamma_ramp( ++ ipp, ramp, params); ++ ++ dce110_ipp_set_legacy_input_gamma_mode(ipp, true); ++ ++ /* set bypass */ ++ dce110_ipp_program_prescale(ipp, PIXEL_FORMAT_UNINITIALIZED); ++ } else { ++ dce110_ipp_set_legacy_input_gamma_mode(ipp, false); ++ ++ dce110_ipp_program_prescale(ipp, params->surface_pixel_format); ++ ++ /* Do degamma step : remove the given gamma value from FB. ++ * For FP16 or no degamma do by pass */ ++ dce110_ipp_set_degamma(ipp, params, false); ++ ++ dce110_opp_set_regamma(opp, ramp, params, false); ++ } ++ ++ /*re-enable low power mode for LUT memory*/ ++ dce110_opp_power_on_regamma_lut(opp, false); ++ ++ return true; ++} ++ ++static enum dc_status bios_parser_crtc_source_select( ++ struct core_stream *stream) ++{ ++ /* call VBIOS table to set CRTC source for the HW ++ * encoder block ++ * note: video bios clears all FMT setting here. */ ++ ++ struct bp_crtc_source_select crtc_source_select = {0}; ++ const struct core_sink *sink = stream->sink; ++ crtc_source_select.engine_id = stream->stream_enc->id; ++ crtc_source_select.controller_id = stream->controller_idx + 1; ++ /*TODO: Need to un-hardcode color depth, dp_audio and account for ++ * the case where signal and sink signal is different (translator ++ * encoder)*/ ++ crtc_source_select.signal = sink->public.sink_signal; ++ crtc_source_select.enable_dp_audio = false; ++ crtc_source_select.sink_signal = sink->public.sink_signal; ++ crtc_source_select.display_output_bit_depth ++ = PANEL_8BIT_COLOR; ++ ++ if (BP_RESULT_OK != dal_bios_parser_crtc_source_select( ++ dal_adapter_service_get_bios_parser(sink->link->adapter_srv), ++ &crtc_source_select)) { ++ return DC_ERROR_UNEXPECTED; ++ } ++ return DC_OK; ++} ++ ++static enum color_space surface_color_to_color_space( ++ struct plane_colorimetry *colorimetry) ++{ ++ enum color_space color_space = COLOR_SPACE_UNKNOWN; ++ ++ switch (colorimetry->color_space) { ++ case SURFACE_COLOR_SPACE_SRGB: ++ case SURFACE_COLOR_SPACE_XRRGB: ++ if (colorimetry->limited_range) ++ color_space = COLOR_SPACE_SRGB_LIMITED_RANGE; ++ else ++ color_space = COLOR_SPACE_SRGB_FULL_RANGE; ++ break; ++ case SURFACE_COLOR_SPACE_BT601: ++ case SURFACE_COLOR_SPACE_XVYCC_BT601: ++ color_space = COLOR_SPACE_YCBCR601; ++ break; ++ case SURFACE_COLOR_SPACE_BT709: ++ case SURFACE_COLOR_SPACE_XVYCC_BT709: ++ color_space = COLOR_SPACE_YCBCR709; ++ break; ++ } ++ ++ return color_space; ++} ++ ++/*******************************FMT**************************************/ ++static void program_fmt( ++ struct output_pixel_processor *opp, ++ struct bit_depth_reduction_params *fmt_bit_depth, ++ struct clamping_and_pixel_encoding_params *clamping) ++{ ++ /* dithering is affected by <CrtcSourceSelect>, hence should be ++ * programmed afterwards */ ++ ++ dce110_opp_program_bit_depth_reduction( ++ opp, ++ fmt_bit_depth); ++ ++ dce110_opp_program_clamping_and_pixel_encoding( ++ opp, ++ clamping); ++ ++ return; ++} ++ ++/***************************PIPE_CONTROL***********************************/ ++static void enable_fe_clock( ++ struct dc_context *ctx, uint8_t controller_id, bool enable) ++{ ++ uint32_t value = 0; ++ uint32_t addr; ++ ++ /*TODO: proper offset*/ ++ addr = HW_REG_DCFE(mmDCFE_CLOCK_CONTROL, controller_id); ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ enable, ++ DCFE_CLOCK_CONTROL, ++ DCFE_CLOCK_ENABLE); ++ ++ dal_write_reg(ctx, addr, value); ++} ++/* ++static void enable_stereo_mixer( ++ struct dc_context *ctx, ++ const struct crtc_mixer_params *params) ++{ ++ TODO ++} ++*/ ++static void disable_stereo_mixer( ++ struct dc_context *ctx) ++{ ++ /*TODO*/ ++} ++ ++static void init_pte(struct dc_context *ctx) ++{ ++ uint32_t addr; ++ uint32_t value = 0; ++ uint32_t chunk_int = 0; ++ uint32_t chunk_mul = 0; ++ ++ addr = mmUNP_DVMM_PTE_CONTROL; ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DVMM_PTE_CONTROL, ++ DVMM_USE_SINGLE_PTE); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DVMM_PTE_CONTROL, ++ DVMM_PTE_BUFFER_MODE0); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DVMM_PTE_CONTROL, ++ DVMM_PTE_BUFFER_MODE1); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ addr = mmDVMM_PTE_REQ; ++ value = dal_read_reg(ctx, addr); ++ ++ chunk_int = get_reg_field_value( ++ value, ++ DVMM_PTE_REQ, ++ HFLIP_PTEREQ_PER_CHUNK_INT); ++ ++ chunk_mul = get_reg_field_value( ++ value, ++ DVMM_PTE_REQ, ++ HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); ++ ++ if (chunk_int != 0x4 || chunk_mul != 0x4) { ++ ++ set_reg_field_value( ++ value, ++ 255, ++ DVMM_PTE_REQ, ++ MAX_PTEREQ_TO_ISSUE); ++ ++ set_reg_field_value( ++ value, ++ 4, ++ DVMM_PTE_REQ, ++ HFLIP_PTEREQ_PER_CHUNK_INT); ++ ++ set_reg_field_value( ++ value, ++ 4, ++ DVMM_PTE_REQ, ++ HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++} ++ ++/** ++ ***************************************************************************** ++ * Function: enable_disp_power_gating ++ * ++ * @brief ++ * enable or disable power gating ++ * ++ * @param [in] enum pipe_gating_control power_gating true - power down, ++ * false - power up ++ ***************************************************************************** ++ */ ++ ++ ++/* this is a workaround for hw bug - it is a trigger on r/w */ ++ ++static void trigger_write_crtc_h_blank_start_end( ++ struct dc_context *ctx, ++ uint8_t controller_id) ++{ ++ uint32_t value; ++ uint32_t addr; ++ ++ addr = HW_REG_CRTC(mmCRTC_H_BLANK_START_END, controller_id); ++ value = dal_read_reg(ctx, addr); ++ dal_write_reg(ctx, addr, value); ++} ++ ++static bool pipe_control_lock( ++ struct dc_context *ctx, ++ uint8_t controller_idx, ++ uint32_t control_mask, ++ bool lock) ++{ ++ uint32_t addr = HW_REG_BLND(mmBLND_V_UPDATE_LOCK, controller_idx); ++ uint32_t value = dal_read_reg(ctx, addr); ++ bool need_to_wait = false; ++ ++ if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) ++ set_reg_field_value( ++ value, ++ lock, ++ BLND_V_UPDATE_LOCK, ++ BLND_DCP_GRPH_V_UPDATE_LOCK); ++ ++ if (control_mask & PIPE_LOCK_CONTROL_SCL) ++ set_reg_field_value( ++ value, ++ lock, ++ BLND_V_UPDATE_LOCK, ++ BLND_SCL_V_UPDATE_LOCK); ++ ++ if (control_mask & PIPE_LOCK_CONTROL_SURFACE) ++ set_reg_field_value( ++ value, ++ lock, ++ BLND_V_UPDATE_LOCK, ++ BLND_DCP_GRPH_SURF_V_UPDATE_LOCK); ++ ++ if (control_mask & PIPE_LOCK_CONTROL_BLENDER) { ++ set_reg_field_value( ++ value, ++ lock, ++ BLND_V_UPDATE_LOCK, ++ BLND_BLND_V_UPDATE_LOCK); ++ need_to_wait = true; ++ } ++ ++ if (control_mask & PIPE_LOCK_CONTROL_MODE) ++ set_reg_field_value( ++ value, ++ lock, ++ BLND_V_UPDATE_LOCK, ++ BLND_V_UPDATE_LOCK_MODE); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ if (!lock && need_to_wait) { ++ uint8_t counter = 0; ++ const uint8_t counter_limit = 100; ++ const uint16_t delay_us = 1000; ++ ++ uint8_t pipe_pending; ++ ++ addr = HW_REG_BLND(mmBLND_REG_UPDATE_STATUS, ++ controller_idx); ++ ++ while (counter < counter_limit) { ++ value = dal_read_reg(ctx, addr); ++ ++ pipe_pending = 0; ++ ++ if (control_mask & PIPE_LOCK_CONTROL_BLENDER) { ++ pipe_pending |= ++ get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ BLND_BLNDC_UPDATE_PENDING); ++ pipe_pending |= get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ BLND_BLNDO_UPDATE_PENDING); ++ } ++ ++ if (control_mask & PIPE_LOCK_CONTROL_SCL) { ++ pipe_pending |= ++ get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ SCL_BLNDC_UPDATE_PENDING); ++ pipe_pending |= ++ get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ SCL_BLNDO_UPDATE_PENDING); ++ } ++ if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) { ++ pipe_pending |= ++ get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ DCP_BLNDC_GRPH_UPDATE_PENDING); ++ pipe_pending |= ++ get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ DCP_BLNDO_GRPH_UPDATE_PENDING); ++ } ++ if (control_mask & PIPE_LOCK_CONTROL_SURFACE) { ++ pipe_pending |= get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ DCP_BLNDC_GRPH_SURF_UPDATE_PENDING); ++ pipe_pending |= get_reg_field_value( ++ value, ++ BLND_REG_UPDATE_STATUS, ++ DCP_BLNDO_GRPH_SURF_UPDATE_PENDING); ++ } ++ ++ if (pipe_pending == 0) ++ break; ++ ++ counter++; ++ dc_service_delay_in_microseconds(ctx, delay_us); ++ } ++ ++ if (counter == counter_limit) { ++ dal_logger_write( ++ ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: wait for update exceeded (wait %d us)\n", ++ __func__, ++ counter * delay_us); ++ dal_logger_write( ++ ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: control %d, remain value %x\n", ++ __func__, ++ control_mask, ++ value); ++ } else { ++ /* OK. */ ++ } ++ } ++ ++ if (!lock && (control_mask & PIPE_LOCK_CONTROL_BLENDER)) ++ trigger_write_crtc_h_blank_start_end(ctx, controller_idx); ++ ++ return true; ++} ++ ++static void set_blender_mode( ++ struct dc_context *ctx, ++ uint8_t controller_id, ++ enum blender_mode mode) ++{ ++ uint32_t value; ++ uint32_t addr = HW_REG_BLND(mmBLND_CONTROL, controller_id); ++ uint32_t blnd_mode; ++ uint32_t feedthrough = 0; ++ ++ switch (mode) { ++ case BLENDER_MODE_OTHER_PIPE: ++ feedthrough = 0; ++ blnd_mode = 1; ++ break; ++ case BLENDER_MODE_BLENDING: ++ feedthrough = 0; ++ blnd_mode = 2; ++ break; ++ case BLENDER_MODE_CURRENT_PIPE: ++ default: ++ feedthrough = 1; ++ blnd_mode = 0; ++ break; ++ } ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ feedthrough, ++ BLND_CONTROL, ++ BLND_FEEDTHROUGH_EN); ++ ++ set_reg_field_value( ++ value, ++ blnd_mode, ++ BLND_CONTROL, ++ BLND_MODE); ++ ++ dal_write_reg(ctx, addr, value); ++} ++/**************************************************************************/ ++static void update_bios_scratch_critical_state(struct adapter_service *as, ++ bool state) ++{ ++ dal_bios_parser_set_scratch_critical_state( ++ dal_adapter_service_get_bios_parser(as), ++ state); ++} ++ ++static void update_info_frame(struct core_stream *stream) ++{ ++ dce110_stream_encoder_update_info_packets( ++ stream->stream_enc, ++ stream->signal, ++ &stream->encoder_info_frame); ++} ++ ++ ++static void enable_stream(struct core_stream *stream) ++{ ++ enum lane_count lane_count = LANE_COUNT_ONE; ++ ++ struct dc_crtc_timing *timing = &stream->public.timing; ++ struct core_link *link = stream->sink->link; ++ ++ /* 1. update AVI info frame (HDMI, DP) ++ * we always need to update info frame ++ */ ++ uint32_t active_total_with_borders; ++ uint32_t early_control = 0; ++ struct timing_generator *tg = stream->tg; ++ ++ update_info_frame(stream); ++ /* enable early control to avoid corruption on DP monitor*/ ++ active_total_with_borders = ++ timing->h_addressable ++ + timing->h_border_left ++ + timing->h_border_right; ++ ++ early_control = active_total_with_borders % lane_count; ++ ++ if (early_control == 0) ++ early_control = lane_count; ++ ++ dce110_timing_generator_set_early_control(tg, early_control); ++ ++ /* enable audio only within mode set */ ++ if (stream->audio != NULL) { ++ dal_audio_enable_output( ++ stream->audio, ++ stream->stream_enc->id, ++ stream->signal); ++ } ++ ++ /* For MST, there are multiply stream go to only one link. ++ * connect DIG back_end to front_end while enable_stream and ++ * disconnect them during disable_stream ++ * BY this, it is logic clean to separate stream and link */ ++ dce110_link_encoder_connect_dig_be_to_fe(link->link_enc, ++ stream->stream_enc->id, true); ++ ++} ++ ++static void disable_stream(struct core_stream *stream) ++{ ++ struct core_link *link = stream->sink->link; ++ ++ dce110_stream_encoder_stop_info_packets( ++ stream->stream_enc, ++ stream->stream_enc->id, ++ stream->signal); ++ ++ if (stream->audio) { ++ /* mute audio */ ++ dal_audio_mute(stream->audio, stream->stream_enc->id, ++ stream->signal); ++ ++ /* TODO: notify audio driver for if audio modes list changed ++ * add audio mode list change flag */ ++ /* dal_audio_disable_azalia_audio_jack_presence(stream->audio, ++ * stream->stream_engine_id); ++ */ ++ } ++ ++ /* blank at encoder level */ ++ dce110_stream_encoder_blank(stream->stream_enc, stream->signal); ++ dce110_link_encoder_connect_dig_be_to_fe( ++ link->link_enc, ++ stream->stream_enc->id, ++ false); ++ ++} ++ ++static void unblank_stream(struct core_stream *stream, ++ struct link_settings *link_settings) ++{ ++ struct encoder_unblank_param params = { { 0 } }; ++ ++ /* only 3 items below are used by unblank */ ++ params.crtc_timing.pixel_clock = ++ stream->public.timing.pix_clk_khz; ++ params.link_settings.link_rate = link_settings->link_rate; ++ params.signal = stream->signal; ++ dce110_stream_encoder_unblank( ++ stream->stream_enc, ¶ms); ++} ++ ++static enum color_space get_output_color_space( ++ const struct dc_crtc_timing *dc_crtc_timing) ++{ ++ enum color_space color_space = COLOR_SPACE_SRGB_FULL_RANGE; ++ ++ switch (dc_crtc_timing->pixel_encoding) { ++ case PIXEL_ENCODING_YCBCR422: ++ case PIXEL_ENCODING_YCBCR444: ++ case PIXEL_ENCODING_YCBCR420: ++ { ++ if ((dc_crtc_timing->timing_standard == ++ TIMING_STANDARD_CEA770) || ++ (dc_crtc_timing->timing_standard == ++ TIMING_STANDARD_CEA861)) { ++ if (dc_crtc_timing->pix_clk_khz > 27030) { ++ if (dc_crtc_timing->flags.Y_ONLY) ++ color_space = ++ COLOR_SPACE_YCBCR709_YONLY; ++ else ++ color_space = COLOR_SPACE_YCBCR709; ++ } else { ++ if (dc_crtc_timing->flags.Y_ONLY) ++ color_space = ++ COLOR_SPACE_YCBCR601_YONLY; ++ else ++ color_space = COLOR_SPACE_YCBCR601; ++ } ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ return color_space; ++} ++ ++static enum dc_status allocate_mst_payload(struct core_stream *stream) ++{ ++ struct link_encoder *link_encoder = stream->sink->link->link_enc; ++ struct stream_encoder *stream_encoder = stream->stream_enc; ++ struct dp_mst_stream_allocation_table table; ++ struct fixed31_32 avg_time_slots_per_mtp; ++ ++ /* TODO: remove hardcode */ ++ table.stream_count = 1; ++ table.stream_allocations[0].engine = stream_encoder->id; ++ ++ dc_helpers_dp_mst_write_payload_allocation_table( ++ stream->ctx, ++ &stream->sink->public, ++ &table.stream_allocations[0], ++ true); ++ ++ dce110_link_encoder_update_mst_stream_allocation_table( ++ link_encoder, ++ &table, ++ false); ++ ++ dc_helpers_dp_mst_poll_for_allocation_change_trigger( ++ stream->ctx, ++ &stream->sink->public); ++ ++ dc_helpers_dp_mst_send_payload_allocation( ++ stream->ctx, ++ &stream->sink->public, ++ true); ++ ++ avg_time_slots_per_mtp = dal_fixed31_32_from_fraction( ++ table.stream_allocations[0].pbn, ++ table.stream_allocations[0].pbn_per_slot); ++ ++ dce110_link_encoder_set_mst_bandwidth( ++ link_encoder, ++ stream_encoder->id, ++ avg_time_slots_per_mtp); ++ ++ return DC_OK; ++ ++} ++ ++static enum dc_status deallocate_mst_payload(struct core_stream *stream) ++{ ++ struct link_encoder *link_encoder = stream->sink->link->link_enc; ++ struct stream_encoder *stream_encoder = stream->stream_enc; ++ struct dp_mst_stream_allocation_table table; ++ struct fixed31_32 avg_time_slots_per_mtp = dal_fixed31_32_from_int(0); ++ ++ /* TODO: remove hardcode */ ++ table.stream_count = 1; ++ table.stream_allocations[0].slot_count = 0; ++ ++ dce110_link_encoder_set_mst_bandwidth( ++ link_encoder, ++ stream_encoder->id, ++ avg_time_slots_per_mtp); ++ ++ dc_helpers_dp_mst_write_payload_allocation_table( ++ stream->ctx, ++ &stream->sink->public, ++ &table.stream_allocations[0], ++ false); ++ ++ dce110_link_encoder_update_mst_stream_allocation_table( ++ link_encoder, ++ &table, ++ false); ++ ++ dc_helpers_dp_mst_poll_for_allocation_change_trigger( ++ stream->ctx, ++ &stream->sink->public); ++ ++ dc_helpers_dp_mst_send_payload_allocation( ++ stream->ctx, ++ &stream->sink->public, ++ false); ++ ++ ++ return DC_OK; ++ ++} ++ ++static enum dc_status apply_single_controller_ctx_to_hw(uint8_t controller_idx, ++ struct validate_context *context, ++ const struct dc *dc) ++{ ++ struct core_stream *stream = ++ context->res_ctx.controller_ctx[controller_idx].stream; ++ ++ struct output_pixel_processor *opp = ++ context->res_ctx.pool.opps[controller_idx]; ++ bool timing_changed = context->res_ctx.controller_ctx[controller_idx] ++ .flags.timing_changed; ++ enum color_space color_space; ++ ++ if (timing_changed) { ++ ++ disable_stream(stream); ++ core_link_disable(stream); ++ ++ /*TODO: AUTO check if timing changed*/ ++ if (false == dal_clock_source_program_pix_clk( ++ stream->clock_source, ++ &stream->pix_clk_params, ++ &stream->pll_settings)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ ++ ++ if (false == dce110_timing_generator_program_timing_generator( ++ stream->tg, ++ &stream->public.timing)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ } ++ ++ /*TODO: mst support - use total stream count*/ ++ dce110_allocate_dmif_buffer(stream->mi, ++ &stream->public.timing, ++ context->target_count); ++ ++ if (timing_changed) { ++ if (false == dce110_timing_generator_enable_crtc( ++ stream->tg)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ } ++ ++ if (DC_OK != bios_parser_crtc_source_select(stream)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ ++ dce110_opp_set_dyn_expansion( ++ opp, ++ COLOR_SPACE_YCBCR601, ++ stream->public.timing.display_color_depth, ++ stream->sink->public.sink_signal); ++ ++ program_fmt( ++ opp, ++ &stream->fmt_bit_depth, ++ &stream->clamping); ++ ++ dce110_link_encoder_setup( ++ stream->sink->link->link_enc, ++ stream->signal); ++ if (ENCODER_RESULT_OK != dce110_stream_encoder_setup( ++ stream->stream_enc, ++ &stream->public.timing, ++ stream->signal, ++ stream->audio != NULL)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ ++ if (stream->audio != NULL) { ++ if (AUDIO_RESULT_OK != dal_audio_setup( ++ stream->audio, ++ &stream->audio_output, ++ &stream->public.audio_info)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ } ++ ++ /* Setup audio rate clock source */ ++ if (stream->audio != NULL) ++ dal_audio_setup_audio_wall_dto( ++ stream->audio, ++ stream->signal, ++ &stream->audio_output.crtc_info, ++ &stream->audio_output.pll_info); ++ ++ /* program blank color */ ++ color_space = get_output_color_space( ++ &stream->public.timing); ++ ++ dce110_timing_generator_program_blank_color( ++ context->res_ctx.pool.timing_generators[controller_idx], ++ color_space); ++ ++ if (timing_changed) { ++ enable_stream(stream); ++ ++ if (DC_OK != core_link_enable(stream)) { ++ BREAK_TO_DEBUGGER(); ++ return DC_ERROR_UNEXPECTED; ++ } ++ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) ++ allocate_mst_payload(stream); ++ ++ } ++ ++ unblank_stream(stream, &stream->sink->link->cur_link_settings); ++ ++ return DC_OK; ++} ++ ++ ++/******************************************************************************/ ++ ++static void power_down_encoders(struct validate_context *context) ++{ ++ int i; ++ struct core_target *target; ++ struct core_stream *stream; ++ ++ for (i = 0; i < context->target_count; i++) { ++ target = context->targets[i]; ++ stream = target->streams[0]; ++ core_link_disable(stream); ++ } ++} ++ ++static void power_down_controllers(struct validate_context *context) ++{ ++ int i; ++ struct core_target *target; ++ struct core_stream *stream; ++ ++ for (i = 0; i < context->target_count; i++) { ++ target = context->targets[i]; ++ stream = target->streams[0]; ++ ++ dce110_timing_generator_disable_crtc(stream->tg); ++ } ++} ++ ++static void power_down_clock_sources(struct validate_context *context) ++{ ++ int i; ++ struct core_target *target; ++ struct core_stream *stream; ++ ++ for (i = 0; i < context->target_count; i++) { ++ target = context->targets[i]; ++ stream = target->streams[0]; ++ ++ if (false == dal_clock_source_power_down_pll( ++ stream->clock_source, ++ stream->controller_idx + 1)) { ++ dal_error( ++ "Failed to power down pll! (clk src index=%d)\n", ++ i); ++ } ++ } ++} ++ ++static void power_down_all_hw_blocks(struct validate_context *context) ++{ ++ power_down_encoders(context); ++ ++ power_down_controllers(context); ++ ++ power_down_clock_sources(context); ++} ++ ++static void disable_vga_and_power_gate_all_controllers( ++ struct validate_context *context) ++{ ++ int i; ++ struct core_target *target; ++ struct core_stream *stream; ++ struct timing_generator *tg; ++ struct bios_parser *bp; ++ struct dc_context *ctx; ++ uint8_t controller_id; ++ ++ bp = dal_adapter_service_get_bios_parser( ++ context->res_ctx.pool.adapter_srv); ++ ++ for (i = 0; i < context->target_count; i++) { ++ target = context->targets[i]; ++ stream = target->streams[0]; ++ tg = stream->tg; ++ ctx = stream->ctx; ++ controller_id = stream->controller_idx; ++ ++ dce110_timing_generator_disable_vga(tg); ++ ++ /* Enable CLOCK gating for each pipe BEFORE controller ++ * powergating. */ ++ dce110_enable_display_pipe_clock_gating(ctx, ++ true); ++ dce110_enable_display_power_gating(ctx, controller_id, bp, ++ PIPE_GATING_CONTROL_ENABLE); ++ } ++} ++ ++/** ++ * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need: ++ * 1. Power down all DC HW blocks ++ * 2. Disable VGA engine on all controllers ++ * 3. Enable power gating for controller ++ * 4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS) ++ */ ++static void enable_accelerated_mode(struct validate_context *context) ++{ ++ struct bios_parser *bp; ++ ++ bp = dal_adapter_service_get_bios_parser( ++ context->res_ctx.pool.adapter_srv); ++ ++ power_down_all_hw_blocks(context); ++ ++ disable_vga_and_power_gate_all_controllers(context); ++ ++ dal_bios_parser_set_scratch_acc_mode_change(bp); ++} ++ ++#if 0 ++static enum clocks_state get_required_clocks_state( ++ struct display_clock *display_clock, ++ struct state_dependent_clocks *req_state_dep_clks) ++{ ++ enum clocks_state clocks_required_state; ++ enum clocks_state dp_link_required_state; ++ enum clocks_state overall_required_state; ++ ++ clocks_required_state = dal_display_clock_get_required_clocks_state( ++ display_clock, req_state_dep_clks); ++ ++ dp_link_required_state = CLOCKS_STATE_ULTRA_LOW; ++ ++ /* overall required state is the max of required state for clocks ++ * (pixel, display clock) and the required state for DP link. */ ++ overall_required_state = ++ clocks_required_state > dp_link_required_state ? ++ clocks_required_state : dp_link_required_state; ++ ++ /* return the min required state */ ++ return overall_required_state; ++} ++ ++static bool dc_pre_clock_change( ++ struct dc_context *ctx, ++ struct minimum_clocks_calculation_result *min_clk_in, ++ enum clocks_state required_clocks_state, ++ struct power_to_dal_info *output) ++{ ++ struct dal_to_power_info input = {0}; ++ ++ input.min_deep_sleep_sclk = min_clk_in->min_deep_sleep_sclk; ++ input.min_mclk = min_clk_in->min_mclk_khz; ++ input.min_sclk = min_clk_in->min_sclk_khz; ++ ++ switch (required_clocks_state) { ++ case CLOCKS_STATE_ULTRA_LOW: ++ input.required_clock = PP_CLOCKS_STATE_ULTRA_LOW; ++ break; ++ case CLOCKS_STATE_LOW: ++ input.required_clock = PP_CLOCKS_STATE_LOW; ++ break; ++ case CLOCKS_STATE_NOMINAL: ++ input.required_clock = PP_CLOCKS_STATE_NOMINAL; ++ break; ++ case CLOCKS_STATE_PERFORMANCE: ++ input.required_clock = PP_CLOCKS_STATE_PERFORMANCE; ++ break; ++ default: ++ input.required_clock = PP_CLOCKS_STATE_NOMINAL; ++ break; ++ } ++ ++ if (!dc_service_pp_pre_dce_clock_change(ctx, &input, output)) { ++ dal_error("DC: dc_service_pp_pre_dce_clock_change failed!\n"); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool dc_set_clocks_and_clock_state ( ++ struct validate_context *context) ++{ ++ struct power_to_dal_info output = {0}; ++ ++ struct display_clock *disp_clk = context->res_ctx.pool.display_clock; ++ struct dc_context *ctx = context->targets[0]->ctx; ++ ++ ++ if (!dc_pre_clock_change( ++ ctx, ++ &context->res_ctx.min_clocks, ++ get_required_clocks_state( ++ context->res_ctx.pool.display_clock, ++ &context->res_ctx.state_clocks), ++ &output)) { ++ /* "output" was not updated by PPLib. ++ * DAL will use default values for set mode. ++ * ++ * Do NOT fail this call. */ ++ return true; ++ } ++ ++ /* PPLib accepted the "clock state" that we need, that means we ++ * can store it as minimum state because PPLib guarantees not go below ++ * that state. ++ * ++ * Update the clock state here (prior to setting Pixel clock, ++ * DVO clock, or Display clock) */ ++ if (!dal_display_clock_set_min_clocks_state( ++ disp_clk, context->res_ctx.required_clocks_state)) { ++ BREAK_TO_DEBUGGER(); ++ dal_error("DC: failed to set minimum clock state!\n"); ++ } ++ ++ ++ /*bm_clk_info.max_mclk_khz = output.max_mclk; ++ bm_clk_info.min_mclk_khz = output.min_mclk; ++ bm_clk_info.max_sclk_khz = output.max_sclk; ++ bm_clk_info.min_sclk_khz = output.min_sclk;*/ ++ ++ /* Now let Bandwidth Manager know about values we got from PPLib. */ ++ /*dal_bandwidth_manager_set_dynamic_clock_info(bw_mgr, &bm_clk_info);*/ ++ ++ return true; ++} ++#endif ++ ++/** ++ * Call display_engine_clock_dce80 to perform the Dclk programming. ++ */ ++static void set_display_clock(struct validate_context *context) ++{ ++ /* Program the display engine clock. ++ * Check DFS bypass mode support or not. DFSbypass feature is only when ++ * BIOS GPU info table reports support. */ ++ ++ if (/*dal_adapter_service_is_dfs_bypass_enabled()*/ false) { ++ /*TODO: set_display_clock_dfs_bypass( ++ hws, ++ path_set, ++ context->res_ctx.pool.display_clock, ++ context->res_ctx.min_clocks.min_dclk_khz);*/ ++ } else ++ dal_display_clock_set_clock(context->res_ctx.pool.display_clock, ++ context->bw_results.dispclk); ++ ++ /* TODO: When changing display engine clock, DMCU WaitLoop must be ++ * reconfigured in order to maintain the same delays within DMCU ++ * programming sequences. */ ++ ++ /* TODO: Start GTC counter */ ++} ++ ++static void set_displaymarks( ++ const struct dc *dc, struct validate_context *context) ++{ ++ uint8_t i, j; ++ uint8_t total_streams = 0; ++ uint8_t target_count = context->target_count; ++ ++ for (i = 0; i < target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ ++ dce110_program_nbp_watermark( ++ stream->mi, ++ context->bw_results ++ .nbp_state_change_watermark[total_streams]); ++ ++ dce110_program_stutter_watermark( ++ stream->mi, ++ context->bw_results ++ .stutter_exit_watermark[total_streams]); ++ ++ dce110_program_urgency_watermark( ++ stream->mi, ++ context->bw_results ++ .urgent_watermark[total_streams], ++ stream->public.timing.h_total, ++ stream->public.timing.pix_clk_khz, ++ 1000 * dc->bw_vbios.blackout_duration ++ .value >> 24); ++ total_streams++; ++ } ++ } ++} ++ ++static void set_safe_displaymarks(struct validate_context *context) ++{ ++ uint8_t i, j; ++ uint8_t target_count = context->target_count; ++ ++ for (i = 0; i < target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ ++ dce110_program_safe_display_marks(stream->mi); ++ } ++ } ++} ++ ++static void dce110_program_bw(struct dc *dc, struct validate_context *context) ++{ ++ set_safe_displaymarks(&dc->current_context); ++ /*TODO: when pplib works*/ ++ /*dc_set_clocks_and_clock_state(context);*/ ++ ++ set_display_clock(&dc->current_context); ++ set_displaymarks(dc, &dc->current_context); ++} ++ ++/*TODO: break out clock sources like timing gen/ encoder*/ ++static void dce110_switch_dp_clk_src( ++ const struct dc_context *ctx, ++ const struct core_stream *stream) ++{ ++ uint32_t pixel_rate_cntl_value; ++ uint32_t addr; ++ enum clock_source_id id = dal_clock_source_get_id(stream->clock_source); ++ ++ /*TODO: proper offset*/ ++ addr = mmCRTC0_PIXEL_RATE_CNTL + stream->controller_idx * ++ (mmCRTC1_PIXEL_RATE_CNTL - mmCRTC0_PIXEL_RATE_CNTL); ++ ++ pixel_rate_cntl_value = dal_read_reg(ctx, addr); ++ ++ if (id == CLOCK_SOURCE_ID_EXTERNAL) { ++ ++ if (!get_reg_field_value(pixel_rate_cntl_value, ++ CRTC0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE)) { ++ ++ set_reg_field_value(pixel_rate_cntl_value, 1, ++ CRTC0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE); ++ } ++ ++ } else { ++ set_reg_field_value(pixel_rate_cntl_value, ++ 0, ++ CRTC0_PIXEL_RATE_CNTL, ++ DP_DTO0_ENABLE); ++ ++ set_reg_field_value(pixel_rate_cntl_value, ++ id - 1, ++ CRTC0_PIXEL_RATE_CNTL, ++ CRTC0_PIXEL_RATE_SOURCE); ++ } ++ dal_write_reg(ctx, addr, pixel_rate_cntl_value); ++} ++ ++static void switch_dp_clock_sources( ++ const struct dc_context *ctx, ++ struct validate_context *val_context) ++{ ++ uint8_t i, j; ++ for (i = 0; i < val_context->target_count; i++) { ++ struct core_target *target = val_context->targets[i]; ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ ++ if (dc_is_dp_signal(stream->signal)) { ++ struct clock_source *clk_src = ++ find_used_clk_src_for_sharing( ++ val_context, stream); ++ ++ if (clk_src != stream->clock_source) { ++ unreference_clock_source( ++ &val_context->res_ctx, ++ stream->clock_source); ++ stream->clock_source = clk_src; ++ reference_clock_source( ++ &val_context->res_ctx, clk_src); ++ dce110_switch_dp_clk_src(ctx, stream); ++ } ++ } ++ } ++ } ++} ++ ++/******************************************************************************* ++ * Public functions ++ ******************************************************************************/ ++ ++/*TODO: const validate_context*/ ++static enum dc_status apply_ctx_to_hw( ++ const struct dc *dc, ++ struct validate_context *context) ++{ ++ enum dc_status status; ++ uint8_t i; ++ struct resource_pool *pool = &context->res_ctx.pool; ++ ++ update_bios_scratch_critical_state(context->res_ctx.pool.adapter_srv, ++ true); ++ set_safe_displaymarks(context); ++ /*TODO: when pplib works*/ ++ /*dc_set_clocks_and_clock_state(context);*/ ++ ++ set_display_clock(context); ++ ++ for (i = 0; i < pool->controller_count; i++) { ++ struct controller_ctx *ctlr_ctx ++ = &context->res_ctx.controller_ctx[i]; ++ if (ctlr_ctx->flags.unchanged || !ctlr_ctx->stream) ++ continue; ++ ++ status = apply_single_controller_ctx_to_hw( ++ i, ++ context, ++ dc); ++ ++ if (DC_OK != status) ++ return status; ++ } ++ set_displaymarks(dc, context); ++ ++ update_bios_scratch_critical_state(context->res_ctx.pool.adapter_srv, ++ false); ++ ++ switch_dp_clock_sources(dc->ctx, context); ++ ++ return DC_OK; ++} ++ ++ ++/******************************************************************************* ++ * Front End programming ++ ******************************************************************************/ ++ ++static bool setup_line_buffer_pixel_depth( ++ const struct core_stream *stream, ++ enum lb_pixel_depth depth, ++ bool blank) ++{ ++ enum lb_pixel_depth current_depth; ++ ++ struct timing_generator *tg = stream->tg; ++ struct transform *xfm = stream->xfm; ++ ++ if (!dce110_transform_get_current_pixel_storage_depth( ++ xfm, ++ ¤t_depth)) ++ return false; ++ ++ if (current_depth != depth) { ++ if (blank) ++ dce110_timing_generator_wait_for_vblank(tg); ++ ++ return dce110_transform_set_pixel_storage_depth(xfm, depth); ++ } ++ ++ return false; ++} ++ ++static void hw_sequencer_build_scaler_parameter_plane( ++ const struct core_stream *stream, ++ struct scaler_data *scaler_data) ++{ ++ /*TODO: per pipe not per stream*/ ++ /*TODO: get from feature from adapterservice*/ ++ scaler_data->flags.bits.SHOW_COLOURED_BORDER = false; ++ ++ scaler_data->flags.bits.SHOULD_PROGRAM_ALPHA = 1; ++ ++ scaler_data->flags.bits.SHOULD_PROGRAM_VIEWPORT = 0; ++ ++ scaler_data->flags.bits.SHOULD_UNLOCK = 0; ++ ++ scaler_data->flags.bits.INTERLACED = 0; ++ ++ scaler_data->dal_pixel_format = stream->format; ++ ++ scaler_data->taps = stream->taps; ++ ++ scaler_data->viewport = stream->viewport; ++ ++ scaler_data->overscan = stream->overscan; ++ ++ scaler_data->ratios = &stream->ratios; ++ ++ /*TODO rotation and adjustment */ ++ scaler_data->h_sharpness = 0; ++ scaler_data->v_sharpness = 0; ++ ++} ++ ++static void set_default_colors( ++ struct input_pixel_processor *ipp, ++ struct output_pixel_processor *opp, ++ enum pixel_format format, ++ enum color_space input_color_space, ++ enum color_space output_color_space, ++ enum dc_color_depth color_depth) ++{ ++ struct default_adjustment default_adjust = { 0 }; ++ ++ default_adjust.force_hw_default = false; ++ default_adjust.color_space = output_color_space; ++ default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW; ++ default_adjust.surface_pixel_format = format; ++ ++ /* display color depth */ ++ default_adjust.color_depth = color_depth; ++ ++ /* Lb color depth */ ++ default_adjust.lb_color_depth = LB_PIXEL_DEPTH_24BPP; ++ /*dal_hw_sequencer_translate_to_lb_color_depth( ++ build_params-> ++ line_buffer_params[path_id][plane_id].depth);*/ ++ ++ dce110_opp_set_csc_default(opp, &default_adjust); ++} ++ ++static void program_scaler( ++ uint8_t controller_idx, ++ struct timing_generator *tg, ++ struct transform *xfm, ++ const struct core_surface *surface, ++ const struct core_stream *stream) ++{ ++ struct scaler_data scaler_data = { { 0 } }; ++ ++ hw_sequencer_build_scaler_parameter_plane( ++ stream, ++ &scaler_data); ++ ++ setup_line_buffer_pixel_depth( ++ stream, ++ LB_PIXEL_DEPTH_24BPP, ++ false); ++ ++ dce110_timing_generator_set_overscan_color_black( ++ tg, ++ surface->public.colorimetry.color_space); ++ ++ dce110_transform_set_scaler(xfm, &scaler_data); ++ ++ dce110_transform_update_viewport( ++ xfm, ++ &scaler_data.viewport, ++ false); ++} ++ ++ ++ ++static void configure_locking(struct dc_context *ctx, uint8_t controller_id) ++{ ++ /* main controller should be in mode 0 (master pipe) */ ++ pipe_control_lock( ++ ctx, ++ controller_id, ++ PIPE_LOCK_CONTROL_MODE, ++ false); ++ ++ /* TODO: for MPO finish the non-root controllers */ ++} ++ ++/** ++ * Program the Front End of the Pipe. ++ * The Back End was already programmed by Set Mode. ++ */ ++static bool set_plane_config( ++ struct core_surface *surface, ++ struct core_target *target) ++{ ++ const struct dc_crtc_timing *dc_crtc_timing = ++ &target->streams[0]->public.timing; ++ struct mem_input *mi = target->streams[0]->mi; ++ struct input_pixel_processor *ipp = target->streams[0]->ipp; ++ struct timing_generator *tg = target->streams[0]->tg; ++ struct transform *xfm = target->streams[0]->xfm; ++ struct output_pixel_processor *opp = target->streams[0]->opp; ++ struct dc_context *ctx = target->streams[0]->ctx; ++ uint8_t controller_idx = target->streams[0]->controller_idx; ++ ++ /* TODO: Clean up change, possibly change to use same type */ ++ enum color_space input_color_space = ++ surface_color_to_color_space(&(surface->public.colorimetry)); ++ ++ configure_locking(ctx, controller_idx); ++ ++ /* While a non-root controller is programmed we ++ * have to lock the root controller. */ ++ pipe_control_lock( ++ ctx, ++ controller_idx, ++ PIPE_LOCK_CONTROL_GRAPHICS | ++ PIPE_LOCK_CONTROL_SCL | ++ PIPE_LOCK_CONTROL_BLENDER | ++ PIPE_LOCK_CONTROL_SURFACE, ++ true); ++ ++ dce110_program_pix_dur(mi, dc_crtc_timing->pix_clk_khz); ++ ++ dce110_timing_generator_program_blanking(tg, dc_crtc_timing); ++ ++ enable_fe_clock(ctx, controller_idx, true); ++ ++ set_default_colors( ++ ipp, ++ opp, ++ target->streams[0]->format, ++ input_color_space, ++ get_output_color_space(dc_crtc_timing), ++ dc_crtc_timing->display_color_depth); ++ ++ /* program Scaler */ ++ program_scaler( ++ controller_idx, tg, xfm, surface, target->streams[0]); ++ ++ set_blender_mode( ++ ctx, ++ controller_idx, ++ BLENDER_MODE_CURRENT_PIPE); ++ ++#if 0 ++ program_alpha_mode( ++ crtc, ++ &pl_cfg->attributes.blend_flags, ++ path_mode->mode.timing.pixel_encoding); ++#endif ++ ++ dce110_mem_input_program_surface_config( ++ mi, ++ &surface->public); ++ ++ pipe_control_lock( ++ ctx, ++ controller_idx, ++ PIPE_LOCK_CONTROL_GRAPHICS | ++ PIPE_LOCK_CONTROL_SCL | ++ PIPE_LOCK_CONTROL_BLENDER | ++ PIPE_LOCK_CONTROL_SURFACE, ++ false); ++ ++ return true; ++} ++ ++static bool update_plane_address( ++ const struct core_surface *surface, ++ struct core_target *target) ++{ ++ struct dc_context *ctx = target->streams[0]->ctx; ++ struct mem_input *mi = target->streams[0]->mi; ++ uint8_t controller_id = target->streams[0]->controller_idx; ++ ++ /* TODO: crtc should be per surface, NOT per-target */ ++ pipe_control_lock( ++ ctx, ++ controller_id, ++ PIPE_LOCK_CONTROL_SURFACE, ++ true); ++ ++ if (false == dce110_mem_input_program_surface_flip_and_addr( ++ mi, &surface->public.address, surface->public.flip_immediate)) ++ return false; ++ ++ pipe_control_lock( ++ ctx, ++ controller_id, ++ PIPE_LOCK_CONTROL_SURFACE, ++ false); ++ ++ return true; ++} ++ ++static void reset_single_stream_hw_ctx(struct core_stream *stream, ++ struct validate_context *context) ++{ ++ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) ++ deallocate_mst_payload(stream); ++ ++ disable_stream(stream); ++ if (stream->audio) { ++ dal_audio_disable_output(stream->audio, ++ stream->stream_enc->id, ++ stream->signal); ++ stream->audio = NULL; ++ } ++ ++ core_link_disable(stream); ++ dce110_timing_generator_blank_crtc(stream->tg); ++ dce110_timing_generator_disable_crtc(stream->tg); ++ dce110_deallocate_dmif_buffer(stream->mi, context->target_count); ++ dce110_transform_set_scaler_bypass(stream->xfm); ++ disable_stereo_mixer(stream->ctx); ++ unreference_clock_source(&context->res_ctx, stream->clock_source); ++} ++ ++static void reset_hw_ctx(struct dc *dc, ++ struct validate_context *context, ++ uint8_t target_count) ++{ ++ uint8_t i; ++ /* look up the targets that have been removed since last commit */ ++ for (i = 0; i < dc->current_context.target_count; i++) { ++ uint8_t controller_idx = dc->current_context.targets[i]-> ++ streams[0]->controller_idx; ++ ++ if (context->res_ctx.controller_ctx[controller_idx].stream && ++ !context->res_ctx.controller_ctx[controller_idx] ++ .flags.timing_changed) ++ continue; ++ ++ reset_single_stream_hw_ctx( ++ dc->current_context.targets[i]->streams[0], ++ &dc->current_context); ++ } ++} ++ ++static void power_down(struct validate_context *context) ++{ ++ power_down_all_hw_blocks(context); ++ disable_vga_and_power_gate_all_controllers(context); ++ ++} ++ ++static bool wait_for_reset_trigger_to_occur( ++ struct dc_context *dc_ctx, ++ struct timing_generator *tg) ++{ ++ bool rc = false; ++ ++ /* To avoid endless loop we wait at most ++ * frames_to_wait_on_triggered_reset frames for the reset to occur. */ ++ const uint32_t frames_to_wait_on_triggered_reset = 10; ++ uint32_t i; ++ ++ for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { ++ ++ if (!dce110_timing_generator_is_counter_moving(tg)) { ++ DC_ERROR("TG counter is not moving!\n"); ++ break; ++ } ++ ++ if (dce110_timing_generator_did_triggered_reset_occur(tg)) { ++ rc = true; ++ /* usually occurs at i=1 */ ++ DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", ++ i); ++ break; ++ } ++ ++ /* Wait for one frame. */ ++ dce110_timing_generator_wait_for_vactive(tg); ++ dce110_timing_generator_wait_for_vblank(tg); ++ } ++ ++ if (false == rc) ++ DC_ERROR("GSL: Timeout on reset trigger!\n"); ++ ++ return rc; ++} ++ ++/* Enable timing synchronization for a group of Timing Generators. */ ++static void enable_timing_synchronization( ++ struct dc_context *dc_ctx, ++ uint32_t timing_generator_num, ++ struct timing_generator *tgs[]) ++{ ++ struct dcp_gsl_params gsl_params = { 0 }; ++ struct trigger_params trigger_params; ++ uint32_t i; ++ ++ DC_SYNC_INFO("GSL: Setting-up...\n"); ++ ++ gsl_params.gsl_group = SYNC_SOURCE_GSL_GROUP0; ++ gsl_params.gsl_purpose = DCP_GSL_PURPOSE_SURFACE_FLIP; ++ ++ for (i = 0; i < timing_generator_num; i++) { ++ /* Designate a single TG in the group as a master. ++ * Since HW doesn't care which one, we always assign ++ * the 1st one in the group. */ ++ gsl_params.timing_server = (0 == i ? true : false); ++ ++ dce110_timing_generator_setup_global_swap_lock(tgs[i], ++ &gsl_params); ++ } ++ ++ /* Reset slave controllers on master VSync */ ++ DC_SYNC_INFO("GSL: enabling trigger-reset\n"); ++ dc_service_memset(&trigger_params, 0, sizeof(trigger_params)); ++ ++ trigger_params.edge = TRIGGER_EDGE_DEFAULT; ++ trigger_params.source = SYNC_SOURCE_GSL_GROUP0; ++ ++ for (i = 1 /* skip the master */; i < timing_generator_num; i++) { ++ dce110_timing_generator_enable_reset_trigger(tgs[i], ++ &trigger_params); ++ ++ DC_SYNC_INFO("GSL: waiting for reset to occur.\n"); ++ wait_for_reset_trigger_to_occur(dc_ctx, tgs[i]); ++ ++ /* Regardless of success of the wait above, remove the reset or ++ * the driver will start timing out on Display requests. */ ++ DC_SYNC_INFO("GSL: disabling trigger-reset.\n"); ++ dce110_timing_generator_disable_reset_trigger(tgs[i]); ++ } ++ ++ /* GSL Vblank synchronization is a one time sync mechanism, assumption ++ * is that the sync'ed displays will not drift out of sync over time*/ ++ DC_SYNC_INFO("GSL: Restoring register states.\n"); ++ for (i = 0; i < timing_generator_num; i++) ++ dce110_timing_generator_tear_down_global_swap_lock(tgs[i]); ++ ++ DC_SYNC_INFO("GSL: Set-up complete.\n"); ++} ++ ++ ++static const struct hw_sequencer_funcs dce110_funcs = { ++ .apply_ctx_to_hw = apply_ctx_to_hw, ++ .reset_hw_ctx = reset_hw_ctx, ++ .set_plane_config = set_plane_config, ++ .update_plane_address = update_plane_address, ++ .enable_memory_requests = dce110_timing_generator_unblank_crtc, ++ .disable_memory_requests = dce110_timing_generator_blank_crtc, ++ .cursor_set_attributes = dce110_ipp_cursor_set_attributes, ++ .cursor_set_position = dce110_ipp_cursor_set_position, ++ .set_gamma_ramp = set_gamma_ramp, ++ .power_down = power_down, ++ .enable_accelerated_mode = enable_accelerated_mode, ++ .get_crtc_positions = dce110_timing_generator_get_crtc_positions, ++ .get_vblank_counter = dce110_timing_generator_get_vblank_counter, ++ .enable_timing_synchronization = enable_timing_synchronization, ++ .disable_vga = dce110_timing_generator_disable_vga, ++ .encoder_create = dce110_link_encoder_create, ++ .encoder_destroy = dce110_link_encoder_destroy, ++ .encoder_power_up = dce110_link_encoder_power_up, ++ .encoder_enable_output = dce110_link_encoder_enable_output, ++ .encoder_disable_output = dce110_link_encoder_disable_output, ++ .encoder_set_dp_phy_pattern = dce110_link_encoder_set_dp_phy_pattern, ++ .encoder_dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings, ++ .encoder_set_lcd_backlight_level = dce110_link_encoder_set_lcd_backlight_level, ++ .clock_gating_power_up = dal_dc_clock_gating_dce110_power_up, ++ .transform_power_up = dce110_transform_power_up, ++ .construct_resource_pool = dce110_construct_resource_pool, ++ .destruct_resource_pool = dce110_destruct_resource_pool, ++ .validate_with_context = dce110_validate_with_context, ++ .validate_bandwidth = dce110_validate_bandwidth, ++ .set_afmt_memory_power_state = dce110_set_afmt_memory_power_state, ++ .enable_display_pipe_clock_gating = dce110_enable_display_pipe_clock_gating, ++ .enable_display_power_gating = dce110_enable_display_power_gating, ++ .program_bw = dce110_program_bw ++}; ++ ++bool dce110_hw_sequencer_construct(struct dc *dc) ++{ ++ dc->hwss = dce110_funcs; ++ ++ return true; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h +new file mode 100644 +index 0000000..def54df +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h +@@ -0,0 +1,36 @@ ++/* ++* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_HWSS_DCE110_H__ ++#define __DC_HWSS_DCE110_H__ ++ ++#include "core_types.h" ++ ++struct dc; ++ ++bool dce110_hw_sequencer_construct(struct dc *dc); ++ ++#endif /* __DC_HWSS_DCE110_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.c +new file mode 100644 +index 0000000..04105ed +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.c +@@ -0,0 +1,85 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/logger_interface.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_ipp.h" ++ ++static const struct dce110_ipp_reg_offsets reg_offsets[] = { ++{ ++ .dcp_offset = (mmDCP0_CUR_CONTROL - mmDCP0_CUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP1_CUR_CONTROL - mmDCP0_CUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP2_CUR_CONTROL - mmDCP0_CUR_CONTROL), ++} ++}; ++ ++bool dce110_ipp_construct( ++ struct dce110_ipp* ipp, ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ if ((inst < 1) || (inst > ARRAY_SIZE(reg_offsets))) ++ return false; ++ ++ ipp->base.ctx = ctx; ++ ++ ipp->base.inst = inst; ++ ++ ipp->offsets = reg_offsets[inst-1]; ++ ++ return true; ++} ++ ++void dce110_ipp_destroy(struct input_pixel_processor **ipp) ++{ ++ dc_service_free((*ipp)->ctx, TO_DCE110_IPP(*ipp)); ++ *ipp = NULL; ++} ++ ++struct input_pixel_processor *dce110_ipp_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct dce110_ipp *ipp = ++ dc_service_alloc(ctx, sizeof(struct dce110_ipp)); ++ ++ if (!ipp) ++ return NULL; ++ ++ if (dce110_ipp_construct(ipp, ctx, inst)) ++ return &ipp->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, ipp); ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.h +new file mode 100644 +index 0000000..1da42ff +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp.h +@@ -0,0 +1,90 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_IPP_DCE110_H__ ++#define __DC_IPP_DCE110_H__ ++ ++#include "inc/ipp.h" ++ ++#define TO_DCE110_IPP(input_pixel_processor)\ ++ container_of(input_pixel_processor, struct dce110_ipp, base) ++ ++struct dce110_ipp_reg_offsets { ++ uint32_t dcp_offset; ++}; ++ ++struct dce110_ipp { ++ struct input_pixel_processor base; ++ struct dce110_ipp_reg_offsets offsets; ++ struct dev_c_lut saved_palette[RGB_256X3X16]; ++}; ++ ++bool dce110_ipp_construct( ++ struct dce110_ipp* ipp, ++ struct dc_context *ctx, ++ enum controller_id id); ++ ++void dce110_ipp_destroy(struct input_pixel_processor **ipp); ++ ++struct input_pixel_processor *dce110_ipp_create( ++ struct dc_context *ctx, ++ enum controller_id id); ++ ++/* CURSOR RELATED */ ++bool dce110_ipp_cursor_set_position( ++ struct input_pixel_processor *ipp, ++ const struct dc_cursor_position *position); ++ ++bool dce110_ipp_cursor_set_attributes( ++ struct input_pixel_processor *ipp, ++ const struct dc_cursor_attributes *attributes); ++ ++/* DEGAMMA RELATED */ ++bool dce110_ipp_set_degamma( ++ struct input_pixel_processor *ipp, ++ const struct gamma_parameters *params, ++ bool force_bypass); ++ ++void dce110_ipp_program_prescale( ++ struct input_pixel_processor *ipp, ++ enum pixel_format pixel_format); ++ ++void dce110_ipp_set_legacy_input_gamma_mode( ++ struct input_pixel_processor *ipp, ++ bool is_legacy); ++ ++bool dce110_ipp_set_legacy_input_gamma_ramp( ++ struct input_pixel_processor *ipp, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params); ++ ++bool dce110_ipp_set_palette( ++ struct input_pixel_processor *ipp, ++ const struct dev_c_lut *palette, ++ uint32_t start, ++ uint32_t length, ++ enum pixel_format surface_pixel_format); ++ ++#endif /*__DC_IPP_DCE110_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_cursor.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_cursor.c +new file mode 100644 +index 0000000..08b7940 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_cursor.c +@@ -0,0 +1,256 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/logger_interface.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_ipp.h" ++ ++#define CURSOR_COLOR_BLACK 0x00000000 ++#define CURSOR_COLOR_WHITE 0xFFFFFFFF ++ ++#define DCP_REG(reg)\ ++ (reg + ipp110->offsets.dcp_offset) ++ ++static void enable( ++ struct dce110_ipp *ipp110, ++ bool enable); ++ ++static void lock( ++ struct dce110_ipp *ipp110, ++ bool enable); ++ ++static void program_position( ++ struct dce110_ipp *ipp110, ++ uint32_t x, ++ uint32_t y); ++ ++static bool program_control( ++ struct dce110_ipp *ipp110, ++ enum dc_cursor_color_format color_format, ++ bool enable_magnification, ++ bool inverse_transparent_clamping); ++ ++static void program_hotspot( ++ struct dce110_ipp *ipp110, ++ uint32_t x, ++ uint32_t y); ++ ++static void program_size( ++ struct dce110_ipp *ipp110, ++ uint32_t width, ++ uint32_t height); ++ ++static void program_address( ++ struct dce110_ipp *ipp110, ++ PHYSICAL_ADDRESS_LOC address); ++ ++ ++bool dce110_ipp_cursor_set_position( ++ struct input_pixel_processor *ipp, ++ const struct dc_cursor_position *position) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ ++ /* lock cursor registers */ ++ lock(ipp110, true); ++ ++ /* Flag passed in structure differentiates cursor enable/disable. */ ++ /* Update if it differs from cached state. */ ++ enable(ipp110, position->enable); ++ ++ program_position(ipp110, position->x, position->y); ++ ++ if (position->hot_spot_enable) ++ program_hotspot( ++ ipp110, ++ position->x_origin, ++ position->y_origin); ++ ++ /* unlock cursor registers */ ++ lock(ipp110, false); ++ ++ return true; ++} ++ ++bool dce110_ipp_cursor_set_attributes( ++ struct input_pixel_processor *ipp, ++ const struct dc_cursor_attributes *attributes) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ /* Lock cursor registers */ ++ lock(ipp110, true); ++ ++ /* Program cursor control */ ++ program_control( ++ ipp110, ++ attributes->color_format, ++ attributes->attribute_flags.bits.ENABLE_MAGNIFICATION, ++ attributes->attribute_flags.bits.INVERSE_TRANSPARENT_CLAMPING); ++ ++ /* Program hot spot coordinates */ ++ program_hotspot(ipp110, attributes->x_hot, attributes->y_hot); ++ ++ /* ++ * Program cursor size -- NOTE: HW spec specifies that HW register ++ * stores size as (height - 1, width - 1) ++ */ ++ program_size(ipp110, attributes->width, attributes->height); ++ ++ /* Program cursor surface address */ ++ program_address(ipp110, attributes->address); ++ ++ /* Unlock Cursor registers. */ ++ lock(ipp110, false); ++ ++ return true; ++} ++ ++static void enable( ++ struct dce110_ipp *ipp110, bool enable) ++{ ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmCUR_CONTROL); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ set_reg_field_value(value, enable, CUR_CONTROL, CURSOR_EN); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static void lock( ++ struct dce110_ipp *ipp110, bool lock) ++{ ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmCUR_UPDATE); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ set_reg_field_value(value, lock, CUR_UPDATE, CURSOR_UPDATE_LOCK); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static void program_position( ++ struct dce110_ipp *ipp110, ++ uint32_t x, ++ uint32_t y) ++{ ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmCUR_POSITION); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ set_reg_field_value(value, x, CUR_POSITION, CURSOR_X_POSITION); ++ set_reg_field_value(value, y, CUR_POSITION, CURSOR_Y_POSITION); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static bool program_control( ++ struct dce110_ipp *ipp110, ++ enum dc_cursor_color_format color_format, ++ bool enable_magnification, ++ bool inverse_transparent_clamping) ++{ ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmCUR_CONTROL); ++ uint32_t mode = 0; ++ ++ switch (color_format) { ++ case CURSOR_MODE_MONO: ++ mode = 0; ++ break; ++ case CURSOR_MODE_COLOR_1BIT_AND: ++ mode = 1; ++ break; ++ case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: ++ mode = 2; ++ break; ++ case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: ++ mode = 3; ++ break; ++ default: ++ return false; ++ } ++ ++ set_reg_field_value(value, mode, CUR_CONTROL, CURSOR_MODE); ++ set_reg_field_value(value, enable_magnification, ++ CUR_CONTROL, CURSOR_2X_MAGNIFY); ++ set_reg_field_value(value, inverse_transparent_clamping, ++ CUR_CONTROL, CUR_INV_TRANS_CLAMP); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ if (color_format == CURSOR_MODE_MONO) { ++ addr = DCP_REG(mmCUR_COLOR1); ++ dal_write_reg(ipp110->base.ctx, addr, CURSOR_COLOR_BLACK); ++ addr = DCP_REG(mmCUR_COLOR2); ++ dal_write_reg(ipp110->base.ctx, addr, CURSOR_COLOR_WHITE); ++ } ++ return true; ++} ++ ++static void program_hotspot( ++ struct dce110_ipp *ipp110, ++ uint32_t x, ++ uint32_t y) ++{ ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmCUR_HOT_SPOT); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ set_reg_field_value(value, x, CUR_HOT_SPOT, CURSOR_HOT_SPOT_X); ++ set_reg_field_value(value, y, CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static void program_size( ++ struct dce110_ipp *ipp110, ++ uint32_t width, ++ uint32_t height) ++{ ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmCUR_SIZE); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ set_reg_field_value(value, width, CUR_SIZE, CURSOR_WIDTH); ++ set_reg_field_value(value, height, CUR_SIZE, CURSOR_HEIGHT); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static void program_address( ++ struct dce110_ipp *ipp110, ++ PHYSICAL_ADDRESS_LOC address) ++{ ++ uint32_t addr = DCP_REG(mmCUR_SURFACE_ADDRESS_HIGH); ++ /* SURFACE_ADDRESS_HIGH: Higher order bits (39:32) of hardware cursor ++ * surface base address in byte. It is 4K byte aligned. ++ * The correct way to program cursor surface address is to first write ++ * to CUR_SURFACE_ADDRESS_HIGH, and then write to CUR_SURFACE_ADDRESS */ ++ ++ dal_write_reg(ipp110->base.ctx, addr, address.high_part); ++ ++ addr = DCP_REG(mmCUR_SURFACE_ADDRESS); ++ dal_write_reg(ipp110->base.ctx, addr, address.low_part); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_gamma.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_gamma.c +new file mode 100644 +index 0000000..f2e8ef4 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_ipp_gamma.c +@@ -0,0 +1,877 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/logger_interface.h" ++#include "include/fixed31_32.h" ++#include "basics/conversion.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_ipp.h" ++ ++#define DCP_REG(reg)\ ++ (reg + ipp110->offsets.dcp_offset) ++ ++enum { ++ MAX_INPUT_LUT_ENTRY = 256 ++}; ++ ++/* CALCULATION OPERATIONS*/ ++static void convert_256_lut_entries_to_gxo_format( ++ const struct gamma_ramp_rgb256x3x16 *lut, ++ struct dev_c_lut16 *gamma) ++{ ++ uint32_t i = 0; ++ ++ ASSERT(lut); ++ ASSERT(gamma); ++ ++ do { ++ gamma->red = lut->red[i]; ++ gamma->green = lut->green[i]; ++ gamma->blue = lut->blue[i]; ++ ++ ++gamma; ++ ++i; ++ } while (i != MAX_INPUT_LUT_ENTRY); ++} ++ ++static void convert_udx_gamma_entries_to_gxo_format( ++ const struct gamma_ramp_dxgi_1 *lut, ++ struct dev_c_lut16 *gamma) ++{ ++ /* TODO here we deal with DXGI gamma table, ++ * originally, values was expressed as 'float', ++ * now values expressed as 'dal_fixed20_12'. */ ++} ++ ++/*PROTOTYPE DECLARATIONS*/ ++static void set_lut_inc( ++ struct dce110_ipp *ipp110, ++ uint8_t inc, ++ bool is_float, ++ bool is_signed); ++ ++static void select_lut(struct dce110_ipp *ipp110); ++ ++static void program_black_offsets( ++ struct dce110_ipp *ipp110, ++ struct dev_c_lut16 *offset); ++ ++static void program_white_offsets( ++ struct dce110_ipp *ipp110, ++ struct dev_c_lut16 *offset); ++ ++static void program_black_white_offset( ++ struct dce110_ipp *ipp110, ++ enum pixel_format surface_pixel_format); ++ ++static void program_lut_gamma( ++ struct dce110_ipp *ipp110, ++ const struct dev_c_lut16 *gamma, ++ const struct gamma_parameters *params); ++ ++static void program_prescale( ++ struct dce110_ipp *ipp110, ++ enum pixel_format pixel_format); ++ ++static void set_legacy_input_gamma_mode( ++ struct dce110_ipp *ipp110, ++ bool is_legacy); ++ ++static bool set_legacy_input_gamma_ramp_rgb256x3x16( ++ struct dce110_ipp *ipp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params); ++ ++static bool set_legacy_input_gamma_ramp_dxgi1( ++ struct dce110_ipp *ipp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params); ++ ++static bool set_default_gamma( ++ struct dce110_ipp *ipp110, ++ enum pixel_format surface_pixel_format); ++ ++static void set_degamma( ++ struct dce110_ipp *ipp110, ++ const struct gamma_parameters *params, ++ bool force_bypass); ++ ++bool dce110_ipp_set_legacy_input_gamma_ramp( ++ struct input_pixel_processor *ipp, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ ++ switch (gamma_ramp->type) { ++ case GAMMA_RAMP_RBG256X3X16: ++ return set_legacy_input_gamma_ramp_rgb256x3x16( ++ ipp110, gamma_ramp, params); ++ case GAMMA_RAMP_DXGI_1: ++ return set_legacy_input_gamma_ramp_dxgi1( ++ ipp110, gamma_ramp, params); ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++} ++ ++bool dce110_ipp_set_palette( ++ struct input_pixel_processor *ipp, ++ const struct dev_c_lut *palette, ++ uint32_t start, ++ uint32_t length, ++ enum pixel_format surface_pixel_format) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ uint32_t i; ++ ++ if (((start + length) > MAX_INPUT_LUT_ENTRY) || (NULL == palette)) { ++ BREAK_TO_DEBUGGER(); ++ /* wrong input */ ++ return false; ++ } ++ ++ for (i = start; i < start + length; i++) { ++ ipp110->saved_palette[i] = palette[i]; ++ ipp110->saved_palette[i] = palette[i]; ++ ipp110->saved_palette[i] = palette[i]; ++ } ++ ++ return set_default_gamma(ipp110, surface_pixel_format); ++} ++ ++bool dce110_ipp_set_degamma( ++ struct input_pixel_processor *ipp, ++ const struct gamma_parameters *params, ++ bool force_bypass) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ ++ set_degamma(ipp110, params, force_bypass); ++ ++ return true; ++} ++ ++void dce110_ipp_program_prescale( ++ struct input_pixel_processor *ipp, ++ enum pixel_format pixel_format) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ ++ program_prescale(ipp110, pixel_format); ++} ++ ++void dce110_ipp_set_legacy_input_gamma_mode( ++ struct input_pixel_processor *ipp, ++ bool is_legacy) ++{ ++ struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); ++ ++ set_legacy_input_gamma_mode(ipp110, is_legacy); ++} ++ ++static void set_lut_inc( ++ struct dce110_ipp *ipp110, ++ uint8_t inc, ++ bool is_float, ++ bool is_signed) ++{ ++ const uint32_t addr = DCP_REG(mmDC_LUT_CONTROL); ++ ++ uint32_t value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ inc, ++ DC_LUT_CONTROL, ++ DC_LUT_INC_R); ++ ++ set_reg_field_value( ++ value, ++ inc, ++ DC_LUT_CONTROL, ++ DC_LUT_INC_G); ++ ++ set_reg_field_value( ++ value, ++ inc, ++ DC_LUT_CONTROL, ++ DC_LUT_INC_B); ++ ++ set_reg_field_value( ++ value, ++ is_float, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_R_FLOAT_POINT_EN); ++ ++ set_reg_field_value( ++ value, ++ is_float, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_G_FLOAT_POINT_EN); ++ ++ set_reg_field_value( ++ value, ++ is_float, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_B_FLOAT_POINT_EN); ++ ++ set_reg_field_value( ++ value, ++ is_signed, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_R_SIGNED_EN); ++ ++ set_reg_field_value( ++ value, ++ is_signed, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_G_SIGNED_EN); ++ ++ set_reg_field_value( ++ value, ++ is_signed, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_B_SIGNED_EN); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static void select_lut(struct dce110_ipp *ipp110) ++{ ++ uint32_t value = 0; ++ ++ set_lut_inc(ipp110, 0, false, false); ++ ++ { ++ const uint32_t addr = DCP_REG(mmDC_LUT_WRITE_EN_MASK); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ /* enable all */ ++ set_reg_field_value( ++ value, ++ 0x7, ++ DC_LUT_WRITE_EN_MASK, ++ DC_LUT_WRITE_EN_MASK); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ } ++ ++ { ++ const uint32_t addr = DCP_REG(mmDC_LUT_RW_MODE); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_LUT_RW_MODE, ++ DC_LUT_RW_MODE); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ } ++ ++ { ++ const uint32_t addr = DCP_REG(mmDC_LUT_CONTROL); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ /* 00 - new u0.12 */ ++ set_reg_field_value( ++ value, ++ 3, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_R_FORMAT); ++ ++ set_reg_field_value( ++ value, ++ 3, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_G_FORMAT); ++ ++ set_reg_field_value( ++ value, ++ 3, ++ DC_LUT_CONTROL, ++ DC_LUT_DATA_B_FORMAT); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ } ++ ++ { ++ const uint32_t addr = DCP_REG(mmDC_LUT_RW_INDEX); ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_LUT_RW_INDEX, ++ DC_LUT_RW_INDEX); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ } ++} ++ ++static void program_black_offsets( ++ struct dce110_ipp *ipp110, ++ struct dev_c_lut16 *offset) ++{ ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmDC_LUT_BLACK_OFFSET_RED), ++ offset->red); ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmDC_LUT_BLACK_OFFSET_GREEN), ++ offset->green); ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmDC_LUT_BLACK_OFFSET_BLUE), ++ offset->blue); ++} ++ ++static void program_white_offsets( ++ struct dce110_ipp *ipp110, ++ struct dev_c_lut16 *offset) ++{ ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmDC_LUT_WHITE_OFFSET_RED), ++ offset->red); ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmDC_LUT_WHITE_OFFSET_GREEN), ++ offset->green); ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmDC_LUT_WHITE_OFFSET_BLUE), ++ offset->blue); ++} ++ ++static void program_black_white_offset( ++ struct dce110_ipp *ipp110, ++ enum pixel_format surface_pixel_format) ++{ ++ struct dev_c_lut16 black_offset; ++ struct dev_c_lut16 white_offset; ++ ++ /* get black offset */ ++ ++ switch (surface_pixel_format) { ++ case PIXEL_FORMAT_FP16: ++ /* sRGB gamut, [0.0...1.0] */ ++ black_offset.red = 0; ++ black_offset.green = 0; ++ black_offset.blue = 0; ++ break; ++ ++ case PIXEL_FORMAT_ARGB2101010_XRBIAS: ++ /* [-1.0...3.0] */ ++ black_offset.red = 0x100; ++ black_offset.green = 0x100; ++ black_offset.blue = 0x100; ++ break; ++ ++ default: ++ black_offset.red = 0; ++ black_offset.green = 0; ++ black_offset.blue = 0; ++ } ++ ++ /* get white offset */ ++ ++ switch (surface_pixel_format) { ++ case PIXEL_FORMAT_FP16: ++ white_offset.red = 0x3BFF; ++ white_offset.green = 0x3BFF; ++ white_offset.blue = 0x3BFF; ++ break; ++ ++ case PIXEL_FORMAT_ARGB2101010_XRBIAS: ++ white_offset.red = 0x37E; ++ white_offset.green = 0x37E; ++ white_offset.blue = 0x37E; ++ break; ++ ++ case PIXEL_FORMAT_ARGB8888: ++ white_offset.red = 0xFF; ++ white_offset.green = 0xFF; ++ white_offset.blue = 0xFF; ++ break; ++ ++ default: ++ white_offset.red = 0x3FF; ++ white_offset.green = 0x3FF; ++ white_offset.blue = 0x3FF; ++ } ++ ++ program_black_offsets(ipp110, &black_offset); ++ program_white_offsets(ipp110, &white_offset); ++} ++ ++static void program_lut_gamma( ++ struct dce110_ipp *ipp110, ++ const struct dev_c_lut16 *gamma, ++ const struct gamma_parameters *params) ++{ ++ uint32_t i = 0; ++ uint32_t value = 0; ++ uint32_t addr; ++ ++ { ++ uint8_t max_tries = 10; ++ uint8_t counter = 0; ++ ++ /* Power on LUT memory */ ++ value = dal_read_reg( ++ ipp110->base.ctx, DCP_REG(mmDCFE_MEM_PWR_CTRL)); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DCFE_MEM_PWR_CTRL, ++ DCP_REGAMMA_MEM_PWR_DIS); ++ ++ dal_write_reg( ++ ipp110->base.ctx, DCP_REG(mmDCFE_MEM_PWR_CTRL), value); ++ ++ while (counter < max_tries) { ++ value = ++ dal_read_reg( ++ ipp110->base.ctx, ++ DCP_REG(mmDCFE_MEM_PWR_STATUS)); ++ ++ if (get_reg_field_value( ++ value, ++ DCFE_MEM_PWR_STATUS, ++ DCP_REGAMMA_MEM_PWR_STATE) == 0) ++ break; ++ ++ ++counter; ++ } ++ ++ if (counter == max_tries) { ++ dal_logger_write(ipp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: regamma lut was not powered on in a timely manner, programming still proceeds\n", ++ __func__); ++ } ++ } ++ ++ program_black_white_offset(ipp110, params->surface_pixel_format); ++ ++ select_lut(ipp110); ++ ++ if (params->surface_pixel_format == PIXEL_FORMAT_INDEX8) { ++ addr = DCP_REG(mmDC_LUT_SEQ_COLOR); ++ ++ do { ++ struct dev_c_lut *index = ++ ipp110->saved_palette + i; ++ ++ set_reg_field_value( ++ value, ++ gamma[index->red].red, ++ DC_LUT_SEQ_COLOR, ++ DC_LUT_SEQ_COLOR); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ ++ set_reg_field_value( ++ value, ++ gamma[index->green].green, ++ DC_LUT_SEQ_COLOR, ++ DC_LUT_SEQ_COLOR); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ ++ set_reg_field_value( ++ value, ++ gamma[index->blue].blue, ++ DC_LUT_SEQ_COLOR, ++ DC_LUT_SEQ_COLOR); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ ++i; ++ } while (i != RGB_256X3X16); ++ } else { ++ addr = DCP_REG(mmDC_LUT_SEQ_COLOR); ++ ++ do { ++ set_reg_field_value( ++ value, ++ gamma[i].red, ++ DC_LUT_SEQ_COLOR, ++ DC_LUT_SEQ_COLOR); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ ++ set_reg_field_value( ++ value, ++ gamma[i].green, ++ DC_LUT_SEQ_COLOR, ++ DC_LUT_SEQ_COLOR); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ ++ set_reg_field_value( ++ value, ++ gamma[i].blue, ++ DC_LUT_SEQ_COLOR, ++ DC_LUT_SEQ_COLOR); ++ dal_write_reg(ipp110->base.ctx, addr, value); ++ ++ ++i; ++ } while (i != RGB_256X3X16); ++ } ++ ++ /* we are done with DCP LUT memory; re-enable low power mode */ ++ value = dal_read_reg(ipp110->base.ctx, DCP_REG(mmDCFE_MEM_PWR_CTRL)); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DCFE_MEM_PWR_CTRL, ++ DCP_REGAMMA_MEM_PWR_DIS); ++ ++ dal_write_reg(ipp110->base.ctx, DCP_REG(mmDCFE_MEM_PWR_CTRL), value); ++} ++ ++static void program_prescale( ++ struct dce110_ipp *ipp110, ++ enum pixel_format pixel_format) ++{ ++ uint32_t prescale_control; ++ uint32_t prescale_values_grph_r = 0; ++ uint32_t prescale_values_grph_g = 0; ++ uint32_t prescale_values_grph_b = 0; ++ ++ uint32_t prescale_num; ++ uint32_t prescale_denom = 1; ++ uint16_t prescale_hw; ++ uint32_t bias_num = 0; ++ uint32_t bias_denom = 1; ++ uint16_t bias_hw; ++ ++ const uint32_t addr_control = DCP_REG(mmPRESCALE_GRPH_CONTROL); ++ ++ prescale_control = dal_read_reg(ipp110->base.ctx, addr_control); ++ ++ set_reg_field_value( ++ prescale_control, ++ 0, ++ PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_BYPASS); ++ ++ switch (pixel_format) { ++ case PIXEL_FORMAT_RGB565: ++ prescale_num = 64; ++ prescale_denom = 63; ++ break; ++ ++ case PIXEL_FORMAT_ARGB8888: ++ /* This function should only be called when using regamma ++ * and bypassing legacy INPUT GAMMA LUT (function name is ++ * misleading) ++ */ ++ prescale_num = 256; ++ prescale_denom = 255; ++ break; ++ ++ case PIXEL_FORMAT_ARGB2101010: ++ prescale_num = 1024; ++ prescale_denom = 1023; ++ break; ++ ++ case PIXEL_FORMAT_ARGB2101010_XRBIAS: ++ prescale_num = 1024; ++ prescale_denom = 510; ++ bias_num = 384; ++ bias_denom = 1024; ++ break; ++ ++ case PIXEL_FORMAT_FP16: ++ prescale_num = 1; ++ break; ++ ++ default: ++ prescale_num = 1; ++ ++ set_reg_field_value( ++ prescale_control, ++ 1, ++ PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_BYPASS); ++ } ++ ++ prescale_hw = fixed_point_to_int_frac( ++ dal_fixed31_32_from_fraction(prescale_num, prescale_denom), ++ 2, 13); ++ ++ bias_hw = fixed_point_to_int_frac( ++ dal_fixed31_32_from_fraction(bias_num, bias_denom), ++ 2, 13); ++ ++ ++ set_reg_field_value( ++ prescale_values_grph_r, ++ prescale_hw, ++ PRESCALE_VALUES_GRPH_R, ++ GRPH_PRESCALE_SCALE_R); ++ ++ set_reg_field_value( ++ prescale_values_grph_r, ++ bias_hw, ++ PRESCALE_VALUES_GRPH_R, ++ GRPH_PRESCALE_BIAS_R); ++ ++ ++ set_reg_field_value( ++ prescale_values_grph_g, ++ prescale_hw, ++ PRESCALE_VALUES_GRPH_G, ++ GRPH_PRESCALE_SCALE_G); ++ ++ set_reg_field_value( ++ prescale_values_grph_g, ++ bias_hw, ++ PRESCALE_VALUES_GRPH_G, ++ GRPH_PRESCALE_BIAS_G); ++ ++ ++ set_reg_field_value( ++ prescale_values_grph_b, ++ prescale_hw, ++ PRESCALE_VALUES_GRPH_B, ++ GRPH_PRESCALE_SCALE_B); ++ ++ set_reg_field_value( ++ prescale_values_grph_b, ++ bias_hw, ++ PRESCALE_VALUES_GRPH_B, ++ GRPH_PRESCALE_BIAS_B); ++ ++ dal_write_reg(ipp110->base.ctx, ++ addr_control, prescale_control); ++ ++ { ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmPRESCALE_VALUES_GRPH_R), ++ prescale_values_grph_r); ++ } ++ ++ { ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmPRESCALE_VALUES_GRPH_G), ++ prescale_values_grph_g); ++ } ++ ++ { ++ dal_write_reg(ipp110->base.ctx, ++ DCP_REG(mmPRESCALE_VALUES_GRPH_B), ++ prescale_values_grph_b); ++ } ++} ++ ++static void set_legacy_input_gamma_mode( ++ struct dce110_ipp *ipp110, ++ bool is_legacy) ++{ ++ const uint32_t addr = DCP_REG(mmINPUT_GAMMA_CONTROL); ++ uint32_t value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ !is_legacy, ++ INPUT_GAMMA_CONTROL, ++ GRPH_INPUT_GAMMA_MODE); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ ++static bool set_legacy_input_gamma_ramp_rgb256x3x16( ++ struct dce110_ipp *ipp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ struct dev_c_lut16 *gamma16 = ++ dc_service_alloc( ++ ipp110->base.ctx, ++ sizeof(struct dev_c_lut16) * MAX_INPUT_LUT_ENTRY); ++ ++ if (!gamma16) ++ return false; ++ ++ convert_256_lut_entries_to_gxo_format( ++ &gamma_ramp->gamma_ramp_rgb256x3x16, gamma16); ++ ++ if ((params->surface_pixel_format != PIXEL_FORMAT_ARGB2101010) && ++ (params->surface_pixel_format != ++ PIXEL_FORMAT_ARGB2101010_XRBIAS) && ++ (params->surface_pixel_format != PIXEL_FORMAT_FP16)) { ++ program_lut_gamma(ipp110, gamma16, params); ++ dc_service_free(ipp110->base.ctx, gamma16); ++ return true; ++ } ++ ++ /* TODO process DirectX-specific formats*/ ++ dc_service_free(ipp110->base.ctx, gamma16); ++ return false; ++} ++ ++static bool set_legacy_input_gamma_ramp_dxgi1( ++ struct dce110_ipp *ipp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ struct dev_c_lut16 *gamma16 = ++ dc_service_alloc( ++ ipp110->base.ctx, ++ sizeof(struct dev_c_lut16) * MAX_INPUT_LUT_ENTRY); ++ ++ if (!gamma16) ++ return false; ++ ++ convert_udx_gamma_entries_to_gxo_format( ++ &gamma_ramp->gamma_ramp_dxgi1, gamma16); ++ ++ if ((params->surface_pixel_format != PIXEL_FORMAT_ARGB2101010) && ++ (params->surface_pixel_format != ++ PIXEL_FORMAT_ARGB2101010_XRBIAS) && ++ (params->surface_pixel_format != PIXEL_FORMAT_FP16)) { ++ program_lut_gamma(ipp110, gamma16, params); ++ dc_service_free(ipp110->base.ctx, gamma16); ++ return true; ++ } ++ ++ /* TODO process DirectX-specific formats*/ ++ dc_service_free(ipp110->base.ctx, gamma16); ++ return false; ++} ++ ++static bool set_default_gamma( ++ struct dce110_ipp *ipp110, ++ enum pixel_format surface_pixel_format) ++{ ++ uint32_t i; ++ ++ struct dev_c_lut16 *gamma16 = NULL; ++ struct gamma_parameters *params = NULL; ++ ++ gamma16 = dc_service_alloc( ++ ipp110->base.ctx, ++ sizeof(struct dev_c_lut16) * MAX_INPUT_LUT_ENTRY); ++ ++ if (!gamma16) ++ return false; ++ ++ params = dc_service_alloc(ipp110->base.ctx, sizeof(*params)); ++ ++ if (!params) { ++ dc_service_free(ipp110->base.ctx, gamma16); ++ return false; ++ } ++ ++ for (i = 0; i < MAX_INPUT_LUT_ENTRY; i++) { ++ gamma16[i].red = gamma16[i].green = ++ gamma16[i].blue = (uint16_t) (i << 8); ++ } ++ ++ params->surface_pixel_format = surface_pixel_format; ++ params->regamma_adjust_type = GRAPHICS_REGAMMA_ADJUST_HW; ++ params->degamma_adjust_type = GRAPHICS_DEGAMMA_ADJUST_HW; ++ params->selected_gamma_lut = GRAPHICS_GAMMA_LUT_REGAMMA; ++ params->disable_adjustments = false; ++ ++ params->regamma.features.value = 0; ++ ++ params->regamma.features.bits.GAMMA_RAMP_ARRAY = 0; ++ params->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB = 1; ++ params->regamma.features.bits.OVERLAY_DEGAMMA_SRGB = 1; ++ ++ for (i = 0; i < 3; i++) { ++ params->regamma.gamma_coeff.a0[i] = 31308; ++ params->regamma.gamma_coeff.a1[i] = 12920; ++ params->regamma.gamma_coeff.a2[i] = 55; ++ params->regamma.gamma_coeff.a3[i] = 55; ++ params->regamma.gamma_coeff.gamma[i] = 2400; ++ ++ } ++ ++ program_lut_gamma(ipp110, gamma16, params); ++ ++ dc_service_free(ipp110->base.ctx, gamma16); ++ dc_service_free(ipp110->base.ctx, params); ++ ++ return true; ++} ++ ++static void set_degamma( ++ struct dce110_ipp *ipp110, ++ const struct gamma_parameters *params, ++ bool force_bypass) ++{ ++ uint32_t value; ++ const uint32_t addr = DCP_REG(mmDEGAMMA_CONTROL); ++ uint32_t degamma_type = ++ params->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB == 1 ? ++ 1 : 2; ++ ++ value = dal_read_reg(ipp110->base.ctx, addr); ++ ++ /* if by pass - no degamma ++ * when legacy and regamma LUT's we do degamma */ ++ if (params->degamma_adjust_type == GRAPHICS_DEGAMMA_ADJUST_BYPASS || ++ (params->surface_pixel_format == PIXEL_FORMAT_FP16 && ++ params->selected_gamma_lut == ++ GRAPHICS_GAMMA_LUT_REGAMMA)) ++ degamma_type = 0; ++ ++ if (force_bypass) ++ degamma_type = 0; ++ ++ set_reg_field_value( ++ value, ++ degamma_type, ++ DEGAMMA_CONTROL, ++ GRPH_DEGAMMA_MODE); ++ ++ set_reg_field_value( ++ value, ++ degamma_type, ++ DEGAMMA_CONTROL, ++ CURSOR_DEGAMMA_MODE); ++ ++ set_reg_field_value( ++ value, ++ degamma_type, ++ DEGAMMA_CONTROL, ++ CURSOR2_DEGAMMA_MODE); ++ ++ dal_write_reg(ipp110->base.ctx, addr, value); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c +new file mode 100644 +index 0000000..0297bd3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c +@@ -0,0 +1,2049 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "core_types.h" ++#include "link_encoder_types.h" ++#include "dce110_link_encoder.h" ++#include "i2caux_interface.h" ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "dce/dce_11_0_enum.h" ++ ++#define DELAY_AFTER_PIXEL_FORMAT_CHANGE 0 /* ms */ ++/* For current ASICs pixel clock - 600MHz */ ++#define MAX_ENCODER_CLK 600000 ++ ++#define DCE11_UNIPHY_MAX_PIXEL_CLK_IN_KHZ 600000 ++ ++#define DEFAULT_AUX_MAX_DATA_SIZE 16 ++#define AUX_MAX_DEFER_WRITE_RETRY 20 ++/* ++ * @brief ++ * Trigger Source Select ++ * ASIC-dependent, actual values for register programming ++ */ ++#define DCE110_DIG_FE_SOURCE_SELECT_INVALID 0x0 ++#define DCE110_DIG_FE_SOURCE_SELECT_DIGA 0x1 ++#define DCE110_DIG_FE_SOURCE_SELECT_DIGB 0x2 ++#define DCE110_DIG_FE_SOURCE_SELECT_DIGC 0x4 ++ ++/* all values are in milliseconds */ ++/* For eDP, after power-up/power/down, ++ * 300/500 msec max. delay from LCDVCC to black video generation */ ++#define PANEL_POWER_UP_TIMEOUT 300 ++#define PANEL_POWER_DOWN_TIMEOUT 500 ++#define HPD_CHECK_INTERVAL 10 ++ ++/* Minimum pixel clock, in KHz. For TMDS signal is 25.00 MHz */ ++#define TMDS_MIN_PIXEL_CLOCK 25000 ++/* Maximum pixel clock, in KHz. For TMDS signal is 165.00 MHz */ ++#define TMDS_MAX_PIXEL_CLOCK 165000 ++/* For current ASICs pixel clock - 600MHz */ ++#define MAX_ENCODER_CLOCK 600000 ++ ++enum { ++ DP_MST_UPDATE_MAX_RETRY = 50 ++}; ++ ++#ifndef mmDP_DPHY_INTERNAL_CTRL ++ #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 ++ #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 ++ #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 ++ #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 ++ #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 ++ #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 ++ #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 ++ #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 ++ #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 ++ #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 ++#endif ++ ++ ++static const uint32_t fe_engine_offsets[] = { ++ mmDIG0_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG1_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG2_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++}; ++ ++ ++static enum transmitter translate_encoder_to_transmitter( ++ struct graphics_object_id encoder) ++{ ++ switch (encoder.id) { ++ case ENCODER_ID_INTERNAL_UNIPHY: ++ switch (encoder.enum_id) { ++ case ENUM_ID_1: ++ return TRANSMITTER_UNIPHY_A; ++ case ENUM_ID_2: ++ return TRANSMITTER_UNIPHY_B; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++ break; ++ case ENCODER_ID_INTERNAL_UNIPHY1: ++ switch (encoder.enum_id) { ++ case ENUM_ID_1: ++ return TRANSMITTER_UNIPHY_C; ++ case ENUM_ID_2: ++ return TRANSMITTER_UNIPHY_D; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++ break; ++ case ENCODER_ID_INTERNAL_UNIPHY2: ++ switch (encoder.enum_id) { ++ case ENUM_ID_1: ++ return TRANSMITTER_UNIPHY_E; ++ case ENUM_ID_2: ++ return TRANSMITTER_UNIPHY_F; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++ break; ++ case ENCODER_ID_INTERNAL_UNIPHY3: ++ switch (encoder.enum_id) { ++ case ENUM_ID_1: ++ return TRANSMITTER_UNIPHY_G; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++ break; ++ case ENCODER_ID_EXTERNAL_NUTMEG: ++ switch (encoder.enum_id) { ++ case ENUM_ID_1: ++ return TRANSMITTER_NUTMEG_CRT; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++ break; ++ case ENCODER_ID_EXTERNAL_TRAVIS: ++ switch (encoder.enum_id) { ++ case ENUM_ID_1: ++ return TRANSMITTER_TRAVIS_CRT; ++ case ENUM_ID_2: ++ return TRANSMITTER_TRAVIS_LCD; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++ break; ++ default: ++ return TRANSMITTER_UNKNOWN; ++ } ++} ++ ++static void enable_phy_bypass_mode( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ bool enable) ++{ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ ++ const uint32_t addr = mmDP_DPHY_CNTL + be_addr_offset; ++ ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, enable, DP_DPHY_CNTL, DPHY_BYPASS); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void disable_prbs_symbols( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ bool disable) ++{ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ ++ const uint32_t addr = mmDP_DPHY_CNTL + be_addr_offset; ++ ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, disable, ++ DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE0); ++ ++ set_reg_field_value(value, disable, ++ DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE1); ++ ++ set_reg_field_value(value, disable, ++ DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE2); ++ ++ set_reg_field_value(value, disable, ++ DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE3); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void disable_prbs_mode( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset) ++{ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ ++ const uint32_t addr = mmDP_DPHY_PRBS_CNTL + be_addr_offset; ++ uint32_t value; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, 0, DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void program_pattern_symbols( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ uint16_t pattern_symbols[8]) ++{ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ ++ addr = mmDP_DPHY_SYM0 + be_addr_offset; ++ ++ value = 0; ++ set_reg_field_value(value, pattern_symbols[0], ++ DP_DPHY_SYM0, DPHY_SYM1); ++ set_reg_field_value(value, pattern_symbols[1], ++ DP_DPHY_SYM0, DPHY_SYM2); ++ set_reg_field_value(value, pattern_symbols[2], ++ DP_DPHY_SYM0, DPHY_SYM3); ++ dal_write_reg(ctx, addr, value); ++ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ ++ addr = mmDP_DPHY_SYM1 + be_addr_offset; ++ ++ value = 0; ++ set_reg_field_value(value, pattern_symbols[3], ++ DP_DPHY_SYM1, DPHY_SYM4); ++ set_reg_field_value(value, pattern_symbols[4], ++ DP_DPHY_SYM1, DPHY_SYM5); ++ set_reg_field_value(value, pattern_symbols[5], ++ DP_DPHY_SYM1, DPHY_SYM6); ++ dal_write_reg(ctx, addr, value); ++ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ addr = mmDP_DPHY_SYM2 + be_addr_offset; ++ value = 0; ++ set_reg_field_value(value, pattern_symbols[6], ++ DP_DPHY_SYM2, DPHY_SYM7); ++ set_reg_field_value(value, pattern_symbols[6], ++ DP_DPHY_SYM2, DPHY_SYM8); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void set_dp_phy_pattern_d102( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset) ++{ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, false); ++ ++ /* For 10-bit PRBS or debug symbols ++ * please use the following sequence: */ ++ ++ /* Enable debug symbols on the lanes */ ++ ++ disable_prbs_symbols(ctx, be_addr_offset, true); ++ ++ /* Disable PRBS mode, ++ * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */ ++ ++ disable_prbs_mode(ctx, be_addr_offset); ++ ++ /* Program debug symbols to be output */ ++ { ++ uint16_t pattern_symbols[8] = { ++ 0x2AA, 0x2AA, 0x2AA, 0x2AA, ++ 0x2AA, 0x2AA, 0x2AA, 0x2AA ++ }; ++ ++ program_pattern_symbols(ctx, ++ be_addr_offset, pattern_symbols); ++ } ++ ++ /* Enable phy bypass mode to enable the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, true); ++} ++ ++static void set_link_training_complete( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ bool complete) ++{ ++ /* This register resides in DP back end block; ++ * transmitter is used for the offset */ ++ ++ const uint32_t addr = mmDP_LINK_CNTL + be_addr_offset; ++ ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, complete, ++ DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void set_dp_phy_pattern_training_pattern( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ uint32_t index) ++{ ++ /* Write Training Pattern */ ++ ++ dal_write_reg(ctx, ++ mmDP_DPHY_TRAINING_PATTERN_SEL + be_addr_offset, index); ++ ++ /* Set HW Register Training Complete to false */ ++ ++ set_link_training_complete(ctx, be_addr_offset, false); ++ ++ /* Disable PHY Bypass mode to output Training Pattern */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, false); ++ ++ /* Disable PRBS mode, ++ * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */ ++ ++ disable_prbs_mode(ctx, be_addr_offset); ++} ++ ++static void set_dp_phy_pattern_symbol_error( ++ struct dc_context *ctx, ++ const int32_t addr_offset) ++{ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, addr_offset, false); ++ ++ /* program correct panel mode*/ ++ { ++ const uint32_t addr = mmDP_DPHY_INTERNAL_CTRL + addr_offset; ++ uint32_t value = 0x0; ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* A PRBS23 pattern is used for most DP electrical measurements. */ ++ ++ /* Enable PRBS symbols on the lanes */ ++ ++ disable_prbs_symbols(ctx, addr_offset, false); ++ ++ /* For PRBS23 Set bit DPHY_PRBS_SEL=1 and Set bit DPHY_PRBS_EN=1 */ ++ { ++ const uint32_t addr = mmDP_DPHY_PRBS_CNTL + addr_offset; ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, 1, ++ DP_DPHY_PRBS_CNTL, DPHY_PRBS_SEL); ++ set_reg_field_value(value, 1, ++ DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN); ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* Enable phy bypass mode to enable the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, addr_offset, true); ++} ++ ++static void set_dp_phy_pattern_prbs7( ++ struct dc_context *ctx, ++ const int32_t addr_offset) ++{ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, addr_offset, false); ++ ++ /* A PRBS7 pattern is used for most DP electrical measurements. */ ++ ++ /* Enable PRBS symbols on the lanes */ ++ ++ disable_prbs_symbols(ctx, addr_offset, false); ++ ++ /* For PRBS7 Set bit DPHY_PRBS_SEL=0 and Set bit DPHY_PRBS_EN=1 */ ++ { ++ const uint32_t addr = mmDP_DPHY_PRBS_CNTL + addr_offset; ++ ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, 0, ++ DP_DPHY_PRBS_CNTL, DPHY_PRBS_SEL); ++ ++ set_reg_field_value(value, 1, ++ DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* Enable phy bypass mode to enable the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, addr_offset, true); ++} ++ ++static void set_dp_phy_pattern_80bit_custom( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ const uint8_t *pattern) ++{ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, false); ++ ++ /* Enable debug symbols on the lanes */ ++ ++ disable_prbs_symbols(ctx, be_addr_offset, true); ++ ++ /* Enable PHY bypass mode to enable the test pattern */ ++ /* TODO is it really needed ? */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, true); ++ ++ /* Program 80 bit custom pattern */ ++ { ++ uint16_t pattern_symbols[8]; ++ ++ pattern_symbols[0] = ++ ((pattern[1] & 0x03) << 8) | pattern[0]; ++ pattern_symbols[1] = ++ ((pattern[2] & 0x0f) << 6) | ((pattern[1] >> 2) & 0x3f); ++ pattern_symbols[2] = ++ ((pattern[3] & 0x3f) << 4) | ((pattern[2] >> 4) & 0x0f); ++ pattern_symbols[3] = ++ (pattern[4] << 2) | ((pattern[3] >> 6) & 0x03); ++ pattern_symbols[4] = ++ ((pattern[6] & 0x03) << 8) | pattern[5]; ++ pattern_symbols[5] = ++ ((pattern[7] & 0x0f) << 6) | ((pattern[6] >> 2) & 0x3f); ++ pattern_symbols[6] = ++ ((pattern[8] & 0x3f) << 4) | ((pattern[7] >> 4) & 0x0f); ++ pattern_symbols[7] = ++ (pattern[9] << 2) | ((pattern[8] >> 6) & 0x03); ++ ++ program_pattern_symbols(ctx, ++ be_addr_offset, pattern_symbols); ++ } ++ ++ /* Enable phy bypass mode to enable the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, true); ++} ++ ++void dce110_link_encoder_setup( ++ struct link_encoder *enc, ++ enum signal_type signal) ++{ ++ const uint32_t addr = mmDIG_BE_CNTL + enc->be_engine_offset; ++ uint32_t value = dal_read_reg(enc->ctx, addr); ++ ++ switch (signal) { ++ case SIGNAL_TYPE_EDP: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ /* DP SST */ ++ set_reg_field_value(value, 0, DIG_BE_CNTL, DIG_MODE); ++ break; ++ case SIGNAL_TYPE_LVDS: ++ /* LVDS */ ++ set_reg_field_value(value, 1, DIG_BE_CNTL, DIG_MODE); ++ break; ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ /* TMDS-DVI */ ++ set_reg_field_value(value, 2, DIG_BE_CNTL, DIG_MODE); ++ break; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ /* TMDS-HDMI */ ++ set_reg_field_value(value, 3, DIG_BE_CNTL, DIG_MODE); ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ /* DP MST */ ++ set_reg_field_value(value, 5, DIG_BE_CNTL, DIG_MODE); ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ /* invalid mode ! */ ++ break; ++ } ++ ++ dal_write_reg(enc->ctx, addr, value); ++} ++ ++static void set_dp_phy_pattern_hbr2_compliance( ++ struct link_encoder *enc, ++ const int32_t be_addr_offset) ++{ ++ /*const int32_t fe_addr_offset = fe_engine_offsets[param->engine]; ++ const int32_t be_addr_offset = enc->be_engine_offset;*/ ++ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* previously there is a register DP_HBR2_EYE_PATTERN ++ * that is enabled to get the pattern. ++ * But it does not work with the latest spec change, ++ * so we are programming the following registers manually. ++ * ++ * The following settings have been confirmed ++ * by Nick Chorney and Sandra Liu */ ++ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(enc->ctx, be_addr_offset, false); ++ ++ /* Setup DIG encoder in DP SST mode */ ++ ++ dce110_link_encoder_setup(enc, SIGNAL_TYPE_DISPLAY_PORT); ++ ++ /* program correct panel mode*/ ++ { ++ const uint32_t addr = mmDP_DPHY_INTERNAL_CTRL + be_addr_offset; ++ uint32_t value = 0x0; ++ dal_write_reg(enc->ctx, addr, value); ++ } ++ ++ /* no vbid after BS (SR) ++ * DP_LINK_FRAMING_CNTL changed history Sandra Liu ++ * 11000260 / 11000104 / 110000FC */ ++ ++ /* TODO DP_LINK_FRAMING_CNTL should always use hardware default value ++ * output except output hbr2_compliance pattern for physical PHY ++ * measurement. This is not normal usage case. SW should reset this ++ * register to hardware default value after end use of HBR2 eye ++ */ ++ BREAK_TO_DEBUGGER(); ++ /* TODO: do we still need this, find out at compliance test ++ addr = mmDP_LINK_FRAMING_CNTL + fe_addr_offset; ++ ++ value = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value(value, 0xFC, ++ DP_LINK_FRAMING_CNTL, DP_IDLE_BS_INTERVAL); ++ set_reg_field_value(value, 1, ++ DP_LINK_FRAMING_CNTL, DP_VBID_DISABLE); ++ set_reg_field_value(value, 1, ++ DP_LINK_FRAMING_CNTL, DP_VID_ENHANCED_FRAME_MODE); ++ ++ dal_write_reg(enc->ctx, addr, value); ++ */ ++ ++ /*TODO add support for this test pattern ++ * support_dp_hbr2_eye_pattern ++ */ ++ ++ /* set link training complete */ ++ set_link_training_complete(enc->ctx, be_addr_offset, true); ++ /* do not enable video stream */ ++ addr = mmDP_VID_STREAM_CNTL + be_addr_offset; ++ ++ value = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value(value, 0, DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE); ++ ++ dal_write_reg(enc->ctx, addr, value); ++ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(enc->ctx, be_addr_offset, false); ++} ++ ++static void set_dp_phy_pattern_passthrough_mode( ++ struct dc_context *ctx, ++ const int32_t be_addr_offset, ++ enum dp_panel_mode panel_mode) ++{ ++ ++ /* program correct panel mode */ ++ { ++ const uint32_t addr = mmDP_DPHY_INTERNAL_CTRL + be_addr_offset; ++ ++ uint32_t value; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ switch (panel_mode) { ++ case DP_PANEL_MODE_EDP: ++ value = 0x1; ++ break; ++ case DP_PANEL_MODE_SPECIAL: ++ value = 0x11; ++ break; ++ default: ++ value = 0x0; ++ break; ++ } ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* set link training complete */ ++ ++ set_link_training_complete(ctx, be_addr_offset, true); ++ ++ /* Disable PHY Bypass mode to setup the test pattern */ ++ ++ enable_phy_bypass_mode(ctx, be_addr_offset, false); ++ ++ /* Disable PRBS mode, ++ * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */ ++ ++ disable_prbs_mode(ctx, be_addr_offset); ++} ++ ++static void construct( ++ struct link_encoder *enc, ++ const struct encoder_init_data *init_data) ++{ ++ struct graphics_object_encoder_cap_info enc_cap_info = {0}; ++ ++ enc->ctx = init_data->ctx; ++ enc->id = init_data->encoder; ++ ++ enc->hpd_source = init_data->hpd_source; ++ enc->connector = init_data->connector; ++ enc->input_signals = SIGNAL_TYPE_ALL; ++ ++ enc->adapter_service = init_data->adapter_service; ++ ++ enc->preferred_engine = ENGINE_ID_UNKNOWN; ++ ++ enc->features.flags.raw = 0; ++ ++ enc->transmitter = translate_encoder_to_transmitter( ++ init_data->encoder); ++ ++ enc->features.flags.bits.IS_AUDIO_CAPABLE = true; ++ ++ enc->features.max_pixel_clock = DCE11_UNIPHY_MAX_PIXEL_CLK_IN_KHZ; ++ ++ /* 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( ++ FEATURE_DP_SINK_DETECT_POLL_DATA_PIN)) ++ enc->features.flags.bits.DP_SINK_DETECT_POLL_DATA_PIN = true; ++ ++ enc->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; ++ ++ /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. ++ * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. ++ * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer ++ * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS. ++ * Prefer DIG assignment is decided by board design. ++ * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design ++ * and VBIOS will filter out 7 UNIPHY for DCE 8.0. ++ * By this, adding DIGG should not hurt DCE 8.0. ++ * This will let DCE 8.1 share DCE 8.0 as much as possible */ ++ ++ switch (enc->transmitter) { ++ case TRANSMITTER_UNIPHY_A: ++ enc->preferred_engine = ENGINE_ID_DIGA; ++ enc->transmitter_offset = 0; ++ enc->be_engine_offset = 0; ++ break; ++ case TRANSMITTER_UNIPHY_B: ++ enc->preferred_engine = ENGINE_ID_DIGB; ++ ++ enc->transmitter_offset = ++ mmBPHYC_UNIPHY1_UNIPHY_TX_CONTROL1 - ++ mmBPHYC_UNIPHY0_UNIPHY_TX_CONTROL1; ++ enc->be_engine_offset = ++ mmDIG1_DIG_BE_CNTL - mmDIG0_DIG_BE_CNTL; ++ break; ++ case TRANSMITTER_UNIPHY_C: ++ enc->preferred_engine = ENGINE_ID_DIGC; ++ enc->transmitter_offset = ++ mmBPHYC_UNIPHY2_UNIPHY_TX_CONTROL1 - ++ mmBPHYC_UNIPHY0_UNIPHY_TX_CONTROL1; ++ enc->be_engine_offset = ++ mmDIG2_DIG_BE_CNTL - mmDIG0_DIG_BE_CNTL; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ enc->preferred_engine = ENGINE_ID_UNKNOWN; ++ enc->transmitter_offset = 0; ++ enc->be_engine_offset = 0; ++ } ++ ++ dal_logger_write(init_data->ctx->logger, ++ LOG_MAJOR_I2C_AUX, ++ LOG_MINOR_I2C_AUX_CFG, ++ "Using channel: %s [%d]\n", ++ DECODE_CHANNEL_ID(init_data->channel), ++ init_data->channel); ++ ++ switch (init_data->channel) { ++ case CHANNEL_ID_DDC1: ++ enc->aux_channel_offset = 0; ++ break; ++ case CHANNEL_ID_DDC2: ++ enc->aux_channel_offset = ++ mmDP_AUX1_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL; ++ break; ++ case CHANNEL_ID_DDC3: ++ enc->aux_channel_offset = ++ mmDP_AUX2_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL; ++ break; ++ default: ++ /* check BIOS object table ! */ ++ dal_logger_write(init_data->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_ENCODER, ++ "%s: Invalid channel ID\n", ++ __func__); ++ enc->aux_channel_offset = 0; ++ } ++ ++ /* Override features with DCE-specific values */ ++ if (dal_adapter_service_get_encoder_cap_info(enc->adapter_service, ++ enc->id, &enc_cap_info)) ++ enc->features.flags.bits.IS_HBR2_CAPABLE = ++ enc_cap_info.dp_hbr2_cap; ++ ++ /* test pattern 3 support */ ++ enc->features.flags.bits.IS_TPS3_CAPABLE = true; ++ enc->features.max_deep_color = COLOR_DEPTH_121212; ++ ++ enc->features.flags.bits.IS_Y_ONLY_CAPABLE = ++ dal_adapter_service_is_feature_supported( ++ FEATURE_SUPPORT_DP_Y_ONLY); ++ ++ enc->features.flags.bits.IS_YCBCR_CAPABLE = ++ dal_adapter_service_is_feature_supported( ++ FEATURE_SUPPORT_DP_YUV); ++} ++ ++struct link_encoder *dce110_link_encoder_create( ++ const struct encoder_init_data *init) ++{ ++ struct link_encoder *enc = ++ dc_service_alloc(init->ctx, sizeof(struct link_encoder)); ++ ++ if (!enc) ++ goto enc_create_fail; ++ ++ construct(enc, init); ++ ++ return enc; ++ ++enc_create_fail: ++ return NULL; ++} ++ ++void dce110_link_encoder_destroy(struct link_encoder **enc) ++{ ++ struct link_encoder *encoder = *enc; ++ dc_service_free(encoder->ctx, encoder); ++ *enc = NULL; ++} ++ ++void dce110_link_encoder_set_dp_phy_pattern( ++ struct link_encoder *enc, ++ const struct encoder_set_dp_phy_pattern_param *param) ++{ ++ const int32_t offset = enc->be_engine_offset; ++ ++ ++ switch (param->dp_phy_pattern) { ++ case DP_TEST_PATTERN_TRAINING_PATTERN1: ++ set_dp_phy_pattern_training_pattern(enc->ctx, ++ offset, 0); ++ break; ++ case DP_TEST_PATTERN_TRAINING_PATTERN2: ++ set_dp_phy_pattern_training_pattern(enc->ctx, ++ offset, 1); ++ break; ++ case DP_TEST_PATTERN_TRAINING_PATTERN3: ++ set_dp_phy_pattern_training_pattern(enc->ctx, ++ offset, 2); ++ break; ++ case DP_TEST_PATTERN_D102: ++ set_dp_phy_pattern_d102(enc->ctx, offset); ++ break; ++ case DP_TEST_PATTERN_SYMBOL_ERROR: ++ set_dp_phy_pattern_symbol_error(enc->ctx, offset); ++ break; ++ case DP_TEST_PATTERN_PRBS7: ++ set_dp_phy_pattern_prbs7(enc->ctx, offset); ++ break; ++ case DP_TEST_PATTERN_80BIT_CUSTOM: ++ set_dp_phy_pattern_80bit_custom( ++ enc->ctx, ++ offset, param->custom_pattern); ++ break; ++ case DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE: ++ set_dp_phy_pattern_hbr2_compliance( ++ enc, offset); ++ break; ++ case DP_TEST_PATTERN_VIDEO_MODE: { ++ set_dp_phy_pattern_passthrough_mode( ++ enc->ctx, ++ offset, ++ param->dp_panel_mode); ++ break; ++ } ++ ++ ++ default: ++ /* invalid phy pattern */ ++ ASSERT_CRITICAL(false); ++ break; ++ } ++} ++ ++enum encoder_result dce110_link_encoder_dp_set_lane_settings( ++ struct link_encoder *enc, ++ const struct link_training_settings *link_settings) ++{ ++ union dpcd_training_lane_set training_lane_set = { { 0 } }; ++ ++ int32_t lane = 0; ++ ++ struct bp_transmitter_control cntl = { 0 }; ++ ++ if (!link_settings) { ++ BREAK_TO_DEBUGGER(); ++ return ENCODER_RESULT_ERROR; ++ } ++ ++ cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS; ++ cntl.transmitter = enc->transmitter; ++ cntl.connector_obj_id = enc->connector; ++ cntl.lanes_number = link_settings->link_settings.lane_count; ++ cntl.hpd_sel = enc->hpd_source; ++ cntl.pixel_clock = link_settings->link_settings.link_rate * ++ LINK_RATE_REF_FREQ_IN_KHZ; ++ ++ for (lane = 0; lane < link_settings->link_settings.lane_count; ++lane) { ++ /* translate lane settings */ ++ ++ training_lane_set.bits.VOLTAGE_SWING_SET = ++ link_settings->lane_settings[lane].VOLTAGE_SWING; ++ training_lane_set.bits.PRE_EMPHASIS_SET = ++ link_settings->lane_settings[lane].PRE_EMPHASIS; ++ ++ /* post cursor 2 setting only applies to HBR2 link rate */ ++ if (link_settings->link_settings.link_rate == LINK_RATE_HIGH2) { ++ /* this is passed to VBIOS ++ * to program post cursor 2 level */ ++ ++ training_lane_set.bits.POST_CURSOR2_SET = ++ link_settings->lane_settings[lane].POST_CURSOR2; ++ } ++ ++ cntl.lane_select = lane; ++ cntl.lane_settings = training_lane_set.raw; ++ ++ /* call VBIOS table to set voltage swing and pre-emphasis */ ++ ++ dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ enc->adapter_service), &cntl); ++ } ++ ++ return ENCODER_RESULT_OK; ++} ++ ++/* return value is bit-vector */ ++static uint8_t get_frontend_source( ++ enum engine_id engine) ++{ ++ switch (engine) { ++ case ENGINE_ID_DIGA: ++ return DCE110_DIG_FE_SOURCE_SELECT_DIGA; ++ case ENGINE_ID_DIGB: ++ return DCE110_DIG_FE_SOURCE_SELECT_DIGB; ++ case ENGINE_ID_DIGC: ++ return DCE110_DIG_FE_SOURCE_SELECT_DIGC; ++ default: ++ ASSERT_CRITICAL(false); ++ return DCE110_DIG_FE_SOURCE_SELECT_INVALID; ++ } ++} ++ ++static void configure_encoder( ++ struct link_encoder *enc, ++ enum engine_id engine, ++ const struct link_settings *link_settings) ++{ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* set number of lanes */ ++ addr = mmDP_CONFIG + enc->be_engine_offset; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, link_settings->lane_count - LANE_COUNT_ONE, ++ DP_CONFIG, DP_UDI_LANES); ++ dal_write_reg(enc->ctx, addr, value); ++ ++} ++ ++static bool is_panel_powered_on(struct link_encoder *link_enc) ++{ ++ uint32_t value; ++ bool ret; ++ ++ value = dal_read_reg(link_enc->ctx, ++ mmLVTMA_PWRSEQ_STATE); ++ ++ ret = get_reg_field_value(value, ++ LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R); ++ ++ return ret == 1; ++} ++ ++/* ++ * @brief ++ * eDP only. Control the power of the eDP panel. ++ */ ++static enum encoder_result link_encoder_edp_power_control( ++ struct link_encoder *link_enc, ++ bool power_up) ++{ ++ struct bp_transmitter_control cntl = { 0 }; ++ enum bp_result bp_result; ++ ++ if (dal_graphics_object_id_get_connector_id(link_enc->connector) != ++ CONNECTOR_ID_EDP) { ++ BREAK_TO_DEBUGGER(); ++ return ENCODER_RESULT_ERROR; ++ } ++ ++ if ((power_up && !is_panel_powered_on(link_enc)) || ++ (!power_up && is_panel_powered_on(link_enc))) { ++ ++ /* Send VBIOS command to prompt eDP panel power */ ++ ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: Panel Power action: %s\n", ++ __func__, (power_up ? "On":"Off")); ++ ++ cntl.action = power_up ? ++ TRANSMITTER_CONTROL_POWER_ON : ++ TRANSMITTER_CONTROL_POWER_OFF; ++ cntl.transmitter = link_enc->transmitter; ++ cntl.connector_obj_id = link_enc->connector; ++ cntl.coherent = false; ++ cntl.lanes_number = LANE_COUNT_FOUR; ++ cntl.hpd_sel = link_enc->hpd_source; ++ ++ bp_result = dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ link_enc->adapter_service), &cntl); ++ ++ if (BP_RESULT_OK != bp_result) { ++ ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: Panel Power bp_result: %d\n", ++ __func__, bp_result); ++ } ++ } else { ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: Skipping Panel Power action: %s\n", ++ __func__, (power_up ? "On":"Off")); ++ } ++ ++ return ENCODER_RESULT_OK; ++} ++ ++/* ++ * @brief ++ * eDP only. ++ */ ++static void link_encoder_edp_wait_for_hpd_ready( ++ struct link_encoder *link_enc, ++ struct graphics_object_id connector, ++ bool power_up) ++{ ++ struct adapter_service *as = link_enc->adapter_service; ++ struct irq *hpd; ++ bool edp_hpd_high = false; ++ uint32_t time_elapsed = 0; ++ uint32_t timeout = power_up ? ++ PANEL_POWER_UP_TIMEOUT : PANEL_POWER_DOWN_TIMEOUT; ++ ++ if (dal_graphics_object_id_get_connector_id(connector) != ++ CONNECTOR_ID_EDP) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ if (!power_up && dal_adapter_service_is_feature_supported( ++ FEATURE_NO_HPD_LOW_POLLING_VCC_OFF)) ++ /* from KV, we will not HPD low after turning off VCC - ++ * instead, we will check the SW timer in power_up(). */ ++ return; ++ ++ /* when we power on/off the eDP panel, ++ * we need to wait until SENSE bit is high/low */ ++ ++ /* obtain HPD */ ++ ++ hpd = dal_adapter_service_obtain_hpd_irq(as, connector); ++ ++ if (!hpd) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ dal_irq_open(hpd); ++ ++ /* wait until timeout or panel detected */ ++ ++ do { ++ uint32_t detected = 0; ++ ++ dal_irq_get_value(hpd, &detected); ++ ++ if (!(detected ^ power_up)) { ++ edp_hpd_high = true; ++ break; ++ } ++ ++ dc_service_sleep_in_milliseconds(link_enc->ctx, HPD_CHECK_INTERVAL); ++ ++ time_elapsed += HPD_CHECK_INTERVAL; ++ } while (time_elapsed < timeout); ++ ++ dal_irq_close(hpd); ++ ++ dal_adapter_service_release_irq(as, hpd); ++ ++ if (false == edp_hpd_high) { ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: wait timed out!\n", __func__); ++ } ++} ++ ++static void aux_initialize( ++ struct link_encoder *link_enc, ++ enum hpd_source_id hpd_source) ++{ ++ uint32_t addr = mmAUX_CONTROL + link_enc->aux_channel_offset; ++ ++ uint32_t value = dal_read_reg(link_enc->ctx, addr); ++ ++ set_reg_field_value(value, hpd_source, AUX_CONTROL, AUX_HPD_SEL); ++ set_reg_field_value(value, 0, AUX_CONTROL, AUX_LS_READ_EN); ++ dal_write_reg(link_enc->ctx, addr, value); ++ ++ addr = mmAUX_DPHY_RX_CONTROL0 + link_enc->aux_channel_offset; ++ value = dal_read_reg(link_enc->ctx, addr); ++ ++ /* 1/4 window (the maximum allowed) */ ++ set_reg_field_value(value, 1, ++ AUX_DPHY_RX_CONTROL0, AUX_RX_RECEIVE_WINDOW); ++ dal_write_reg(link_enc->ctx, ++ mmAUX_DPHY_RX_CONTROL0 + link_enc->aux_channel_offset, ++ value); ++ ++} ++ ++/*todo: cloned in stream enc, fix*/ ++static bool is_panel_backlight_on(struct link_encoder *link_enc) ++{ ++ uint32_t value; ++ ++ value = dal_read_reg(link_enc->ctx, mmLVTMA_PWRSEQ_CNTL); ++ ++ return get_reg_field_value(value, LVTMA_PWRSEQ_CNTL, LVTMA_BLON); ++} ++ ++/*todo: cloned in stream enc, fix*/ ++/* ++ * @brief ++ * eDP only. Control the backlight of the eDP panel ++ */ ++static enum encoder_result link_encoder_edp_backlight_control( ++ struct link_encoder *link_enc, ++ bool enable) ++{ ++ struct bp_transmitter_control cntl = { 0 }; ++ ++ if (dal_graphics_object_id_get_connector_id(link_enc->connector) ++ != CONNECTOR_ID_EDP) { ++ BREAK_TO_DEBUGGER(); ++ return ENCODER_RESULT_ERROR; ++ } ++ ++ if (enable && is_panel_backlight_on(link_enc)) { ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: panel already powered up. Do nothing.\n", ++ __func__); ++ return ENCODER_RESULT_OK; ++ } ++ ++ if (!enable && !is_panel_powered_on(link_enc)) { ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: panel already powered down. Do nothing.\n", ++ __func__); ++ return ENCODER_RESULT_OK; ++ } ++ ++ /* Send VBIOS command to control eDP panel backlight */ ++ ++ dal_logger_write(link_enc->ctx->logger, ++ LOG_MAJOR_HW_TRACE, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ "%s: backlight action: %s\n", ++ __func__, (enable ? "On":"Off")); ++ ++ cntl.action = enable ? ++ TRANSMITTER_CONTROL_BACKLIGHT_ON : ++ TRANSMITTER_CONTROL_BACKLIGHT_OFF; ++ /*cntl.engine_id = ctx->engine;*/ ++ cntl.transmitter = link_enc->transmitter; ++ cntl.connector_obj_id = link_enc->connector; ++ /*todo: unhardcode*/ ++ cntl.lanes_number = LANE_COUNT_FOUR; ++ cntl.hpd_sel = link_enc->hpd_source; ++ ++ /* For eDP, the following delays might need to be considered ++ * after link training completed: ++ * idle period - min. accounts for required BS-Idle pattern, ++ * max. allows for source frame synchronization); ++ * 50 msec max. delay from valid video data from source ++ * to video on dislpay or backlight enable. ++ * ++ * Disable the delay for now. ++ * Enable it in the future if necessary. ++ */ ++ /* dc_service_sleep_in_milliseconds(50); */ ++ ++ dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ link_enc->adapter_service), &cntl); ++ ++ return ENCODER_RESULT_OK; ++} ++ ++/* ++ * @brief ++ * Configure digital transmitter and enable both encoder and transmitter ++ * Actual output will be available after calling unblank() ++ */ ++enum encoder_result dce110_link_encoder_enable_output( ++ struct link_encoder *enc, ++ const struct link_settings *link_settings, ++ enum engine_id engine, ++ enum clock_source_id clock_source, ++ enum signal_type signal, ++ enum dc_color_depth color_depth, ++ uint32_t pixel_clock) ++{ ++ struct bp_transmitter_control cntl = { 0 }; ++ ++ if (enc->connector.id == CONNECTOR_ID_EDP) { ++ /* power up eDP panel */ ++ ++ link_encoder_edp_power_control( ++ enc, true); ++ ++ link_encoder_edp_wait_for_hpd_ready( ++ enc, enc->connector, true); ++ ++ /* have to turn off the backlight ++ * before power down eDP panel */ ++ link_encoder_edp_backlight_control( ++ enc, true); ++ } ++ ++ /* Enable the PHY */ ++ ++ /* number_of_lanes is used for pixel clock adjust, ++ * but it's not passed to asic_control. ++ * We need to set number of lanes manually. */ ++ if (dc_is_dp_signal(signal)) ++ configure_encoder(enc, engine, link_settings); ++ ++ cntl.action = TRANSMITTER_CONTROL_ENABLE; ++ cntl.engine_id = engine; ++ cntl.transmitter = enc->transmitter; ++ cntl.pll_id = clock_source; ++ cntl.signal = signal; ++ cntl.lanes_number = link_settings->lane_count; ++ cntl.hpd_sel = enc->hpd_source; ++ if (dc_is_dp_signal(signal)) ++ cntl.pixel_clock = link_settings->link_rate ++ * LINK_RATE_REF_FREQ_IN_KHZ; ++ else ++ cntl.pixel_clock = pixel_clock; ++ cntl.color_depth = color_depth; ++ ++ if (DELAY_AFTER_PIXEL_FORMAT_CHANGE) ++ dc_service_sleep_in_milliseconds( ++ enc->ctx, ++ DELAY_AFTER_PIXEL_FORMAT_CHANGE); ++ ++ dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ enc->adapter_service), ++ &cntl); ++ ++ return ENCODER_RESULT_OK; ++} ++ ++static bool is_dig_enabled(const struct link_encoder *link_enc) ++{ ++ uint32_t value; ++ ++ value = dal_read_reg(link_enc->ctx, ++ mmDIG_BE_EN_CNTL + link_enc->be_engine_offset); ++ ++ return get_reg_field_value(value, DIG_BE_EN_CNTL, DIG_ENABLE); ++} ++ ++static void link_encoder_disable(struct link_encoder *link_enc) ++{ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* reset training pattern */ ++ addr = mmDP_DPHY_TRAINING_PATTERN_SEL + link_enc->be_engine_offset; ++ value = dal_read_reg(link_enc->ctx, addr); ++ set_reg_field_value(value, 0, ++ DP_DPHY_TRAINING_PATTERN_SEL, ++ DPHY_TRAINING_PATTERN_SEL); ++ dal_write_reg(link_enc->ctx, addr, value); ++ ++ /* reset training complete */ ++ addr = mmDP_LINK_CNTL + link_enc->be_engine_offset; ++ value = dal_read_reg(link_enc->ctx, addr); ++ set_reg_field_value(value, 0, DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE); ++ dal_write_reg(link_enc->ctx, addr, value); ++ ++ /* reset panel mode */ ++ addr = mmDP_DPHY_INTERNAL_CTRL + link_enc->be_engine_offset; ++ value = 0; ++ dal_write_reg(link_enc->ctx, addr, value); ++} ++ ++/* ++ * @brief ++ * Disable transmitter and its encoder ++ */ ++enum encoder_result dce110_link_encoder_disable_output( ++ struct link_encoder *link_enc, ++ enum signal_type signal) ++{ ++ struct bp_transmitter_control cntl = { 0 }; ++ ++ if (link_enc->connector.id == CONNECTOR_ID_EDP) { ++ /* have to turn off the backlight ++ * before power down eDP panel */ ++ link_encoder_edp_backlight_control( ++ link_enc, false); ++ } ++ ++ if (!is_dig_enabled(link_enc) && ++ dal_adapter_service_should_optimize(link_enc->adapter_service, ++ OF_SKIP_POWER_DOWN_INACTIVE_ENCODER)) { ++ return ENCODER_RESULT_OK; ++ } ++ /* Power-down RX and disable GPU PHY should be paired. ++ * Disabling PHY without powering down RX may cause ++ * symbol lock loss, on which we will get DP Sink interrupt. */ ++ ++ /* There is a case for the DP active dongles ++ * where we want to disable the PHY but keep RX powered, ++ * for those we need to ignore DP Sink interrupt ++ * by checking lane count that has been set ++ * on the last do_enable_output(). */ ++ ++ /* disable transmitter */ ++ cntl.action = TRANSMITTER_CONTROL_DISABLE; ++ cntl.transmitter = link_enc->transmitter; ++ cntl.hpd_sel = link_enc->hpd_source; ++ cntl.signal = signal; ++ cntl.connector_obj_id = link_enc->connector; ++ ++ dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ link_enc->adapter_service), &cntl); ++ ++ /* disable encoder */ ++ if (dc_is_dp_signal(signal)) ++ link_encoder_disable(link_enc); ++ ++ if (link_enc->connector.id == CONNECTOR_ID_EDP) { ++ /* power down eDP panel */ ++ /* TODO: Power control cause regression, we should implement ++ * it properly, for now just comment it. ++ * ++ * link_encoder_edp_wait_for_hpd_ready( ++ link_enc, ++ link_enc->connector, ++ false); ++ ++ * link_encoder_edp_power_control( ++ link_enc, false); */ ++ } ++ ++ return ENCODER_RESULT_OK; ++} ++ ++static void hpd_initialize( ++ struct link_encoder *enc, ++ enum hpd_source_id hpd_source) ++{ ++ /* Associate HPD with DIG_BE */ ++ const uint32_t addr = mmDIG_BE_CNTL + enc->be_engine_offset; ++ uint32_t value = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value(value, hpd_source, DIG_BE_CNTL, DIG_HPD_SELECT); ++ dal_write_reg(enc->ctx, addr, value); ++} ++ ++enum encoder_result dce110_link_encoder_power_up( ++ struct link_encoder *enc) ++{ ++ struct bp_transmitter_control cntl = { 0 }; ++ ++ enum bp_result result; ++ ++ cntl.action = TRANSMITTER_CONTROL_INIT; ++ cntl.engine_id = ENGINE_ID_UNKNOWN; ++ cntl.transmitter = enc->transmitter; ++ cntl.connector_obj_id = enc->connector; ++ cntl.lanes_number = LANE_COUNT_FOUR; ++ cntl.coherent = false; ++ cntl.hpd_sel = enc->hpd_source; ++ ++ result = dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ enc->adapter_service), ++ &cntl); ++ ++ if (result != BP_RESULT_OK) { ++ dal_logger_write(enc->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_ENCODER, ++ "%s: Failed to execute VBIOS command table!\n", ++ __func__); ++ BREAK_TO_DEBUGGER(); ++ return ENCODER_RESULT_ERROR; ++ } ++ ++ if (enc->connector.id == CONNECTOR_ID_LVDS) { ++ cntl.action = TRANSMITTER_CONTROL_BACKLIGHT_BRIGHTNESS; ++ ++ result = dal_bios_parser_transmitter_control( ++ dal_adapter_service_get_bios_parser( ++ enc->adapter_service), ++ &cntl); ++ ASSERT(result == BP_RESULT_OK); ++ ++ } else if (enc->connector.id == CONNECTOR_ID_EDP) { ++ link_encoder_edp_power_control(enc, true); ++ ++ link_encoder_edp_wait_for_hpd_ready( ++ enc, enc->connector, true); ++ ++ } ++ aux_initialize(enc, enc->hpd_source); ++ ++ /* reinitialize HPD. ++ * hpd_initialize() will pass DIG_FE id to HW context. ++ * All other routine within HW context will use fe_engine_offset ++ * as DIG_FE id even caller pass DIG_FE id. ++ * So this routine must be called first. */ ++ hpd_initialize(enc, enc->hpd_source); ++ ++ return ENCODER_RESULT_OK; ++} ++ ++ ++static bool validate_dvi_output( ++ const struct link_encoder *enc, ++ enum signal_type connector_signal, ++ enum signal_type signal, ++ const struct dc_crtc_timing *crtc_timing) ++{ ++ uint32_t max_pixel_clock = TMDS_MAX_PIXEL_CLOCK; ++ ++ if (enc->features.max_pixel_clock < TMDS_MAX_PIXEL_CLOCK) ++ max_pixel_clock = enc->features.max_pixel_clock; ++ ++ if (signal == SIGNAL_TYPE_DVI_DUAL_LINK) ++ max_pixel_clock <<= 1; ++ ++ /* This handles the case of HDMI downgrade to DVI we don't want to ++ * we don't want to cap the pixel clock if the DDI is not DVI. ++ */ ++ if (connector_signal != SIGNAL_TYPE_DVI_DUAL_LINK && ++ connector_signal != SIGNAL_TYPE_DVI_SINGLE_LINK) ++ max_pixel_clock = enc->features.max_pixel_clock; ++ ++ /* DVI only support RGB pixel encoding */ ++ if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB) ++ return false; ++ ++ if (crtc_timing->pix_clk_khz < TMDS_MIN_PIXEL_CLOCK) ++ return false; ++ ++ if (crtc_timing->pix_clk_khz > max_pixel_clock) ++ return false; ++ ++ /* DVI supports 6/8bpp single-link and 10/16bpp dual-link */ ++ switch (crtc_timing->display_color_depth) { ++ case COLOR_DEPTH_666: ++ case COLOR_DEPTH_888: ++ break; ++ case COLOR_DEPTH_101010: ++ case COLOR_DEPTH_161616: ++ if (signal != SIGNAL_TYPE_DVI_DUAL_LINK) ++ return false; ++ break; ++ default: ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool validate_hdmi_output( ++ const struct link_encoder *enc, ++ const struct dc_crtc_timing *crtc_timing, ++ uint32_t max_tmds_clk_from_edid_in_mhz, ++ enum dc_color_depth max_hdmi_deep_color, ++ uint32_t max_hdmi_pixel_clock) ++{ ++ enum dc_color_depth max_deep_color = max_hdmi_deep_color; ++ ++ /* expressed in KHz */ ++ uint32_t pixel_clock = 0; ++ ++ if (max_deep_color > enc->features.max_deep_color) ++ max_deep_color = enc->features.max_deep_color; ++ ++ if (max_deep_color < crtc_timing->display_color_depth) ++ return false; ++ ++ if (crtc_timing->pix_clk_khz < TMDS_MIN_PIXEL_CLOCK) ++ return false; ++ ++ switch (crtc_timing->display_color_depth) { ++ case COLOR_DEPTH_666: ++ pixel_clock = (crtc_timing->pix_clk_khz * 3) >> 2; ++ break; ++ case COLOR_DEPTH_888: ++ pixel_clock = crtc_timing->pix_clk_khz; ++ break; ++ case COLOR_DEPTH_101010: ++ pixel_clock = (crtc_timing->pix_clk_khz * 10) >> 3; ++ break; ++ case COLOR_DEPTH_121212: ++ pixel_clock = (crtc_timing->pix_clk_khz * 3) >> 1; ++ break; ++ case COLOR_DEPTH_161616: ++ pixel_clock = crtc_timing->pix_clk_khz << 1; ++ break; ++ default: ++ break; ++ } ++ ++ if (max_tmds_clk_from_edid_in_mhz > 0) ++ if (pixel_clock > max_tmds_clk_from_edid_in_mhz * 1000) ++ return false; ++ ++ if ((pixel_clock == 0) || ++ (pixel_clock > max_hdmi_pixel_clock) || ++ (pixel_clock > enc->features.max_pixel_clock)) ++ return false; ++ ++ /* ++ * Restriction: allow non-CE mode (IT mode) to support RGB only. ++ * When it is IT mode, the format mode will be 0, ++ * but currently the code is broken, ++ * VIDEO FORMAT is always 0 in validatepathMode(). ++ * Due to overscan change - need fix there and test the impact - to do. ++ */ ++ if (crtc_timing->timing_standard != TIMING_STANDARD_CEA861 && ++ crtc_timing->timing_standard != TIMING_STANDARD_HDMI) ++ if (crtc_timing->pixel_encoding != ++ PIXEL_ENCODING_RGB) ++ return false; ++ ++ return true; ++} ++ ++static bool validate_rgb_output( ++ const struct link_encoder *enc, ++ const struct dc_crtc_timing *crtc_timing) ++{ ++ if (crtc_timing->pix_clk_khz > enc->features.max_pixel_clock) ++ return false; ++ ++ if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB) ++ return false; ++ ++ return true; ++} ++ ++static bool validate_dp_output( ++ const struct link_encoder *enc, ++ const struct dc_crtc_timing *crtc_timing) ++{ ++ if (crtc_timing->pix_clk_khz > enc->features.max_pixel_clock) ++ return false; ++ ++ /* default RGB only */ ++ if (crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) ++ return true; ++ ++ if (enc->features.flags.bits.IS_YCBCR_CAPABLE) ++ return true; ++ ++ /* for DCE 8.x or later DP Y-only feature, ++ * we need ASIC cap + FeatureSupportDPYonly, not support 666 */ ++ if (crtc_timing->flags.Y_ONLY && ++ enc->features.flags.bits.IS_YCBCR_CAPABLE && ++ crtc_timing->display_color_depth != COLOR_DEPTH_666) ++ return true; ++ ++ return false; ++} ++ ++static bool validate_wireless_output( ++ const struct link_encoder *enc, ++ const struct dc_crtc_timing *crtc_timing) ++{ ++ if (crtc_timing->pix_clk_khz > enc->features.max_pixel_clock) ++ return false; ++ ++ /* Wireless only supports YCbCr444 */ ++ if (crtc_timing->pixel_encoding == ++ PIXEL_ENCODING_YCBCR444) ++ return true; ++ ++ return false; ++} ++ ++enum encoder_result dce110_link_encoder_validate_output_with_stream( ++ struct link_encoder *enc, ++ const struct core_stream *stream) ++{ ++ bool is_valid; ++ ++ switch (stream->signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ is_valid = validate_dvi_output( ++ enc, ++ stream->sink->link->public.connector_signal, ++ stream->signal, ++ &stream->public.timing); ++ break; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ is_valid = validate_hdmi_output( ++ enc, ++ &stream->public.timing, ++ stream->max_tmds_clk_from_edid_in_mhz, ++ stream->max_hdmi_deep_color, ++ stream->max_hdmi_pixel_clock); ++ break; ++ case SIGNAL_TYPE_RGB: ++ is_valid = validate_rgb_output( ++ enc, &stream->public.timing); ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ is_valid = validate_dp_output( ++ enc, &stream->public.timing); ++ break; ++ case SIGNAL_TYPE_WIRELESS: ++ is_valid = validate_wireless_output( ++ enc, &stream->public.timing); ++ break; ++ default: ++ is_valid = true; ++ break; ++ } ++ ++ return is_valid ? ENCODER_RESULT_OK : ENCODER_RESULT_ERROR; ++} ++ ++/* ++ * get_supported_stream_engines ++ * ++ * @brief ++ * get a list of supported engine ++ * ++ * @param ++ * const struct encoder_impl *enc - not used. ++ * ++ * @return ++ * list of engines with supported ones enabled. ++ */ ++union supported_stream_engines dce110_get_supported_stream_engines( ++ const struct link_encoder *enc) ++{ ++ union supported_stream_engines result = {.u_all = 0}; ++ ++ result.engine.ENGINE_ID_DIGA = 1; ++ result.engine.ENGINE_ID_DIGB = 1; ++ result.engine.ENGINE_ID_DIGC = 1; ++ ++ if (enc->connector.id == CONNECTOR_ID_EDP /*|| wireless*/) ++ result.u_all = (1 << enc->preferred_engine); ++ ++ return result; ++} ++ ++void dce110_link_encoder_set_lcd_backlight_level( ++ struct link_encoder *enc, ++ uint32_t level) ++{ ++ struct dc_context *ctx = enc->ctx; ++ ++ const uint32_t backlight_update_pending_max_retry = 1000; ++ ++ uint32_t backlight; ++ uint32_t backlight_period; ++ uint32_t backlight_lock; ++ ++ uint32_t i; ++ uint32_t backlight_24bit; ++ uint32_t backlight_17bit; ++ uint32_t backlight_16bit; ++ uint32_t masked_pwm_period; ++ uint8_t rounding_bit; ++ uint8_t bit_count; ++ uint64_t active_duty_cycle; ++ ++ backlight = dal_read_reg(ctx, mmBL_PWM_CNTL); ++ backlight_period = dal_read_reg(ctx, mmBL_PWM_PERIOD_CNTL); ++ backlight_lock = dal_read_reg(ctx, mmBL_PWM_GRP1_REG_LOCK); ++ ++ /* ++ * 1. Convert 8-bit value to 17 bit U1.16 format ++ * (1 integer, 16 fractional bits) ++ */ ++ ++ /* 1.1 multiply 8 bit value by 0x10101 to get a 24 bit value, ++ * effectively multiplying value by 256/255 ++ * eg. for a level of 0xEF, backlight_24bit = 0xEF * 0x10101 = 0xEFEFEF ++ */ ++ backlight_24bit = level * 0x10101; ++ ++ /* 1.2 The upper 16 bits of the 24 bit value is the fraction, lower 8 ++ * used for rounding, take most significant bit of fraction for ++ * rounding, e.g. for 0xEFEFEF, rounding bit is 1 ++ */ ++ rounding_bit = (backlight_24bit >> 7) & 1; ++ ++ /* 1.3 Add the upper 16 bits of the 24 bit value with the rounding bit ++ * resulting in a 17 bit value e.g. 0xEFF0 = (0xEFEFEF >> 8) + 1 ++ */ ++ backlight_17bit = (backlight_24bit >> 8) + rounding_bit; ++ ++ /* ++ * 2. Find 16 bit backlight active duty cycle, where 0 <= backlight ++ * active duty cycle <= backlight period ++ */ ++ ++ /* 2.1 Apply bitmask for backlight period value based on value of BITCNT ++ */ ++ { ++ uint32_t pwm_period_bitcnt = get_reg_field_value( ++ backlight_period, ++ BL_PWM_PERIOD_CNTL, ++ BL_PWM_PERIOD_BITCNT); ++ if (pwm_period_bitcnt == 0) ++ bit_count = 16; ++ else ++ bit_count = pwm_period_bitcnt; ++ } ++ ++ /* e.g. maskedPwmPeriod = 0x24 when bitCount is 6 */ ++ masked_pwm_period = ++ get_reg_field_value( ++ backlight_period, ++ BL_PWM_PERIOD_CNTL, ++ BL_PWM_PERIOD) & ((1 << bit_count) - 1); ++ ++ /* 2.2 Calculate integer active duty cycle required upper 16 bits ++ * contain integer component, lower 16 bits contain fractional component ++ * of active duty cycle e.g. 0x21BDC0 = 0xEFF0 * 0x24 ++ */ ++ active_duty_cycle = backlight_17bit * masked_pwm_period; ++ ++ /* 2.3 Calculate 16 bit active duty cycle from integer and fractional ++ * components shift by bitCount then mask 16 bits and add rounding bit ++ * from MSB of fraction e.g. 0x86F7 = ((0x21BDC0 >> 6) & 0xFFF) + 0 ++ */ ++ backlight_16bit = active_duty_cycle >> bit_count; ++ backlight_16bit &= 0xFFFF; ++ backlight_16bit += (active_duty_cycle >> (bit_count - 1)) & 0x1; ++ set_reg_field_value( ++ backlight, ++ backlight_16bit, ++ BL_PWM_CNTL, ++ BL_ACTIVE_INT_FRAC_CNT); ++ ++ /* ++ * 3. Program register with updated value ++ */ ++ ++ /* 3.1 Lock group 2 backlight registers */ ++ set_reg_field_value( ++ backlight_lock, ++ 1, ++ BL_PWM_GRP1_REG_LOCK, ++ BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN); ++ set_reg_field_value( ++ backlight_lock, ++ 1, ++ BL_PWM_GRP1_REG_LOCK, ++ BL_PWM_GRP1_REG_LOCK); ++ dal_write_reg(ctx, mmBL_PWM_GRP1_REG_LOCK, backlight_lock); ++ ++ /* 3.2 Write new active duty cycle */ ++ dal_write_reg(ctx, mmBL_PWM_CNTL, backlight); ++ ++ /* 3.3 Unlock group 2 backlight registers */ ++ set_reg_field_value( ++ backlight_lock, ++ 0, ++ BL_PWM_GRP1_REG_LOCK, ++ BL_PWM_GRP1_REG_LOCK); ++ dal_write_reg(ctx, mmBL_PWM_GRP1_REG_LOCK, backlight_lock); ++ ++ /* 5.4.4 Wait for pending bit to be cleared */ ++ for (i = 0; i < backlight_update_pending_max_retry; ++i) { ++ backlight_lock = dal_read_reg(ctx, mmBL_PWM_GRP1_REG_LOCK); ++ if (!get_reg_field_value( ++ backlight_lock, ++ BL_PWM_GRP1_REG_LOCK, ++ BL_PWM_GRP1_REG_UPDATE_PENDING)) ++ break; ++ ++ dc_service_delay_in_microseconds(ctx, 10); ++ } ++} ++ ++/*TODO: move to correct dce specific file*/ ++/** ++* set_afmt_memory_power_state ++* ++* @brief ++* Power up audio formatter memory that is mapped to specified DIG ++*/ ++void dce110_set_afmt_memory_power_state( ++ const struct dc_context *ctx, ++ enum engine_id id, ++ bool enable) ++{ ++ uint32_t value; ++ uint32_t mem_pwr_force; ++ ++ value = dal_read_reg(ctx, mmDCO_MEM_PWR_CTRL); ++ ++ if (enable) ++ mem_pwr_force = 0; ++ else ++ mem_pwr_force = 3; ++ ++ /* force shutdown mode for appropriate AFMT memory */ ++ switch (id) { ++ case ENGINE_ID_DIGA: ++ set_reg_field_value( ++ value, ++ mem_pwr_force, ++ DCO_MEM_PWR_CTRL, ++ HDMI0_MEM_PWR_FORCE); ++ break; ++ case ENGINE_ID_DIGB: ++ set_reg_field_value( ++ value, ++ mem_pwr_force, ++ DCO_MEM_PWR_CTRL, ++ HDMI1_MEM_PWR_FORCE); ++ break; ++ case ENGINE_ID_DIGC: ++ set_reg_field_value( ++ value, ++ mem_pwr_force, ++ DCO_MEM_PWR_CTRL, ++ HDMI2_MEM_PWR_FORCE); ++ break; ++ default: ++ dal_logger_write( ++ ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_ENCODER, ++ "%s: Invalid Engine Id\n", ++ __func__); ++ break; ++ } ++ ++ dal_write_reg(ctx, mmDCO_MEM_PWR_CTRL, value); ++} ++ ++void dce110_link_encoder_update_mst_stream_allocation_table( ++ struct link_encoder *enc, ++ const struct dp_mst_stream_allocation_table *table, ++ bool is_removal) ++{ ++ int32_t addr_offset = enc->be_engine_offset; ++ uint32_t value0; ++ uint32_t value1; ++ uint32_t retries = 0; ++ ++ /* For CZ, there are only 3 pipes. So Virtual channel is up 3.*/ ++ ++ /* --- Set MSE Stream Attribute - ++ * Setup VC Payload Table on Tx Side, ++ * Issue allocation change trigger ++ * to commit payload on both tx and rx side */ ++ ++ value0 = dal_read_reg(enc->ctx, mmDP_MSE_SAT0 + addr_offset); ++ value1 = dal_read_reg(enc->ctx, mmDP_MSE_SAT1 + addr_offset); ++ ++ if (table->stream_count >= 1) { ++ set_reg_field_value( ++ value0, ++ table->stream_allocations[0].engine, ++ DP_MSE_SAT0, ++ DP_MSE_SAT_SRC0); ++ ++ set_reg_field_value( ++ value0, ++ table->stream_allocations[0].slot_count, ++ DP_MSE_SAT0, ++ DP_MSE_SAT_SLOT_COUNT0); ++ } ++ ++ if (table->stream_count >= 2) { ++ set_reg_field_value( ++ value0, ++ table->stream_allocations[1].engine, ++ DP_MSE_SAT0, ++ DP_MSE_SAT_SRC1); ++ ++ set_reg_field_value( ++ value0, ++ table->stream_allocations[1].slot_count, ++ DP_MSE_SAT0, ++ DP_MSE_SAT_SLOT_COUNT1); ++ } ++ ++ if (table->stream_count >= 3) { ++ set_reg_field_value( ++ value1, ++ table->stream_allocations[2].engine, ++ DP_MSE_SAT1, ++ DP_MSE_SAT_SRC2); ++ ++ set_reg_field_value( ++ value1, ++ table->stream_allocations[2].slot_count, ++ DP_MSE_SAT1, ++ DP_MSE_SAT_SLOT_COUNT2); ++ } ++ ++ /* update ASIC MSE stream allocation table */ ++ dal_write_reg(enc->ctx, mmDP_MSE_SAT0 + addr_offset, value0); ++ dal_write_reg(enc->ctx, mmDP_MSE_SAT1 + addr_offset, value1); ++ ++ /* --- wait for transaction finish */ ++ ++ /* send allocation change trigger (ACT) ? ++ * this step first sends the ACT, ++ * then double buffers the SAT into the hardware ++ * making the new allocation active on the DP MST mode link */ ++ ++ value0 = dal_read_reg(enc->ctx, mmDP_MSE_SAT_UPDATE + addr_offset); ++ ++ /* DP_MSE_SAT_UPDATE: ++ * 0 - No Action ++ * 1 - Update SAT with trigger ++ * 2 - Update SAT without trigger */ ++ ++ set_reg_field_value( ++ value0, ++ 1, ++ DP_MSE_SAT_UPDATE, ++ DP_MSE_SAT_UPDATE); ++ ++ dal_write_reg(enc->ctx, mmDP_MSE_SAT_UPDATE + addr_offset, value0); ++ ++ /* wait for update to complete ++ * (i.e. DP_MSE_SAT_UPDATE field is reset to 0) ++ * then wait for the transmission ++ * of at least 16 MTP headers on immediate local link. ++ * i.e. DP_MSE_16_MTP_KEEPOUT field (read only) is reset to 0 ++ * a value of 1 indicates that DP MST mode ++ * is in the 16 MTP keepout region after a VC has been added. ++ * MST stream bandwidth (VC rate) can be configured ++ * after this bit is cleared */ ++ ++ do { ++ dc_service_delay_in_microseconds(enc->ctx, 10); ++ ++ value0 = dal_read_reg(enc->ctx, ++ mmDP_MSE_SAT_UPDATE + addr_offset); ++ ++ value1 = get_reg_field_value( ++ value0, ++ DP_MSE_SAT_UPDATE, ++ DP_MSE_16_MTP_KEEPOUT); ++ ++ /* bit field DP_MSE_SAT_UPDATE is set to 1 already */ ++ if (value1) ++ break; ++ ++retries; ++ } while (retries < DP_MST_UPDATE_MAX_RETRY); ++ ++ /* TODO should not need. clean this after light up ++ * if (is_removal) ++ * dal_write_reg(enc->ctx, addr, value); ++ */ ++} ++ ++void dce110_link_encoder_set_mst_bandwidth( ++ struct link_encoder *enc, ++ enum engine_id engine, ++ struct fixed31_32 avg_time_slots_per_mtp) ++{ ++ uint32_t x = dal_fixed31_32_floor( ++ avg_time_slots_per_mtp); ++ ++ uint32_t y = dal_fixed31_32_ceil( ++ dal_fixed31_32_shl( ++ dal_fixed31_32_sub_int( ++ avg_time_slots_per_mtp, ++ x), ++ 26)); ++ ++ { ++ const uint32_t addr = mmDP_MSE_RATE_CNTL + ++ fe_engine_offsets[engine]; ++ uint32_t value = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ x, ++ DP_MSE_RATE_CNTL, ++ DP_MSE_RATE_X); ++ ++ set_reg_field_value( ++ value, ++ y, ++ DP_MSE_RATE_CNTL, ++ DP_MSE_RATE_Y); ++ ++ dal_write_reg(enc->ctx, addr, value); ++ } ++ ++ /* wait for update to be completed on the link ++ * i.e. DP_MSE_RATE_UPDATE_PENDING field (read only) ++ * is reset to 0 (not pending) */ ++ { ++ const uint32_t addr = mmDP_MSE_RATE_UPDATE + ++ fe_engine_offsets[engine]; ++ uint32_t value, field; ++ uint32_t retries = 0; ++ ++ do { ++ value = dal_read_reg(enc->ctx, addr); ++ ++ field = get_reg_field_value( ++ value, ++ DP_MSE_RATE_UPDATE, ++ DP_MSE_RATE_UPDATE_PENDING); ++ ++ if (!(field & ++ DP_MSE_RATE_UPDATE__DP_MSE_RATE_UPDATE_PENDING_MASK)) ++ break; ++ ++ dc_service_delay_in_microseconds(enc->ctx, 10); ++ ++ ++retries; ++ } while (retries < DP_MST_UPDATE_MAX_RETRY); ++ } ++} ++ ++void dce110_link_encoder_connect_dig_be_to_fe( ++ struct link_encoder *enc, ++ enum engine_id engine, ++ bool connect) ++{ ++ uint32_t addr; ++ uint32_t value; ++ uint32_t field; ++ ++ if (engine != ENGINE_ID_UNKNOWN) { ++ addr = mmDIG_BE_CNTL + enc->be_engine_offset; ++ value = dal_read_reg(enc->ctx, addr); ++ ++ field = get_reg_field_value( ++ value, ++ DIG_BE_CNTL, ++ DIG_FE_SOURCE_SELECT); ++ ++ if (connect) ++ field |= get_frontend_source(engine); ++ else ++ field &= ~get_frontend_source(engine); ++ ++ set_reg_field_value( ++ value, ++ field, ++ DIG_BE_CNTL, ++ DIG_FE_SOURCE_SELECT); ++ dal_write_reg(enc->ctx, addr, value); ++ } ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h +new file mode 100644 +index 0000000..4331bf0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h +@@ -0,0 +1,91 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_LINK_ENCODER__DCE110_H__ ++#define __DC_LINK_ENCODER__DCE110_H__ ++ ++struct link_encoder *dce110_link_encoder_create( ++ const struct encoder_init_data *init); ++void dce110_link_encoder_destroy(struct link_encoder **enc); ++ ++void dce110_link_encoder_set_dp_phy_pattern( ++ struct link_encoder *enc, ++ const struct encoder_set_dp_phy_pattern_param *param); ++ ++enum encoder_result dce110_link_encoder_power_up(struct link_encoder *enc); ++ ++enum encoder_result dce110_link_encoder_dp_set_lane_settings( ++ struct link_encoder *enc, ++ const struct link_training_settings *link_settings); ++ ++union supported_stream_engines dce110_get_supported_stream_engines( ++ const struct link_encoder *enc); ++ ++enum encoder_result dce110_link_encoder_validate_output_with_stream( ++ struct link_encoder *enc, ++ const struct core_stream *stream); ++ ++void dce110_link_encoder_set_lcd_backlight_level( ++ struct link_encoder *enc, ++ uint32_t level); ++ ++void dce110_link_encoder_setup( ++ struct link_encoder *enc, ++ enum signal_type signal); ++ ++enum encoder_result dce110_link_encoder_enable_output( ++ struct link_encoder *enc, ++ const struct link_settings *link_settings, ++ enum engine_id engine, ++ enum clock_source_id clock_source, ++ enum signal_type signal, ++ enum dc_color_depth color_depth, ++ uint32_t pixel_clock); ++ ++enum encoder_result dce110_link_encoder_disable_output( ++ struct link_encoder *link_enc, ++ enum signal_type signal); ++ ++void dce110_set_afmt_memory_power_state( ++ const struct dc_context *ctx, ++ enum engine_id id, ++ bool enable); ++ ++void dce110_link_encoder_update_mst_stream_allocation_table( ++ struct link_encoder *enc, ++ const struct dp_mst_stream_allocation_table *table, ++ bool is_removal); ++ ++void dce110_link_encoder_set_mst_bandwidth( ++ struct link_encoder *enc, ++ enum engine_id engine, ++ struct fixed31_32 avg_time_slots_per_mtp); ++ ++void dce110_link_encoder_connect_dig_be_to_fe( ++ struct link_encoder *enc, ++ enum engine_id engine, ++ bool connect); ++ ++#endif /* __DC_LINK_ENCODER__DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c +new file mode 100644 +index 0000000..7391a0a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c +@@ -0,0 +1,969 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++/* TODO: this needs to be looked at, used by Stella's workaround*/ ++#include "gmc/gmc_8_2_d.h" ++#include "gmc/gmc_8_2_sh_mask.h" ++ ++#include "include/logger_interface.h" ++#include "adapter_service_interface.h" ++#include "inc/bandwidth_calcs.h" ++ ++#include "dce110_mem_input.h" ++ ++#define MAX_WATERMARK 0xFFFF ++#define SAFE_NBP_MARK 0x7FFF ++ ++#define DCP_REG(reg) (reg + mem_input110->offsets.dcp) ++#define DMIF_REG(reg) (reg + mem_input110->offsets.dmif) ++#define PIPE_REG(reg) (reg + mem_input110->offsets.pipe) ++ ++static const struct dce110_mem_input_reg_offsets reg_offsets[] = { ++{ ++ .dcp = 0, ++ .dmif = 0, ++ .pipe = 0, ++}, ++{ ++ .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL ++ - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE1_DMIF_BUFFER_CONTROL - mmPIPE0_DMIF_BUFFER_CONTROL), ++}, ++{ ++ .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL ++ - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE2_DMIF_BUFFER_CONTROL - mmPIPE0_DMIF_BUFFER_CONTROL), ++} ++}; ++ ++static void set_flip_control( ++ struct dce110_mem_input *mem_input110, ++ bool immediate) ++{ ++ uint32_t value = 0; ++ ++ value = dal_read_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_FLIP_CONTROL)); ++ set_reg_field_value(value, 0, ++ GRPH_FLIP_CONTROL, ++ GRPH_SURFACE_UPDATE_IMMEDIATE_EN); ++ set_reg_field_value(value, 0, ++ GRPH_FLIP_CONTROL, ++ GRPH_SURFACE_UPDATE_H_RETRACE_EN); ++ if (immediate == true) ++ set_reg_field_value(value, 1, ++ GRPH_FLIP_CONTROL, ++ GRPH_SURFACE_UPDATE_IMMEDIATE_EN); ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_FLIP_CONTROL), ++ value); ++} ++ ++static void program_sec_addr( ++ struct dce110_mem_input *mem_input110, ++ PHYSICAL_ADDRESS_LOC address) ++{ ++ uint32_t value = 0; ++ uint32_t temp = 0; ++ /*high register MUST be programmed first*/ ++ temp = address.high_part & ++GRPH_SECONDARY_SURFACE_ADDRESS_HIGH__GRPH_SECONDARY_SURFACE_ADDRESS_HIGH_MASK; ++ ++ set_reg_field_value(value, temp, ++ GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, ++ GRPH_SECONDARY_SURFACE_ADDRESS_HIGH); ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_SECONDARY_SURFACE_ADDRESS_HIGH), ++ value); ++ ++ temp = 0; ++ value = 0; ++ temp = address.low_part >> ++ GRPH_SECONDARY_SURFACE_ADDRESS__GRPH_SECONDARY_SURFACE_ADDRESS__SHIFT; ++ ++ set_reg_field_value(value, temp, ++ GRPH_SECONDARY_SURFACE_ADDRESS, ++ GRPH_SECONDARY_SURFACE_ADDRESS); ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_SECONDARY_SURFACE_ADDRESS), ++ value); ++} ++ ++static void program_pri_addr( ++ struct dce110_mem_input *mem_input110, ++ PHYSICAL_ADDRESS_LOC address) ++{ ++ uint32_t value = 0; ++ uint32_t temp = 0; ++ ++ /*high register MUST be programmed first*/ ++ temp = address.high_part & ++GRPH_PRIMARY_SURFACE_ADDRESS_HIGH__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_MASK; ++ ++ set_reg_field_value(value, temp, ++ GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, ++ GRPH_PRIMARY_SURFACE_ADDRESS_HIGH); ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_PRIMARY_SURFACE_ADDRESS_HIGH), ++ value); ++ ++ temp = 0; ++ value = 0; ++ temp = address.low_part >> ++ GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS__SHIFT; ++ ++ set_reg_field_value(value, temp, ++ GRPH_PRIMARY_SURFACE_ADDRESS, ++ GRPH_PRIMARY_SURFACE_ADDRESS); ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_PRIMARY_SURFACE_ADDRESS), ++ value); ++} ++ ++static void program_addr( ++ struct dce110_mem_input *mem_input110, ++ const struct dc_plane_address *addr) ++{ ++ switch (addr->type) { ++ case PLN_ADDR_TYPE_GRAPHICS: ++ program_pri_addr( ++ mem_input110, ++ addr->grph.addr); ++ break; ++ case PLN_ADDR_TYPE_GRPH_STEREO: ++ program_pri_addr( ++ mem_input110, ++ addr->grph_stereo.left_addr); ++ program_sec_addr( ++ mem_input110, ++ addr->grph_stereo.right_addr); ++ break; ++ case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: ++ case PLN_ADDR_TYPE_VIDEO_INTERLACED: ++ case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE_STEREO: ++ case PLN_ADDR_TYPE_VIDEO_INTERLACED_STEREO: ++ default: ++ /* not supported */ ++ BREAK_TO_DEBUGGER(); ++ } ++} ++ ++static void enable(struct dce110_mem_input *mem_input110) ++{ ++ uint32_t value = 0; ++ ++ value = dal_read_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_ENABLE)); ++ set_reg_field_value(value, 1, GRPH_ENABLE, GRPH_ENABLE); ++ dal_write_reg(mem_input110->base.ctx, ++ DCP_REG(mmGRPH_ENABLE), ++ value); ++} ++ ++static void program_tiling( ++ struct dce110_mem_input *mem_input110, ++ const union plane_tiling_info *info, ++ const enum surface_pixel_format pixel_format) ++{ ++ uint32_t value = 0; ++ ++ value = dal_read_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_CONTROL)); ++ ++ set_reg_field_value(value, info->grph.NUM_BANKS, ++ GRPH_CONTROL, GRPH_NUM_BANKS); ++ ++ set_reg_field_value(value, info->grph.BANK_WIDTH, ++ GRPH_CONTROL, GRPH_BANK_WIDTH); ++ ++ set_reg_field_value(value, info->grph.BANK_HEIGHT, ++ GRPH_CONTROL, GRPH_BANK_HEIGHT); ++ ++ set_reg_field_value(value, info->grph.TILE_ASPECT, ++ GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT); ++ ++ set_reg_field_value(value, info->grph.TILE_SPLIT, ++ GRPH_CONTROL, GRPH_TILE_SPLIT); ++ ++ set_reg_field_value(value, info->grph.TILE_MODE, ++ GRPH_CONTROL, GRPH_MICRO_TILE_MODE); ++ ++ set_reg_field_value(value, info->grph.PIPE_CONFIG, ++ GRPH_CONTROL, GRPH_PIPE_CONFIG); ++ ++ set_reg_field_value(value, info->grph.ARRAY_MODE, ++ GRPH_CONTROL, GRPH_ARRAY_MODE); ++ ++ set_reg_field_value(value, 1, ++ GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE); ++ ++ set_reg_field_value(value, 0, ++ GRPH_CONTROL, GRPH_Z); ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_CONTROL), ++ value); ++} ++ ++static void program_size_and_rotation( ++ struct dce110_mem_input *mem_input110, ++ enum dc_rotation_angle rotation, ++ const union plane_size *plane_size) ++{ ++ uint32_t value = 0; ++ union plane_size local_size = *plane_size; ++ ++ if (rotation == ROTATION_ANGLE_90 || ++ rotation == ROTATION_ANGLE_270) { ++ ++ uint32_t swap; ++ ++ swap = local_size.grph.surface_size.x; ++ local_size.grph.surface_size.x = ++ local_size.grph.surface_size.y; ++ local_size.grph.surface_size.y = swap; ++ ++ swap = local_size.grph.surface_size.width; ++ local_size.grph.surface_size.width = ++ local_size.grph.surface_size.height; ++ local_size.grph.surface_size.height = swap; ++ } ++ ++ set_reg_field_value(value, local_size.grph.surface_size.x, ++ GRPH_X_START, GRPH_X_START); ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_X_START), ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, local_size.grph.surface_size.y, ++ GRPH_Y_START, GRPH_Y_START); ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_Y_START), ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, local_size.grph.surface_size.width, ++ GRPH_X_END, GRPH_X_END); ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_X_END), ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, local_size.grph.surface_size.height, ++ GRPH_Y_END, GRPH_Y_END); ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_Y_END), ++ value); ++ ++ value = 0; ++ set_reg_field_value(value, local_size.grph.surface_pitch, ++ GRPH_PITCH, GRPH_PITCH); ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_PITCH), ++ value); ++ ++ ++ value = 0; ++ switch (rotation) { ++ case ROTATION_ANGLE_90: ++ set_reg_field_value(value, 3, ++ HW_ROTATION, GRPH_ROTATION_ANGLE); ++ break; ++ case ROTATION_ANGLE_180: ++ set_reg_field_value(value, 2, ++ HW_ROTATION, GRPH_ROTATION_ANGLE); ++ break; ++ case ROTATION_ANGLE_270: ++ set_reg_field_value(value, 1, ++ HW_ROTATION, GRPH_ROTATION_ANGLE); ++ break; ++ default: ++ set_reg_field_value(value, 0, ++ HW_ROTATION, GRPH_ROTATION_ANGLE); ++ break; ++ } ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmHW_ROTATION), ++ value); ++} ++ ++static void program_pixel_format( ++ struct dce110_mem_input *mem_input110, ++ enum surface_pixel_format format) ++{ ++ if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN && ++ format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { ++ uint32_t value = 0; ++ ++ /* handle colour twizzle formats, swapping R and B */ ++ if (format == SURFACE_PIXEL_FORMAT_GRPH_BGRA8888 || ++ format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010 || ++ format == ++ SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS || ++ format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { ++ set_reg_field_value( ++ value, 2, GRPH_SWAP_CNTL, GRPH_RED_CROSSBAR); ++ set_reg_field_value( ++ value, 2, GRPH_SWAP_CNTL, GRPH_BLUE_CROSSBAR); ++ } ++ ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_SWAP_CNTL), ++ value); ++ ++ ++ value = dal_read_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_CONTROL)); ++ ++ switch (format) { ++ case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: ++ set_reg_field_value( ++ value, 0, GRPH_CONTROL, GRPH_DEPTH); ++ set_reg_field_value( ++ value, 0, GRPH_CONTROL, GRPH_FORMAT); ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_RGB565: ++ set_reg_field_value( ++ value, 1, GRPH_CONTROL, GRPH_DEPTH); ++ set_reg_field_value( ++ value, 1, GRPH_CONTROL, GRPH_FORMAT); ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: ++ case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: ++ set_reg_field_value( ++ value, 2, GRPH_CONTROL, GRPH_DEPTH); ++ set_reg_field_value( ++ value, 0, GRPH_CONTROL, GRPH_FORMAT); ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: ++ case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: ++ case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: ++ set_reg_field_value( ++ value, 2, GRPH_CONTROL, GRPH_DEPTH); ++ set_reg_field_value( ++ value, 1, GRPH_CONTROL, GRPH_FORMAT); ++ break; ++ case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: ++ case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: ++ set_reg_field_value( ++ value, 3, GRPH_CONTROL, GRPH_DEPTH); ++ set_reg_field_value( ++ value, 0, GRPH_CONTROL, GRPH_FORMAT); ++ break; ++ default: ++ break; ++ } ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmGRPH_CONTROL), ++ value); ++ ++ /*TODO [hwentlan] MOVE THIS TO CONTROLLER GAMMA!!!!!*/ ++ value = dal_read_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmPRESCALE_GRPH_CONTROL)); ++ ++ if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { ++ set_reg_field_value( ++ value, 1, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_SELECT); ++ set_reg_field_value( ++ value, 1, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_R_SIGN); ++ set_reg_field_value( ++ value, 1, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_G_SIGN); ++ set_reg_field_value( ++ value, 1, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_B_SIGN); ++ } else { ++ set_reg_field_value( ++ value, 0, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_SELECT); ++ set_reg_field_value( ++ value, 0, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_R_SIGN); ++ set_reg_field_value( ++ value, 0, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_G_SIGN); ++ set_reg_field_value( ++ value, 0, PRESCALE_GRPH_CONTROL, ++ GRPH_PRESCALE_B_SIGN); ++ } ++ dal_write_reg( ++ mem_input110->base.ctx, ++ DCP_REG(mmPRESCALE_GRPH_CONTROL), ++ value); ++ } ++} ++ ++bool dce110_mem_input_program_surface_flip_and_addr( ++ struct mem_input *mem_input, ++ const struct dc_plane_address *address, ++ bool flip_immediate) ++{ ++ struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); ++ ++ set_flip_control(mem_input110, flip_immediate); ++ program_addr(mem_input110, ++ address); ++ ++ return true; ++} ++ ++bool dce110_mem_input_program_surface_config( ++ struct mem_input *mem_input, ++ const struct dc_surface *surface) ++{ ++ struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); ++ ++ enable(mem_input110); ++ program_tiling(mem_input110, &surface->tiling_info, surface->format); ++ program_size_and_rotation(mem_input110, ++ surface->rotation, &surface->plane_size); ++ program_pixel_format(mem_input110, surface->format); ++ ++ return true; ++} ++ ++static void program_urgency_watermark( ++ const struct dc_context *ctx, ++ const uint32_t offset, ++ struct bw_watermarks marks_low, ++ uint32_t total_dest_line_time_ns) ++{ ++ /* register value */ ++ uint32_t urgency_cntl = 0; ++ uint32_t wm_mask_cntl = 0; ++ ++ uint32_t urgency_addr = offset + mmDPG_PIPE_URGENCY_CONTROL; ++ uint32_t wm_addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ ++ /*Write mask to enable reading/writing of watermark set A*/ ++ wm_mask_cntl = dal_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 1, ++ DPG_WATERMARK_MASK_CONTROL, ++ URGENCY_WATERMARK_MASK); ++ dal_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ urgency_cntl = dal_read_reg(ctx, urgency_addr); ++ ++ set_reg_field_value( ++ urgency_cntl, ++ marks_low.a_mark, ++ DPG_PIPE_URGENCY_CONTROL, ++ URGENCY_LOW_WATERMARK); ++ ++ set_reg_field_value( ++ urgency_cntl, ++ total_dest_line_time_ns, ++ DPG_PIPE_URGENCY_CONTROL, ++ URGENCY_HIGH_WATERMARK); ++ dal_write_reg(ctx, urgency_addr, urgency_cntl); ++ ++ ++ /*Write mask to enable reading/writing of watermark set B*/ ++ wm_mask_cntl = dal_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 2, ++ DPG_WATERMARK_MASK_CONTROL, ++ URGENCY_WATERMARK_MASK); ++ dal_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ urgency_cntl = dal_read_reg(ctx, urgency_addr); ++ ++ set_reg_field_value(urgency_cntl, ++ marks_low.b_mark, ++ DPG_PIPE_URGENCY_CONTROL, ++ URGENCY_LOW_WATERMARK); ++ ++ set_reg_field_value(urgency_cntl, ++ total_dest_line_time_ns, ++ DPG_PIPE_URGENCY_CONTROL, ++ URGENCY_HIGH_WATERMARK); ++ dal_write_reg(ctx, urgency_addr, urgency_cntl); ++} ++ ++void dce110_program_stutter_watermark( ++ struct mem_input *mi, ++ struct bw_watermarks marks) ++{ ++ const struct dc_context *ctx = mi->ctx; ++ const uint32_t offset = TO_DCE110_MEM_INPUT(mi)->offsets.dmif; ++ /* register value */ ++ uint32_t stutter_cntl = 0; ++ uint32_t wm_mask_cntl = 0; ++ ++ uint32_t stutter_addr = offset + mmDPG_PIPE_STUTTER_CONTROL; ++ uint32_t wm_addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ ++ /*Write mask to enable reading/writing of watermark set A*/ ++ ++ wm_mask_cntl = dal_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 1, ++ DPG_WATERMARK_MASK_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); ++ dal_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ stutter_cntl = dal_read_reg(ctx, stutter_addr); ++ ++ set_reg_field_value(stutter_cntl, ++ 1, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_ENABLE); ++ set_reg_field_value(stutter_cntl, ++ 1, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_IGNORE_FBC); ++ ++ /*Write watermark set A*/ ++ set_reg_field_value(stutter_cntl, ++ marks.a_mark, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK); ++ dal_write_reg(ctx, stutter_addr, stutter_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set B*/ ++ wm_mask_cntl = dal_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 2, ++ DPG_WATERMARK_MASK_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); ++ dal_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ stutter_cntl = dal_read_reg(ctx, stutter_addr); ++ set_reg_field_value(stutter_cntl, ++ 1, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_ENABLE); ++ set_reg_field_value(stutter_cntl, ++ 1, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_IGNORE_FBC); ++ ++ /*Write watermark set B*/ ++ set_reg_field_value(stutter_cntl, ++ marks.b_mark, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK); ++ dal_write_reg(ctx, stutter_addr, stutter_cntl); ++} ++ ++void dce110_program_nbp_watermark( ++ struct mem_input *mi, ++ struct bw_watermarks marks) ++{ ++ const struct dc_context *ctx = mi->ctx; ++ const uint32_t offset = TO_DCE110_MEM_INPUT(mi)->offsets.dmif; ++ uint32_t value; ++ uint32_t addr; ++ /* Write mask to enable reading/writing of watermark set A */ ++ addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_WATERMARK_MASK_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK_MASK); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_ENABLE); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); ++ dal_write_reg(ctx, addr, value); ++ ++ /* Write watermark set A */ ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ marks.a_mark, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK); ++ dal_write_reg(ctx, addr, value); ++ ++ /* Write mask to enable reading/writing of watermark set B */ ++ addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 2, ++ DPG_WATERMARK_MASK_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK_MASK); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_ENABLE); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); ++ dal_write_reg(ctx, addr, value); ++ ++ /* Write watermark set B */ ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ marks.b_mark, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK); ++ dal_write_reg(ctx, addr, value); ++} ++ ++void dce110_program_safe_display_marks(struct mem_input *mi) ++{ ++ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mi); ++ struct bw_watermarks max_marks = { MAX_WATERMARK, MAX_WATERMARK }; ++ struct bw_watermarks nbp_marks = { SAFE_NBP_MARK, SAFE_NBP_MARK }; ++ ++ program_urgency_watermark( ++ mi->ctx, bm_dce110->offsets.dmif, max_marks, MAX_WATERMARK); ++ dce110_program_stutter_watermark(mi, max_marks); ++ dce110_program_nbp_watermark(mi, nbp_marks); ++ ++} ++ ++void dce110_program_urgency_watermark( ++ struct mem_input *mi, ++ struct bw_watermarks marks, ++ uint32_t h_total, ++ uint32_t pixel_clk_in_khz, ++ uint32_t pstate_blackout_duration_ns) ++{ ++ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mi); ++ uint32_t total_dest_line_time_ns = 1000000ULL * h_total ++ / pixel_clk_in_khz + pstate_blackout_duration_ns; ++ ++ program_urgency_watermark( ++ mi->ctx, ++ bm_dce110->offsets.dmif, ++ marks, ++ total_dest_line_time_ns); ++} ++ ++static uint32_t get_dmif_switch_time_us(struct dc_crtc_timing *timing) ++{ ++ uint32_t frame_time; ++ uint32_t pixels_per_second; ++ uint32_t pixels_per_frame; ++ uint32_t refresh_rate; ++ const uint32_t us_in_sec = 1000000; ++ const uint32_t min_single_frame_time_us = 30000; ++ /*return double of frame time*/ ++ const uint32_t single_frame_time_multiplier = 2; ++ ++ if (timing == NULL) ++ return single_frame_time_multiplier * min_single_frame_time_us; ++ ++ /*TODO: should we use pixel format normalized pixel clock here?*/ ++ pixels_per_second = timing->pix_clk_khz * 1000; ++ pixels_per_frame = timing->h_total * timing->v_total; ++ ++ if (!pixels_per_second || !pixels_per_frame) { ++ /* avoid division by zero */ ++ ASSERT(pixels_per_frame); ++ ASSERT(pixels_per_second); ++ return single_frame_time_multiplier * min_single_frame_time_us; ++ } ++ ++ refresh_rate = pixels_per_second / pixels_per_frame; ++ ++ if (!refresh_rate) { ++ /* avoid division by zero*/ ++ ASSERT(refresh_rate); ++ return single_frame_time_multiplier * min_single_frame_time_us; ++ } ++ ++ frame_time = us_in_sec / refresh_rate; ++ ++ if (frame_time < min_single_frame_time_us) ++ frame_time = min_single_frame_time_us; ++ ++ frame_time *= single_frame_time_multiplier; ++ ++ return frame_time; ++} ++ ++void dce110_allocate_dmif_buffer( ++ struct mem_input *mi, ++ struct dc_crtc_timing *timing, ++ uint32_t paths_num) ++{ ++ const uint32_t retry_delay = 10; ++ uint32_t retry_count = get_dmif_switch_time_us(timing) / retry_delay; ++ ++ struct dce110_mem_input *bm110 = TO_DCE110_MEM_INPUT(mi); ++ uint32_t addr = bm110->offsets.pipe + mmPIPE0_DMIF_BUFFER_CONTROL; ++ uint32_t value; ++ uint32_t field; ++ ++ if (bm110->supported_stutter_mode ++ & STUTTER_MODE_NO_DMIF_BUFFER_ALLOCATION) ++ goto register_underflow_int; ++ ++ /*Allocate DMIF buffer*/ ++ value = dal_read_reg(mi->ctx, addr); ++ field = get_reg_field_value( ++ value, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED); ++ if (field == 2) ++ goto register_underflow_int; ++ ++ set_reg_field_value( ++ value, ++ 2, ++ PIPE0_DMIF_BUFFER_CONTROL, ++ DMIF_BUFFERS_ALLOCATED); ++ ++ dal_write_reg(mi->ctx, addr, value); ++ ++ do { ++ value = dal_read_reg(mi->ctx, addr); ++ field = get_reg_field_value( ++ value, ++ PIPE0_DMIF_BUFFER_CONTROL, ++ DMIF_BUFFERS_ALLOCATION_COMPLETED); ++ ++ if (field) ++ break; ++ ++ dc_service_delay_in_microseconds(mi->ctx, retry_delay); ++ retry_count--; ++ ++ } while (retry_count > 0); ++ ++ if (field == 0) ++ dal_logger_write(mi->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: DMIF allocation failed", ++ __func__); ++ ++ /* ++ * Stella Wong proposed the following change ++ * ++ * Value of mcHubRdReqDmifLimit.ENABLE: ++ * 00 - disable DMIF rdreq limit ++ * 01 - enable DMIF rdreq limit, disabled by DMIF stall = 1 || urg != 0 ++ * 02 - enable DMIF rdreq limit, disable by DMIF stall = 1 ++ * 03 - force enable DMIF rdreq limit, ignore DMIF stall / urgent ++ */ ++ addr = mmMC_HUB_RDREQ_DMIF_LIMIT; ++ value = dal_read_reg(mi->ctx, addr); ++ if (paths_num > 1) ++ set_reg_field_value(value, 0, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE); ++ else ++ set_reg_field_value(value, 3, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE); ++ dal_write_reg(mi->ctx, addr, value); ++ ++register_underflow_int: ++ /*todo*/; ++ /*register_interrupt(bm110, irq_source, ctrl_id);*/ ++} ++ ++static void deallocate_dmif_buffer_helper( ++ struct dc_context *ctx, uint32_t offset) ++{ ++ uint32_t value; ++ uint32_t count = 0xBB8; /* max retry count */ ++ ++ value = dal_read_reg(ctx, mmPIPE0_DMIF_BUFFER_CONTROL + offset); ++ ++ if (!get_reg_field_value( ++ value, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED)) ++ return; ++ ++ set_reg_field_value( ++ value, 0, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED); ++ ++ dal_write_reg( ++ ctx, mmPIPE0_DMIF_BUFFER_CONTROL + offset, value); ++ ++ do { ++ value = dal_read_reg(ctx, mmPIPE0_DMIF_BUFFER_CONTROL + offset); ++ dc_service_delay_in_microseconds(ctx, 10); ++ count--; ++ } while (count > 0 && ++ !get_reg_field_value( ++ value, ++ PIPE0_DMIF_BUFFER_CONTROL, ++ DMIF_BUFFERS_ALLOCATION_COMPLETED)); ++} ++ ++void dce110_deallocate_dmif_buffer(struct mem_input *mi, uint32_t paths_num) ++{ ++ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mi); ++ uint32_t value; ++ ++ if (!(bm_dce110->supported_stutter_mode & ++ STUTTER_MODE_NO_DMIF_BUFFER_ALLOCATION)) { ++ ++ /* De-allocate DMIF buffer first */ ++ if (mmPIPE0_DMIF_BUFFER_CONTROL + bm_dce110->offsets.pipe != 0) ++ deallocate_dmif_buffer_helper( ++ mi->ctx, bm_dce110->offsets.pipe); ++ } ++ ++ /* TODO: unregister underflow interrupt ++ unregisterInterrupt(); ++ */ ++ ++ /* Value of mcHubRdReqDmifLimit.ENABLE. ++ * 00 - disable dmif rdreq limit ++ * 01 - enable dmif rdreq limit, disable by dmif stall=1||urg!=0 ++ * 02 - enable dmif rdreq limit, disable by dmif stall=1 ++ * 03 - force enable dmif rdreq limit, ignore dmif stall/urgent ++ * Stella Wong proposed this change. */ ++ value = dal_read_reg(mi->ctx, mmMC_HUB_RDREQ_DMIF_LIMIT); ++ if (paths_num > 1) ++ set_reg_field_value(value, 0, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE); ++ else ++ set_reg_field_value(value, 3, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE); ++ ++ dal_write_reg(mi->ctx, mmMC_HUB_RDREQ_DMIF_LIMIT, value); ++} ++ ++void dce110_program_pix_dur(struct mem_input *mi, uint32_t pix_clk_khz) ++{ ++ uint64_t pix_dur; ++ uint32_t addr = mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL1 ++ + TO_DCE110_MEM_INPUT(mi)->offsets.dmif; ++ uint32_t value = dal_read_reg(mi->ctx, addr); ++ ++ if (pix_clk_khz == 0) ++ return; ++ ++ pix_dur = 1000000000 / pix_clk_khz; ++ ++ set_reg_field_value( ++ value, ++ pix_dur, ++ DPG_PIPE_ARBITRATION_CONTROL1, ++ PIXEL_DURATION); ++ ++ dal_write_reg(mi->ctx, addr, value); ++} ++ ++/*****************************************/ ++/* Constructor, Destructor */ ++/*****************************************/ ++ ++bool dce110_mem_input_construct( ++ struct dce110_mem_input *mem_input110, ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ if ((inst < 1) || (inst > ARRAY_SIZE(reg_offsets))) ++ return false; ++ ++ mem_input110->base.ctx = ctx; ++ ++ mem_input110->base.inst = inst; ++ ++ mem_input110->offsets = reg_offsets[inst - 1]; ++ ++ mem_input110->supported_stutter_mode = 0; ++ dal_adapter_service_get_feature_value(FEATURE_STUTTER_MODE, ++ &(mem_input110->supported_stutter_mode), ++ sizeof(mem_input110->supported_stutter_mode)); ++ ++ return true; ++} ++ ++void dce110_mem_input_destroy(struct mem_input **mem_input) ++{ ++ dc_service_free((*mem_input)->ctx, TO_DCE110_MEM_INPUT(*mem_input)); ++ *mem_input = NULL; ++} ++ ++struct mem_input *dce110_mem_input_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct dce110_mem_input *mem_input110 = ++ dc_service_alloc(ctx, sizeof(struct dce110_mem_input)); ++ ++ if (!mem_input110) ++ return NULL; ++ ++ if (dce110_mem_input_construct(mem_input110, ++ ctx, inst)) ++ return &mem_input110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, mem_input110); ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h +new file mode 100644 +index 0000000..9c6d278 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h +@@ -0,0 +1,88 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_MEM_INPUT_DCE110_H__ ++#define __DC_MEM_INPUT_DCE110_H__ ++ ++#include "inc/mem_input.h" ++ ++#define TO_DCE110_MEM_INPUT(mi)\ ++ container_of(mi, struct dce110_mem_input, base) ++ ++struct dce110_mem_input_reg_offsets { ++ uint32_t dcp; ++ uint32_t dmif; ++ uint32_t pipe; ++}; ++ ++struct dce110_mem_input { ++ struct mem_input base; ++ struct dce110_mem_input_reg_offsets offsets; ++ uint32_t supported_stutter_mode; ++}; ++ ++struct mem_input *dce110_mem_input_create( ++ struct dc_context *ctx, ++ uint32_t inst); ++ ++void dce110_mem_input_destroy(struct mem_input **mem_input); ++ ++bool dce110_mem_input_program_surface_flip_and_addr( ++ struct mem_input *mem_input, ++ const struct dc_plane_address *address, ++ bool flip_immediate); ++ ++bool dce110_mem_input_program_surface_config( ++ struct mem_input *mem_input, ++ const struct dc_surface *surface); ++ ++void dce110_program_nbp_watermark( ++ struct mem_input *mem_input, ++ struct bw_watermarks marks); ++ ++void dce110_program_stutter_watermark( ++ struct mem_input *mem_input, ++ struct bw_watermarks marks); ++ ++void dce110_program_urgency_watermark( ++ struct mem_input *mem_input, ++ struct bw_watermarks marks, ++ uint32_t h_total, ++ uint32_t pixel_clk_in_khz, ++ uint32_t pstate_blackout_duration_ns); ++ ++void dce110_program_safe_display_marks(struct mem_input *mi); ++ ++void dce110_allocate_dmif_buffer( ++ struct mem_input *mem_input, ++ struct dc_crtc_timing *timing, ++ uint32_t paths_num); ++ ++void dce110_deallocate_dmif_buffer( ++ struct mem_input *mem_input, uint32_t paths_num); ++ ++void dce110_program_pix_dur( ++ struct mem_input *mem_input, uint32_t pix_clk_khz); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c +new file mode 100644 +index 0000000..0fdffac +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c +@@ -0,0 +1,296 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_opp.h" ++ ++#define FROM_OPP(opp)\ ++ container_of(opp, struct dce110_opp, base) ++ ++enum { ++ MAX_LUT_ENTRY = 256, ++ MAX_NUMBER_OF_ENTRIES = 256 ++}; ++ ++static const struct dce110_opp_reg_offsets reg_offsets[] = { ++{ ++ .fmt_offset = (mmFMT0_FMT_CONTROL - mmFMT0_FMT_CONTROL), ++ .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++}, ++{ .fmt_offset = (mmFMT1_FMT_CONTROL - mmFMT0_FMT_CONTROL), ++ .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++}, ++{ .fmt_offset = (mmFMT2_FMT_CONTROL - mmFMT0_FMT_CONTROL), ++ .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++} ++}; ++ ++static void build_evenly_distributed_points( ++ struct gamma_pixel *points, ++ uint32_t numberof_points, ++ struct fixed31_32 max_value, ++ struct fixed31_32 divider1, ++ struct fixed31_32 divider2, ++ struct fixed31_32 divider3) ++{ ++ struct gamma_pixel *p = points; ++ struct gamma_pixel *p_last = p + numberof_points - 1; ++ ++ uint32_t i = 0; ++ ++ do { ++ struct fixed31_32 value = dal_fixed31_32_div_int( ++ dal_fixed31_32_mul_int(max_value, i), ++ numberof_points - 1); ++ ++ p->r = value; ++ p->g = value; ++ p->b = value; ++ ++ ++p; ++ ++i; ++ } while (i != numberof_points); ++ ++ p->r = dal_fixed31_32_div(p_last->r, divider1); ++ p->g = dal_fixed31_32_div(p_last->g, divider1); ++ p->b = dal_fixed31_32_div(p_last->b, divider1); ++ ++ ++p; ++ ++ p->r = dal_fixed31_32_div(p_last->r, divider2); ++ p->g = dal_fixed31_32_div(p_last->g, divider2); ++ p->b = dal_fixed31_32_div(p_last->b, divider2); ++ ++ ++p; ++ ++ p->r = dal_fixed31_32_div(p_last->r, divider3); ++ p->g = dal_fixed31_32_div(p_last->g, divider3); ++ p->b = dal_fixed31_32_div(p_last->b, divider3); ++} ++ ++/*****************************************/ ++/* Constructor, Destructor */ ++/*****************************************/ ++ ++bool dce110_opp_construct(struct dce110_opp *opp110, ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ if ((inst < 1) || (inst > ARRAY_SIZE(reg_offsets))) ++ return false; ++ ++ opp110->base.ctx = ctx; ++ ++ opp110->base.inst = inst; ++ ++ opp110->offsets = reg_offsets[inst - 1]; ++ ++ opp110->regamma.hw_points_num = 128; ++ opp110->regamma.coordinates_x = NULL; ++ opp110->regamma.rgb_resulted = NULL; ++ opp110->regamma.rgb_regamma = NULL; ++ opp110->regamma.coeff128 = NULL; ++ opp110->regamma.coeff128_oem = NULL; ++ opp110->regamma.coeff128_dx = NULL; ++ opp110->regamma.axis_x_256 = NULL; ++ opp110->regamma.axis_x_1025 = NULL; ++ opp110->regamma.rgb_oem = NULL; ++ opp110->regamma.rgb_user = NULL; ++ opp110->regamma.extra_points = 3; ++ opp110->regamma.use_half_points = false; ++ opp110->regamma.x_max1 = dal_fixed31_32_one; ++ opp110->regamma.x_max2 = dal_fixed31_32_from_int(2); ++ opp110->regamma.x_min = dal_fixed31_32_zero; ++ opp110->regamma.divider1 = dal_fixed31_32_from_fraction(3, 2); ++ opp110->regamma.divider2 = dal_fixed31_32_from_int(2); ++ opp110->regamma.divider3 = dal_fixed31_32_from_fraction(5, 2); ++ ++ opp110->regamma.rgb_user = dc_service_alloc( ++ ctx, ++ sizeof(struct pwl_float_data) * ++ (DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points)); ++ if (!opp110->regamma.rgb_user) ++ goto failure_1; ++ ++ opp110->regamma.rgb_oem = dc_service_alloc( ++ ctx, ++ sizeof(struct pwl_float_data) * ++ (DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points)); ++ if (!opp110->regamma.rgb_oem) ++ goto failure_2; ++ ++ opp110->regamma.rgb_resulted = dc_service_alloc( ++ ctx, ++ sizeof(struct pwl_result_data) * ++ (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); ++ if (!opp110->regamma.rgb_resulted) ++ goto failure_3; ++ ++ opp110->regamma.rgb_regamma = dc_service_alloc( ++ ctx, ++ sizeof(struct pwl_float_data_ex) * ++ (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); ++ if (!opp110->regamma.rgb_regamma) ++ goto failure_4; ++ ++ opp110->regamma.coordinates_x = dc_service_alloc( ++ ctx, ++ sizeof(struct hw_x_point) * ++ (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); ++ if (!opp110->regamma.coordinates_x) ++ goto failure_5; ++ ++ opp110->regamma.axis_x_256 = dc_service_alloc( ++ ctx, ++ sizeof(struct gamma_pixel) * ++ (MAX_LUT_ENTRY + opp110->regamma.extra_points)); ++ if (!opp110->regamma.axis_x_256) ++ goto failure_6; ++ ++ opp110->regamma.axis_x_1025 = dc_service_alloc( ++ ctx, ++ sizeof(struct gamma_pixel) * ++ (DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points)); ++ if (!opp110->regamma.axis_x_1025) ++ goto failure_7; ++ ++ opp110->regamma.coeff128 = dc_service_alloc( ++ ctx, ++ sizeof(struct pixel_gamma_point) * ++ (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); ++ if (!opp110->regamma.coeff128) ++ goto failure_8; ++ ++ opp110->regamma.coeff128_oem = dc_service_alloc( ++ ctx, ++ sizeof(struct pixel_gamma_point) * ++ (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); ++ if (!opp110->regamma.coeff128_oem) ++ goto failure_9; ++ ++ opp110->regamma.coeff128_dx = dc_service_alloc( ++ ctx, ++ sizeof(struct pixel_gamma_point) * ++ (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); ++ if (!opp110->regamma.coeff128_dx) ++ goto failure_10; ++ ++ /* init palette */ ++ { ++ uint32_t i = 0; ++ ++ do { ++ opp110->regamma.saved_palette[i].red = (uint8_t)i; ++ opp110->regamma.saved_palette[i].green = (uint8_t)i; ++ opp110->regamma.saved_palette[i].blue = (uint8_t)i; ++ ++ ++i; ++ } while (i != MAX_LUT_ENTRY); ++ } ++ ++ build_evenly_distributed_points( ++ opp110->regamma.axis_x_256, ++ MAX_LUT_ENTRY, ++ opp110->regamma.x_max1, ++ opp110->regamma.divider1, ++ opp110->regamma.divider2, ++ opp110->regamma.divider3); ++ ++ build_evenly_distributed_points( ++ opp110->regamma.axis_x_1025, ++ DX_GAMMA_RAMP_MAX, ++ opp110->regamma.x_max1, ++ opp110->regamma.divider1, ++ opp110->regamma.divider2, ++ opp110->regamma.divider3); ++ ++ return true; ++ ++failure_10: ++ dc_service_free(ctx, opp110->regamma.coeff128_oem); ++failure_9: ++ dc_service_free(ctx, opp110->regamma.coeff128); ++failure_8: ++ dc_service_free(ctx, opp110->regamma.axis_x_1025); ++failure_7: ++ dc_service_free(ctx, opp110->regamma.axis_x_256); ++failure_6: ++ dc_service_free(ctx, opp110->regamma.coordinates_x); ++failure_5: ++ dc_service_free(ctx, opp110->regamma.rgb_regamma); ++failure_4: ++ dc_service_free(ctx, opp110->regamma.rgb_resulted); ++failure_3: ++ dc_service_free(ctx, opp110->regamma.rgb_oem); ++failure_2: ++ dc_service_free(ctx, opp110->regamma.rgb_user); ++failure_1: ++ ++ return true; ++} ++ ++void dce110_opp_destroy(struct output_pixel_processor **opp) ++{ ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.coeff128_dx); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.coeff128_oem); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.coeff128); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.axis_x_1025); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.axis_x_256); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.coordinates_x); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.rgb_regamma); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.rgb_resulted); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.rgb_oem); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)->regamma.rgb_user); ++ dc_service_free((*opp)->ctx, FROM_OPP(*opp)); ++ *opp = NULL; ++} ++ ++struct output_pixel_processor *dce110_opp_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct dce110_opp *opp = ++ dc_service_alloc(ctx, sizeof(struct dce110_opp)); ++ ++ if (!opp) ++ return NULL; ++ ++ if (dce110_opp_construct(opp, ++ ctx, inst)) ++ return &opp->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, opp); ++ return NULL; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h +new file mode 100644 +index 0000000..71fe624 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h +@@ -0,0 +1,140 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_OPP_DCE110_H__ ++#define __DC_OPP_DCE110_H__ ++ ++#include "dc_types.h" ++#include "inc/opp.h" ++ ++enum dce110_opp_reg_type { ++ DCE110_OPP_REG_DCP = 0, ++ DCE110_OPP_REG_DCFE, ++ DCE110_OPP_REG_FMT, ++ ++ DCE110_OPP_REG_MAX ++}; ++ ++struct dce110_regamma { ++ struct gamma_curve arr_curve_points[16]; ++ struct curve_points arr_points[3]; ++ uint32_t hw_points_num; ++ struct hw_x_point *coordinates_x; ++ struct pwl_result_data *rgb_resulted; ++ ++ /* re-gamma curve */ ++ struct pwl_float_data_ex *rgb_regamma; ++ /* coeff used to map user evenly distributed points ++ * to our hardware points (predefined) for gamma 256 */ ++ struct pixel_gamma_point *coeff128; ++ struct pixel_gamma_point *coeff128_oem; ++ /* coeff used to map user evenly distributed points ++ * to our hardware points (predefined) for gamma 1025 */ ++ struct pixel_gamma_point *coeff128_dx; ++ /* evenly distributed points, gamma 256 software points 0-255 */ ++ struct gamma_pixel *axis_x_256; ++ /* evenly distributed points, gamma 1025 software points 0-1025 */ ++ struct gamma_pixel *axis_x_1025; ++ /* OEM supplied gamma for regamma LUT */ ++ struct pwl_float_data *rgb_oem; ++ /* user supplied gamma */ ++ struct pwl_float_data *rgb_user; ++ struct dev_c_lut saved_palette[RGB_256X3X16]; ++ uint32_t extra_points; ++ bool use_half_points; ++ struct fixed31_32 x_max1; ++ struct fixed31_32 x_max2; ++ struct fixed31_32 x_min; ++ struct fixed31_32 divider1; ++ struct fixed31_32 divider2; ++ struct fixed31_32 divider3; ++}; ++ ++/* OPP RELATED */ ++#define TO_DCE110_OPP(opp)\ ++ container_of(opp, struct dce110_opp, base) ++ ++struct dce110_opp_reg_offsets { ++ uint32_t fmt_offset; ++ uint32_t dcp_offset; ++ uint32_t dcfe_offset; ++}; ++ ++struct dce110_opp { ++ struct output_pixel_processor base; ++ struct dce110_opp_reg_offsets offsets; ++ struct dce110_regamma regamma; ++}; ++ ++bool dce110_opp_construct(struct dce110_opp *opp110, ++ struct dc_context *ctx, ++ uint32_t inst); ++ ++void dce110_opp_destroy(struct output_pixel_processor **opp); ++ ++struct output_pixel_processor *dce110_opp_create( ++ struct dc_context *ctx, ++ uint32_t inst); ++ ++/* REGAMMA RELATED */ ++void dce110_opp_power_on_regamma_lut( ++ struct output_pixel_processor *opp, ++ bool power_on); ++ ++bool dce110_opp_set_regamma( ++ struct output_pixel_processor *opp, ++ const struct gamma_ramp *ramp, ++ const struct gamma_parameters *params, ++ bool force_bypass); ++ ++bool dce110_opp_map_legacy_and_regamma_hw_to_x_user( ++ struct output_pixel_processor *opp, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params); ++ ++void dce110_opp_set_csc_adjustment( ++ struct output_pixel_processor *opp, ++ const struct grph_csc_adjustment *adjust); ++ ++void dce110_opp_set_csc_default( ++ struct output_pixel_processor *opp, ++ const struct default_adjustment *default_adjust); ++ ++/* FORMATTER RELATED */ ++void dce110_opp_program_bit_depth_reduction( ++ struct output_pixel_processor *opp, ++ const struct bit_depth_reduction_params *params); ++ ++void dce110_opp_program_clamping_and_pixel_encoding( ++ struct output_pixel_processor *opp, ++ const struct clamping_and_pixel_encoding_params *params); ++ ++ ++void dce110_opp_set_dyn_expansion( ++ struct output_pixel_processor *opp, ++ enum color_space color_sp, ++ enum dc_color_depth color_dpth, ++ enum signal_type signal); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c +new file mode 100644 +index 0000000..91430c0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c +@@ -0,0 +1,904 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "dce110_opp.h" ++#include "basics/conversion.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#define DCP_REG(reg)\ ++ (reg + opp110->offsets.dcp_offset) ++ ++enum { ++ OUTPUT_CSC_MATRIX_SIZE = 12 ++}; ++ ++struct out_csc_color_matrix { ++ enum color_space color_space; ++ uint16_t regval[OUTPUT_CSC_MATRIX_SIZE]; ++}; ++ ++static const struct out_csc_color_matrix global_color_matrix[] = { ++{ COLOR_SPACE_SRGB_FULL_RANGE, ++ { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, ++{ COLOR_SPACE_SRGB_LIMITED_RANGE, ++ { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, ++{ COLOR_SPACE_YCBCR601, ++ { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, ++ 0xF6B9, 0xE00, 0x1000} }, ++{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, ++ 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, ++/* YOnly same as YCbCr709 but Y in Full range -To do. */ ++{ COLOR_SPACE_YCBCR601_YONLY, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, ++ 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, ++{ COLOR_SPACE_YCBCR709_YONLY, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, ++ 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } ++}; ++ ++enum csc_color_mode { ++ /* 00 - BITS2:0 Bypass */ ++ CSC_COLOR_MODE_GRAPHICS_BYPASS, ++ /* 01 - hard coded coefficient TV RGB */ ++ CSC_COLOR_MODE_GRAPHICS_PREDEFINED, ++ /* 04 - programmable OUTPUT CSC coefficient */ ++ CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, ++}; ++ ++static void program_color_matrix( ++ struct dce110_opp *opp110, ++ const struct out_csc_color_matrix *tbl_entry, ++ enum grph_color_adjust_option options) ++{ ++ struct dc_context *ctx = opp110->base.ctx; ++ { ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_C11_C12); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[0], ++ OUTPUT_CSC_C11_C12, ++ OUTPUT_CSC_C11); ++ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[1], ++ OUTPUT_CSC_C11_C12, ++ OUTPUT_CSC_C12); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_C13_C14); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[2], ++ OUTPUT_CSC_C13_C14, ++ OUTPUT_CSC_C13); ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[3], ++ OUTPUT_CSC_C13_C14, ++ OUTPUT_CSC_C14); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_C21_C22); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[4], ++ OUTPUT_CSC_C21_C22, ++ OUTPUT_CSC_C21); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[5], ++ OUTPUT_CSC_C21_C22, ++ OUTPUT_CSC_C22); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_C23_C24); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[6], ++ OUTPUT_CSC_C23_C24, ++ OUTPUT_CSC_C23); ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[7], ++ OUTPUT_CSC_C23_C24, ++ OUTPUT_CSC_C24); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_C31_C32); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[8], ++ OUTPUT_CSC_C31_C32, ++ OUTPUT_CSC_C31); ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[9], ++ OUTPUT_CSC_C31_C32, ++ OUTPUT_CSC_C32); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_C33_C34); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[10], ++ OUTPUT_CSC_C33_C34, ++ OUTPUT_CSC_C33); ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ value, ++ tbl_entry->regval[11], ++ OUTPUT_CSC_C33_C34, ++ OUTPUT_CSC_C34); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++} ++ ++/* ++ * initialize_color_float_adj_reference_values ++ * This initialize display color adjust input from API to HW range for later ++ * calculation use. This is shared by all the display color adjustment. ++ * @param : ++ * @return None ++ */ ++static void initialize_color_float_adj_reference_values( ++ const struct grph_csc_adjustment *adjust, ++ struct fixed31_32 *grph_cont, ++ struct fixed31_32 *grph_sat, ++ struct fixed31_32 *grph_bright, ++ struct fixed31_32 *sin_grph_hue, ++ struct fixed31_32 *cos_grph_hue) ++{ ++ /* Hue adjustment could be negative. -45 ~ +45 */ ++ struct fixed31_32 hue = ++ dal_fixed31_32_mul( ++ dal_fixed31_32_from_fraction(adjust->grph_hue, 180), ++ dal_fixed31_32_pi); ++ ++ *sin_grph_hue = dal_fixed31_32_sin(hue); ++ *cos_grph_hue = dal_fixed31_32_cos(hue); ++ ++ if (adjust->adjust_divider) { ++ *grph_cont = ++ dal_fixed31_32_from_fraction( ++ adjust->grph_cont, ++ adjust->adjust_divider); ++ *grph_sat = ++ dal_fixed31_32_from_fraction( ++ adjust->grph_sat, ++ adjust->adjust_divider); ++ *grph_bright = ++ dal_fixed31_32_from_fraction( ++ adjust->grph_bright, ++ adjust->adjust_divider); ++ } else { ++ *grph_cont = dal_fixed31_32_from_int(adjust->grph_cont); ++ *grph_sat = dal_fixed31_32_from_int(adjust->grph_sat); ++ *grph_bright = dal_fixed31_32_from_int(adjust->grph_bright); ++ } ++} ++ ++static inline struct fixed31_32 fixed31_32_clamp( ++ struct fixed31_32 value, ++ int32_t min_numerator, ++ int32_t max_numerator, ++ int32_t denominator) ++{ ++ return dal_fixed31_32_clamp( ++ value, ++ dal_fixed31_32_from_fraction( ++ min_numerator, ++ denominator), ++ dal_fixed31_32_from_fraction( ++ max_numerator, ++ denominator)); ++} ++ ++static void setup_reg_format( ++ struct fixed31_32 *coefficients, ++ uint16_t *reg_values) ++{ ++ enum { ++ LENGTH = 12, ++ DENOMINATOR = 10000 ++ }; ++ ++ static const int32_t min_numerator[] = { ++ -3 * DENOMINATOR, ++ -DENOMINATOR ++ }; ++ ++ static const int32_t max_numerator[] = { ++ DENOMINATOR, ++ DENOMINATOR ++ }; ++ ++ static const uint8_t integer_bits[] = { 2, 0 }; ++ ++ uint32_t i = 0; ++ ++ do { ++ const uint32_t index = (i % 4) == 3; ++ ++ reg_values[i] = fixed_point_to_int_frac( ++ fixed31_32_clamp(coefficients[(i + 8) % LENGTH], ++ min_numerator[index], ++ max_numerator[index], ++ DENOMINATOR), ++ integer_bits[index], 13); ++ ++ ++i; ++ } while (i != LENGTH); ++} ++ ++/** ++ ***************************************************************************** ++ * Function: setup_adjustments ++ * @note prepare to setup the values ++ * ++ * @see ++ * ++ ***************************************************************************** ++ */ ++static void setup_adjustments(const struct grph_csc_adjustment *adjust, ++ struct dc_csc_adjustments *adjustments) ++{ ++ if (adjust->adjust_divider != 0) { ++ adjustments->brightness = ++ dal_fixed31_32_from_fraction(adjust->grph_bright, ++ adjust->adjust_divider); ++ adjustments->contrast = ++ dal_fixed31_32_from_fraction(adjust->grph_cont, ++ adjust->adjust_divider); ++ adjustments->saturation = ++ dal_fixed31_32_from_fraction(adjust->grph_sat, ++ adjust->adjust_divider); ++ } else { ++ adjustments->brightness = ++ dal_fixed31_32_from_fraction(adjust->grph_bright, 1); ++ adjustments->contrast = ++ dal_fixed31_32_from_fraction(adjust->grph_cont, 1); ++ adjustments->saturation = ++ dal_fixed31_32_from_fraction(adjust->grph_sat, 1); ++ } ++ ++ /* convert degrees into radians */ ++ adjustments->hue = ++ dal_fixed31_32_mul( ++ dal_fixed31_32_from_fraction(adjust->grph_hue, 180), ++ dal_fixed31_32_pi); ++} ++ ++static void prepare_tv_rgb_ideal( ++ struct fixed31_32 *matrix) ++{ ++ static const int32_t matrix_[] = { ++ 85546875, 0, 0, 6250000, ++ 0, 85546875, 0, 6250000, ++ 0, 0, 85546875, 6250000 ++ }; ++ ++ uint32_t i = 0; ++ ++ do { ++ matrix[i] = dal_fixed31_32_from_fraction( ++ matrix_[i], ++ 100000000); ++ ++i; ++ } while (i != ARRAY_SIZE(matrix_)); ++} ++ ++/** ++ ***************************************************************************** ++ * Function: dal_transform_wide_gamut_set_rgb_adjustment_legacy ++ * ++ * @param [in] const struct grph_csc_adjustment *adjust ++ * ++ * @return ++ * void ++ * ++ * @note calculate and program color adjustments for sRGB color space ++ * ++ * @see ++ * ++ ***************************************************************************** ++ */ ++static void set_rgb_adjustment_legacy( ++ struct dce110_opp *opp110, ++ const struct grph_csc_adjustment *adjust) ++{ ++ const struct fixed31_32 k1 = ++ dal_fixed31_32_from_fraction(701000, 1000000); ++ const struct fixed31_32 k2 = ++ dal_fixed31_32_from_fraction(236568, 1000000); ++ const struct fixed31_32 k3 = ++ dal_fixed31_32_from_fraction(-587000, 1000000); ++ const struct fixed31_32 k4 = ++ dal_fixed31_32_from_fraction(464432, 1000000); ++ const struct fixed31_32 k5 = ++ dal_fixed31_32_from_fraction(-114000, 1000000); ++ const struct fixed31_32 k6 = ++ dal_fixed31_32_from_fraction(-701000, 1000000); ++ const struct fixed31_32 k7 = ++ dal_fixed31_32_from_fraction(-299000, 1000000); ++ const struct fixed31_32 k8 = ++ dal_fixed31_32_from_fraction(-292569, 1000000); ++ const struct fixed31_32 k9 = ++ dal_fixed31_32_from_fraction(413000, 1000000); ++ const struct fixed31_32 k10 = ++ dal_fixed31_32_from_fraction(-92482, 1000000); ++ const struct fixed31_32 k11 = ++ dal_fixed31_32_from_fraction(-114000, 1000000); ++ const struct fixed31_32 k12 = ++ dal_fixed31_32_from_fraction(385051, 1000000); ++ const struct fixed31_32 k13 = ++ dal_fixed31_32_from_fraction(-299000, 1000000); ++ const struct fixed31_32 k14 = ++ dal_fixed31_32_from_fraction(886000, 1000000); ++ const struct fixed31_32 k15 = ++ dal_fixed31_32_from_fraction(-587000, 1000000); ++ const struct fixed31_32 k16 = ++ dal_fixed31_32_from_fraction(-741914, 1000000); ++ const struct fixed31_32 k17 = ++ dal_fixed31_32_from_fraction(886000, 1000000); ++ const struct fixed31_32 k18 = ++ dal_fixed31_32_from_fraction(-144086, 1000000); ++ ++ const struct fixed31_32 luma_r = ++ dal_fixed31_32_from_fraction(299, 1000); ++ const struct fixed31_32 luma_g = ++ dal_fixed31_32_from_fraction(587, 1000); ++ const struct fixed31_32 luma_b = ++ dal_fixed31_32_from_fraction(114, 1000); ++ ++ struct out_csc_color_matrix tbl_entry; ++ struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; ++ ++ struct fixed31_32 grph_cont; ++ struct fixed31_32 grph_sat; ++ struct fixed31_32 grph_bright; ++ struct fixed31_32 sin_grph_hue; ++ struct fixed31_32 cos_grph_hue; ++ ++ initialize_color_float_adj_reference_values( ++ adjust, &grph_cont, &grph_sat, ++ &grph_bright, &sin_grph_hue, &cos_grph_hue); ++ ++ /* COEF_1_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + ++ * Sin(GrphHue) * K2)) */ ++ /* (Cos(GrphHue) * K1 + Sin(GrphHue) * K2) */ ++ matrix[0] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k1), ++ dal_fixed31_32_mul(sin_grph_hue, k2)); ++ /* GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2 */ ++ matrix[0] = dal_fixed31_32_mul(grph_sat, matrix[0]); ++ /* (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2)) */ ++ matrix[0] = dal_fixed31_32_add(luma_r, matrix[0]); ++ /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * ++ * K2)) */ ++ matrix[0] = dal_fixed31_32_mul(grph_cont, matrix[0]); ++ ++ /* COEF_1_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + ++ * Sin(GrphHue) * K4)) */ ++ /* (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ ++ matrix[1] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k3), ++ dal_fixed31_32_mul(sin_grph_hue, k4)); ++ /* GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ ++ matrix[1] = dal_fixed31_32_mul(grph_sat, matrix[1]); ++ /* (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)) */ ++ matrix[1] = dal_fixed31_32_add(luma_g, matrix[1]); ++ /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * ++ * K4)) */ ++ matrix[1] = dal_fixed31_32_mul(grph_cont, matrix[1]); ++ ++ /* COEF_1_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + ++ * Sin(GrphHue) * K6)) */ ++ /* (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ ++ matrix[2] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k5), ++ dal_fixed31_32_mul(sin_grph_hue, k6)); ++ /* GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ ++ matrix[2] = dal_fixed31_32_mul(grph_sat, matrix[2]); ++ /* LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ ++ matrix[2] = dal_fixed31_32_add(luma_b, matrix[2]); ++ /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * ++ * K6)) */ ++ matrix[2] = dal_fixed31_32_mul(grph_cont, matrix[2]); ++ ++ /* COEF_1_4 = GrphBright */ ++ matrix[3] = grph_bright; ++ ++ /* COEF_2_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + ++ * Sin(GrphHue) * K8)) */ ++ /* (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ ++ matrix[4] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k7), ++ dal_fixed31_32_mul(sin_grph_hue, k8)); ++ /* GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ ++ matrix[4] = dal_fixed31_32_mul(grph_sat, matrix[4]); ++ /* (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)) */ ++ matrix[4] = dal_fixed31_32_add(luma_r, matrix[4]); ++ /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * ++ * K8)) */ ++ matrix[4] = dal_fixed31_32_mul(grph_cont, matrix[4]); ++ ++ /* COEF_2_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + ++ * Sin(GrphHue) * K10)) */ ++ /* (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ ++ matrix[5] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k9), ++ dal_fixed31_32_mul(sin_grph_hue, k10)); ++ /* GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ ++ matrix[5] = dal_fixed31_32_mul(grph_sat, matrix[5]); ++ /* (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ ++ matrix[5] = dal_fixed31_32_add(luma_g, matrix[5]); ++ /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * ++ * K10)) */ ++ matrix[5] = dal_fixed31_32_mul(grph_cont, matrix[5]); ++ ++ /* COEF_2_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + ++ * Sin(GrphHue) * K12)) */ ++ /* (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ ++ matrix[6] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k11), ++ dal_fixed31_32_mul(sin_grph_hue, k12)); ++ /* GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ ++ matrix[6] = dal_fixed31_32_mul(grph_sat, matrix[6]); ++ /* (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ ++ matrix[6] = dal_fixed31_32_add(luma_b, matrix[6]); ++ /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * ++ * K12)) */ ++ matrix[6] = dal_fixed31_32_mul(grph_cont, matrix[6]); ++ ++ /* COEF_2_4 = GrphBright */ ++ matrix[7] = grph_bright; ++ ++ /* COEF_3_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + ++ * Sin(GrphHue) * K14)) */ ++ /* (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ ++ matrix[8] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k13), ++ dal_fixed31_32_mul(sin_grph_hue, k14)); ++ /* GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ ++ matrix[8] = dal_fixed31_32_mul(grph_sat, matrix[8]); ++ /* (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ ++ matrix[8] = dal_fixed31_32_add(luma_r, matrix[8]); ++ /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * ++ * K14)) */ ++ matrix[8] = dal_fixed31_32_mul(grph_cont, matrix[8]); ++ ++ /* COEF_3_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + ++ * Sin(GrphHue) * K16)) */ ++ /* GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16) */ ++ matrix[9] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k15), ++ dal_fixed31_32_mul(sin_grph_hue, k16)); ++ /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ ++ matrix[9] = dal_fixed31_32_mul(grph_sat, matrix[9]); ++ /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ ++ matrix[9] = dal_fixed31_32_add(luma_g, matrix[9]); ++ /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * ++ * K16)) */ ++ matrix[9] = dal_fixed31_32_mul(grph_cont, matrix[9]); ++ ++ /* COEF_3_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + ++ * Sin(GrphHue) * K18)) */ ++ /* (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ ++ matrix[10] = ++ dal_fixed31_32_add( ++ dal_fixed31_32_mul(cos_grph_hue, k17), ++ dal_fixed31_32_mul(sin_grph_hue, k18)); ++ /* GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ ++ matrix[10] = dal_fixed31_32_mul(grph_sat, matrix[10]); ++ /* (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ ++ matrix[10] = dal_fixed31_32_add(luma_b, matrix[10]); ++ /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * ++ * K18)) */ ++ matrix[10] = dal_fixed31_32_mul(grph_cont, matrix[10]); ++ ++ /* COEF_3_4 = GrphBright */ ++ matrix[11] = grph_bright; ++ ++ tbl_entry.color_space = adjust->c_space; ++ ++ convert_float_matrix(tbl_entry.regval, matrix, OUTPUT_CSC_MATRIX_SIZE); ++ ++ program_color_matrix( ++ opp110, &tbl_entry, adjust->color_adjust_option); ++} ++ ++/** ++ ***************************************************************************** ++ * Function: dal_transform_wide_gamut_set_rgb_limited_range_adjustment ++ * ++ * @param [in] const struct grph_csc_adjustment *adjust ++ * ++ * @return ++ * void ++ * ++ * @note calculate and program color adjustments for sRGB limited color space ++ * ++ * @see ++ * ++ ***************************************************************************** ++ */ ++static void set_rgb_limited_range_adjustment( ++ struct dce110_opp *opp110, ++ const struct grph_csc_adjustment *adjust) ++{ ++ struct out_csc_color_matrix reg_matrix; ++ struct fixed31_32 change_matrix[OUTPUT_CSC_MATRIX_SIZE]; ++ struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; ++ struct dc_csc_adjustments adjustments; ++ struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE]; ++ ++ prepare_tv_rgb_ideal(ideals); ++ ++ setup_adjustments(adjust, &adjustments); ++ ++ calculate_adjustments(ideals, &adjustments, matrix); ++ ++ dc_service_memmove(change_matrix, matrix, sizeof(matrix)); ++ ++ /* from 1 -> 3 */ ++ matrix[8] = change_matrix[0]; ++ matrix[9] = change_matrix[1]; ++ matrix[10] = change_matrix[2]; ++ matrix[11] = change_matrix[3]; ++ ++ /* from 2 -> 1 */ ++ matrix[0] = change_matrix[4]; ++ matrix[1] = change_matrix[5]; ++ matrix[2] = change_matrix[6]; ++ matrix[3] = change_matrix[7]; ++ ++ /* from 3 -> 2 */ ++ matrix[4] = change_matrix[8]; ++ matrix[5] = change_matrix[9]; ++ matrix[6] = change_matrix[10]; ++ matrix[7] = change_matrix[11]; ++ ++ dc_service_memset(®_matrix, 0, sizeof(struct out_csc_color_matrix)); ++ ++ setup_reg_format(matrix, reg_matrix.regval); ++ ++ program_color_matrix(opp110, ®_matrix, GRPH_COLOR_MATRIX_SW); ++} ++ ++static void prepare_yuv_ideal( ++ bool b601, ++ struct fixed31_32 *matrix) ++{ ++ static const int32_t matrix_1[] = { ++ 25578516, 50216016, 9752344, 6250000, ++ -14764391, -28985609, 43750000, 50000000, ++ 43750000, -36635164, -7114836, 50000000 ++ }; ++ ++ static const int32_t matrix_2[] = { ++ 18187266, 61183125, 6176484, 6250000, ++ -10025059, -33724941, 43750000, 50000000, ++ 43750000, -39738379, -4011621, 50000000 ++ }; ++ ++ const int32_t *matrix_x = b601 ? matrix_1 : matrix_2; ++ ++ uint32_t i = 0; ++ ++ do { ++ matrix[i] = dal_fixed31_32_from_fraction( ++ matrix_x[i], ++ 100000000); ++ ++i; ++ } while (i != ARRAY_SIZE(matrix_1)); ++} ++ ++/** ++ ***************************************************************************** ++ * Function: dal_transform_wide_gamut_set_yuv_adjustment ++ * ++ * @param [in] const struct grph_csc_adjustment *adjust ++ * ++ * @return ++ * void ++ * ++ * @note calculate and program color adjustments for YUV color spaces ++ * ++ * @see ++ * ++ ***************************************************************************** ++ */ ++static void set_yuv_adjustment( ++ struct dce110_opp *opp110, ++ const struct grph_csc_adjustment *adjust) ++{ ++ bool b601 = (adjust->c_space == COLOR_SPACE_YPBPR601) || ++ (adjust->c_space == COLOR_SPACE_YCBCR601) || ++ (adjust->c_space == COLOR_SPACE_YCBCR601_YONLY); ++ struct out_csc_color_matrix reg_matrix; ++ struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; ++ struct dc_csc_adjustments adjustments; ++ struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE]; ++ ++ prepare_yuv_ideal(b601, ideals); ++ ++ setup_adjustments(adjust, &adjustments); ++ ++ if ((adjust->c_space == COLOR_SPACE_YCBCR601_YONLY) || ++ (adjust->c_space == COLOR_SPACE_YCBCR709_YONLY)) ++ calculate_adjustments_y_only( ++ ideals, &adjustments, matrix); ++ else ++ calculate_adjustments( ++ ideals, &adjustments, matrix); ++ ++ dc_service_memset(®_matrix, 0, sizeof(struct out_csc_color_matrix)); ++ ++ setup_reg_format(matrix, reg_matrix.regval); ++ ++ program_color_matrix(opp110, ®_matrix, GRPH_COLOR_MATRIX_SW); ++} ++ ++static bool configure_graphics_mode( ++ struct dce110_opp *opp110, ++ enum csc_color_mode config, ++ enum graphics_csc_adjust_type csc_adjust_type, ++ enum color_space color_space) ++{ ++ struct dc_context *ctx = opp110->base.ctx; ++ uint32_t addr = DCP_REG(mmOUTPUT_CSC_CONTROL); ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ ++ if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { ++ if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { ++ set_reg_field_value( ++ value, ++ 4, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ } else { ++ ++ switch (color_space) { ++ case COLOR_SPACE_SRGB_FULL_RANGE: ++ /* by pass */ ++ set_reg_field_value( ++ value, ++ 0, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ case COLOR_SPACE_SRGB_LIMITED_RANGE: ++ /* TV RGB */ ++ set_reg_field_value( ++ value, ++ 1, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ case COLOR_SPACE_YCBCR601: ++ case COLOR_SPACE_YPBPR601: ++ case COLOR_SPACE_YCBCR601_YONLY: ++ /* YCbCr601 */ ++ set_reg_field_value( ++ value, ++ 2, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ case COLOR_SPACE_YCBCR709: ++ case COLOR_SPACE_YPBPR709: ++ case COLOR_SPACE_YCBCR709_YONLY: ++ /* YCbCr709 */ ++ set_reg_field_value( ++ value, ++ 3, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ default: ++ return false; ++ } ++ } ++ } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { ++ switch (color_space) { ++ case COLOR_SPACE_SRGB_FULL_RANGE: ++ /* by pass */ ++ set_reg_field_value( ++ value, ++ 0, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ case COLOR_SPACE_SRGB_LIMITED_RANGE: ++ /* TV RGB */ ++ set_reg_field_value( ++ value, ++ 1, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ case COLOR_SPACE_YCBCR601: ++ case COLOR_SPACE_YPBPR601: ++ case COLOR_SPACE_YCBCR601_YONLY: ++ /* YCbCr601 */ ++ set_reg_field_value( ++ value, ++ 2, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ case COLOR_SPACE_YCBCR709: ++ case COLOR_SPACE_YPBPR709: ++ case COLOR_SPACE_YCBCR709_YONLY: ++ /* YCbCr709 */ ++ set_reg_field_value( ++ value, ++ 3, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ break; ++ default: ++ return false; ++ } ++ ++ } else ++ /* by pass */ ++ set_reg_field_value( ++ value, ++ 0, ++ OUTPUT_CSC_CONTROL, ++ OUTPUT_CSC_GRPH_MODE); ++ ++ addr = DCP_REG(mmOUTPUT_CSC_CONTROL); ++ dal_write_reg(ctx, addr, value); ++ ++ return true; ++} ++ ++void dce110_opp_set_csc_adjustment( ++ struct output_pixel_processor *opp, ++ const struct grph_csc_adjustment *adjust) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ enum csc_color_mode config = ++ CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; ++ ++ /* Apply color adjustments: brightness, saturation, hue, contrast and ++ * CSC. No need for different color space routine, color space defines ++ * the ideal values only, but keep original design to allow quick switch ++ * to the old legacy routines */ ++ switch (adjust->c_space) { ++ case COLOR_SPACE_SRGB_FULL_RANGE: ++ set_rgb_adjustment_legacy(opp110, adjust); ++ break; ++ case COLOR_SPACE_SRGB_LIMITED_RANGE: ++ set_rgb_limited_range_adjustment( ++ opp110, adjust); ++ break; ++ case COLOR_SPACE_YCBCR601: ++ case COLOR_SPACE_YCBCR709: ++ case COLOR_SPACE_YCBCR601_YONLY: ++ case COLOR_SPACE_YCBCR709_YONLY: ++ case COLOR_SPACE_YPBPR601: ++ case COLOR_SPACE_YPBPR709: ++ set_yuv_adjustment(opp110, adjust); ++ break; ++ default: ++ set_rgb_adjustment_legacy(opp110, adjust); ++ break; ++ } ++ ++ /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ ++ configure_graphics_mode(opp110, config, adjust->csc_adjust_type, ++ adjust->c_space); ++} ++ ++void dce110_opp_set_csc_default( ++ struct output_pixel_processor *opp, ++ const struct default_adjustment *default_adjust) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ enum csc_color_mode config = ++ CSC_COLOR_MODE_GRAPHICS_PREDEFINED; ++ ++ if (default_adjust->force_hw_default == false) { ++ const struct out_csc_color_matrix *elm; ++ /* currently parameter not in use */ ++ enum grph_color_adjust_option option = ++ GRPH_COLOR_MATRIX_HW_DEFAULT; ++ uint32_t i; ++ /* ++ * HW default false we program locally defined matrix ++ * HW default true we use predefined hw matrix and we ++ * do not need to program matrix ++ * OEM wants the HW default via runtime parameter. ++ */ ++ option = GRPH_COLOR_MATRIX_SW; ++ ++ for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { ++ elm = &global_color_matrix[i]; ++ if (elm->color_space != default_adjust->color_space) ++ continue; ++ /* program the matrix with default values from this ++ * file */ ++ program_color_matrix(opp110, elm, option); ++ config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; ++ break; ++ } ++ } ++ ++ /* configure the what we programmed : ++ * 1. Default values from this file ++ * 2. Use hardware default from ROM_A and we do not need to program ++ * matrix */ ++ ++ configure_graphics_mode(opp110, config, ++ default_adjust->csc_adjust_type, ++ default_adjust->color_space); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c +new file mode 100644 +index 0000000..fdf87bd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c +@@ -0,0 +1,610 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_opp.h" ++ ++#define FMT_REG(reg)\ ++ (reg + opp110->offsets.fmt_offset) ++ ++/** ++ * set_truncation ++ * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp ++ * 2) enable truncation ++ * 3) HW remove 12bit FMT support for DCE11 power saving reason. ++ */ ++static void set_truncation( ++ struct dce110_opp *opp110, ++ const struct bit_depth_reduction_params *params) ++{ ++ uint32_t value = 0; ++ uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); ++ ++ /*Disable truncation*/ ++ value = dal_read_reg(opp110->base.ctx, addr); ++ set_reg_field_value(value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); ++ set_reg_field_value(value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); ++ set_reg_field_value(value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); ++ ++ dal_write_reg(opp110->base.ctx, addr, value); ++ ++ /* no 10bpc trunc on DCE11*/ ++ if (params->flags.TRUNCATE_ENABLED == 0 || ++ params->flags.TRUNCATE_DEPTH == 2) ++ return; ++ ++ /*Set truncation depth and Enable truncation*/ ++ set_reg_field_value(value, 1, ++ FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); ++ set_reg_field_value(value, params->flags.TRUNCATE_MODE, ++ FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); ++ set_reg_field_value(value, params->flags.TRUNCATE_DEPTH, ++ FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); ++ ++ dal_write_reg(opp110->base.ctx, addr, value); ++ ++} ++ ++/** ++ * set_spatial_dither ++ * 1) set spatial dithering mode: pattern of seed ++ * 2) set spatical dithering depth: 0 for 18bpp or 1 for 24bpp ++ * 3) set random seed ++ * 4) set random mode ++ * lfsr is reset every frame or not reset ++ * RGB dithering method ++ * 0: RGB data are all dithered with x^28+x^3+1 ++ * 1: R data is dithered with x^28+x^3+1 ++ * G data is dithered with x^28+X^9+1 ++ * B data is dithered with x^28+x^13+1 ++ * enable high pass filter or not ++ * 5) enable spatical dithering ++ */ ++static void set_spatial_dither( ++ struct dce110_opp *opp110, ++ const struct bit_depth_reduction_params *params) ++{ ++ uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); ++ uint32_t depth_cntl_value = 0; ++ uint32_t fmt_cntl_value = 0; ++ uint32_t dither_r_value = 0; ++ uint32_t dither_g_value = 0; ++ uint32_t dither_b_value = 0; ++ ++ /*Disable spatial (random) dithering*/ ++ depth_cntl_value = dal_read_reg(opp110->base.ctx, addr); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE); ++ set_reg_field_value(depth_cntl_value, 0, ++ FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE); ++ ++ dal_write_reg(opp110->base.ctx, addr, depth_cntl_value); ++ ++ /* no 10bpc on DCE11*/ ++ if (params->flags.SPATIAL_DITHER_ENABLED == 0 || ++ params->flags.SPATIAL_DITHER_DEPTH == 2) ++ return; ++ ++ addr = FMT_REG(mmFMT_CONTROL); ++ fmt_cntl_value = dal_read_reg(opp110->base.ctx, addr); ++ /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ ++ if (params->flags.FRAME_RANDOM == 1) { ++ if (params->flags.SPATIAL_DITHER_DEPTH == 0 || ++ params->flags.SPATIAL_DITHER_DEPTH == 1) { ++ set_reg_field_value(fmt_cntl_value, 15, ++ FMT_CONTROL, ++ FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); ++ set_reg_field_value(fmt_cntl_value, 2, ++ FMT_CONTROL, ++ FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); ++ } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { ++ set_reg_field_value(fmt_cntl_value, 3, ++ FMT_CONTROL, ++ FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); ++ set_reg_field_value(fmt_cntl_value, 1, ++ FMT_CONTROL, ++ FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); ++ } else ++ return; ++ } else { ++ set_reg_field_value(fmt_cntl_value, 0, ++ FMT_CONTROL, ++ FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); ++ set_reg_field_value(fmt_cntl_value, 0, ++ FMT_CONTROL, ++ FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); ++ } ++ ++ dal_write_reg(opp110->base.ctx, addr, fmt_cntl_value); ++ ++ /*Set seed for random values for ++ * spatial dithering for R,G,B channels*/ ++ addr = FMT_REG(mmFMT_DITHER_RAND_R_SEED); ++ set_reg_field_value(dither_r_value, params->r_seed_value, ++ FMT_DITHER_RAND_R_SEED, ++ FMT_RAND_R_SEED); ++ dal_write_reg(opp110->base.ctx, addr, dither_r_value); ++ ++ addr = FMT_REG(mmFMT_DITHER_RAND_G_SEED); ++ set_reg_field_value(dither_g_value, ++ params->g_seed_value, ++ FMT_DITHER_RAND_G_SEED, ++ FMT_RAND_G_SEED); ++ dal_write_reg(opp110->base.ctx, addr, dither_g_value); ++ ++ addr = FMT_REG(mmFMT_DITHER_RAND_B_SEED); ++ set_reg_field_value(dither_b_value, params->b_seed_value, ++ FMT_DITHER_RAND_B_SEED, ++ FMT_RAND_B_SEED); ++ dal_write_reg(opp110->base.ctx, addr, dither_b_value); ++ ++ /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero ++ * offset for the R/Cr channel, lower 4LSB ++ * is forced to zeros. Typically set to 0 ++ * RGB and 0x80000 YCbCr. ++ */ ++ /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero ++ * offset for the G/Y channel, lower 4LSB is ++ * forced to zeros. Typically set to 0 RGB ++ * and 0x80000 YCbCr. ++ */ ++ /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero ++ * offset for the B/Cb channel, lower 4LSB is ++ * forced to zeros. Typically set to 0 RGB and ++ * 0x80000 YCbCr. ++ */ ++ ++ /*Set spatial dithering bit depth*/ ++ set_reg_field_value(depth_cntl_value, ++ params->flags.SPATIAL_DITHER_DEPTH, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_SPATIAL_DITHER_DEPTH); ++ ++ /* Set spatial dithering mode ++ * (default is Seed patterrn AAAA...) ++ */ ++ set_reg_field_value(depth_cntl_value, ++ params->flags.SPATIAL_DITHER_MODE, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_SPATIAL_DITHER_MODE); ++ ++ /*Reset only at startup*/ ++ set_reg_field_value(depth_cntl_value, ++ params->flags.FRAME_RANDOM, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_RGB_RANDOM_ENABLE); ++ ++ /*Set RGB data dithered with x^28+x^3+1*/ ++ set_reg_field_value(depth_cntl_value, ++ params->flags.RGB_RANDOM, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_RGB_RANDOM_ENABLE); ++ ++ /*Disable High pass filter*/ ++ set_reg_field_value(depth_cntl_value, ++ params->flags.HIGHPASS_RANDOM, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_HIGHPASS_RANDOM_ENABLE); ++ ++ /*Enable spatial dithering*/ ++ set_reg_field_value(depth_cntl_value, ++ 1, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_SPATIAL_DITHER_EN); ++ ++ addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); ++ dal_write_reg(opp110->base.ctx, addr, depth_cntl_value); ++ ++} ++ ++/** ++ * SetTemporalDither (Frame Modulation) ++ * 1) set temporal dither depth ++ * 2) select pattern: from hard-coded pattern or programmable pattern ++ * 3) select optimized strips for BGR or RGB LCD sub-pixel ++ * 4) set s matrix ++ * 5) set t matrix ++ * 6) set grey level for 0.25, 0.5, 0.75 ++ * 7) enable temporal dithering ++ */ ++static void set_temporal_dither( ++ struct dce110_opp *opp110, ++ const struct bit_depth_reduction_params *params) ++{ ++ uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); ++ uint32_t value; ++ ++ /*Disable temporal (frame modulation) dithering first*/ ++ value = dal_read_reg(opp110->base.ctx, addr); ++ ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_EN); ++ ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_RESET); ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_OFFSET); ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_DEPTH); ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_LEVEL); ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_25FRC_SEL); ++ ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_50FRC_SEL); ++ ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_75FRC_SEL); ++ ++ dal_write_reg(opp110->base.ctx, addr, value); ++ ++ /* no 10bpc dither on DCE11*/ ++ if (params->flags.FRAME_MODULATION_ENABLED == 0 || ++ params->flags.FRAME_MODULATION_DEPTH == 2) ++ return; ++ ++ /* Set temporal dithering depth*/ ++ set_reg_field_value(value, ++ params->flags.FRAME_MODULATION_DEPTH, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_DEPTH); ++ ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_RESET); ++ ++ set_reg_field_value(value, ++ 0, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_OFFSET); ++ ++ /*Select legacy pattern based on FRC and Temporal level*/ ++ addr = FMT_REG(mmFMT_TEMPORAL_DITHER_PATTERN_CONTROL); ++ dal_write_reg(opp110->base.ctx, addr, 0); ++ /*Set s matrix*/ ++ addr = FMT_REG( ++ mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX); ++ dal_write_reg(opp110->base.ctx, addr, 0); ++ /*Set t matrix*/ ++ addr = FMT_REG( ++ mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX); ++ dal_write_reg(opp110->base.ctx, addr, 0); ++ ++ /*Select patterns for 0.25, 0.5 and 0.75 grey level*/ ++ set_reg_field_value(value, ++ params->flags.TEMPORAL_LEVEL, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_LEVEL); ++ ++ set_reg_field_value(value, ++ params->flags.FRC25, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_25FRC_SEL); ++ ++ set_reg_field_value(value, ++ params->flags.FRC50, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_50FRC_SEL); ++ ++ set_reg_field_value(value, ++ params->flags.FRC75, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_75FRC_SEL); ++ ++ /*Enable bit reduction by temporal (frame modulation) dithering*/ ++ set_reg_field_value(value, ++ 1, ++ FMT_BIT_DEPTH_CONTROL, ++ FMT_TEMPORAL_DITHER_EN); ++ ++ addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); ++ dal_write_reg(opp110->base.ctx, addr, value); ++ ++} ++ ++/** ++ * Set Clamping ++ * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) ++ * 1 for 8 bpc ++ * 2 for 10 bpc ++ * 3 for 12 bpc ++ * 7 for programable ++ * 2) Enable clamp if Limited range requested ++ */ ++static void set_clamping( ++ struct dce110_opp *opp110, ++ const struct clamping_and_pixel_encoding_params *params) ++{ ++ uint32_t clamp_cntl_value = 0; ++ uint32_t red_clamp_value = 0; ++ uint32_t green_clamp_value = 0; ++ uint32_t blue_clamp_value = 0; ++ uint32_t addr = FMT_REG(mmFMT_CLAMP_CNTL); ++ ++ clamp_cntl_value = dal_read_reg(opp110->base.ctx, addr); ++ ++ set_reg_field_value(clamp_cntl_value, ++ 0, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_DATA_EN); ++ ++ set_reg_field_value(clamp_cntl_value, ++ 0, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_COLOR_FORMAT); ++ ++ switch (params->clamping_level) { ++ case CLAMPING_FULL_RANGE: ++ break; ++ ++ case CLAMPING_LIMITED_RANGE_8BPC: ++ set_reg_field_value(clamp_cntl_value, ++ 1, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_DATA_EN); ++ ++ set_reg_field_value(clamp_cntl_value, ++ 1, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_COLOR_FORMAT); ++ ++ break; ++ ++ case CLAMPING_LIMITED_RANGE_10BPC: ++ set_reg_field_value(clamp_cntl_value, ++ 1, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_DATA_EN); ++ ++ set_reg_field_value(clamp_cntl_value, ++ 2, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_COLOR_FORMAT); ++ ++ break; ++ case CLAMPING_LIMITED_RANGE_12BPC: ++ set_reg_field_value(clamp_cntl_value, ++ 1, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_DATA_EN); ++ ++ set_reg_field_value(clamp_cntl_value, ++ 3, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_COLOR_FORMAT); ++ ++ break; ++ case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: ++ set_reg_field_value(clamp_cntl_value, ++ 1, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_DATA_EN); ++ ++ set_reg_field_value(clamp_cntl_value, ++ 7, ++ FMT_CLAMP_CNTL, ++ FMT_CLAMP_COLOR_FORMAT); ++ ++ /*set the defaults*/ ++ set_reg_field_value(red_clamp_value, ++ 0x10, ++ FMT_CLAMP_COMPONENT_R, ++ FMT_CLAMP_LOWER_R); ++ ++ set_reg_field_value(red_clamp_value, ++ 0xFEF, ++ FMT_CLAMP_COMPONENT_R, ++ FMT_CLAMP_UPPER_R); ++ ++ addr = FMT_REG(mmFMT_CLAMP_COMPONENT_R); ++ dal_write_reg(opp110->base.ctx, addr, red_clamp_value); ++ ++ set_reg_field_value(green_clamp_value, ++ 0x10, ++ FMT_CLAMP_COMPONENT_G, ++ FMT_CLAMP_LOWER_G); ++ ++ set_reg_field_value(green_clamp_value, ++ 0xFEF, ++ FMT_CLAMP_COMPONENT_G, ++ FMT_CLAMP_UPPER_G); ++ ++ addr = FMT_REG(mmFMT_CLAMP_COMPONENT_G); ++ dal_write_reg(opp110->base.ctx, addr, green_clamp_value); ++ ++ set_reg_field_value(blue_clamp_value, ++ 0x10, ++ FMT_CLAMP_COMPONENT_B, ++ FMT_CLAMP_LOWER_B); ++ ++ set_reg_field_value(blue_clamp_value, ++ 0xFEF, ++ FMT_CLAMP_COMPONENT_B, ++ FMT_CLAMP_UPPER_B); ++ ++ addr = FMT_REG(mmFMT_CLAMP_COMPONENT_B); ++ dal_write_reg(opp110->base.ctx, addr, blue_clamp_value); ++ ++ break; ++ ++ default: ++ break; ++ } ++ ++ addr = FMT_REG(mmFMT_CLAMP_CNTL); ++ /*Set clamp control*/ ++ dal_write_reg(opp110->base.ctx, addr, clamp_cntl_value); ++ ++} ++ ++/** ++ * set_pixel_encoding ++ * ++ * Set Pixel Encoding ++ * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly ++ * 1: YCbCr 4:2:2 ++ */ ++static void set_pixel_encoding( ++ struct dce110_opp *opp110, ++ const struct clamping_and_pixel_encoding_params *params) ++{ ++ uint32_t fmt_cntl_value; ++ uint32_t addr = FMT_REG(mmFMT_CONTROL); ++ ++ /*RGB 4:4:4 or YCbCr 4:4:4 - 0; YCbCr 4:2:2 -1.*/ ++ fmt_cntl_value = dal_read_reg(opp110->base.ctx, addr); ++ ++ set_reg_field_value(fmt_cntl_value, ++ 0, ++ FMT_CONTROL, ++ FMT_PIXEL_ENCODING); ++ ++ if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { ++ set_reg_field_value(fmt_cntl_value, ++ 1, ++ FMT_CONTROL, ++ FMT_PIXEL_ENCODING); ++ ++ /*00 - Pixels drop mode ,01 - Pixels average mode*/ ++ set_reg_field_value(fmt_cntl_value, ++ 0, ++ FMT_CONTROL, ++ FMT_SUBSAMPLING_MODE); ++ ++ /*00 - Cb before Cr ,01 - Cr before Cb*/ ++ set_reg_field_value(fmt_cntl_value, ++ 0, ++ FMT_CONTROL, ++ FMT_SUBSAMPLING_ORDER); ++ } ++ dal_write_reg(opp110->base.ctx, addr, fmt_cntl_value); ++ ++} ++ ++void dce110_opp_program_bit_depth_reduction( ++ struct output_pixel_processor *opp, ++ const struct bit_depth_reduction_params *params) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ ++ set_truncation(opp110, params); ++ set_spatial_dither(opp110, params); ++ set_temporal_dither(opp110, params); ++} ++ ++void dce110_opp_program_clamping_and_pixel_encoding( ++ struct output_pixel_processor *opp, ++ const struct clamping_and_pixel_encoding_params *params) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ ++ set_clamping(opp110, params); ++ set_pixel_encoding(opp110, params); ++} ++ ++void dce110_opp_set_dyn_expansion( ++ struct output_pixel_processor *opp, ++ enum color_space color_sp, ++ enum dc_color_depth color_dpth, ++ enum signal_type signal) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ uint32_t value; ++ bool enable_dyn_exp = false; ++ uint32_t addr = FMT_REG(mmFMT_DYNAMIC_EXP_CNTL); ++ ++ value = dal_read_reg(opp->ctx, addr); ++ ++ set_reg_field_value(value, 0, ++ FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); ++ set_reg_field_value(value, 0, ++ FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); ++ ++ /* From HW programming guide: ++ FMT_DYNAMIC_EXP_EN = 0 for limited RGB or YCbCr output ++ FMT_DYNAMIC_EXP_EN = 1 for RGB full range only*/ ++ if (color_sp == COLOR_SPACE_SRGB_FULL_RANGE) ++ enable_dyn_exp = true; ++ ++ /*00 - 10-bit -> 12-bit dynamic expansion*/ ++ /*01 - 8-bit -> 12-bit dynamic expansion*/ ++ if (signal == SIGNAL_TYPE_HDMI_TYPE_A) { ++ switch (color_dpth) { ++ case COLOR_DEPTH_888: ++ set_reg_field_value(value, enable_dyn_exp ? 1:0, ++ FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); ++ set_reg_field_value(value, 1, ++ FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); ++ break; ++ case COLOR_DEPTH_101010: ++ set_reg_field_value(value, enable_dyn_exp ? 1:0, ++ FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); ++ set_reg_field_value(value, 0, ++ FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); ++ break; ++ case COLOR_DEPTH_121212: ++ break; ++ default: ++ break; ++ } ++ } ++ ++ dal_write_reg(opp->ctx, addr, value); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c +new file mode 100644 +index 0000000..4cba172 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c +@@ -0,0 +1,2473 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_opp.h" ++ ++#define DCP_REG(reg)\ ++ (reg + opp110->offsets.dcp_offset) ++ ++#define DCFE_REG(reg)\ ++ (reg + opp110->offsets.dcfe_offset) ++ ++enum { ++ MAX_PWL_ENTRY = 128, ++ MAX_REGIONS_NUMBER = 16 ++ ++}; ++ ++struct curve_config { ++ uint32_t offset; ++ int8_t segments[MAX_REGIONS_NUMBER]; ++ int8_t begin; ++}; ++ ++/* BASE */ ++static bool find_software_points( ++ struct dce110_opp *opp110, ++ struct fixed31_32 hw_point, ++ enum channel_name channel, ++ uint32_t *index_to_start, ++ uint32_t *index_left, ++ uint32_t *index_right, ++ enum hw_point_position *pos) ++{ ++ const uint32_t max_number = ++ RGB_256X3X16 + opp110->regamma.extra_points; ++ ++ struct fixed31_32 left, right; ++ ++ uint32_t i = *index_to_start; ++ ++ while (i < max_number) { ++ if (channel == CHANNEL_NAME_RED) { ++ left = opp110-> ++ regamma.axis_x_256[i].r; ++ ++ if (i < max_number - 1) ++ right = opp110-> ++ regamma.axis_x_256[i + 1].r; ++ else ++ right = opp110-> ++ regamma.axis_x_256[max_number - 1].r; ++ } else if (channel == CHANNEL_NAME_GREEN) { ++ left = opp110->regamma.axis_x_256[i].g; ++ ++ if (i < max_number - 1) ++ right = opp110-> ++ regamma.axis_x_256[i + 1].g; ++ else ++ right = opp110-> ++ regamma.axis_x_256[max_number - 1].g; ++ } else { ++ left = opp110->regamma.axis_x_256[i].b; ++ ++ if (i < max_number - 1) ++ right = opp110-> ++ regamma.axis_x_256[i + 1].b; ++ else ++ right = opp110-> ++ regamma.axis_x_256[max_number - 1].b; ++ } ++ ++ if (dal_fixed31_32_le(left, hw_point) && ++ dal_fixed31_32_le(hw_point, right)) { ++ *index_to_start = i; ++ *index_left = i; ++ ++ if (i < max_number - 1) ++ *index_right = i + 1; ++ else ++ *index_right = max_number - 1; ++ ++ *pos = HW_POINT_POSITION_MIDDLE; ++ ++ return true; ++ } else if ((i == *index_to_start) && ++ dal_fixed31_32_le(hw_point, left)) { ++ *index_to_start = i; ++ *index_left = i; ++ *index_right = i; ++ ++ *pos = HW_POINT_POSITION_LEFT; ++ ++ return true; ++ } else if ((i == max_number - 1) && ++ dal_fixed31_32_le(right, hw_point)) { ++ *index_to_start = i; ++ *index_left = i; ++ *index_right = i; ++ ++ *pos = HW_POINT_POSITION_RIGHT; ++ ++ return true; ++ } ++ ++ ++i; ++ } ++ ++ return false; ++} ++ ++static bool find_software_points_dx( ++ struct dce110_opp *opp110, ++ struct fixed31_32 hw_point, ++ enum channel_name channel, ++ uint32_t *index_to_start, ++ uint32_t *index_left, ++ uint32_t *index_right, ++ enum hw_point_position *pos) ++{ ++ const uint32_t max_number = DX_GAMMA_RAMP_MAX + ++ opp110->regamma.extra_points; ++ ++ struct fixed31_32 left, right; ++ ++ uint32_t i = *index_to_start; ++ ++ while (i < max_number) { ++ if (channel == CHANNEL_NAME_RED) { ++ left = opp110->regamma.axis_x_1025[i].r; ++ ++ if (i < DX_GAMMA_RAMP_MAX - 1) ++ right = opp110-> ++ regamma.axis_x_1025[i + 1].r; ++ else ++ right = opp110-> ++ regamma.axis_x_1025[DX_GAMMA_RAMP_MAX-1].r; ++ } else if (channel == CHANNEL_NAME_GREEN) { ++ left = opp110->regamma.axis_x_1025[i].g; ++ ++ if (i < DX_GAMMA_RAMP_MAX - 1) ++ right = opp110-> ++ regamma.axis_x_1025[i + 1].g; ++ else ++ right = opp110-> ++ regamma.axis_x_1025[DX_GAMMA_RAMP_MAX-1].g; ++ } else { ++ left = opp110->regamma.axis_x_1025[i].b; ++ ++ if (i < DX_GAMMA_RAMP_MAX - 1) ++ right = opp110-> ++ regamma.axis_x_1025[i + 1].b; ++ else ++ right = opp110-> ++ regamma.axis_x_1025[DX_GAMMA_RAMP_MAX-1].b; ++ } ++ ++ if (dal_fixed31_32_le(left, hw_point) && ++ dal_fixed31_32_le(hw_point, right)) { ++ *index_to_start = i; ++ *index_left = i; ++ ++ if (i < DX_GAMMA_RAMP_MAX - 1) ++ *index_right = i + 1; ++ else ++ *index_right = DX_GAMMA_RAMP_MAX - 1; ++ ++ *pos = HW_POINT_POSITION_MIDDLE; ++ ++ return true; ++ } else if ((i == *index_to_start) && ++ dal_fixed31_32_le(hw_point, left)) { ++ *index_to_start = i; ++ *index_left = i; ++ *index_right = i; ++ ++ *pos = HW_POINT_POSITION_LEFT; ++ ++ return true; ++ } else if ((i == max_number - 1) && ++ dal_fixed31_32_le(right, hw_point)) { ++ *index_to_start = i; ++ *index_left = i; ++ *index_right = i; ++ ++ *pos = HW_POINT_POSITION_RIGHT; ++ ++ return true; ++ } ++ ++ ++i; ++ } ++ ++ return false; ++} ++ ++static bool build_custom_gamma_mapping_coefficients_worker( ++ struct dce110_opp *opp110, ++ struct pixel_gamma_point *coeff, ++ enum channel_name channel, ++ uint32_t number_of_points, ++ enum pixel_format pixel_format) ++{ ++ uint32_t i = 0; ++ ++ while (i <= number_of_points) { ++ struct fixed31_32 coord_x; ++ ++ uint32_t index_to_start = 0; ++ uint32_t index_left = 0; ++ uint32_t index_right = 0; ++ ++ enum hw_point_position hw_pos; ++ ++ struct gamma_point *point; ++ ++ struct fixed31_32 left_pos; ++ struct fixed31_32 right_pos; ++ ++ if (pixel_format == PIXEL_FORMAT_FP16) ++ coord_x = opp110-> ++ regamma.coordinates_x[i].adjusted_x; ++ else if (channel == CHANNEL_NAME_RED) ++ coord_x = opp110-> ++ regamma.coordinates_x[i].regamma_y_red; ++ else if (channel == CHANNEL_NAME_GREEN) ++ coord_x = opp110-> ++ regamma.coordinates_x[i].regamma_y_green; ++ else ++ coord_x = opp110-> ++ regamma.coordinates_x[i].regamma_y_blue; ++ ++ if (!find_software_points( ++ opp110, coord_x, channel, ++ &index_to_start, &index_left, &index_right, &hw_pos)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (index_left >= RGB_256X3X16 + ++ opp110->regamma.extra_points) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (index_right >= RGB_256X3X16 + ++ opp110->regamma.extra_points) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (channel == CHANNEL_NAME_RED) { ++ point = &coeff[i].r; ++ ++ left_pos = opp110-> ++ regamma.axis_x_256[index_left].r; ++ right_pos = opp110-> ++ regamma.axis_x_256[index_right].r; ++ } else if (channel == CHANNEL_NAME_GREEN) { ++ point = &coeff[i].g; ++ ++ left_pos = opp110-> ++ regamma.axis_x_256[index_left].g; ++ right_pos = opp110-> ++ regamma.axis_x_256[index_right].g; ++ } else { ++ point = &coeff[i].b; ++ ++ left_pos = opp110-> ++ regamma.axis_x_256[index_left].b; ++ right_pos = opp110-> ++ regamma.axis_x_256[index_right].b; ++ } ++ ++ if (hw_pos == HW_POINT_POSITION_MIDDLE) ++ point->coeff = dal_fixed31_32_div( ++ dal_fixed31_32_sub( ++ coord_x, ++ left_pos), ++ dal_fixed31_32_sub( ++ right_pos, ++ left_pos)); ++ else if (hw_pos == HW_POINT_POSITION_LEFT) ++ point->coeff = opp110->regamma.x_min; ++ else if (hw_pos == HW_POINT_POSITION_RIGHT) ++ point->coeff = opp110->regamma.x_max2; ++ else { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ point->left_index = index_left; ++ point->right_index = index_right; ++ point->pos = hw_pos; ++ ++ ++i; ++ } ++ ++ return true; ++} ++ ++static inline bool build_custom_gamma_mapping_coefficients( ++ struct dce110_opp *opp110, ++ enum channel_name channel, ++ uint32_t number_of_points, ++ enum pixel_format pixel_format) ++{ ++ return build_custom_gamma_mapping_coefficients_worker( ++ opp110, opp110->regamma.coeff128, channel, ++ number_of_points, pixel_format); ++} ++ ++static inline bool build_oem_custom_gamma_mapping_coefficients( ++ struct dce110_opp *opp110, ++ enum channel_name channel, ++ uint32_t number_of_points, ++ enum pixel_format pixel_format) ++{ ++ return build_custom_gamma_mapping_coefficients_worker( ++ opp110, opp110->regamma.coeff128_oem, channel, ++ number_of_points, pixel_format); ++} ++ ++static bool build_custom_dx_gamma_mapping_coefficients( ++ struct dce110_opp *opp110, ++ enum channel_name channel, ++ uint32_t number_of_points, ++ enum pixel_format pixel_format) ++{ ++ uint32_t i = 0; ++ ++ while (i <= number_of_points) { ++ struct fixed31_32 coord_x; ++ ++ uint32_t index_to_start = 0; ++ uint32_t index_left = 0; ++ uint32_t index_right = 0; ++ ++ enum hw_point_position hw_pos; ++ ++ struct gamma_point *point; ++ ++ struct fixed31_32 left_pos; ++ struct fixed31_32 right_pos; ++ ++ if (pixel_format == PIXEL_FORMAT_FP16) ++ coord_x = opp110-> ++ regamma.coordinates_x[i].adjusted_x; ++ else if (channel == CHANNEL_NAME_RED) ++ coord_x = opp110-> ++ regamma.coordinates_x[i].regamma_y_red; ++ else if (channel == CHANNEL_NAME_GREEN) ++ coord_x = opp110-> ++ regamma.coordinates_x[i].regamma_y_green; ++ else ++ coord_x = opp110-> ++ regamma.coordinates_x[i].regamma_y_blue; ++ ++ if (!find_software_points_dx( ++ opp110, coord_x, channel, ++ &index_to_start, &index_left, &index_right, &hw_pos)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (index_left >= DX_GAMMA_RAMP_MAX + ++ opp110->regamma.extra_points) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (index_right >= DX_GAMMA_RAMP_MAX + ++ opp110->regamma.extra_points) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (channel == CHANNEL_NAME_RED) { ++ point = &opp110->regamma.coeff128_dx[i].r; ++ ++ left_pos = opp110-> ++ regamma.axis_x_1025[index_left].r; ++ right_pos = opp110-> ++ regamma.axis_x_1025[index_right].r; ++ } else if (channel == CHANNEL_NAME_GREEN) { ++ point = &opp110->regamma.coeff128_dx[i].g; ++ ++ left_pos = opp110-> ++ regamma.axis_x_1025[index_left].g; ++ right_pos = opp110-> ++ regamma.axis_x_1025[index_right].g; ++ } else { ++ point = &opp110->regamma.coeff128_dx[i].b; ++ ++ left_pos = opp110-> ++ regamma.axis_x_1025[index_left].b; ++ right_pos = opp110-> ++ regamma.axis_x_1025[index_right].b; ++ } ++ ++ if (hw_pos == HW_POINT_POSITION_MIDDLE) ++ point->coeff = dal_fixed31_32_div( ++ dal_fixed31_32_sub( ++ coord_x, ++ left_pos), ++ dal_fixed31_32_sub( ++ right_pos, ++ left_pos)); ++ else if (hw_pos == HW_POINT_POSITION_LEFT) ++ point->coeff = opp110->regamma.x_min; ++ else if (hw_pos == HW_POINT_POSITION_RIGHT) ++ point->coeff = opp110->regamma.x_max2; ++ else { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ point->left_index = index_left; ++ point->right_index = index_right; ++ point->pos = hw_pos; ++ ++ ++i; ++ } ++ ++ return true; ++} ++ ++static struct fixed31_32 calculate_mapped_value( ++ struct dce110_opp *opp110, ++ struct pwl_float_data *rgb, ++ const struct pixel_gamma_point *coeff, ++ enum channel_name channel, ++ uint32_t max_index) ++{ ++ const struct gamma_point *point; ++ ++ struct fixed31_32 result; ++ ++ if (channel == CHANNEL_NAME_RED) ++ point = &coeff->r; ++ else if (channel == CHANNEL_NAME_GREEN) ++ point = &coeff->g; ++ else ++ point = &coeff->b; ++ ++ if ((point->left_index < 0) || (point->left_index > max_index)) { ++ BREAK_TO_DEBUGGER(); ++ return dal_fixed31_32_zero; ++ } ++ ++ if ((point->right_index < 0) || (point->right_index > max_index)) { ++ BREAK_TO_DEBUGGER(); ++ return dal_fixed31_32_zero; ++ } ++ ++ if (point->pos == HW_POINT_POSITION_MIDDLE) ++ if (channel == CHANNEL_NAME_RED) ++ result = dal_fixed31_32_add( ++ dal_fixed31_32_mul( ++ point->coeff, ++ dal_fixed31_32_sub( ++ rgb[point->right_index].r, ++ rgb[point->left_index].r)), ++ rgb[point->left_index].r); ++ else if (channel == CHANNEL_NAME_GREEN) ++ result = dal_fixed31_32_add( ++ dal_fixed31_32_mul( ++ point->coeff, ++ dal_fixed31_32_sub( ++ rgb[point->right_index].g, ++ rgb[point->left_index].g)), ++ rgb[point->left_index].g); ++ else ++ result = dal_fixed31_32_add( ++ dal_fixed31_32_mul( ++ point->coeff, ++ dal_fixed31_32_sub( ++ rgb[point->right_index].b, ++ rgb[point->left_index].b)), ++ rgb[point->left_index].b); ++ else if (point->pos == HW_POINT_POSITION_LEFT) { ++ BREAK_TO_DEBUGGER(); ++ result = opp110->regamma.x_min; ++ } else { ++ BREAK_TO_DEBUGGER(); ++ result = opp110->regamma.x_max1; ++ } ++ ++ return result; ++} ++ ++static inline struct fixed31_32 calculate_regamma_user_mapped_value( ++ struct dce110_opp *opp110, ++ const struct pixel_gamma_point *coeff, ++ enum channel_name channel, ++ uint32_t max_index) ++{ ++ return calculate_mapped_value( ++ opp110, opp110->regamma.rgb_oem, ++ coeff, channel, max_index); ++} ++ ++static inline struct fixed31_32 calculate_user_mapped_value( ++ struct dce110_opp *opp110, ++ const struct pixel_gamma_point *coeff, ++ enum channel_name channel, ++ uint32_t max_index) ++{ ++ return calculate_mapped_value( ++ opp110, opp110->regamma.rgb_user, ++ coeff, channel, max_index); ++} ++ ++static inline struct fixed31_32 calculate_oem_mapped_value( ++ struct dce110_opp *opp110, ++ uint32_t index, ++ enum channel_name channel, ++ uint32_t max_index) ++{ ++ return calculate_regamma_user_mapped_value( ++ opp110, opp110->regamma.coeff128_oem + ++ index, channel, max_index); ++} ++ ++static void scale_oem_gamma( ++ struct dce110_opp *opp110, ++ const struct regamma_ramp *regamma_ramp) ++{ ++ const uint16_t max_driver = 0xFFFF; ++ const uint16_t max_os = 0xFF00; ++ ++ uint16_t scale = max_os; ++ ++ uint32_t i; ++ ++ struct pwl_float_data *rgb = opp110->regamma.rgb_oem; ++ struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1; ++ ++ /* find OEM maximum */ ++ ++ i = 0; ++ ++ do { ++ if ((regamma_ramp->gamma[i] > max_os) || ++ (regamma_ramp->gamma[i + RGB_256X3X16] > max_os) || ++ (regamma_ramp->gamma[i + 2 * RGB_256X3X16] > max_os)) { ++ scale = max_driver; ++ break; ++ } ++ ++ ++i; ++ } while (i != RGB_256X3X16); ++ ++ /* scale */ ++ ++ i = 0; ++ ++ do { ++ rgb->r = dal_fixed31_32_div_int( ++ dal_fixed31_32_from_int( ++ regamma_ramp->gamma[i]), ++ scale); ++ rgb->g = dal_fixed31_32_div_int( ++ dal_fixed31_32_from_int( ++ regamma_ramp->gamma[i + RGB_256X3X16]), ++ scale); ++ rgb->b = dal_fixed31_32_div_int( ++ dal_fixed31_32_from_int( ++ regamma_ramp->gamma[i + 2 * RGB_256X3X16]), ++ scale); ++ ++ ++rgb; ++ ++i; ++ } while (i != RGB_256X3X16); ++ ++ /* add 3 extra points, 2 physical plus 1 virtual */ ++ ++ rgb->r = dal_fixed31_32_mul(rgb_last->r, ++ opp110->regamma.divider1); ++ rgb->g = dal_fixed31_32_mul(rgb_last->g, ++ opp110->regamma.divider1); ++ rgb->b = dal_fixed31_32_mul(rgb_last->b, ++ opp110->regamma.divider1); ++ ++ ++rgb; ++ ++ rgb->r = dal_fixed31_32_mul(rgb_last->r, ++ opp110->regamma.divider2); ++ rgb->g = dal_fixed31_32_mul(rgb_last->g, ++ opp110->regamma.divider2); ++ rgb->b = dal_fixed31_32_mul(rgb_last->b, ++ opp110->regamma.divider2); ++ ++ ++rgb; ++ ++ rgb->r = dal_fixed31_32_mul(rgb_last->r, ++ opp110->regamma.divider3); ++ rgb->g = dal_fixed31_32_mul(rgb_last->g, ++ opp110->regamma.divider3); ++ rgb->b = dal_fixed31_32_mul(rgb_last->b, ++ opp110->regamma.divider3); ++} ++ ++static inline void copy_rgb_regamma_to_coordinates_x( ++ struct dce110_opp *opp110) ++{ ++ struct hw_x_point *coords = opp110->regamma.coordinates_x; ++ const struct pwl_float_data_ex *rgb_regamma = ++ opp110->regamma.rgb_regamma; ++ ++ uint32_t i = 0; ++ ++ while (i <= opp110->regamma.hw_points_num) { ++ coords->regamma_y_red = rgb_regamma->r; ++ coords->regamma_y_green = rgb_regamma->g; ++ coords->regamma_y_blue = rgb_regamma->b; ++ ++ ++coords; ++ ++rgb_regamma; ++ ++i; ++ } ++} ++ ++static bool calculate_interpolated_hardware_curve( ++ struct dce110_opp *opp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ struct pwl_result_data *rgb_resulted = ++ opp110->regamma.rgb_resulted; ++ ++ const struct pixel_gamma_point *coeff; ++ uint32_t max_entries = opp110->regamma.extra_points - 1; ++ ++ uint32_t i = 0; ++ ++ if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16) { ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_RED, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_GREEN, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_BLUE, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ coeff = opp110->regamma.coeff128; ++ max_entries += RGB_256X3X16; ++ } else if (gamma_ramp->type == GAMMA_RAMP_DXGI_1) { ++ if (!build_custom_dx_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_RED, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_dx_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_GREEN, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_dx_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_BLUE, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ coeff = opp110->regamma.coeff128_dx; ++ max_entries += DX_GAMMA_RAMP_MAX; ++ } else { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ while (i <= opp110->regamma.hw_points_num) { ++ rgb_resulted->red = calculate_user_mapped_value( ++ opp110, coeff, CHANNEL_NAME_RED, max_entries); ++ rgb_resulted->green = calculate_user_mapped_value( ++ opp110, coeff, CHANNEL_NAME_GREEN, max_entries); ++ rgb_resulted->blue = calculate_user_mapped_value( ++ opp110, coeff, CHANNEL_NAME_BLUE, max_entries); ++ ++ ++coeff; ++ ++rgb_resulted; ++ ++i; ++ } ++ ++ return true; ++} ++ ++static void map_standard_regamma_hw_to_x_user( ++ struct dce110_opp *opp110, ++ enum gamma_ramp_type type, ++ const struct gamma_parameters *params) ++{ ++ struct pwl_result_data *rgb_resulted = ++ opp110->regamma.rgb_resulted; ++ const struct pwl_float_data_ex *rgb_regamma = ++ opp110->regamma.rgb_regamma; ++ ++ uint32_t i = 0; ++ ++ while (i <= opp110->regamma.hw_points_num) { ++ rgb_resulted->red = rgb_regamma->r; ++ rgb_resulted->green = rgb_regamma->g; ++ rgb_resulted->blue = rgb_regamma->b; ++ ++ ++rgb_resulted; ++ ++rgb_regamma; ++ ++i; ++ } ++} ++ ++bool dce110_opp_map_legacy_and_regamma_hw_to_x_user( ++ struct output_pixel_processor *opp, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ ++ if (params->regamma.features.bits.GAMMA_RAMP_ARRAY || ++ params->regamma.features.bits.APPLY_DEGAMMA) { ++ ++ const uint32_t max_entries = ++ RGB_256X3X16 + opp110->regamma.extra_points - 1; ++ ++ const struct pixel_gamma_point *coeff = ++ opp110->regamma.coeff128; ++ struct pwl_result_data *rgb_resulted = ++ opp110->regamma.rgb_resulted; ++ ++ uint32_t i = 0; ++ ++ scale_oem_gamma(opp110, ¶ms->regamma.regamma_ramp); ++ ++ copy_rgb_regamma_to_coordinates_x(opp110); ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_RED, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_GREEN, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_BLUE, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ while (i <= opp110->regamma.hw_points_num) { ++ rgb_resulted->red = ++ calculate_regamma_user_mapped_value(opp110, ++ coeff, ++ CHANNEL_NAME_RED, max_entries); ++ rgb_resulted->green = ++ calculate_regamma_user_mapped_value(opp110, ++ coeff, ++ CHANNEL_NAME_GREEN, max_entries); ++ rgb_resulted->blue = ++ calculate_regamma_user_mapped_value(opp110, ++ coeff, ++ CHANNEL_NAME_BLUE, max_entries); ++ ++ ++coeff; ++ ++rgb_resulted; ++ ++i; ++ } ++ } else ++ map_standard_regamma_hw_to_x_user(opp110, ++ gamma_ramp->type, ++ params); ++ ++ return true; ++} ++ ++static bool map_regamma_hw_to_x_user( ++ struct dce110_opp *opp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ /* setup to spare calculated ideal regamma values */ ++ if (params->regamma.features.bits.GAMMA_RAMP_ARRAY || ++ params->regamma.features.bits.APPLY_DEGAMMA) { ++ ++ const uint32_t max_entries = ++ RGB_256X3X16 + opp110->regamma.extra_points - 1; ++ ++ const struct pixel_gamma_point *coeff = ++ opp110->regamma.coeff128; ++ struct hw_x_point *coords = ++ opp110->regamma.coordinates_x; ++ ++ uint32_t i = 0; ++ ++ scale_oem_gamma(opp110, ¶ms->regamma.regamma_ramp); ++ ++ copy_rgb_regamma_to_coordinates_x(opp110); ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_RED, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_GREEN, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_BLUE, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ while (i <= opp110->regamma.hw_points_num) { ++ coords->regamma_y_red = ++ calculate_regamma_user_mapped_value(opp110, ++ coeff, ++ CHANNEL_NAME_RED, max_entries); ++ coords->regamma_y_green = ++ calculate_regamma_user_mapped_value(opp110, ++ coeff, ++ CHANNEL_NAME_GREEN, max_entries); ++ coords->regamma_y_blue = ++ calculate_regamma_user_mapped_value(opp110, ++ coeff, ++ CHANNEL_NAME_BLUE, max_entries); ++ ++ ++coeff; ++ ++coords; ++ ++i; ++ } ++ } else { ++ copy_rgb_regamma_to_coordinates_x(opp110); ++ } ++ ++ return calculate_interpolated_hardware_curve(opp110, gamma_ramp, ++ params); ++} ++ ++static void build_regamma_coefficients( ++ const struct regamma_lut *regamma, ++ bool is_degamma_srgb, ++ struct gamma_coefficients *coefficients) ++{ ++ /* sRGB should apply 2.4 */ ++ static const int32_t numerator01[3] = { 31308, 31308, 31308 }; ++ static const int32_t numerator02[3] = { 12920, 12920, 12920 }; ++ static const int32_t numerator03[3] = { 55, 55, 55 }; ++ static const int32_t numerator04[3] = { 55, 55, 55 }; ++ static const int32_t numerator05[3] = { 2400, 2400, 2400 }; ++ ++ /* Non-sRGB should apply 2.2 */ ++ static const int32_t numerator11[3] = { 180000, 180000, 180000 }; ++ static const int32_t numerator12[3] = { 4500, 4500, 4500 }; ++ static const int32_t numerator13[3] = { 99, 99, 99 }; ++ static const int32_t numerator14[3] = { 99, 99, 99 }; ++ static const int32_t numerator15[3] = { 2200, 2200, 2200 }; ++ ++ const int32_t *numerator1; ++ const int32_t *numerator2; ++ const int32_t *numerator3; ++ const int32_t *numerator4; ++ const int32_t *numerator5; ++ ++ uint32_t i = 0; ++ ++ if (!regamma->features.bits.GAMMA_RAMP_ARRAY) { ++ numerator1 = regamma->gamma_coeff.a0; ++ numerator2 = regamma->gamma_coeff.a1; ++ numerator3 = regamma->gamma_coeff.a2; ++ numerator4 = regamma->gamma_coeff.a3; ++ numerator5 = regamma->gamma_coeff.gamma; ++ } else if (is_degamma_srgb) { ++ numerator1 = numerator01; ++ numerator2 = numerator02; ++ numerator3 = numerator03; ++ numerator4 = numerator04; ++ numerator5 = numerator05; ++ } else { ++ numerator1 = numerator11; ++ numerator2 = numerator12; ++ numerator3 = numerator13; ++ numerator4 = numerator14; ++ numerator5 = numerator15; ++ } ++ ++ do { ++ coefficients->a0[i] = dal_fixed31_32_from_fraction( ++ numerator1[i], 10000000); ++ coefficients->a1[i] = dal_fixed31_32_from_fraction( ++ numerator2[i], 1000); ++ coefficients->a2[i] = dal_fixed31_32_from_fraction( ++ numerator3[i], 1000); ++ coefficients->a3[i] = dal_fixed31_32_from_fraction( ++ numerator4[i], 1000); ++ coefficients->user_gamma[i] = dal_fixed31_32_from_fraction( ++ numerator5[i], 1000); ++ ++ ++i; ++ } while (i != ARRAY_SIZE(regamma->gamma_coeff.a0)); ++} ++ ++static struct fixed31_32 translate_from_linear_space( ++ struct fixed31_32 arg, ++ struct fixed31_32 a0, ++ struct fixed31_32 a1, ++ struct fixed31_32 a2, ++ struct fixed31_32 a3, ++ struct fixed31_32 gamma) ++{ ++ const struct fixed31_32 one = dal_fixed31_32_from_int(1); ++ ++ if (dal_fixed31_32_le(arg, dal_fixed31_32_neg(a0))) ++ return dal_fixed31_32_sub( ++ a2, ++ dal_fixed31_32_mul( ++ dal_fixed31_32_add( ++ one, ++ a3), ++ dal_fixed31_32_pow( ++ dal_fixed31_32_neg(arg), ++ dal_fixed31_32_recip(gamma)))); ++ else if (dal_fixed31_32_le(a0, arg)) ++ return dal_fixed31_32_sub( ++ dal_fixed31_32_mul( ++ dal_fixed31_32_add( ++ one, ++ a3), ++ dal_fixed31_32_pow( ++ arg, ++ dal_fixed31_32_recip(gamma))), ++ a2); ++ else ++ return dal_fixed31_32_mul( ++ arg, ++ a1); ++} ++ ++static inline struct fixed31_32 translate_from_linear_space_ex( ++ struct fixed31_32 arg, ++ struct gamma_coefficients *coeff, ++ uint32_t color_index) ++{ ++ return translate_from_linear_space( ++ arg, ++ coeff->a0[color_index], ++ coeff->a1[color_index], ++ coeff->a2[color_index], ++ coeff->a3[color_index], ++ coeff->user_gamma[color_index]); ++} ++ ++static bool build_regamma_curve( ++ struct dce110_opp *opp110, ++ const struct gamma_parameters *params) ++{ ++ struct pwl_float_data_ex *rgb = opp110->regamma.rgb_regamma; ++ ++ uint32_t i; ++ ++ if (!params->regamma.features.bits.GAMMA_RAMP_ARRAY && ++ params->regamma.features.bits.APPLY_DEGAMMA) { ++ struct gamma_coefficients coeff; ++ ++ struct hw_x_point *coord_x = ++ opp110->regamma.coordinates_x; ++ ++ build_regamma_coefficients( ++ ¶ms->regamma, ++ params->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB, ++ &coeff); ++ ++ /* Use opp110->regamma.coordinates_x to retrieve ++ * coordinates chosen base on given user curve (future task). ++ * The x values are exponentially distributed and currently ++ * it is hard-coded, the user curve shape is ignored. ++ * The future task is to recalculate opp110- ++ * regamma.coordinates_x based on input/user curve, ++ * translation from 256/1025 to 128 pwl points. ++ */ ++ ++ i = 0; ++ ++ while (i != opp110->regamma.hw_points_num + 1) { ++ rgb->r = translate_from_linear_space_ex( ++ coord_x->adjusted_x, &coeff, 0); ++ rgb->g = translate_from_linear_space_ex( ++ coord_x->adjusted_x, &coeff, 1); ++ rgb->b = translate_from_linear_space_ex( ++ coord_x->adjusted_x, &coeff, 2); ++ ++ ++coord_x; ++ ++rgb; ++ ++i; ++ } ++ } else { ++ const uint32_t max_entries = ++ RGB_256X3X16 + opp110->regamma.extra_points - 1; ++ ++ /* interpolate between 256 input points and output 185 points */ ++ ++ scale_oem_gamma(opp110, ¶ms->regamma.regamma_ramp); ++ ++ if (!build_oem_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_RED, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_oem_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_GREEN, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!build_oem_custom_gamma_mapping_coefficients( ++ opp110, CHANNEL_NAME_BLUE, ++ opp110->regamma.hw_points_num, ++ params->surface_pixel_format)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ i = 0; ++ ++ while (i != opp110->regamma.hw_points_num + 1) { ++ rgb->r = calculate_oem_mapped_value( ++ opp110, i, CHANNEL_NAME_RED, max_entries); ++ rgb->g = calculate_oem_mapped_value( ++ opp110, i, CHANNEL_NAME_GREEN, max_entries); ++ rgb->b = calculate_oem_mapped_value( ++ opp110, i, CHANNEL_NAME_BLUE, max_entries); ++ ++rgb; ++ ++i; ++ } ++ } ++ ++ return true; ++} ++ ++static void build_new_custom_resulted_curve( ++ struct dce110_opp *opp110, ++ const struct gamma_parameters *params) ++{ ++ struct pwl_result_data *rgb = opp110->regamma.rgb_resulted; ++ struct pwl_result_data *rgb_plus_1 = rgb + 1; ++ ++ uint32_t i; ++ ++ i = 0; ++ ++ while (i != opp110->regamma.hw_points_num + 1) { ++ rgb->red = dal_fixed31_32_clamp( ++ rgb->red, opp110->regamma.x_min, ++ opp110->regamma.x_max1); ++ rgb->green = dal_fixed31_32_clamp( ++ rgb->green, opp110->regamma.x_min, ++ opp110->regamma.x_max1); ++ rgb->blue = dal_fixed31_32_clamp( ++ rgb->blue, opp110->regamma.x_min, ++ opp110->regamma.x_max1); ++ ++ ++rgb; ++ ++i; ++ } ++ ++ rgb = opp110->regamma.rgb_resulted; ++ ++ i = 1; ++ ++ while (i != opp110->regamma.hw_points_num + 1) { ++ if (dal_fixed31_32_lt(rgb_plus_1->red, rgb->red)) ++ rgb_plus_1->red = rgb->red; ++ if (dal_fixed31_32_lt(rgb_plus_1->green, rgb->green)) ++ rgb_plus_1->green = rgb->green; ++ if (dal_fixed31_32_lt(rgb_plus_1->blue, rgb->blue)) ++ rgb_plus_1->blue = rgb->blue; ++ ++ rgb->delta_red = dal_fixed31_32_sub( ++ rgb_plus_1->red, ++ rgb->red); ++ rgb->delta_green = dal_fixed31_32_sub( ++ rgb_plus_1->green, ++ rgb->green); ++ rgb->delta_blue = dal_fixed31_32_sub( ++ rgb_plus_1->blue, ++ rgb->blue); ++ ++ ++rgb_plus_1; ++ ++rgb; ++ ++i; ++ } ++} ++ ++static bool rebuild_curve_configuration_magic( ++ struct dce110_opp *opp110) ++{ ++ const struct fixed31_32 magic_number = ++ dal_fixed31_32_from_fraction(249, 1000); ++ ++ struct fixed31_32 y_r; ++ struct fixed31_32 y_g; ++ struct fixed31_32 y_b; ++ ++ struct fixed31_32 y1_min; ++ struct fixed31_32 y2_max; ++ struct fixed31_32 y3_max; ++ ++ y_r = opp110->regamma.rgb_resulted[0].red; ++ y_g = opp110->regamma.rgb_resulted[0].green; ++ y_b = opp110->regamma.rgb_resulted[0].blue; ++ ++ y1_min = dal_fixed31_32_min(y_r, dal_fixed31_32_min(y_g, y_b)); ++ ++ opp110->regamma.arr_points[0].x = ++ opp110->regamma.coordinates_x[0].adjusted_x; ++ opp110->regamma.arr_points[0].y = y1_min; ++ opp110->regamma.arr_points[0].slope = dal_fixed31_32_div( ++ opp110->regamma.arr_points[0].y, ++ opp110->regamma.arr_points[0].x); ++ ++ opp110->regamma.arr_points[1].x = dal_fixed31_32_add( ++ opp110->regamma.coordinates_x ++ [opp110->regamma.hw_points_num - 1].adjusted_x, ++ magic_number); ++ ++ opp110->regamma.arr_points[2].x = ++ opp110->regamma.arr_points[1].x; ++ ++ y_r = opp110->regamma.rgb_resulted ++ [opp110->regamma.hw_points_num - 1].red; ++ y_g = opp110->regamma.rgb_resulted ++ [opp110->regamma.hw_points_num - 1].green; ++ y_b = opp110->regamma.rgb_resulted ++ [opp110->regamma.hw_points_num - 1].blue; ++ ++ y2_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); ++ ++ opp110->regamma.arr_points[1].y = y2_max; ++ ++ y_r = opp110->regamma.rgb_resulted ++ [opp110->regamma.hw_points_num].red; ++ y_g = opp110->regamma.rgb_resulted ++ [opp110->regamma.hw_points_num].green; ++ y_b = opp110->regamma.rgb_resulted ++ [opp110->regamma.hw_points_num].blue; ++ ++ y3_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); ++ ++ opp110->regamma.arr_points[2].y = y3_max; ++ ++ opp110->regamma.arr_points[2].slope = dal_fixed31_32_one; ++ ++ return true; ++} ++ ++static bool build_custom_float( ++ struct fixed31_32 value, ++ const struct custom_float_format *format, ++ bool *negative, ++ uint32_t *mantissa, ++ uint32_t *exponenta) ++{ ++ uint32_t exp_offset = (1 << (format->exponenta_bits - 1)) - 1; ++ ++ const struct fixed31_32 mantissa_constant_plus_max_fraction = ++ dal_fixed31_32_from_fraction( ++ (1LL << (format->mantissa_bits + 1)) - 1, ++ 1LL << format->mantissa_bits); ++ ++ struct fixed31_32 mantiss; ++ ++ if (dal_fixed31_32_eq( ++ value, ++ dal_fixed31_32_zero)) { ++ *negative = false; ++ *mantissa = 0; ++ *exponenta = 0; ++ return true; ++ } ++ ++ if (dal_fixed31_32_lt( ++ value, ++ dal_fixed31_32_zero)) { ++ *negative = format->sign; ++ value = dal_fixed31_32_neg(value); ++ } else { ++ *negative = false; ++ } ++ ++ if (dal_fixed31_32_lt( ++ value, ++ dal_fixed31_32_one)) { ++ uint32_t i = 1; ++ ++ do { ++ value = dal_fixed31_32_shl(value, 1); ++ ++i; ++ } while (dal_fixed31_32_lt( ++ value, ++ dal_fixed31_32_one)); ++ ++ --i; ++ ++ if (exp_offset <= i) { ++ *mantissa = 0; ++ *exponenta = 0; ++ return true; ++ } ++ ++ *exponenta = exp_offset - i; ++ } else if (dal_fixed31_32_le( ++ mantissa_constant_plus_max_fraction, ++ value)) { ++ uint32_t i = 1; ++ ++ do { ++ value = dal_fixed31_32_shr(value, 1); ++ ++i; ++ } while (dal_fixed31_32_lt( ++ mantissa_constant_plus_max_fraction, ++ value)); ++ ++ *exponenta = exp_offset + i - 1; ++ } else { ++ *exponenta = exp_offset; ++ } ++ ++ mantiss = dal_fixed31_32_sub( ++ value, ++ dal_fixed31_32_one); ++ ++ if (dal_fixed31_32_lt( ++ mantiss, ++ dal_fixed31_32_zero) || ++ dal_fixed31_32_lt( ++ dal_fixed31_32_one, ++ mantiss)) ++ mantiss = dal_fixed31_32_zero; ++ else ++ mantiss = dal_fixed31_32_shl( ++ mantiss, ++ format->mantissa_bits); ++ ++ *mantissa = dal_fixed31_32_floor(mantiss); ++ ++ return true; ++} ++ ++static bool setup_custom_float( ++ const struct custom_float_format *format, ++ bool negative, ++ uint32_t mantissa, ++ uint32_t exponenta, ++ uint32_t *result) ++{ ++ uint32_t i = 0; ++ uint32_t j = 0; ++ ++ uint32_t value = 0; ++ ++ /* verification code: ++ * once calculation is ok we can remove it */ ++ ++ const uint32_t mantissa_mask = ++ (1 << (format->mantissa_bits + 1)) - 1; ++ ++ const uint32_t exponenta_mask = ++ (1 << (format->exponenta_bits + 1)) - 1; ++ ++ if (mantissa & ~mantissa_mask) { ++ BREAK_TO_DEBUGGER(); ++ mantissa = mantissa_mask; ++ } ++ ++ if (exponenta & ~exponenta_mask) { ++ BREAK_TO_DEBUGGER(); ++ exponenta = exponenta_mask; ++ } ++ ++ /* end of verification code */ ++ ++ while (i < format->mantissa_bits) { ++ uint32_t mask = 1 << i; ++ ++ if (mantissa & mask) ++ value |= mask; ++ ++ ++i; ++ } ++ ++ while (j < format->exponenta_bits) { ++ uint32_t mask = 1 << j; ++ ++ if (exponenta & mask) ++ value |= mask << i; ++ ++ ++j; ++ } ++ ++ if (negative && format->sign) ++ value |= 1 << (i + j); ++ ++ *result = value; ++ ++ return true; ++} ++ ++static bool convert_to_custom_float_format( ++ struct fixed31_32 value, ++ const struct custom_float_format *format, ++ uint32_t *result) ++{ ++ uint32_t mantissa; ++ uint32_t exponenta; ++ bool negative; ++ ++ return build_custom_float( ++ value, format, &negative, &mantissa, &exponenta) && ++ setup_custom_float( ++ format, negative, mantissa, exponenta, result); ++} ++ ++static bool convert_to_custom_float_format_ex( ++ struct fixed31_32 value, ++ const struct custom_float_format *format, ++ struct custom_float_value *result) ++{ ++ return build_custom_float( ++ value, format, ++ &result->negative, &result->mantissa, &result->exponenta) && ++ setup_custom_float( ++ format, result->negative, result->mantissa, result->exponenta, ++ &result->value); ++} ++ ++static bool convert_to_custom_float( ++ struct dce110_opp *opp110) ++{ ++ struct custom_float_format fmt; ++ ++ struct pwl_result_data *rgb = opp110->regamma.rgb_resulted; ++ ++ uint32_t i = 0; ++ ++ fmt.exponenta_bits = 6; ++ fmt.mantissa_bits = 12; ++ fmt.sign = true; ++ ++ if (!convert_to_custom_float_format( ++ opp110->regamma.arr_points[0].x, ++ &fmt, ++ &opp110->regamma.arr_points[0].custom_float_x)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ opp110->regamma.arr_points[0].offset, ++ &fmt, ++ &opp110->regamma.arr_points[0].custom_float_offset)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ opp110->regamma.arr_points[0].slope, ++ &fmt, ++ &opp110->regamma.arr_points[0].custom_float_slope)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ fmt.mantissa_bits = 10; ++ fmt.sign = false; ++ ++ if (!convert_to_custom_float_format( ++ opp110->regamma.arr_points[1].x, ++ &fmt, ++ &opp110->regamma.arr_points[1].custom_float_x)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ opp110->regamma.arr_points[1].y, ++ &fmt, ++ &opp110->regamma.arr_points[1].custom_float_y)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ opp110->regamma.arr_points[2].slope, ++ &fmt, ++ &opp110->regamma.arr_points[2].custom_float_slope)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ fmt.mantissa_bits = 12; ++ fmt.sign = true; ++ ++ while (i != opp110->regamma.hw_points_num) { ++ if (!convert_to_custom_float_format( ++ rgb->red, ++ &fmt, ++ &rgb->red_reg)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ rgb->green, ++ &fmt, ++ &rgb->green_reg)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ rgb->blue, ++ &fmt, ++ &rgb->blue_reg)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ rgb->delta_red, ++ &fmt, ++ &rgb->delta_red_reg)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ rgb->delta_green, ++ &fmt, ++ &rgb->delta_green_reg)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!convert_to_custom_float_format( ++ rgb->delta_blue, ++ &fmt, ++ &rgb->delta_blue_reg)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ ++rgb; ++ ++i; ++ } ++ ++ return true; ++} ++ ++static bool round_custom_float_6_12( ++ struct hw_x_point *x) ++{ ++ struct custom_float_format fmt; ++ ++ struct custom_float_value value; ++ ++ fmt.exponenta_bits = 6; ++ fmt.mantissa_bits = 12; ++ fmt.sign = true; ++ ++ if (!convert_to_custom_float_format_ex( ++ x->x, &fmt, &value)) ++ return false; ++ ++ x->adjusted_x = x->x; ++ ++ if (value.mantissa) { ++ BREAK_TO_DEBUGGER(); ++ ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool build_hw_curve_configuration( ++ const struct curve_config *curve_config, ++ struct gamma_curve *gamma_curve, ++ struct curve_points *curve_points, ++ struct hw_x_point *points, ++ uint32_t *number_of_points) ++{ ++ const int8_t max_regions_number = ARRAY_SIZE(curve_config->segments); ++ ++ int8_t i; ++ ++ uint8_t segments_calculation[8] = { 0 }; ++ ++ struct fixed31_32 region1 = dal_fixed31_32_zero; ++ struct fixed31_32 region2; ++ struct fixed31_32 increment; ++ ++ uint32_t index = 0; ++ uint32_t segments = 0; ++ uint32_t max_number; ++ ++ bool result = false; ++ ++ if (!number_of_points) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ max_number = *number_of_points; ++ ++ i = 0; ++ ++ while (i != max_regions_number) { ++ gamma_curve[i].offset = 0; ++ gamma_curve[i].segments_num = 0; ++ ++ ++i; ++ } ++ ++ i = 0; ++ ++ while (i != max_regions_number) { ++ /* number should go in uninterruptible sequence */ ++ if (curve_config->segments[i] == -1) ++ break; ++ ++ ASSERT(curve_config->segments[i] >= 0); ++ ++ segments += (1 << curve_config->segments[i]); ++ ++ ++i; ++ } ++ ++ if (segments > max_number) { ++ BREAK_TO_DEBUGGER(); ++ } else { ++ int32_t divisor; ++ uint32_t offset = 0; ++ int8_t begin = curve_config->begin; ++ int32_t region_number = 0; ++ ++ i = begin; ++ ++ while ((index < max_number) && ++ (region_number < max_regions_number) && ++ (i <= 1)) { ++ int32_t j = 0; ++ ++ segments = curve_config->segments[region_number]; ++ divisor = 1 << segments; ++ ++ if (segments == -1) { ++ if (i > 0) { ++ region1 = dal_fixed31_32_shl( ++ dal_fixed31_32_one, ++ i - 1); ++ region2 = dal_fixed31_32_shl( ++ dal_fixed31_32_one, ++ i); ++ } else { ++ region1 = dal_fixed31_32_shr( ++ dal_fixed31_32_one, ++ -(i - 1)); ++ region2 = dal_fixed31_32_shr( ++ dal_fixed31_32_one, ++ -i); ++ } ++ ++ break; ++ } ++ ++ if (i > -1) { ++ region1 = dal_fixed31_32_shl( ++ dal_fixed31_32_one, ++ i); ++ region2 = dal_fixed31_32_shl( ++ dal_fixed31_32_one, ++ i + 1); ++ } else { ++ region1 = dal_fixed31_32_shr( ++ dal_fixed31_32_one, ++ -i); ++ region2 = dal_fixed31_32_shr( ++ dal_fixed31_32_one, ++ -(i + 1)); ++ } ++ ++ gamma_curve[region_number].offset = offset; ++ gamma_curve[region_number].segments_num = segments; ++ ++ offset += divisor; ++ ++ ++segments_calculation[segments]; ++ ++ increment = dal_fixed31_32_div_int( ++ dal_fixed31_32_sub( ++ region2, ++ region1), ++ divisor); ++ ++ points[index].x = region1; ++ ++ round_custom_float_6_12(points + index); ++ ++ ++index; ++ ++region_number; ++ ++ while ((index < max_number) && (j < divisor - 1)) { ++ region1 = dal_fixed31_32_add( ++ region1, ++ increment); ++ ++ points[index].x = region1; ++ points[index].adjusted_x = region1; ++ ++ ++index; ++ ++j; ++ } ++ ++ ++i; ++ } ++ ++ points[index].x = region1; ++ ++ round_custom_float_6_12(points + index); ++ ++ *number_of_points = index; ++ ++ result = true; ++ } ++ ++ curve_points[0].x = points[0].adjusted_x; ++ curve_points[0].offset = dal_fixed31_32_zero; ++ ++ curve_points[1].x = points[index - 1].adjusted_x; ++ curve_points[1].offset = dal_fixed31_32_zero; ++ ++ curve_points[2].x = points[index].adjusted_x; ++ curve_points[2].offset = dal_fixed31_32_zero; ++ ++ return result; ++} ++ ++static bool setup_distribution_points( ++ struct dce110_opp *opp110) ++{ ++ uint32_t hw_points_num = MAX_PWL_ENTRY * 2; ++ ++ struct curve_config cfg; ++ ++ cfg.offset = 0; ++ ++ cfg.segments[0] = 3; ++ cfg.segments[1] = 4; ++ cfg.segments[2] = 4; ++ cfg.segments[3] = 4; ++ cfg.segments[4] = 4; ++ cfg.segments[5] = 4; ++ cfg.segments[6] = 4; ++ cfg.segments[7] = 4; ++ cfg.segments[8] = 5; ++ cfg.segments[9] = 5; ++ cfg.segments[10] = 0; ++ cfg.segments[11] = -1; ++ cfg.segments[12] = -1; ++ cfg.segments[13] = -1; ++ cfg.segments[14] = -1; ++ cfg.segments[15] = -1; ++ ++ cfg.begin = -10; ++ ++ if (!build_hw_curve_configuration( ++ &cfg, opp110->regamma.arr_curve_points, ++ opp110->regamma.arr_points, ++ opp110->regamma.coordinates_x, &hw_points_num)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ opp110->regamma.hw_points_num = hw_points_num; ++ ++ return true; ++} ++ ++ ++/* ++ ***************************************************************************** ++ * Function: regamma_config_regions_and_segments ++ * ++ * build regamma curve by using predefined hw points ++ * uses interface parameters ,like EDID coeff. ++ * ++ * @param : parameters interface parameters ++ * @return void ++ * ++ * @note ++ * ++ * @see ++ * ++ ***************************************************************************** ++ */ ++static void regamma_config_regions_and_segments( ++ struct dce110_opp *opp110) ++{ ++ struct gamma_curve *curve; ++ uint32_t value = 0; ++ ++ { ++ set_reg_field_value( ++ value, ++ opp110->regamma.arr_points[0].custom_float_x, ++ REGAMMA_CNTLA_START_CNTL, ++ REGAMMA_CNTLA_EXP_REGION_START); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ REGAMMA_CNTLA_START_CNTL, ++ REGAMMA_CNTLA_EXP_REGION_START_SEGMENT); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_START_CNTL), ++ value); ++ } ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ opp110->regamma.arr_points[0].custom_float_slope, ++ REGAMMA_CNTLA_SLOPE_CNTL, ++ REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_SLOPE_CNTL), value); ++ } ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ opp110->regamma.arr_points[1].custom_float_x, ++ REGAMMA_CNTLA_END_CNTL1, ++ REGAMMA_CNTLA_EXP_REGION_END); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_END_CNTL1), value); ++ } ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ opp110->regamma.arr_points[2].custom_float_slope, ++ REGAMMA_CNTLA_END_CNTL2, ++ REGAMMA_CNTLA_EXP_REGION_END_BASE); ++ ++ set_reg_field_value( ++ value, ++ opp110->regamma.arr_points[1].custom_float_y, ++ REGAMMA_CNTLA_END_CNTL2, ++ REGAMMA_CNTLA_EXP_REGION_END_SLOPE); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_END_CNTL2), value); ++ } ++ ++ curve = opp110->regamma.arr_curve_points; ++ ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_0_1, ++ REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_0_1, ++ REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_0_1, ++ REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_0_1, ++ REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS); ++ ++ dal_write_reg( ++ opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_0_1), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_2_3, ++ REGAMMA_CNTLA_EXP_REGION2_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_2_3, ++ REGAMMA_CNTLA_EXP_REGION2_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_2_3, ++ REGAMMA_CNTLA_EXP_REGION3_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_2_3, ++ REGAMMA_CNTLA_EXP_REGION3_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_2_3), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_4_5, ++ REGAMMA_CNTLA_EXP_REGION4_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_4_5, ++ REGAMMA_CNTLA_EXP_REGION4_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_4_5, ++ REGAMMA_CNTLA_EXP_REGION5_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_4_5, ++ REGAMMA_CNTLA_EXP_REGION5_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_4_5), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_6_7, ++ REGAMMA_CNTLA_EXP_REGION6_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_6_7, ++ REGAMMA_CNTLA_EXP_REGION6_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_6_7, ++ REGAMMA_CNTLA_EXP_REGION7_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_6_7, ++ REGAMMA_CNTLA_EXP_REGION7_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_6_7), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_8_9, ++ REGAMMA_CNTLA_EXP_REGION8_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_8_9, ++ REGAMMA_CNTLA_EXP_REGION8_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_8_9, ++ REGAMMA_CNTLA_EXP_REGION9_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_8_9, ++ REGAMMA_CNTLA_EXP_REGION9_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_8_9), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_10_11, ++ REGAMMA_CNTLA_EXP_REGION10_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_10_11, ++ REGAMMA_CNTLA_EXP_REGION10_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_10_11, ++ REGAMMA_CNTLA_EXP_REGION11_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_10_11, ++ REGAMMA_CNTLA_EXP_REGION11_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_10_11), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_12_13, ++ REGAMMA_CNTLA_EXP_REGION12_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_12_13, ++ REGAMMA_CNTLA_EXP_REGION12_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_12_13, ++ REGAMMA_CNTLA_EXP_REGION13_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_12_13, ++ REGAMMA_CNTLA_EXP_REGION13_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_12_13), ++ value); ++ } ++ ++ curve += 2; ++ { ++ value = 0; ++ set_reg_field_value( ++ value, ++ curve[0].offset, ++ REGAMMA_CNTLA_REGION_14_15, ++ REGAMMA_CNTLA_EXP_REGION14_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[0].segments_num, ++ REGAMMA_CNTLA_REGION_14_15, ++ REGAMMA_CNTLA_EXP_REGION14_NUM_SEGMENTS); ++ ++ set_reg_field_value( ++ value, ++ curve[1].offset, ++ REGAMMA_CNTLA_REGION_14_15, ++ REGAMMA_CNTLA_EXP_REGION15_LUT_OFFSET); ++ ++ set_reg_field_value( ++ value, ++ curve[1].segments_num, ++ REGAMMA_CNTLA_REGION_14_15, ++ REGAMMA_CNTLA_EXP_REGION15_NUM_SEGMENTS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_CNTLA_REGION_14_15), ++ value); ++ } ++} ++ ++static void program_pwl( ++ struct dce110_opp *opp110, ++ const struct gamma_parameters *params) ++{ ++ uint32_t value; ++ ++ { ++ uint8_t max_tries = 10; ++ uint8_t counter = 0; ++ ++ /* Power on LUT memory */ ++ value = dal_read_reg(opp110->base.ctx, ++ DCFE_REG(mmDCFE_MEM_PWR_CTRL)); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DCFE_MEM_PWR_CTRL, ++ DCP_REGAMMA_MEM_PWR_DIS); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); ++ ++ while (counter < max_tries) { ++ value = ++ dal_read_reg( ++ opp110->base.ctx, ++ DCFE_REG(mmDCFE_MEM_PWR_STATUS)); ++ ++ if (get_reg_field_value( ++ value, ++ DCFE_MEM_PWR_STATUS, ++ DCP_REGAMMA_MEM_PWR_STATE) == 0) ++ break; ++ ++ ++counter; ++ } ++ ++ if (counter == max_tries) { ++ dal_logger_write(opp110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "%s: regamma lut was not powered on " ++ "in a timely manner," ++ " programming still proceeds\n", ++ __func__); ++ } ++ } ++ ++ value = 0; ++ ++ set_reg_field_value( ++ value, ++ 7, ++ REGAMMA_LUT_WRITE_EN_MASK, ++ REGAMMA_LUT_WRITE_EN_MASK); ++ ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_LUT_WRITE_EN_MASK), value); ++ dal_write_reg(opp110->base.ctx, ++ DCP_REG(mmREGAMMA_LUT_INDEX), 0); ++ ++ /* Program REGAMMA_LUT_DATA */ ++ { ++ const uint32_t addr = DCP_REG(mmREGAMMA_LUT_DATA); ++ ++ uint32_t i = 0; ++ ++ struct pwl_result_data *rgb = ++ opp110->regamma.rgb_resulted; ++ ++ while (i != opp110->regamma.hw_points_num) { ++ dal_write_reg(opp110->base.ctx, addr, rgb->red_reg); ++ dal_write_reg(opp110->base.ctx, addr, rgb->green_reg); ++ dal_write_reg(opp110->base.ctx, addr, rgb->blue_reg); ++ ++ dal_write_reg(opp110->base.ctx, addr, ++ rgb->delta_red_reg); ++ dal_write_reg(opp110->base.ctx, addr, ++ rgb->delta_green_reg); ++ dal_write_reg(opp110->base.ctx, addr, ++ rgb->delta_blue_reg); ++ ++ ++rgb; ++ ++i; ++ } ++ } ++ ++ /* we are done with DCP LUT memory; re-enable low power mode */ ++ value = dal_read_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL)); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DCFE_MEM_PWR_CTRL, ++ DCP_REGAMMA_MEM_PWR_DIS); ++ ++ dal_write_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); ++} ++ ++void dce110_opp_power_on_regamma_lut( ++ struct output_pixel_processor *opp, ++ bool power_on) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ ++ uint32_t value = ++ dal_read_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL)); ++ ++ set_reg_field_value( ++ value, ++ power_on, ++ DCFE_MEM_PWR_CTRL, ++ DCP_REGAMMA_MEM_PWR_DIS); ++ ++ set_reg_field_value( ++ value, ++ power_on, ++ DCFE_MEM_PWR_CTRL, ++ DCP_LUT_MEM_PWR_DIS); ++ ++ dal_write_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); ++} ++ ++static bool scale_gamma( ++ struct dce110_opp *opp110, ++ const struct gamma_ramp *gamma_ramp, ++ const struct gamma_parameters *params) ++{ ++ const struct gamma_ramp_rgb256x3x16 *gamma; ++ bool use_palette = params->surface_pixel_format == PIXEL_FORMAT_INDEX8; ++ ++ const uint16_t max_driver = 0xFFFF; ++ const uint16_t max_os = 0xFF00; ++ ++ uint16_t scaler = max_os; ++ ++ uint32_t i; ++ ++ struct dev_c_lut *palette = opp110->regamma.saved_palette; ++ ++ struct pwl_float_data *rgb = opp110->regamma.rgb_user; ++ struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1; ++ ++ if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16) ++ gamma = &gamma_ramp->gamma_ramp_rgb256x3x16; ++ else ++ return false; /* invalid option */ ++ ++ i = 0; ++ ++ do { ++ if ((gamma->red[i] > max_os) || ++ (gamma->green[i] > max_os) || ++ (gamma->blue[i] > max_os)) { ++ scaler = max_driver; ++ break; ++ } ++ ++i; ++ } while (i != RGB_256X3X16); ++ ++ i = 0; ++ ++ if (use_palette) ++ do { ++ rgb->r = dal_fixed31_32_from_fraction( ++ gamma->red[palette->red], scaler); ++ rgb->g = dal_fixed31_32_from_fraction( ++ gamma->green[palette->green], scaler); ++ rgb->b = dal_fixed31_32_from_fraction( ++ gamma->blue[palette->blue], scaler); ++ ++ ++palette; ++ ++rgb; ++ ++i; ++ } while (i != RGB_256X3X16); ++ else ++ do { ++ rgb->r = dal_fixed31_32_from_fraction( ++ gamma->red[i], scaler); ++ rgb->g = dal_fixed31_32_from_fraction( ++ gamma->green[i], scaler); ++ rgb->b = dal_fixed31_32_from_fraction( ++ gamma->blue[i], scaler); ++ ++ ++rgb; ++ ++i; ++ } while (i != RGB_256X3X16); ++ ++ rgb->r = dal_fixed31_32_mul(rgb_last->r, ++ opp110->regamma.divider1); ++ rgb->g = dal_fixed31_32_mul(rgb_last->g, ++ opp110->regamma.divider1); ++ rgb->b = dal_fixed31_32_mul(rgb_last->b, ++ opp110->regamma.divider1); ++ ++ ++rgb; ++ ++ rgb->r = dal_fixed31_32_mul(rgb_last->r, ++ opp110->regamma.divider2); ++ rgb->g = dal_fixed31_32_mul(rgb_last->g, ++ opp110->regamma.divider2); ++ rgb->b = dal_fixed31_32_mul(rgb_last->b, ++ opp110->regamma.divider2); ++ ++ ++rgb; ++ ++ rgb->r = dal_fixed31_32_mul(rgb_last->r, ++ opp110->regamma.divider3); ++ rgb->g = dal_fixed31_32_mul(rgb_last->g, ++ opp110->regamma.divider3); ++ rgb->b = dal_fixed31_32_mul(rgb_last->b, ++ opp110->regamma.divider3); ++ ++ return true; ++} ++ ++ ++static void configure_regamma_mode( ++ struct dce110_opp *opp110, ++ const struct gamma_parameters *params, ++ bool force_bypass) ++{ ++ const uint32_t addr = DCP_REG(mmREGAMMA_CONTROL); ++ ++ enum wide_gamut_regamma_mode mode = ++ WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A; ++ ++ uint32_t value = dal_read_reg(opp110->base.ctx, addr); ++ ++ if (force_bypass) { ++ ++ set_reg_field_value( ++ value, ++ 0, ++ REGAMMA_CONTROL, ++ GRPH_REGAMMA_MODE); ++ ++ dal_write_reg(opp110->base.ctx, addr, value); ++ ++ return; ++ } ++ ++ if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_BYPASS) ++ mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS; ++ else if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_HW) { ++ if (params->surface_pixel_format == PIXEL_FORMAT_FP16) ++ mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS; ++ else ++ mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24; ++ } ++ ++ switch (mode) { ++ case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS: ++ set_reg_field_value( ++ value, ++ 0, ++ REGAMMA_CONTROL, ++ GRPH_REGAMMA_MODE); ++ break; ++ case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24: ++ set_reg_field_value( ++ value, ++ 1, ++ REGAMMA_CONTROL, ++ GRPH_REGAMMA_MODE); ++ break; ++ case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22: ++ set_reg_field_value( ++ value, ++ 2, ++ REGAMMA_CONTROL, ++ GRPH_REGAMMA_MODE); ++ break; ++ case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A: ++ set_reg_field_value( ++ value, ++ 3, ++ REGAMMA_CONTROL, ++ GRPH_REGAMMA_MODE); ++ break; ++ case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B: ++ set_reg_field_value( ++ value, ++ 4, ++ REGAMMA_CONTROL, ++ GRPH_REGAMMA_MODE); ++ break; ++ default: ++ break; ++ } ++ ++ dal_write_reg(opp110->base.ctx, addr, value); ++} ++ ++bool dce110_opp_set_regamma( ++ struct output_pixel_processor *opp, ++ const struct gamma_ramp *ramp, ++ const struct gamma_parameters *params, ++ bool force_bypass) ++{ ++ struct dce110_opp *opp110 = TO_DCE110_OPP(opp); ++ ++ if (force_bypass) { ++ configure_regamma_mode(opp110, params, true); ++ } else { ++ /* 1. Scale gamma to 0 - 1 to m_pRgbUser */ ++ if (!scale_gamma(opp110, ramp, params)) { ++ ASSERT_CRITICAL(false); ++ /* invalid option */ ++ return false; ++ } ++ ++ /* 2. Configure regamma curve without analysis (future task) */ ++ /* and program the PWL regions and segments */ ++ if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_SW || ++ params->surface_pixel_format == PIXEL_FORMAT_FP16) { ++ ++ /* 3. Setup x exponentially distributed points */ ++ if (!setup_distribution_points(opp110)) { ++ ASSERT_CRITICAL(false); ++ /* invalid option */ ++ return false; ++ } ++ ++ /* 4. Build ideal regamma curve */ ++ if (!build_regamma_curve(opp110, params)) { ++ ASSERT_CRITICAL(false); ++ /* invalid parameters or bug */ ++ return false; ++ } ++ ++ /* 5. Map user gamma (evenly distributed x points) to ++ * new curve when x is y from ideal regamma , step 5 */ ++ if (!map_regamma_hw_to_x_user( ++ opp110, ramp, params)) { ++ ASSERT_CRITICAL(false); ++ /* invalid parameters or bug */ ++ return false; ++ } ++ ++ /* 6.Build and verify resulted curve */ ++ build_new_custom_resulted_curve(opp110, params); ++ ++ /* 7. Build and translate x to hw format */ ++ if (!rebuild_curve_configuration_magic(opp110)) { ++ ASSERT_CRITICAL(false); ++ /* invalid parameters or bug */ ++ return false; ++ } ++ ++ /* 8. convert all params to the custom float format */ ++ if (!convert_to_custom_float(opp110)) { ++ ASSERT_CRITICAL(false); ++ /* invalid parameters or bug */ ++ return false; ++ } ++ ++ /* 9. program regamma curve configuration */ ++ regamma_config_regions_and_segments(opp110); ++ ++ /* 10. Program PWL */ ++ program_pwl(opp110, params); ++ } ++ ++ /* ++ * 11. program regamma config ++ */ ++ configure_regamma_mode(opp110, params, false); ++ } ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c +new file mode 100644 +index 0000000..d2594a9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c +@@ -0,0 +1,1276 @@ ++/* ++* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dc_services.h" ++ ++#include "resource.h" ++#include "include/irq_service_interface.h" ++#include "dce110/dce110_timing_generator.h" ++#include "dce110/dce110_link_encoder.h" ++#include "dce110/dce110_mem_input.h" ++#include "dce110/dce110_ipp.h" ++#include "dce110/dce110_transform.h" ++#include "dce110/dce110_stream_encoder.h" ++#include "dce110/dce110_opp.h" ++#include "link_encoder_types.h" ++#include "stream_encoder_types.h" ++ ++enum dce110_clk_src_array_id { ++ DCE110_CLK_SRC_PLL0 = 0, ++ DCE110_CLK_SRC_PLL1, ++ DCE110_CLK_SRC_EXT, ++ ++ DCE110_CLK_SRC_TOTAL ++}; ++ ++static void set_vendor_info_packet(struct core_stream *stream, ++ struct hw_info_packet *info_packet) ++{ ++ uint32_t length = 0; ++ bool hdmi_vic_mode = false; ++ uint8_t checksum = 0; ++ uint32_t i = 0; ++ enum dc_timing_3d_format format; ++ ++ ASSERT_CRITICAL(stream != NULL); ++ ASSERT_CRITICAL(info_packet != NULL); ++ ++ format = stream->public.timing.timing_3d_format; ++ ++ /* Can be different depending on packet content */ ++ length = 5; ++ ++ if (stream->public.timing.hdmi_vic != 0 ++ && stream->public.timing.h_total >= 3840 ++ && stream->public.timing.v_total >= 2160) ++ hdmi_vic_mode = true; ++ ++ /* According to HDMI 1.4a CTS, VSIF should be sent ++ * for both 3D stereo and HDMI VIC modes. ++ * For all other modes, there is no VSIF sent. */ ++ ++ if (format == TIMING_3D_FORMAT_NONE && !hdmi_vic_mode) ++ return; ++ ++ /* 24bit IEEE Registration identifier (0x000c03). LSB first. */ ++ info_packet->sb[1] = 0x03; ++ info_packet->sb[2] = 0x0C; ++ info_packet->sb[3] = 0x00; ++ ++ /*PB4: 5 lower bytes = 0 (reserved). 3 higher bits = HDMI_Video_Format. ++ * The value for HDMI_Video_Format are: ++ * 0x0 (0b000) - No additional HDMI video format is presented in this ++ * packet ++ * 0x1 (0b001) - Extended resolution format present. 1 byte of HDMI_VIC ++ * parameter follows ++ * 0x2 (0b010) - 3D format indication present. 3D_Structure and ++ * potentially 3D_Ext_Data follows ++ * 0x3..0x7 (0b011..0b111) - reserved for future use */ ++ if (format != TIMING_3D_FORMAT_NONE) ++ info_packet->sb[4] = (2 << 5); ++ else if (hdmi_vic_mode) ++ info_packet->sb[4] = (1 << 5); ++ ++ /* PB5: If PB4 claims 3D timing (HDMI_Video_Format = 0x2): ++ * 4 lower bites = 0 (reserved). 4 higher bits = 3D_Structure. ++ * The value for 3D_Structure are: ++ * 0x0 - Frame Packing ++ * 0x1 - Field Alternative ++ * 0x2 - Line Alternative ++ * 0x3 - Side-by-Side (full) ++ * 0x4 - L + depth ++ * 0x5 - L + depth + graphics + graphics-depth ++ * 0x6 - Top-and-Bottom ++ * 0x7 - Reserved for future use ++ * 0x8 - Side-by-Side (Half) ++ * 0x9..0xE - Reserved for future use ++ * 0xF - Not used */ ++ switch (format) { ++ case TIMING_3D_FORMAT_HW_FRAME_PACKING: ++ case TIMING_3D_FORMAT_SW_FRAME_PACKING: ++ info_packet->sb[5] = (0x0 << 4); ++ break; ++ ++ case TIMING_3D_FORMAT_SIDE_BY_SIDE: ++ case TIMING_3D_FORMAT_SBS_SW_PACKED: ++ info_packet->sb[5] = (0x8 << 4); ++ length = 6; ++ break; ++ ++ case TIMING_3D_FORMAT_TOP_AND_BOTTOM: ++ case TIMING_3D_FORMAT_TB_SW_PACKED: ++ info_packet->sb[5] = (0x6 << 4); ++ break; ++ ++ default: ++ break; ++ } ++ ++ /*PB5: If PB4 is set to 0x1 (extended resolution format) ++ * fill PB5 with the correct HDMI VIC code */ ++ if (hdmi_vic_mode) ++ info_packet->sb[5] = stream->public.timing.hdmi_vic; ++ ++ /* Header */ ++ info_packet->hb0 = 0x81; /* VSIF packet type. */ ++ info_packet->hb1 = 0x01; /* Version */ ++ ++ /* 4 lower bits = Length, 4 higher bits = 0 (reserved) */ ++ info_packet->hb2 = (uint8_t) (length); ++ ++ /* Calculate checksum */ ++ checksum = 0; ++ checksum += info_packet->hb0; ++ checksum += info_packet->hb1; ++ checksum += info_packet->hb2; ++ ++ for (i = 1; i <= length; i++) ++ checksum += info_packet->sb[i]; ++ ++ info_packet->sb[0] = (uint8_t) (0x100 - checksum); ++ ++ info_packet->valid = true; ++} ++ ++static enum ds_color_space build_default_color_space( ++ struct core_stream *stream) ++{ ++ enum ds_color_space color_space = ++ DS_COLOR_SPACE_SRGB_FULLRANGE; ++ struct dc_crtc_timing *timing = &stream->public.timing; ++ ++ switch (stream->signal) { ++ /* TODO: implement other signal color space setting */ ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ break; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ { ++ uint32_t pix_clk_khz; ++ ++ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 && ++ timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) { ++ if (timing->timing_standard == ++ TIMING_STANDARD_CEA770 && ++ timing->timing_standard == ++ TIMING_STANDARD_CEA861) ++ color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; ++ ++ pix_clk_khz = timing->pix_clk_khz / 10; ++ if (timing->h_addressable == 640 && ++ timing->v_addressable == 480 && ++ (pix_clk_khz == 2520 || pix_clk_khz == 2517)) ++ color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; ++ } else { ++ if (timing->timing_standard == ++ TIMING_STANDARD_CEA770 || ++ timing->timing_standard == ++ TIMING_STANDARD_CEA861) { ++ ++ color_space = ++ (timing->pix_clk_khz > PIXEL_CLOCK) ? ++ DS_COLOR_SPACE_YCBCR709 : ++ DS_COLOR_SPACE_YCBCR601; ++ } ++ } ++ break; ++ } ++ default: ++ switch (timing->pixel_encoding) { ++ case PIXEL_ENCODING_YCBCR422: ++ case PIXEL_ENCODING_YCBCR444: ++ if (timing->pix_clk_khz > PIXEL_CLOCK) ++ color_space = DS_COLOR_SPACE_YCBCR709; ++ else ++ color_space = DS_COLOR_SPACE_YCBCR601; ++ break; ++ default: ++ break; ++ } ++ break; ++ } ++ return color_space; ++} ++ ++static void set_avi_info_frame(struct hw_info_packet *info_packet, ++ struct core_stream *stream) ++{ ++ enum ds_color_space color_space = DS_COLOR_SPACE_UNKNOWN; ++ struct info_frame info_frame = { {0} }; ++ uint32_t pixel_encoding = 0; ++ enum scanning_type scan_type = SCANNING_TYPE_NODATA; ++ enum dc_aspect_ratio aspect = ASPECT_RATIO_NO_DATA; ++ bool itc = false; ++ uint8_t cn0_cn1 = 0; ++ uint8_t *check_sum = NULL; ++ uint8_t byte_index = 0; ++ ++ if (info_packet == NULL) ++ return; ++ ++ color_space = build_default_color_space(stream); ++ ++ /* Initialize header */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.header. ++ info_frame_type = INFO_FRAME_AVI; ++ /* InfoFrameVersion_3 is defined by CEA861F (Section 6.4), but shall ++ * not be used in HDMI 2.0 (Section 10.1) */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.header.version = ++ INFO_FRAME_VERSION_2; ++ info_frame.avi_info_packet.info_packet_hdmi.bits.header.length = ++ INFO_FRAME_SIZE_AVI; ++ ++ /* IDO-defined (Y2,Y1,Y0 = 1,1,1) shall not be used by devices built ++ * according to HDMI 2.0 spec (Section 10.1) ++ * Add "case PixelEncoding_YCbCr420: pixelEncoding = 3; break;" ++ * when YCbCr 4:2:0 is supported by DAL hardware. */ ++ ++ switch (stream->public.timing.pixel_encoding) { ++ case PIXEL_ENCODING_YCBCR422: ++ pixel_encoding = 1; ++ break; ++ ++ case PIXEL_ENCODING_YCBCR444: ++ pixel_encoding = 2; ++ break; ++ ++ case PIXEL_ENCODING_RGB: ++ default: ++ pixel_encoding = 0; ++ } ++ ++ /* Y0_Y1_Y2 : The pixel encoding */ ++ /* H14b AVI InfoFrame has extension on Y-field from 2 bits to 3 bits */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.Y0_Y1_Y2 = ++ pixel_encoding; ++ ++ ++ /* A0 = 1 Active Format Information valid */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.A0 = ++ ACTIVE_FORMAT_VALID; ++ ++ /* B0, B1 = 3; Bar info data is valid */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.B0_B1 = ++ BAR_INFO_BOTH_VALID; ++ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.SC0_SC1 = ++ PICTURE_SCALING_UNIFORM; ++ ++ /* S0, S1 : Underscan / Overscan */ ++ /* TODO: un-hardcode scan type */ ++ scan_type = SCANNING_TYPE_UNDERSCAN; ++ info_frame.avi_info_packet.info_packet_hdmi.bits.S0_S1 = scan_type; ++ ++ /* C0, C1 : Colorimetry */ ++ if (color_space == DS_COLOR_SPACE_YCBCR709) ++ info_frame.avi_info_packet.info_packet_hdmi.bits.C0_C1 = ++ COLORIMETRY_ITU709; ++ else if (color_space == DS_COLOR_SPACE_YCBCR601) ++ info_frame.avi_info_packet.info_packet_hdmi.bits.C0_C1 = ++ COLORIMETRY_ITU601; ++ else ++ info_frame.avi_info_packet.info_packet_hdmi.bits.C0_C1 = ++ COLORIMETRY_NO_DATA; ++ ++ ++ /* TODO: un-hardcode aspect ratio */ ++ aspect = stream->public.timing.aspect_ratio; ++ ++ switch (aspect) { ++ case ASPECT_RATIO_4_3: ++ case ASPECT_RATIO_16_9: ++ info_frame.avi_info_packet.info_packet_hdmi.bits.M0_M1 = aspect; ++ break; ++ ++ case ASPECT_RATIO_NO_DATA: ++ case ASPECT_RATIO_64_27: ++ case ASPECT_RATIO_256_135: ++ default: ++ info_frame.avi_info_packet.info_packet_hdmi.bits.M0_M1 = 0; ++ } ++ ++ /* Active Format Aspect ratio - same as Picture Aspect Ratio. */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.R0_R3 = ++ ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE; ++ ++ /* TODO: un-hardcode cn0_cn1 and itc */ ++ cn0_cn1 = 0; ++ itc = false; ++ ++ if (itc) { ++ info_frame.avi_info_packet.info_packet_hdmi.bits.ITC = 1; ++ info_frame.avi_info_packet.info_packet_hdmi.bits.CN0_CN1 = ++ cn0_cn1; ++ } ++ ++ /* TODO: un-hardcode q0_q1 */ ++ if (color_space == DS_COLOR_SPACE_SRGB_FULLRANGE) ++ info_frame.avi_info_packet.info_packet_hdmi.bits.Q0_Q1 = ++ RGB_QUANTIZATION_FULL_RANGE; ++ else if (color_space == DS_COLOR_SPACE_SRGB_LIMITEDRANGE) ++ info_frame.avi_info_packet.info_packet_hdmi.bits.Q0_Q1 = ++ RGB_QUANTIZATION_LIMITED_RANGE; ++ else ++ info_frame.avi_info_packet.info_packet_hdmi.bits.Q0_Q1 = ++ RGB_QUANTIZATION_DEFAULT_RANGE; ++ ++ /* TODO : We should handle YCC quantization, ++ * but we do not have matrix calculation */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.YQ0_YQ1 = ++ YYC_QUANTIZATION_LIMITED_RANGE; ++ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.VIC0_VIC7 = ++ stream->public.timing.vic; ++ ++ /* pixel repetition ++ * PR0 - PR3 start from 0 whereas pHwPathMode->mode.timing.flags.pixel ++ * repetition start from 1 */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.PR0_PR3 = 0; ++ ++ /* Bar Info ++ * barTop: Line Number of End of Top Bar. ++ * barBottom: Line Number of Start of Bottom Bar. ++ * barLeft: Pixel Number of End of Left Bar. ++ * barRight: Pixel Number of Start of Right Bar. */ ++ info_frame.avi_info_packet.info_packet_hdmi.bits.bar_top = ++ stream->public.timing.v_border_top; ++ info_frame.avi_info_packet.info_packet_hdmi.bits.bar_bottom = ++ (stream->public.timing.v_border_top ++ - stream->public.timing.v_border_bottom + 1); ++ info_frame.avi_info_packet.info_packet_hdmi.bits.bar_left = ++ stream->public.timing.h_border_left; ++ info_frame.avi_info_packet.info_packet_hdmi.bits.bar_right = ++ (stream->public.timing.h_total ++ - stream->public.timing.h_border_right + 1); ++ ++ /* check_sum - Calculate AFMT_AVI_INFO0 ~ AFMT_AVI_INFO3 */ ++ check_sum = ++ &info_frame. ++ avi_info_packet.info_packet_hdmi.packet_raw_data.sb[0]; ++ *check_sum = INFO_FRAME_AVI + INFO_FRAME_SIZE_AVI ++ + INFO_FRAME_VERSION_2; ++ ++ for (byte_index = 1; byte_index <= INFO_FRAME_SIZE_AVI; byte_index++) ++ *check_sum += info_frame.avi_info_packet.info_packet_hdmi. ++ packet_raw_data.sb[byte_index]; ++ ++ /* one byte complement */ ++ *check_sum = (uint8_t) (0x100 - *check_sum); ++ ++ /* Store in hw_path_mode */ ++ info_packet->hb0 = ++ info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.hb0; ++ info_packet->hb1 = ++ info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.hb1; ++ info_packet->hb2 = ++ info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.hb2; ++ ++ for (byte_index = 0; byte_index < sizeof(info_packet->sb); byte_index++) ++ info_packet->sb[byte_index] = info_frame.avi_info_packet. ++ info_packet_hdmi.packet_raw_data.sb[byte_index]; ++ ++ info_packet->valid = true; ++} ++ ++static void translate_info_frame(const struct hw_info_frame *hw_info_frame, ++ struct encoder_info_frame *encoder_info_frame) ++{ ++ dc_service_memset( ++ encoder_info_frame, 0, sizeof(struct encoder_info_frame)); ++ ++ /* For gamut we recalc checksum */ ++ if (hw_info_frame->gamut_packet.valid) { ++ uint8_t chk_sum = 0; ++ uint8_t *ptr; ++ uint8_t i; ++ ++ dc_service_memmove( ++ &encoder_info_frame->gamut, ++ &hw_info_frame->gamut_packet, ++ sizeof(struct hw_info_packet)); ++ ++ /*start of the Gamut data. */ ++ ptr = &encoder_info_frame->gamut.sb[3]; ++ ++ for (i = 0; i <= encoder_info_frame->gamut.sb[1]; i++) ++ chk_sum += ptr[i]; ++ ++ encoder_info_frame->gamut.sb[2] = (uint8_t) (0x100 - chk_sum); ++ } ++ ++ if (hw_info_frame->avi_info_packet.valid) { ++ dc_service_memmove( ++ &encoder_info_frame->avi, ++ &hw_info_frame->avi_info_packet, ++ sizeof(struct hw_info_packet)); ++ } ++ ++ if (hw_info_frame->vendor_info_packet.valid) { ++ dc_service_memmove( ++ &encoder_info_frame->vendor, ++ &hw_info_frame->vendor_info_packet, ++ sizeof(struct hw_info_packet)); ++ } ++ ++ if (hw_info_frame->spd_packet.valid) { ++ dc_service_memmove( ++ &encoder_info_frame->spd, ++ &hw_info_frame->spd_packet, ++ sizeof(struct hw_info_packet)); ++ } ++ ++ if (hw_info_frame->vsc_packet.valid) { ++ dc_service_memmove( ++ &encoder_info_frame->vsc, ++ &hw_info_frame->vsc_packet, ++ sizeof(struct hw_info_packet)); ++ } ++} ++ ++static void build_info_frame(struct core_stream *stream) ++{ ++ enum signal_type signal = SIGNAL_TYPE_NONE; ++ struct hw_info_frame info_frame = { { 0 } }; ++ ++ /* default all packets to invalid */ ++ info_frame.avi_info_packet.valid = false; ++ info_frame.gamut_packet.valid = false; ++ info_frame.vendor_info_packet.valid = false; ++ info_frame.spd_packet.valid = false; ++ info_frame.vsc_packet.valid = false; ++ ++ signal = stream->sink->public.sink_signal; ++ ++ /* HDMi and DP have different info packets*/ ++ if (signal == SIGNAL_TYPE_HDMI_TYPE_A) { ++ set_avi_info_frame(&info_frame.avi_info_packet, ++ stream); ++ set_vendor_info_packet(stream, &info_frame.vendor_info_packet); ++ } ++ ++ translate_info_frame(&info_frame, ++ &stream->encoder_info_frame); ++} ++ ++ ++bool dce110_construct_resource_pool( ++ struct adapter_service *adapter_serv, ++ struct dc *dc, ++ struct resource_pool *pool) ++{ ++ unsigned int i; ++ struct clock_source_init_data clk_src_init_data = { 0 }; ++ struct audio_init_data audio_init_data = { 0 }; ++ struct dc_context *ctx = dc->ctx; ++ pool->adapter_srv = adapter_serv; ++ ++ pool->stream_engines.engine.ENGINE_ID_DIGA = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGB = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGC = 1; ++ ++ clk_src_init_data.as = adapter_serv; ++ clk_src_init_data.ctx = ctx; ++ clk_src_init_data.clk_src_id.enum_id = ENUM_ID_1; ++ clk_src_init_data.clk_src_id.type = OBJECT_TYPE_CLOCK_SOURCE; ++ pool->clk_src_count = DCE110_CLK_SRC_TOTAL; ++ ++ clk_src_init_data.clk_src_id.id = CLOCK_SOURCE_ID_PLL0; ++ pool->clock_sources[DCE110_CLK_SRC_PLL0] = dal_clock_source_create( ++ &clk_src_init_data); ++ clk_src_init_data.clk_src_id.id = CLOCK_SOURCE_ID_PLL1; ++ pool->clock_sources[DCE110_CLK_SRC_PLL1] = dal_clock_source_create( ++ &clk_src_init_data); ++ clk_src_init_data.clk_src_id.id = CLOCK_SOURCE_ID_EXTERNAL; ++ pool->clock_sources[DCE110_CLK_SRC_EXT] = dal_clock_source_create( ++ &clk_src_init_data); ++ ++ for (i = 0; i < pool->clk_src_count; i++) { ++ if (pool->clock_sources[i] == NULL) { ++ dal_error("DC: failed to create clock sources!\n"); ++ BREAK_TO_DEBUGGER(); ++ goto clk_src_create_fail; ++ } ++ } ++ ++ pool->display_clock = dal_display_clock_dce110_create(ctx, adapter_serv); ++ if (pool->display_clock == NULL) { ++ dal_error("DC: failed to create display clock!\n"); ++ BREAK_TO_DEBUGGER(); ++ goto disp_clk_create_fail; ++ } ++ ++ { ++ struct irq_service_init_data init_data; ++ init_data.ctx = dc->ctx; ++ pool->irqs = dal_irq_service_create( ++ dal_adapter_service_get_dce_version( ++ dc->res_pool.adapter_srv), ++ &init_data); ++ if (!pool->irqs) ++ goto irqs_create_fail; ++ ++ } ++ ++ pool->controller_count = ++ dal_adapter_service_get_func_controllers_num(adapter_serv); ++ pool->stream_enc_count = 3; ++ pool->scaler_filter = dal_scaler_filter_create(ctx); ++ if (pool->scaler_filter == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error("DC: failed to create filter!\n"); ++ goto filter_create_fail; ++ } ++ ++ for (i = 0; i < pool->controller_count; i++) { ++ pool->timing_generators[i] = dce110_timing_generator_create( ++ adapter_serv, ++ ctx, ++ i + 1); ++ if (pool->timing_generators[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error("DC: failed to create tg!\n"); ++ goto controller_create_fail; ++ } ++ ++ pool->mis[i] = dce110_mem_input_create( ++ ctx, ++ i + 1); ++ if (pool->mis[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error( ++ "DC: failed to create memory input!\n"); ++ goto controller_create_fail; ++ } ++ ++ pool->ipps[i] = dce110_ipp_create( ++ ctx, ++ i + 1); ++ if (pool->ipps[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error( ++ "DC: failed to create input pixel processor!\n"); ++ goto controller_create_fail; ++ } ++ ++ pool->transforms[i] = dce110_transform_create( ++ ctx, ++ i + 1); ++ if (pool->transforms[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error( ++ "DC: failed to create transform!\n"); ++ goto controller_create_fail; ++ } ++ dce110_transform_set_scaler_filter( ++ pool->transforms[i], ++ pool->scaler_filter); ++ ++ pool->opps[i] = dce110_opp_create( ++ ctx, ++ i + 1); ++ if (pool->opps[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error( ++ "DC: failed to create output pixel processor!\n"); ++ goto controller_create_fail; ++ } ++ } ++ ++ audio_init_data.as = adapter_serv; ++ audio_init_data.ctx = ctx; ++ pool->audio_count = 0; ++ for (i = 0; i < pool->controller_count; i++) { ++ struct graphics_object_id obj_id; ++ ++ obj_id = dal_adapter_service_enum_audio_object(adapter_serv, i); ++ if (false == dal_graphics_object_id_is_valid(obj_id)) { ++ /* no more valid audio objects */ ++ break; ++ } ++ ++ audio_init_data.audio_stream_id = obj_id; ++ pool->audios[i] = dal_audio_create(&audio_init_data); ++ if (pool->audios[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error("DC: failed to create DPPs!\n"); ++ goto audio_create_fail; ++ } ++ pool->audio_count++; ++ } ++ ++ for (i = 0; i < pool->stream_enc_count; i++) { ++ struct stream_enc_init_data enc_init_data = { 0 }; ++ /* TODO: rework fragile code*/ ++ enc_init_data.stream_engine_id = i; ++ enc_init_data.adapter_service = adapter_serv; ++ enc_init_data.ctx = dc->ctx; ++ if (pool->stream_engines.u_all & 1 << i) { ++ pool->stream_enc[i] = dce110_stream_encoder_create( ++ &enc_init_data); ++ ++ if (pool->stream_enc[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dal_error("DC: failed to create stream_encoder!\n"); ++ goto stream_enc_create_fail; ++ } ++ } ++ } ++ ++ return true; ++ ++stream_enc_create_fail: ++ for (i = 0; i < pool->stream_enc_count; i++) { ++ if (pool->stream_enc[i] != NULL) ++ dce110_stream_encoder_destroy(&pool->stream_enc[i]); ++ } ++ ++audio_create_fail: ++ for (i = 0; i < pool->controller_count; i++) { ++ if (pool->audios[i] != NULL) ++ dal_audio_destroy(&pool->audios[i]); ++ } ++ ++controller_create_fail: ++ for (i = 0; i < pool->controller_count; i++) { ++ if (pool->opps[i] != NULL) ++ dce110_opp_destroy(&pool->opps[i]); ++ ++ if (pool->transforms[i] != NULL) ++ dce110_transform_destroy(&pool->transforms[i]); ++ ++ if (pool->ipps[i] != NULL) ++ dce110_ipp_destroy(&pool->ipps[i]); ++ ++ if (pool->mis[i] != NULL) ++ dce110_mem_input_destroy(&pool->mis[i]); ++ ++ if (pool->timing_generators[i] != NULL) ++ dce110_timing_generator_destroy( ++ &pool->timing_generators[i]); ++ } ++ ++filter_create_fail: ++ dal_irq_service_destroy(&pool->irqs); ++ ++irqs_create_fail: ++ dal_display_clock_destroy(&pool->display_clock); ++ ++disp_clk_create_fail: ++clk_src_create_fail: ++ for (i = 0; i < pool->clk_src_count; i++) { ++ if (pool->clock_sources[i] != NULL) ++ dal_clock_source_destroy(&pool->clock_sources[i]); ++ } ++ return false; ++} ++ ++void dce110_destruct_resource_pool(struct resource_pool *pool) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < pool->controller_count; i++) { ++ if (pool->opps[i] != NULL) ++ dce110_opp_destroy(&pool->opps[i]); ++ ++ if (pool->transforms[i] != NULL) ++ dce110_transform_destroy(&pool->transforms[i]); ++ ++ if (pool->ipps[i] != NULL) ++ dce110_ipp_destroy(&pool->ipps[i]); ++ ++ if (pool->mis[i] != NULL) ++ dce110_mem_input_destroy(&pool->mis[i]); ++ ++ if (pool->timing_generators[i] != NULL) ++ dce110_timing_generator_destroy( ++ &pool->timing_generators[i]); ++ } ++ ++ for (i = 0; i < pool->stream_enc_count; i++) { ++ if (pool->stream_enc[i] != NULL) ++ dce110_stream_encoder_destroy(&pool->stream_enc[i]); ++ } ++ ++ for (i = 0; i < pool->clk_src_count; i++) { ++ if (pool->clock_sources[i] != NULL) { ++ dal_clock_source_destroy(&pool->clock_sources[i]); ++ } ++ } ++ ++ for (i = 0; i < pool->audio_count; i++) { ++ if (pool->audios[i] != NULL) { ++ dal_audio_destroy(&pool->audios[i]); ++ } ++ } ++ if (pool->display_clock != NULL) { ++ dal_display_clock_destroy(&pool->display_clock); ++ } ++ ++ if (pool->scaler_filter != NULL) { ++ dal_scaler_filter_destroy(&pool->scaler_filter); ++ } ++ if (pool->irqs != NULL) { ++ dal_irq_service_destroy(&pool->irqs); ++ } ++ ++ if (pool->adapter_srv != NULL) { ++ dal_adapter_service_destroy(&pool->adapter_srv); ++ } ++} ++ ++static void attach_stream_to_controller( ++ struct resource_context *res_ctx, ++ struct core_stream *stream) ++{ ++ res_ctx->controller_ctx[stream->controller_idx].stream = stream; ++} ++ ++static bool assign_first_free_controller( ++ struct resource_context *res_ctx, ++ struct core_stream *stream) ++{ ++ uint8_t i; ++ for (i = 0; i < res_ctx->pool.controller_count; i++) { ++ if (!res_ctx->controller_ctx[i].stream) { ++ stream->tg = res_ctx->pool.timing_generators[i]; ++ stream->mi = res_ctx->pool.mis[i]; ++ stream->ipp = res_ctx->pool.ipps[i]; ++ stream->xfm = res_ctx->pool.transforms[i]; ++ stream->opp = res_ctx->pool.opps[i]; ++ stream->controller_idx = i; ++ stream->dis_clk = res_ctx->pool.display_clock; ++ return true; ++ } ++ } ++ return false; ++} ++ ++static void set_stream_engine_in_use( ++ struct resource_context *res_ctx, ++ struct stream_encoder *stream_enc) ++{ ++ int i; ++ ++ for (i = 0; i < res_ctx->pool.stream_enc_count; i++) { ++ if (res_ctx->pool.stream_enc[i] == stream_enc) ++ res_ctx->is_stream_enc_acquired[i] = true; ++ } ++} ++ ++static struct stream_encoder *find_first_free_match_stream_enc_for_link( ++ struct resource_context *res_ctx, ++ struct core_link *link) ++{ ++ uint8_t i; ++ ++ for (i = 0; i < res_ctx->pool.stream_enc_count; i++) { ++ if (!res_ctx->is_stream_enc_acquired[i] && ++ res_ctx->pool.stream_enc[i]) { ++ if (res_ctx->pool.stream_enc[i]->id == ++ link->link_enc->preferred_engine) ++ return res_ctx->pool.stream_enc[i]; ++ } ++ } ++ ++ /* TODO: Handle MST*/ ++ ++ return NULL; ++} ++ ++/* TODO: release audio object */ ++static void set_audio_in_use( ++ struct resource_context *res_ctx, ++ struct audio *audio) ++{ ++ int i; ++ for (i = 0; i < res_ctx->pool.audio_count; i++) { ++ if (res_ctx->pool.audios[i] == audio) { ++ res_ctx->is_audio_acquired[i] = true; ++ } ++ } ++} ++ ++static struct audio *find_first_free_audio(struct resource_context *res_ctx) ++{ ++ int i; ++ for (i = 0; i < res_ctx->pool.audio_count; i++) { ++ if (res_ctx->is_audio_acquired[i] == false) { ++ return res_ctx->pool.audios[i]; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static struct clock_source *find_first_free_pll( ++ struct resource_context *res_ctx) ++{ ++ if (res_ctx->clock_source_ref_count[DCE110_CLK_SRC_PLL0] == 0) { ++ return res_ctx->pool.clock_sources[DCE110_CLK_SRC_PLL0]; ++ } ++ if (res_ctx->clock_source_ref_count[DCE110_CLK_SRC_PLL1] == 0) { ++ return res_ctx->pool.clock_sources[DCE110_CLK_SRC_PLL1]; ++ } ++ ++ return 0; ++} ++ ++static bool check_timing_change(struct core_stream *cur_stream, ++ struct core_stream *new_stream) ++{ ++ if (cur_stream == NULL) ++ return true; ++ ++ /* If sink pointer changed, it means this is a hotplug, we should do ++ * full hw setting. ++ */ ++ if (cur_stream->sink != new_stream->sink) ++ return true; ++ ++ return !is_same_timing( ++ &cur_stream->public.timing, ++ &new_stream->public.timing); ++} ++ ++static enum dc_status map_resources( ++ const struct dc *dc, ++ struct validate_context *context) ++{ ++ uint8_t i, j; ++ ++ /* mark resources used for targets that are already active */ ++ for (i = 0; i < context->target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ if (context->target_flags[i].unchanged) ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ attach_stream_to_controller( ++ &context->res_ctx, ++ stream); ++ ++ set_stream_engine_in_use( ++ &context->res_ctx, ++ stream->stream_enc); ++ ++ reference_clock_source( ++ &context->res_ctx, ++ stream->clock_source); ++ ++ if (stream->audio) { ++ set_audio_in_use(&context->res_ctx, ++ stream->audio); ++ } ++ } ++ } ++ ++ /* acquire new resources */ ++ for (i = 0; i < context->target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ if (context->target_flags[i].unchanged) ++ continue; ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ struct core_stream *curr_stream; ++ ++ if (!assign_first_free_controller( ++ &context->res_ctx, stream)) ++ return DC_NO_CONTROLLER_RESOURCE; ++ ++ attach_stream_to_controller(&context->res_ctx, stream); ++ ++ stream->stream_enc = ++ find_first_free_match_stream_enc_for_link( ++ &context->res_ctx, ++ stream->sink->link); ++ ++ if (!stream->stream_enc) ++ return DC_NO_STREAM_ENG_RESOURCE; ++ ++ set_stream_engine_in_use( ++ &context->res_ctx, ++ stream->stream_enc); ++ stream->signal = ++ stream->sink->public.sink_signal; ++ ++ if (dc_is_dp_signal(stream->signal)) ++ stream->clock_source = context->res_ctx. ++ pool.clock_sources[DCE110_CLK_SRC_EXT]; ++ else ++ stream->clock_source = ++ find_used_clk_src_for_sharing( ++ context, stream); ++ if (stream->clock_source == NULL) ++ stream->clock_source = ++ find_first_free_pll(&context->res_ctx); ++ ++ if (stream->clock_source == NULL) ++ return DC_NO_CLOCK_SOURCE_RESOURCE; ++ ++ reference_clock_source( ++ &context->res_ctx, ++ stream->clock_source); ++ ++ /* TODO: Add check if ASIC support and EDID audio */ ++ if (!stream->sink->converter_disable_audio && ++ dc_is_audio_capable_signal( ++ stream->signal)) { ++ stream->audio = find_first_free_audio( ++ &context->res_ctx); ++ ++ if (!stream->audio) ++ return DC_NO_STREAM_AUDIO_RESOURCE; ++ ++ set_audio_in_use(&context->res_ctx, ++ stream->audio); ++ } ++ curr_stream = ++ dc->current_context.res_ctx.controller_ctx ++ [stream->controller_idx].stream; ++ context->res_ctx.controller_ctx[stream->controller_idx] ++ .flags.timing_changed = ++ check_timing_change(curr_stream, stream); ++ ++ } ++ } ++ ++ return DC_OK; ++} ++ ++static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) ++{ ++ switch (crtc_id) { ++ case CONTROLLER_ID_D0: ++ return DTO_SOURCE_ID0; ++ case CONTROLLER_ID_D1: ++ return DTO_SOURCE_ID1; ++ case CONTROLLER_ID_D2: ++ return DTO_SOURCE_ID2; ++ case CONTROLLER_ID_D3: ++ return DTO_SOURCE_ID3; ++ case CONTROLLER_ID_D4: ++ return DTO_SOURCE_ID4; ++ case CONTROLLER_ID_D5: ++ return DTO_SOURCE_ID5; ++ default: ++ return DTO_SOURCE_UNKNOWN; ++ } ++} ++ ++static void build_audio_output( ++ const struct core_stream *stream, ++ struct audio_output *audio_output) ++{ ++ audio_output->engine_id = stream->stream_enc->id; ++ ++ audio_output->signal = stream->signal; ++ ++ /* audio_crtc_info */ ++ ++ audio_output->crtc_info.h_total = ++ stream->public.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->public.timing.h_addressable ++ + stream->public.timing.h_border_left ++ + stream->public.timing.h_border_right; ++ ++ audio_output->crtc_info.v_active = ++ stream->public.timing.v_addressable ++ + stream->public.timing.v_border_top ++ + stream->public.timing.v_border_bottom; ++ ++ audio_output->crtc_info.pixel_repetition = 1; ++ ++ audio_output->crtc_info.interlaced = ++ stream->public.timing.flags.INTERLACE; ++ ++ audio_output->crtc_info.refresh_rate = ++ (stream->public.timing.pix_clk_khz*1000)/ ++ (stream->public.timing.h_total*stream->public.timing.v_total); ++ ++ audio_output->crtc_info.color_depth = ++ stream->public.timing.display_color_depth; ++ ++ audio_output->crtc_info.requested_pixel_clock = ++ stream->pix_clk_params.requested_pix_clk; ++ ++ /* TODO - Investigate why calculated pixel clk has to be ++ * requested pixel clk */ ++ audio_output->crtc_info.calculated_pixel_clock = ++ stream->pix_clk_params.requested_pix_clk; ++ ++ /* TODO: This is needed for DP */ ++ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT) { ++ audio_output->pll_info.dp_dto_source_clock_in_khz = ++ dal_display_clock_get_dp_ref_clk_frequency( ++ stream->dis_clk); ++ } ++ ++ audio_output->pll_info.feed_back_divider = ++ stream->pll_settings.feedback_divider; ++ ++ audio_output->pll_info.dto_source = ++ translate_to_dto_source( ++ stream->controller_idx + 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 = ++ stream->pll_settings.ss_percentage; ++} ++ ++static void get_pixel_clock_parameters( ++ const struct core_stream *stream, ++ struct pixel_clk_params *pixel_clk_params) ++{ ++ pixel_clk_params->requested_pix_clk = stream->public.timing.pix_clk_khz; ++ pixel_clk_params->encoder_object_id = stream->sink->link->link_enc->id; ++ pixel_clk_params->signal_type = stream->sink->public.sink_signal; ++ pixel_clk_params->controller_id = stream->controller_idx + 1; ++ /* TODO: un-hardcode*/ ++ 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->public.timing.display_color_depth; ++ pixel_clk_params->flags.DISPLAY_BLANKED = 1; ++} ++ ++static enum dc_status build_stream_hw_param(struct core_stream *stream) ++{ ++ /*TODO: unhardcode*/ ++ stream->max_tmds_clk_from_edid_in_mhz = 0; ++ stream->max_hdmi_deep_color = COLOR_DEPTH_121212; ++ stream->max_hdmi_pixel_clock = 600000; ++ ++ get_pixel_clock_parameters(stream, &stream->pix_clk_params); ++ dal_clock_source_get_pix_clk_dividers( ++ stream->clock_source, ++ &stream->pix_clk_params, ++ &stream->pll_settings); ++ ++ build_audio_output(stream, &stream->audio_output); ++ ++ return DC_OK; ++} ++ ++static enum dc_status validate_mapped_resource( ++ const struct dc *dc, ++ struct validate_context *context) ++{ ++ enum dc_status status = DC_OK; ++ uint8_t i, j; ++ ++ for (i = 0; i < context->target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ if (context->target_flags[i].unchanged) ++ continue; ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ struct core_link *link = stream->sink->link; ++ status = build_stream_hw_param(stream); ++ ++ if (status != DC_OK) ++ return status; ++ ++ if (!dce110_timing_generator_validate_timing( ++ stream->tg, ++ &stream->public.timing, ++ SIGNAL_TYPE_HDMI_TYPE_A)) ++ return DC_FAIL_CONTROLLER_VALIDATE; ++ ++ ++ if (dce110_link_encoder_validate_output_with_stream( ++ link->link_enc, ++ stream) ++ != ENCODER_RESULT_OK) ++ return DC_FAIL_ENC_VALIDATE; ++ ++ /* TODO: validate audio ASIC caps, encoder */ ++ ++ status = dc_link_validate_mode_timing(stream->sink, ++ link, ++ &stream->public.timing); ++ ++ if (status != DC_OK) ++ return status; ++ ++ build_info_frame(stream); ++ } ++ } ++ ++ return DC_OK; ++} ++ ++enum dc_status dce110_validate_bandwidth( ++ const struct dc *dc, ++ struct validate_context *context) ++{ ++ uint8_t i, j; ++ enum dc_status result = DC_ERROR_UNEXPECTED; ++ uint8_t number_of_displays = 0; ++ ++ memset(&context->bw_mode_data, 0, sizeof(context->bw_mode_data)); ++ ++ for (i = 0; i < context->target_count; i++) { ++ struct core_target *target = context->targets[i]; ++ for (j = 0; j < target->stream_count; j++) { ++ struct core_stream *stream = target->streams[j]; ++ struct bw_calcs_input_single_display *disp = &context-> ++ bw_mode_data.displays_data[number_of_displays]; ++ ++ if (target->status.surface_count == 0) { ++ disp->graphics_scale_ratio = int_to_fixed(1); ++ disp->graphics_h_taps = 4; ++ disp->graphics_v_taps = 4; ++ ++ } else { ++ disp->graphics_scale_ratio = ++ fixed31_32_to_bw_fixed( ++ stream->ratios.vert.value); ++ disp->graphics_h_taps = stream->taps.h_taps; ++ disp->graphics_v_taps = stream->taps.v_taps; ++ } ++ ++ disp->graphics_src_width = ++ stream->public.timing.h_addressable; ++ disp->graphics_src_height = ++ stream->public.timing.v_addressable; ++ disp->h_total = stream->public.timing.h_total; ++ disp->pixel_rate = frc_to_fixed( ++ stream->public.timing.pix_clk_khz, 1000); ++ ++ /*TODO: get from surface*/ ++ disp->graphics_bytes_per_pixel = 4; ++ disp->graphics_tiling_mode = tiled; ++ ++ /* DCE11 defaults*/ ++ disp->graphics_lb_bpc = 10; ++ disp->graphics_interlace_mode = false; ++ disp->fbc_enable = false; ++ disp->lpt_enable = false; ++ disp->graphics_stereo_mode = mono; ++ disp->underlay_mode = ul_none; ++ ++ number_of_displays++; ++ } ++ } ++ ++ context->bw_mode_data.number_of_displays = number_of_displays; ++ context->bw_mode_data.display_synchronization_enabled = false; ++ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_BWM, ++ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS, ++ "%s: Start bandwidth calculations", ++ __func__); ++ if (true == bw_calcs( ++ dc->ctx, ++ &dc->bw_dceip, ++ &dc->bw_vbios, ++ &context->bw_mode_data, ++ &context->bw_results)) ++ result = DC_OK; ++ else { ++ result = DC_FAIL_BANDWIDTH_VALIDATE; ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_BWM, ++ LOG_MINOR_BWM_MODE_VALIDATION, ++ "%s: Bandwidth validation failed!", ++ __func__); ++ } ++ ++ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_BWM, ++ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS, ++ "%s: Finish bandwidth calculations\n nbpMark: %d", ++ __func__, ++ context->bw_results.nbp_state_change_watermark[0].b_mark); ++ ++ return result; ++} ++ ++static void set_target_unchanged( ++ struct validate_context *context, ++ uint8_t target_idx) ++{ ++ uint8_t i; ++ struct core_target *target = context->targets[target_idx]; ++ context->target_flags[target_idx].unchanged = true; ++ for (i = 0; i < target->stream_count; i++) { ++ uint8_t index = target->streams[i]->controller_idx; ++ context->res_ctx.controller_ctx[index].flags.unchanged = true; ++ } ++} ++ ++enum dc_status dce110_validate_with_context( ++ const struct dc *dc, ++ const struct dc_validation_set set[], ++ uint8_t set_count, ++ struct validate_context *context) ++{ ++ enum dc_status result = DC_ERROR_UNEXPECTED; ++ uint8_t i, j; ++ struct dc_context *dc_ctx = dc->ctx; ++ ++ for (i = 0; i < set_count; i++) { ++ context->targets[i] = DC_TARGET_TO_CORE(set[i].target); ++ ++ for (j = 0; j < dc->current_context.target_count; j++) ++ if (dc->current_context.targets[j] == context->targets[i]) ++ set_target_unchanged(context, i); ++ ++ if (!context->target_flags[i].unchanged) ++ if (!logical_attach_surfaces_to_target( ++ (struct dc_surface **)set[i].surfaces, ++ set[i].surface_count, ++ &context->targets[i]->public)) { ++ DC_ERROR("Failed to attach surface to target!\n"); ++ return DC_FAIL_ATTACH_SURFACES; ++ } ++ } ++ ++ context->target_count = set_count; ++ ++ context->res_ctx.pool = dc->res_pool; ++ ++ result = map_resources(dc, context); ++ ++ if (result == DC_OK) ++ result = validate_mapped_resource(dc, context); ++ ++ if (result == DC_OK) ++ build_scaling_params_for_context(dc, context); ++ ++ if (result == DC_OK) ++ result = dce110_validate_bandwidth(dc, context); ++ ++ return result; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h +new file mode 100644 +index 0000000..e113d11 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h +@@ -0,0 +1,55 @@ ++/* ++* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_RESOURCE_DCE110_H__ ++#define __DC_RESOURCE_DCE110_H__ ++ ++#include "core_types.h" ++ ++struct adapter_service; ++struct dc; ++struct resource_pool; ++struct dc_validation_set; ++ ++ ++bool dce110_construct_resource_pool( ++ struct adapter_service *adapter_serv, ++ struct dc *dc, ++ struct resource_pool *pool); ++ ++void dce110_destruct_resource_pool(struct resource_pool *pool); ++ ++enum dc_status dce110_validate_with_context( ++ const struct dc *dc, ++ const struct dc_validation_set set[], ++ uint8_t set_count, ++ struct validate_context *context); ++ ++enum dc_status dce110_validate_bandwidth( ++ const struct dc *dc, ++ struct validate_context *context); ++ ++#endif /* __DC_RESOURCE_DCE110_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c +new file mode 100644 +index 0000000..a9edf96 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c +@@ -0,0 +1,1168 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "stream_encoder_types.h" ++#include "dce110_stream_encoder.h" ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "dce/dce_11_0_enum.h" ++ ++static const uint32_t fe_engine_offsets[] = { ++ mmDIG0_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG1_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG2_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++}; ++ ++#define VBI_LINE_0 0 ++#define DP_BLANK_MAX_RETRY 20 ++#define HDMI_CLOCK_CHANNEL_RATE_MORE_340M 340000 ++ ++#ifndef HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN_MASK ++ #define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN_MASK 0x2 ++ #define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN__SHIFT 0x1 ++#endif ++ ++static void construct( ++ struct stream_encoder *enc, ++ struct stream_enc_init_data *init) ++{ ++ enc->ctx = init->ctx; ++ enc->id = init->stream_engine_id; ++ enc->adapter_service = init->adapter_service; ++} ++ ++struct stream_encoder *dce110_stream_encoder_create( ++ struct stream_enc_init_data *init) ++{ ++ struct stream_encoder *enc = ++ dc_service_alloc(init->ctx, sizeof(struct stream_encoder)); ++ ++ if (!enc) ++ goto enc_create_fail; ++ ++ construct(enc, init); ++ ++ return enc; ++ ++enc_create_fail: ++ return NULL; ++} ++ ++void dce110_stream_encoder_destroy(struct stream_encoder **enc) ++{ ++ dc_service_free((*enc)->ctx, *enc); ++ *enc = NULL; ++} ++ ++static void stop_hdmi_info_packets(struct dc_context *ctx, uint32_t offset) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ /* stop generic packets 0 & 1 on HDMI */ ++ addr = mmHDMI_GENERIC_PACKET_CONTROL0 + offset; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC1_CONT); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC1_LINE); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC1_SEND); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC0_CONT); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC0_LINE); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC0_SEND); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ /* stop generic packets 2 & 3 on HDMI */ ++ addr = mmHDMI_GENERIC_PACKET_CONTROL1 + offset; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL1, ++ HDMI_GENERIC2_CONT); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL1, ++ HDMI_GENERIC2_LINE); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL1, ++ HDMI_GENERIC2_SEND); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL1, ++ HDMI_GENERIC3_CONT); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL1, ++ HDMI_GENERIC3_LINE); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_GENERIC_PACKET_CONTROL1, ++ HDMI_GENERIC3_SEND); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ /* stop AVI packet on HDMI */ ++ addr = mmHDMI_INFOFRAME_CONTROL0 + offset; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AVI_INFO_SEND); ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AVI_INFO_CONT); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void stop_dp_info_packets(struct dc_context *ctx, int32_t offset) ++{ ++ /* stop generic packets on DP */ ++ ++ const uint32_t addr = mmDP_SEC_CNTL + offset; ++ ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP0_ENABLE); ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP1_ENABLE); ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP2_ENABLE); ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP3_ENABLE); ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_AVI_ENABLE); ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_MPG_ENABLE); ++ set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_STREAM_ENABLE); ++ ++ /* this register shared with audio info frame. ++ * therefore we need to keep master enabled ++ * if at least one of the fields is not 0 */ ++ ++ if (value) ++ set_reg_field_value( ++ value, ++ 1, ++ DP_SEC_CNTL, ++ DP_SEC_STREAM_ENABLE); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++void dce110_stream_encoder_stop_info_packets( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ enum signal_type signal) ++{ ++ if (dc_is_hdmi_signal(signal)) ++ stop_hdmi_info_packets( ++ enc->ctx, ++ fe_engine_offsets[engine]); ++ else if (dc_is_dp_signal(signal)) ++ stop_dp_info_packets( ++ enc->ctx, ++ fe_engine_offsets[engine]); ++} ++ ++ ++static void update_avi_info_packet( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ enum signal_type signal, ++ const struct encoder_info_packet *info_packet) ++{ ++ const int32_t offset = fe_engine_offsets[engine]; ++ ++ uint32_t regval; ++ uint32_t addr; ++ ++ if (info_packet->valid) { ++ const uint32_t *content = ++ (const uint32_t *) &info_packet->sb[0]; ++ ++ { ++ regval = content[0]; ++ ++ dal_write_reg( ++ enc->ctx, ++ mmAFMT_AVI_INFO0 + offset, ++ regval); ++ } ++ { ++ regval = content[1]; ++ ++ dal_write_reg( ++ enc->ctx, ++ mmAFMT_AVI_INFO1 + offset, ++ regval); ++ } ++ { ++ regval = content[2]; ++ ++ dal_write_reg( ++ enc->ctx, ++ mmAFMT_AVI_INFO2 + offset, ++ regval); ++ } ++ { ++ regval = content[3]; ++ ++ /* move version to AVI_INFO3 */ ++ set_reg_field_value( ++ regval, ++ info_packet->hb1, ++ AFMT_AVI_INFO3, ++ AFMT_AVI_INFO_VERSION); ++ ++ dal_write_reg( ++ enc->ctx, ++ mmAFMT_AVI_INFO3 + offset, ++ regval); ++ } ++ ++ if (dc_is_hdmi_signal(signal)) { ++ ++ uint32_t control0val; ++ uint32_t control1val; ++ ++ addr = mmHDMI_INFOFRAME_CONTROL0 + offset; ++ ++ control0val = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value( ++ control0val, ++ 1, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AVI_INFO_SEND); ++ ++ set_reg_field_value( ++ control0val, ++ 1, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AVI_INFO_CONT); ++ ++ dal_write_reg(enc->ctx, addr, control0val); ++ ++ addr = mmHDMI_INFOFRAME_CONTROL1 + offset; ++ ++ control1val = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value( ++ control1val, ++ VBI_LINE_0 + 2, ++ HDMI_INFOFRAME_CONTROL1, ++ HDMI_AVI_INFO_LINE); ++ ++ dal_write_reg(enc->ctx, addr, control1val); ++ } ++ } else if (dc_is_hdmi_signal(signal)) { ++ addr = mmHDMI_INFOFRAME_CONTROL0 + offset; ++ ++ regval = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value( ++ regval, ++ 0, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AVI_INFO_SEND); ++ ++ set_reg_field_value( ++ regval, ++ 0, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AVI_INFO_CONT); ++ ++ dal_write_reg(enc->ctx, addr, regval); ++ } ++} ++ ++static void update_generic_info_packet( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ uint32_t packet_index, ++ const struct encoder_info_packet *info_packet) ++{ ++ uint32_t addr; ++ uint32_t regval; ++ /* choose which generic packet to use */ ++ { ++ addr = mmAFMT_VBI_PACKET_CONTROL + fe_engine_offsets[engine]; ++ ++ regval = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value( ++ regval, ++ packet_index, ++ AFMT_VBI_PACKET_CONTROL, ++ AFMT_GENERIC_INDEX); ++ ++ dal_write_reg(enc->ctx, addr, regval); ++ } ++ ++ /* write generic packet header ++ * (4th byte is for GENERIC0 only) */ ++ { ++ addr = mmAFMT_GENERIC_HDR + fe_engine_offsets[engine]; ++ ++ regval = 0; ++ ++ set_reg_field_value( ++ regval, ++ info_packet->hb0, ++ AFMT_GENERIC_HDR, ++ AFMT_GENERIC_HB0); ++ ++ set_reg_field_value( ++ regval, ++ info_packet->hb1, ++ AFMT_GENERIC_HDR, ++ AFMT_GENERIC_HB1); ++ ++ set_reg_field_value( ++ regval, ++ info_packet->hb2, ++ AFMT_GENERIC_HDR, ++ AFMT_GENERIC_HB2); ++ ++ set_reg_field_value( ++ regval, ++ info_packet->hb3, ++ AFMT_GENERIC_HDR, ++ AFMT_GENERIC_HB3); ++ ++ dal_write_reg(enc->ctx, addr, regval); ++ } ++ ++ /* write generic packet contents ++ * (we never use last 4 bytes) ++ * there are 8 (0-7) mmDIG0_AFMT_GENERIC0_x registers */ ++ { ++ const uint32_t *content = ++ (const uint32_t *) &info_packet->sb[0]; ++ ++ uint32_t counter = 0; ++ ++ addr = mmAFMT_GENERIC_0 + fe_engine_offsets[engine]; ++ ++ do { ++ dal_write_reg(enc->ctx, addr++, *content++); ++ ++ ++counter; ++ } while (counter < 7); ++ } ++ ++ dal_write_reg( ++ enc->ctx, ++ mmAFMT_GENERIC_7 + fe_engine_offsets[engine], ++ 0); ++ ++ /* force double-buffered packet update */ ++ { ++ addr = mmAFMT_VBI_PACKET_CONTROL + fe_engine_offsets[engine]; ++ ++ regval = dal_read_reg(enc->ctx, addr); ++ ++ set_reg_field_value( ++ regval, ++ (packet_index == 0), ++ AFMT_VBI_PACKET_CONTROL, ++ AFMT_GENERIC0_UPDATE); ++ ++ set_reg_field_value( ++ regval, ++ (packet_index == 2), ++ AFMT_VBI_PACKET_CONTROL, ++ AFMT_GENERIC2_UPDATE); ++ ++ dal_write_reg(enc->ctx, addr, regval); ++ } ++} ++ ++static void update_hdmi_info_packet( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ uint32_t packet_index, ++ const struct encoder_info_packet *info_packet) ++{ ++ uint32_t cont, send, line; ++ uint32_t addr = fe_engine_offsets[engine]; ++ uint32_t regval; ++ ++ if (info_packet->valid) { ++ update_generic_info_packet( ++ enc, ++ engine, ++ packet_index, ++ info_packet); ++ ++ /* enable transmission of packet(s) - ++ * packet transmission begins on the next frame */ ++ cont = 1; ++ /* send packet(s) every frame */ ++ send = 1; ++ /* select line number to send packets on */ ++ line = 2; ++ } else { ++ cont = 0; ++ send = 0; ++ line = 0; ++ } ++ ++ /* choose which generic packet control to use */ ++ ++ switch (packet_index) { ++ case 0: ++ case 1: ++ addr += mmHDMI_GENERIC_PACKET_CONTROL0; ++ break; ++ case 2: ++ case 3: ++ addr += mmHDMI_GENERIC_PACKET_CONTROL1; ++ break; ++ default: ++ /* invalid HW packet index */ ++ dal_logger_write( ++ enc->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_ENCODER, ++ "Invalid HW packet index: %s()\n", ++ __func__); ++ break; ++ } ++ ++ regval = dal_read_reg(enc->ctx, addr); ++ ++ switch (packet_index) { ++ case 0: ++ case 2: ++ set_reg_field_value( ++ regval, ++ cont, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC0_CONT); ++ set_reg_field_value( ++ regval, ++ send, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC0_SEND); ++ set_reg_field_value( ++ regval, ++ line, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC0_LINE); ++ break; ++ case 1: ++ case 3: ++ set_reg_field_value( ++ regval, ++ cont, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC1_CONT); ++ set_reg_field_value( ++ regval, ++ send, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC1_SEND); ++ set_reg_field_value( ++ regval, ++ line, ++ HDMI_GENERIC_PACKET_CONTROL0, ++ HDMI_GENERIC1_LINE); ++ break; ++ default: ++ /* invalid HW packet index */ ++ dal_logger_write( ++ enc->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_ENCODER, ++ "Invalid HW packet index: %s()\n", ++ __func__); ++ break; ++ } ++ ++ dal_write_reg(enc->ctx, addr, regval); ++} ++ ++static void update_dp_info_packet( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ uint32_t packet_index, ++ const struct encoder_info_packet *info_packet) ++{ ++ const uint32_t addr = mmDP_SEC_CNTL + fe_engine_offsets[engine]; ++ ++ uint32_t value; ++ ++ if (info_packet->valid) ++ update_generic_info_packet( ++ enc, ++ engine, ++ packet_index, ++ info_packet); ++ ++ /* enable/disable transmission of packet(s). ++ * If enabled, packet transmission begins on the next frame */ ++ ++ value = dal_read_reg(enc->ctx, addr); ++ ++ switch (packet_index) { ++ case 0: ++ set_reg_field_value( ++ value, ++ info_packet->valid, ++ DP_SEC_CNTL, ++ DP_SEC_GSP0_ENABLE); ++ break; ++ case 1: ++ set_reg_field_value( ++ value, ++ info_packet->valid, ++ DP_SEC_CNTL, ++ DP_SEC_GSP1_ENABLE); ++ break; ++ case 2: ++ set_reg_field_value( ++ value, ++ info_packet->valid, ++ DP_SEC_CNTL, ++ DP_SEC_GSP2_ENABLE); ++ break; ++ case 3: ++ set_reg_field_value( ++ value, ++ info_packet->valid, ++ DP_SEC_CNTL, ++ DP_SEC_GSP3_ENABLE); ++ break; ++ default: ++ /* invalid HW packet index */ ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ /* This bit is the master enable bit. ++ * When enabling secondary stream engine, ++ * this master bit must also be set. ++ * This register shared with audio info frame. ++ * Therefore we need to enable master bit ++ * if at least on of the fields is not 0 */ ++ ++ if (value) ++ set_reg_field_value( ++ value, ++ 1, ++ DP_SEC_CNTL, ++ DP_SEC_STREAM_ENABLE); ++ ++ dal_write_reg(enc->ctx, addr, value); ++} ++ ++void dce110_stream_encoder_update_info_packets( ++ struct stream_encoder *enc, ++ enum signal_type signal, ++ const struct encoder_info_frame *info_frame) ++{ ++ if (dc_is_hdmi_signal(signal)) { ++ update_avi_info_packet( ++ enc, ++ enc->id, ++ signal, ++ &info_frame->avi); ++ update_hdmi_info_packet(enc, enc->id, 0, &info_frame->vendor); ++ update_hdmi_info_packet(enc, enc->id, 1, &info_frame->gamut); ++ update_hdmi_info_packet(enc, enc->id, 2, &info_frame->spd); ++ } else if (dc_is_dp_signal(signal)) ++ update_dp_info_packet(enc, enc->id, 0, &info_frame->vsc); ++} ++ ++static void dp_steer_fifo_reset( ++ struct dc_context *ctx, ++ enum engine_id engine, ++ bool reset) ++{ ++ const uint32_t addr = mmDP_STEER_FIFO + fe_engine_offsets[engine]; ++ ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, reset, DP_STEER_FIFO, DP_STEER_FIFO_RESET); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++/* ++ * @brief ++ * Output blank data, ++ * prevents output of the actual surface data on active transmitter ++ */ ++enum encoder_result dce110_stream_encoder_blank( ++ struct stream_encoder *enc, ++ enum signal_type signal) ++{ ++ enum engine_id engine = enc->id; ++ const uint32_t addr = mmDP_VID_STREAM_CNTL + fe_engine_offsets[engine]; ++ uint32_t value = dal_read_reg(enc->ctx, addr); ++ uint32_t retries = 0; ++ uint32_t max_retries = DP_BLANK_MAX_RETRY * 10; ++ ++ if (!dc_is_dp_signal(signal)) ++ return ENCODER_RESULT_OK; ++ ++ /* Note: For CZ, we are changing driver default to disable ++ * stream deferred to next VBLANK. If results are positive, we ++ * will make the same change to all DCE versions. There are a ++ * handful of panels that cannot handle disable stream at ++ * HBLANK and will result in a white line flash across the ++ * screen on stream disable. */ ++ ++ /* Specify the video stream disable point ++ * (2 = start of the next vertical blank) */ ++ set_reg_field_value( ++ value, ++ 2, ++ DP_VID_STREAM_CNTL, ++ DP_VID_STREAM_DIS_DEFER); ++ /* Larger delay to wait until VBLANK - use max retry of ++ * 10us*3000=30ms. This covers 16.6ms of typical 60 Hz mode + ++ * a little more because we may not trust delay accuracy. */ ++ max_retries = DP_BLANK_MAX_RETRY * 150; ++ ++ /* disable DP stream */ ++ set_reg_field_value(value, 0, DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ /* the encoder stops sending the video stream ++ * at the start of the vertical blanking. ++ * Poll for DP_VID_STREAM_STATUS == 0 */ ++ ++ do { ++ value = dal_read_reg(enc->ctx, addr); ++ ++ if (!get_reg_field_value( ++ value, ++ DP_VID_STREAM_CNTL, ++ DP_VID_STREAM_STATUS)) ++ break; ++ ++ dc_service_delay_in_microseconds(enc->ctx, 10); ++ ++ ++retries; ++ } while (retries < max_retries); ++ ++ ASSERT(retries <= max_retries); ++ ++ /* Tell the DP encoder to ignore timing from CRTC, must be done after ++ * the polling. If we set DP_STEER_FIFO_RESET before DP stream blank is ++ * complete, stream status will be stuck in video stream enabled state, ++ * i.e. DP_VID_STREAM_STATUS stuck at 1. */ ++ dp_steer_fifo_reset(enc->ctx, engine, true); ++ ++ return ENCODER_RESULT_OK; ++} ++ ++static void unblank_dp_output( ++ struct stream_encoder *enc, ++ enum engine_id engine) ++{ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* set DIG_START to 0x1 to resync FIFO */ ++ addr = mmDIG_FE_CNTL + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, 1, DIG_FE_CNTL, DIG_START); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ /* switch DP encoder to CRTC data */ ++ dp_steer_fifo_reset(enc->ctx, engine, false); ++ ++ /* wait 100us for DIG/DP logic to prime ++ * (i.e. a few video lines) */ ++ dc_service_delay_in_microseconds(enc->ctx, 100); ++ ++ /* the hardware would start sending video at the start of the next DP ++ * frame (i.e. rising edge of the vblank). ++ * NOTE: We used to program DP_VID_STREAM_DIS_DEFER = 2 here, but this ++ * register has no effect on enable transition! HW always guarantees ++ * VID_STREAM enable at start of next frame, and this is not ++ * programmable */ ++ addr = mmDP_VID_STREAM_CNTL + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value( ++ value, ++ true, ++ DP_VID_STREAM_CNTL, ++ DP_VID_STREAM_ENABLE); ++ dal_write_reg(enc->ctx, addr, value); ++ ++} ++ ++static void setup_vid_stream( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ uint32_t m_vid, ++ uint32_t n_vid) ++{ ++ uint32_t addr; ++ uint32_t value; ++ ++ /* enable auto measurement */ ++ addr = mmDP_VID_TIMING + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, 0, DP_VID_TIMING, DP_VID_M_N_GEN_EN); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ /* auto measurement need 1 full 0x8000 symbol cycle to kick in, ++ * therefore program initial value for Mvid and Nvid */ ++ addr = mmDP_VID_N + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, n_vid, DP_VID_N, DP_VID_N); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ addr = mmDP_VID_M + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, m_vid, DP_VID_M, DP_VID_M); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ addr = mmDP_VID_TIMING + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, 1, DP_VID_TIMING, DP_VID_M_N_GEN_EN); ++ dal_write_reg(enc->ctx, addr, value); ++} ++/* ++ * @brief ++ * Stop sending blank data, ++ * output the actual surface data on active transmitter ++ */ ++enum encoder_result dce110_stream_encoder_unblank( ++ struct stream_encoder *enc, ++ const struct encoder_unblank_param *param) ++{ ++ bool is_dp_signal = param->signal == SIGNAL_TYPE_DISPLAY_PORT ++ || param->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ++ || param->signal == SIGNAL_TYPE_EDP; ++ ++ if (!is_dp_signal) ++ return ENCODER_RESULT_OK; ++ ++ if (param->link_settings.link_rate != LINK_RATE_UNKNOWN) { ++ uint32_t n_vid = 0x8000; ++ uint32_t m_vid; ++ ++ /* M / N = Fstream / Flink ++ * m_vid / n_vid = pixel rate / link rate */ ++ ++ uint64_t m_vid_l = n_vid; ++ ++ m_vid_l *= param->crtc_timing.pixel_clock; ++ m_vid_l = div_u64(m_vid_l, ++ param->link_settings.link_rate ++ * LINK_RATE_REF_FREQ_IN_KHZ); ++ ++ m_vid = (uint32_t) m_vid_l; ++ ++ setup_vid_stream(enc, ++ enc->id, m_vid, n_vid); ++ } ++ ++ unblank_dp_output(enc, enc->id); ++ ++ return ENCODER_RESULT_OK; ++} ++ ++static void set_dp_stream_attributes( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ const struct dc_crtc_timing *timing) ++{ ++ const uint32_t addr = mmDP_PIXEL_FORMAT + fe_engine_offsets[engine]; ++ uint32_t value = dal_read_reg(enc->ctx, addr); ++ ++ /* set pixel encoding */ ++ switch (timing->pixel_encoding) { ++ case PIXEL_ENCODING_YCBCR422: ++ set_reg_field_value( ++ value, ++ DP_PIXEL_ENCODING_YCBCR422, ++ DP_PIXEL_FORMAT, ++ DP_PIXEL_ENCODING); ++ break; ++ case PIXEL_ENCODING_YCBCR444: ++ set_reg_field_value( ++ value, ++ DP_PIXEL_ENCODING_YCBCR444, ++ DP_PIXEL_FORMAT, ++ DP_PIXEL_ENCODING); ++ ++ if (timing->flags.Y_ONLY) ++ if (timing->display_color_depth != COLOR_DEPTH_666) ++ /* HW testing only, no use case yet. ++ * Color depth of Y-only could be ++ * 8, 10, 12, 16 bits */ ++ set_reg_field_value( ++ value, ++ DP_PIXEL_ENCODING_Y_ONLY, ++ DP_PIXEL_FORMAT, ++ DP_PIXEL_ENCODING); ++ /* Note: DP_MSA_MISC1 bit 7 is the indicator ++ * of Y-only mode. ++ * This bit is set in HW if register ++ * DP_PIXEL_ENCODING is programmed to 0x4 */ ++ break; ++ default: ++ set_reg_field_value( ++ value, ++ DP_PIXEL_ENCODING_RGB444, ++ DP_PIXEL_FORMAT, ++ DP_PIXEL_ENCODING); ++ break; ++ } ++ ++ /* set color depth */ ++ ++ switch (timing->display_color_depth) { ++ case COLOR_DEPTH_888: ++ set_reg_field_value( ++ value, ++ DP_COMPONENT_DEPTH_8BPC, ++ DP_PIXEL_FORMAT, ++ DP_COMPONENT_DEPTH); ++ break; ++ case COLOR_DEPTH_101010: ++ set_reg_field_value( ++ value, ++ DP_COMPONENT_DEPTH_10BPC, ++ DP_PIXEL_FORMAT, ++ DP_COMPONENT_DEPTH); ++ break; ++ case COLOR_DEPTH_121212: ++ set_reg_field_value( ++ value, ++ DP_COMPONENT_DEPTH_12BPC, ++ DP_PIXEL_FORMAT, ++ DP_COMPONENT_DEPTH); ++ break; ++ default: ++ set_reg_field_value( ++ value, ++ DP_COMPONENT_DEPTH_6BPC, ++ DP_PIXEL_FORMAT, ++ DP_COMPONENT_DEPTH); ++ break; ++ } ++ ++ /* set dynamic range and YCbCr range */ ++ set_reg_field_value(value, 0, DP_PIXEL_FORMAT, DP_DYN_RANGE); ++ set_reg_field_value(value, 0, DP_PIXEL_FORMAT, DP_YCBCR_RANGE); ++ ++ dal_write_reg(enc->ctx, addr, value); ++} ++ ++static void setup_hdmi( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t output_pixel_clock = timing->pix_clk_khz; ++ uint32_t value; ++ uint32_t addr; ++ ++ addr = mmHDMI_CONTROL + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, 1, HDMI_CONTROL, HDMI_PACKET_GEN_VERSION); ++ set_reg_field_value(value, 1, HDMI_CONTROL, HDMI_KEEPOUT_MODE); ++ set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE); ++ set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN); ++ set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE); ++ ++ switch (timing->display_color_depth) { ++ case COLOR_DEPTH_888: ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_DEPTH); ++ break; ++ case COLOR_DEPTH_101010: ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_DEPTH); ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_ENABLE); ++ output_pixel_clock = (timing->pix_clk_khz * 30) / 24; ++ break; ++ case COLOR_DEPTH_121212: ++ set_reg_field_value( ++ value, ++ 2, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_DEPTH); ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_ENABLE); ++ output_pixel_clock = (timing->pix_clk_khz * 36) / 24; ++ break; ++ case COLOR_DEPTH_161616: ++ set_reg_field_value( ++ value, ++ 3, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_DEPTH); ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_DEEP_COLOR_ENABLE); ++ output_pixel_clock = (timing->pix_clk_khz * 48) / 24; ++ break; ++ default: ++ break; ++ } ++ ++ if (output_pixel_clock >= HDMI_CLOCK_CHANNEL_RATE_MORE_340M) { ++ /* enable HDMI data scrambler */ ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_DATA_SCRAMBLE_EN); ++ ++ /* HDMI_CLOCK_CHANNEL_RATE_MORE_340M ++ * Clock channel frequency is 1/4 of character rate.*/ ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_CLOCK_CHANNEL_RATE); ++ } else if (timing->flags.LTE_340MCSC_SCRAMBLE) { ++ ++ /* TODO: New feature for DCE11, still need to implement */ ++ ++ /* enable HDMI data scrambler */ ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_CONTROL, ++ HDMI_DATA_SCRAMBLE_EN); ++ ++ /* HDMI_CLOCK_CHANNEL_FREQ_EQUAL_TO_CHAR_RATE ++ * Clock channel frequency is the same ++ * as character rate */ ++ set_reg_field_value( ++ value, ++ 0, ++ HDMI_CONTROL, ++ HDMI_CLOCK_CHANNEL_RATE); ++ } ++ ++ dal_write_reg(enc->ctx, addr, value); ++ ++ addr = mmHDMI_VBI_PACKET_CONTROL + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT); ++ set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND); ++ set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND); ++ ++ dal_write_reg(enc->ctx, addr, value); ++ ++ /* following belongs to audio */ ++ addr = mmHDMI_INFOFRAME_CONTROL0 + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ HDMI_INFOFRAME_CONTROL0, ++ HDMI_AUDIO_INFO_SEND); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ addr = mmAFMT_INFOFRAME_CONTROL0 + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ AFMT_INFOFRAME_CONTROL0, ++ AFMT_AUDIO_INFO_UPDATE); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ addr = mmHDMI_INFOFRAME_CONTROL1 + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value( ++ value, ++ VBI_LINE_0 + 2, ++ HDMI_INFOFRAME_CONTROL1, ++ HDMI_AUDIO_INFO_LINE); ++ dal_write_reg(enc->ctx, addr, value); ++ ++ addr = mmHDMI_GC + fe_engine_offsets[engine]; ++ value = dal_read_reg(enc->ctx, addr); ++ set_reg_field_value(value, 0, HDMI_GC, HDMI_GC_AVMUTE); ++ dal_write_reg(enc->ctx, addr, value); ++ ++} ++ ++static void set_tmds_stream_attributes( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ enum signal_type signal, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t addr = mmDIG_FE_CNTL + fe_engine_offsets[engine]; ++ uint32_t value = dal_read_reg(enc->ctx, addr); ++ ++ switch (timing->pixel_encoding) { ++ case PIXEL_ENCODING_YCBCR422: ++ set_reg_field_value(value, 1, DIG_FE_CNTL, TMDS_PIXEL_ENCODING); ++ break; ++ default: ++ set_reg_field_value(value, 0, DIG_FE_CNTL, TMDS_PIXEL_ENCODING); ++ break; ++ } ++ ++ switch (timing->pixel_encoding) { ++ case COLOR_DEPTH_101010: ++ if ((signal == SIGNAL_TYPE_DVI_SINGLE_LINK ++ || signal == SIGNAL_TYPE_DVI_DUAL_LINK) ++ && timing->pixel_encoding == PIXEL_ENCODING_RGB) ++ set_reg_field_value( ++ value, ++ 2, ++ DIG_FE_CNTL, ++ TMDS_COLOR_FORMAT); ++ else ++ set_reg_field_value( ++ value, ++ 0, ++ DIG_FE_CNTL, ++ TMDS_COLOR_FORMAT); ++ break; ++ default: ++ set_reg_field_value(value, 0, DIG_FE_CNTL, TMDS_COLOR_FORMAT); ++ break; ++ } ++ dal_write_reg(enc->ctx, addr, value); ++} ++ ++/* ++ * @brief ++ * Associate digital encoder with specified output transmitter ++ * and configure to output signal. ++ * Encoder will be activated later in enable_output() ++ */ ++enum encoder_result dce110_stream_encoder_setup( ++ struct stream_encoder *enc, ++ struct dc_crtc_timing *crtc_timing, ++ enum signal_type signal, ++ bool enable_audio) ++{ ++ if (!dc_is_dp_signal(signal)) { ++ struct bp_encoder_control cntl; ++ ++ cntl.action = ENCODER_CONTROL_SETUP; ++ cntl.engine_id = enc->id; ++ cntl.signal = signal; ++ cntl.enable_dp_audio = enable_audio; ++ cntl.pixel_clock = crtc_timing->pix_clk_khz; ++ cntl.lanes_number = (signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? ++ LANE_COUNT_EIGHT : LANE_COUNT_FOUR; ++ cntl.color_depth = crtc_timing->display_color_depth; ++ ++ if (dal_bios_parser_encoder_control( ++ dal_adapter_service_get_bios_parser( ++ enc->adapter_service), ++ &cntl) != BP_RESULT_OK) ++ return ENCODER_RESULT_ERROR; ++ } ++ ++ switch (signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ /* set signal format */ ++ set_tmds_stream_attributes( ++ enc, enc->id, signal, ++ crtc_timing); ++ break; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ /* set signal format */ ++ set_tmds_stream_attributes( ++ enc, enc->id, signal, ++ crtc_timing); ++ /* setup HDMI engine */ ++ setup_hdmi( ++ enc, enc->id, crtc_timing); ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ /* set signal format */ ++ set_dp_stream_attributes(enc, enc->id, crtc_timing); ++ break; ++ default: ++ break; ++ } ++ ++ return ENCODER_RESULT_OK; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h +new file mode 100644 +index 0000000..d2c7b90 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h +@@ -0,0 +1,64 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_STREAM_ENCODER_DCE110_H__ ++#define __DC_STREAM_ENCODER_DCE110_H__ ++ ++struct stream_enc_init_data { ++ enum engine_id stream_engine_id; ++ struct adapter_service *adapter_service; ++ struct dc_context *ctx; ++}; ++ ++struct stream_encoder *dce110_stream_encoder_create( ++ struct stream_enc_init_data *init); ++ ++void dce110_stream_encoder_destroy(struct stream_encoder **enc); ++ ++void dce110_stream_encoder_stop_info_packets( ++ struct stream_encoder *enc, ++ enum engine_id engine, ++ enum signal_type signal); ++ ++void dce110_stream_encoder_update_info_packets( ++ struct stream_encoder *enc, ++ enum signal_type signal, ++ const struct encoder_info_frame *info_frame); ++ ++enum encoder_result dce110_stream_encoder_blank( ++ struct stream_encoder *enc, ++ enum signal_type signal); ++ ++enum encoder_result dce110_stream_encoder_unblank( ++ struct stream_encoder *enc, ++ const struct encoder_unblank_param *param); ++ ++enum encoder_result dce110_stream_encoder_setup( ++ struct stream_encoder *enc, ++ struct dc_crtc_timing *crtc_timing, ++ enum signal_type signal, ++ bool enable_audio); ++ ++#endif /* __DC_STREAM_ENCODER_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c +new file mode 100644 +index 0000000..198ff28 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c +@@ -0,0 +1,1878 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dc_types.h" ++ ++#include "include/grph_object_id.h" ++#include "include/adapter_service_interface.h" ++#include "include/logger_interface.h" ++#include "include/timing_generator_types.h" ++#include "dce110_timing_generator.h" ++ ++ ++enum tg_regs_idx { ++ IDX_CRTC_UPDATE_LOCK, ++ IDX_CRTC_MASTER_UPDATE_LOCK, ++ IDX_CRTC_MASTER_UPDATE_MODE, ++ IDX_CRTC_H_TOTAL, ++ IDX_CRTC_V_TOTAL, ++ IDX_CRTC_H_BLANK_START_END, ++ IDX_CRTC_V_BLANK_START_END, ++ IDX_CRTC_H_SYNC_A, ++ IDX_CRTC_V_SYNC_A, ++ IDX_CRTC_H_SYNC_A_CNTL, ++ IDX_CRTC_V_SYNC_A_CNTL, ++ IDX_CRTC_INTERLACE_CONTROL, ++ IDX_CRTC_BLANK_CONTROL, ++ IDX_PIPE_PG_STATUS, ++ ++ IDX_CRTC_TEST_PATTERN_COLOR, ++ IDX_CRTC_TEST_PATTERN_CONTROL, ++ IDX_CRTC_TEST_PATTERN_PARAMETERS, ++ IDX_CRTC_FLOW_CONTROL, ++ IDX_CRTC_STATUS, ++ IDX_CRTC_STATUS_POSITION, ++ IDX_CRTC_STATUS_FRAME_COUNT, ++ IDX_CRTC_STEREO_CONTROL, ++ IDX_CRTC_STEREO_STATUS, ++ IDX_CRTC_STEREO_FORCE_NEXT_EYE, ++ IDX_CRTC_3D_STRUCTURE_CONTROL, ++ IDX_CRTC_DOUBLE_BUFFER_CONTROL, ++ IDX_CRTC_V_TOTAL_MIN, ++ IDX_CRTC_V_TOTAL_MAX, ++ IDX_CRTC_V_TOTAL_CONTROL, ++ IDX_CRTC_NOM_VERT_POSITION, ++ IDX_CRTC_STATIC_SCREEN_CONTROL, ++ IDX_CRTC_TRIGB_CNTL, ++ IDX_CRTC_FORCE_COUNT_CNTL, ++ IDX_CRTC_GSL_CONTROL, ++ IDX_CRTC_GSL_WINDOW, ++ ++ IDX_CRTC_CONTROL, ++ IDX_CRTC_START_LINE_CONTROL, ++ IDX_CRTC_COUNT_CONTROL, ++ ++ IDX_MODE_EXT_OVERSCAN_LEFT_RIGHT, ++ IDX_MODE_EXT_OVERSCAN_TOP_BOTTOM, ++ IDX_DCP_GSL_CONTROL, ++ IDX_GRPH_UPDATE, ++ ++ IDX_CRTC_VBI_END, ++ ++ IDX_BLND_UNDERFLOW_INTERRUPT, ++ IDX_CRTC_BLACK_COLOR, ++ IDX_CRTC_OVERSCAN_COLOR, ++ IDX_CRTC_BLANK_DATA_COLOR, ++ ++ TG_REGS_IDX_SIZE ++}; ++ ++enum black_color_format { ++ BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ ++ BLACK_COLOR_FORMAT_RGB_LIMITED, ++ BLACK_COLOR_FORMAT_YUV_TV, ++ BLACK_COLOR_FORMAT_YUV_CV, ++ BLACK_COLOR_FORMAT_YUV_SUPER_AA, ++ ++ BLACK_COLOR_FORMAT_COUNT ++}; ++ ++#define regs_for_controller(id)\ ++[CONTROLLER_ID_D ## id - 1] =\ ++{[IDX_CRTC_UPDATE_LOCK] = mmCRTC ## id ## _CRTC_UPDATE_LOCK,\ ++[IDX_CRTC_MASTER_UPDATE_LOCK] = mmCRTC ## id ## _CRTC_MASTER_UPDATE_LOCK,\ ++[IDX_CRTC_MASTER_UPDATE_MODE] = mmCRTC ## id ## _CRTC_MASTER_UPDATE_MODE,\ ++[IDX_CRTC_H_TOTAL] = mmCRTC ## id ## _CRTC_H_TOTAL,\ ++[IDX_CRTC_V_TOTAL] = mmCRTC ## id ## _CRTC_V_TOTAL,\ ++[IDX_CRTC_H_BLANK_START_END] = mmCRTC ## id ## _CRTC_H_BLANK_START_END,\ ++[IDX_CRTC_V_BLANK_START_END] = mmCRTC ## id ## _CRTC_V_BLANK_START_END,\ ++[IDX_CRTC_H_SYNC_A] = mmCRTC ## id ## _CRTC_H_SYNC_A,\ ++[IDX_CRTC_V_SYNC_A] = mmCRTC ## id ## _CRTC_V_SYNC_A,\ ++[IDX_CRTC_H_SYNC_A_CNTL] = mmCRTC ## id ## _CRTC_H_SYNC_A_CNTL,\ ++[IDX_CRTC_V_SYNC_A_CNTL] = mmCRTC ## id ## _CRTC_V_SYNC_A_CNTL,\ ++[IDX_CRTC_INTERLACE_CONTROL] = mmCRTC ## id ## _CRTC_INTERLACE_CONTROL,\ ++[IDX_CRTC_BLANK_CONTROL] = mmCRTC ## id ## _CRTC_BLANK_CONTROL,\ ++[IDX_PIPE_PG_STATUS] = mmPIPE ## id ## _PG_STATUS,\ ++[IDX_CRTC_TEST_PATTERN_COLOR] = mmCRTC ## id ## _CRTC_TEST_PATTERN_COLOR,\ ++[IDX_CRTC_TEST_PATTERN_CONTROL] = mmCRTC ## id ## _CRTC_TEST_PATTERN_CONTROL,\ ++[IDX_CRTC_TEST_PATTERN_PARAMETERS] =\ ++mmCRTC ## id ## _CRTC_TEST_PATTERN_PARAMETERS,\ ++[IDX_CRTC_FLOW_CONTROL] = mmCRTC ## id ## _CRTC_FLOW_CONTROL,\ ++[IDX_CRTC_STATUS] = mmCRTC ## id ## _CRTC_STATUS,\ ++[IDX_CRTC_STATUS_POSITION] = mmCRTC ## id ## _CRTC_STATUS_POSITION,\ ++[IDX_CRTC_STATUS_FRAME_COUNT] = mmCRTC ## id ## _CRTC_STATUS_FRAME_COUNT,\ ++[IDX_CRTC_STEREO_CONTROL] = mmCRTC ## id ## _CRTC_STEREO_CONTROL,\ ++[IDX_CRTC_STEREO_STATUS] = mmCRTC ## id ## _CRTC_STEREO_STATUS,\ ++[IDX_CRTC_STEREO_FORCE_NEXT_EYE] = \ ++mmCRTC ## id ## _CRTC_STEREO_FORCE_NEXT_EYE,\ ++[IDX_CRTC_3D_STRUCTURE_CONTROL] = mmCRTC ## id ## _CRTC_3D_STRUCTURE_CONTROL,\ ++[IDX_CRTC_DOUBLE_BUFFER_CONTROL] =\ ++mmCRTC ## id ## _CRTC_DOUBLE_BUFFER_CONTROL,\ ++[IDX_CRTC_V_TOTAL_MIN] = mmCRTC ## id ## _CRTC_V_TOTAL_MIN,\ ++[IDX_CRTC_V_TOTAL_MAX] = mmCRTC ## id ## _CRTC_V_TOTAL_MAX,\ ++[IDX_CRTC_V_TOTAL_CONTROL] = mmCRTC ## id ## _CRTC_V_TOTAL_CONTROL,\ ++[IDX_CRTC_NOM_VERT_POSITION] = mmCRTC ## id ## _CRTC_NOM_VERT_POSITION,\ ++[IDX_CRTC_STATIC_SCREEN_CONTROL] =\ ++mmCRTC ## id ## _CRTC_STATIC_SCREEN_CONTROL,\ ++[IDX_CRTC_TRIGB_CNTL] = mmCRTC ## id ## _CRTC_TRIGB_CNTL,\ ++[IDX_CRTC_FORCE_COUNT_CNTL] = mmCRTC ## id ## _CRTC_FORCE_COUNT_NOW_CNTL,\ ++[IDX_CRTC_GSL_CONTROL] = mmCRTC ## id ## _CRTC_GSL_CONTROL,\ ++[IDX_CRTC_GSL_WINDOW] = mmCRTC ## id ## _CRTC_GSL_WINDOW,\ ++[IDX_CRTC_CONTROL] = mmCRTC ## id ## _CRTC_CONTROL,\ ++[IDX_CRTC_START_LINE_CONTROL] = mmCRTC ## id ## _CRTC_START_LINE_CONTROL,\ ++[IDX_CRTC_COUNT_CONTROL] = mmCRTC ## id ## _CRTC_COUNT_CONTROL,\ ++[IDX_MODE_EXT_OVERSCAN_LEFT_RIGHT] = mmSCL ## id ## _EXT_OVERSCAN_LEFT_RIGHT,\ ++[IDX_MODE_EXT_OVERSCAN_TOP_BOTTOM] = mmSCL ## id ## _EXT_OVERSCAN_TOP_BOTTOM,\ ++[IDX_DCP_GSL_CONTROL] = mmDCP ## id ## _DCP_GSL_CONTROL,\ ++[IDX_GRPH_UPDATE] = mmDCP ## id ## _GRPH_UPDATE,\ ++[IDX_CRTC_VBI_END] = mmCRTC ## id ## _CRTC_VBI_END,\ ++[IDX_BLND_UNDERFLOW_INTERRUPT] = mmBLND ## id ## _BLND_UNDERFLOW_INTERRUPT,\ ++[IDX_CRTC_BLACK_COLOR] = mmCRTC ## id ## _CRTC_BLACK_COLOR,\ ++[IDX_CRTC_OVERSCAN_COLOR] = mmCRTC ## id ## _CRTC_OVERSCAN_COLOR,\ ++[IDX_CRTC_BLANK_DATA_COLOR] = mmCRTC ## id ## _CRTC_BLANK_DATA_COLOR,\ ++} ++ ++#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 ++ ++#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) ++#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) ++ ++ ++#define FROM_TIMING_GENERATOR(tg)\ ++ container_of(tg, struct dce110_timing_generator, base) ++ ++static uint32_t tg_regs[][TG_REGS_IDX_SIZE] = { ++ regs_for_controller(0), ++ regs_for_controller(1), ++ regs_for_controller(2), ++}; ++ ++/******************************************************************************* ++ * GSL Sync related values */ ++ ++/* In VSync mode, after 4 units of time, master pipe will generate ++ * flip_ready signal */ ++#define VFLIP_READY_DELAY 4 ++/* In HSync mode, after 2 units of time, master pipe will generate ++ * flip_ready signal */ ++#define HFLIP_READY_DELAY 2 ++/* 6 lines delay between forcing flip and checking all pipes ready */ ++#define HFLIP_CHECK_DELAY 6 ++/* 3 lines before end of frame */ ++#define FLIP_READY_BACK_LOOKUP 3 ++ ++/* Trigger Source Select - ASIC-dependant, actual values for the ++ * register programming */ ++enum trigger_source_select { ++ TRIGGER_SOURCE_SELECT_LOGIC_ZERO = 0, ++ TRIGGER_SOURCE_SELECT_CRTC_VSYNCA = 1, ++ TRIGGER_SOURCE_SELECT_CRTC_HSYNCA = 2, ++ TRIGGER_SOURCE_SELECT_CRTC_VSYNCB = 3, ++ TRIGGER_SOURCE_SELECT_CRTC_HSYNCB = 4, ++ TRIGGER_SOURCE_SELECT_GENERICF = 5, ++ TRIGGER_SOURCE_SELECT_GENERICE = 6, ++ TRIGGER_SOURCE_SELECT_VSYNCA = 7, ++ TRIGGER_SOURCE_SELECT_HSYNCA = 8, ++ TRIGGER_SOURCE_SELECT_VSYNCB = 9, ++ TRIGGER_SOURCE_SELECT_HSYNCB = 10, ++ TRIGGER_SOURCE_SELECT_HPD1 = 11, ++ TRIGGER_SOURCE_SELECT_HPD2 = 12, ++ TRIGGER_SOURCE_SELECT_GENERICD = 13, ++ TRIGGER_SOURCE_SELECT_GENERICC = 14, ++ TRIGGER_SOURCE_SELECT_VIDEO_CAPTURE = 15, ++ TRIGGER_SOURCE_SELECT_GSL_GROUP0 = 16, ++ TRIGGER_SOURCE_SELECT_GSL_GROUP1 = 17, ++ TRIGGER_SOURCE_SELECT_GSL_GROUP2 = 18, ++ TRIGGER_SOURCE_SELECT_BLONY = 19, ++ TRIGGER_SOURCE_SELECT_GENERICA = 20, ++ TRIGGER_SOURCE_SELECT_GENERICB = 21, ++ TRIGGER_SOURCE_SELECT_GSL_ALLOW_FLIP = 22, ++ TRIGGER_SOURCE_SELECT_MANUAL_TRIGGER = 23 ++}; ++ ++/* Trigger Source Select - ASIC-dependant, actual values for the ++ * register programming */ ++enum trigger_polarity_select { ++ TRIGGER_POLARITY_SELECT_LOGIC_ZERO = 0, ++ TRIGGER_POLARITY_SELECT_CRTC = 1, ++ TRIGGER_POLARITY_SELECT_GENERICA = 2, ++ TRIGGER_POLARITY_SELECT_GENERICB = 3, ++ TRIGGER_POLARITY_SELECT_HSYNCA = 4, ++ TRIGGER_POLARITY_SELECT_HSYNCB = 5, ++ TRIGGER_POLARITY_SELECT_VIDEO_CAPTURE = 6, ++ TRIGGER_POLARITY_SELECT_GENERICC = 7 ++}; ++ ++/******************************************************************************/ ++ ++bool dce110_timing_generator_construct( ++ struct timing_generator *tg, ++ enum controller_id id) ++{ ++ tg->controller_id = id; ++ return true; ++} ++ ++static const struct crtc_black_color black_color_format[] = { ++ /* BlackColorFormat_RGB_FullRange */ ++ {0, 0, 0}, ++ /* BlackColorFormat_RGB_Limited */ ++ {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE}, ++ /* BlackColorFormat_YUV_TV */ ++ {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV}, ++ /* BlackColorFormat_YUV_CV */ ++ {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4CV, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV}, ++ /* BlackColorFormat_YUV_SuperAA */ ++ {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA} ++}; ++ ++void dce110_timing_generator_color_space_to_black_color( ++ enum color_space colorspace, ++ struct crtc_black_color *black_color) ++{ ++ switch (colorspace) { ++ case COLOR_SPACE_YPBPR601: ++ *black_color = black_color_format[BLACK_COLOR_FORMAT_YUV_TV]; ++ break; ++ ++ case COLOR_SPACE_YPBPR709: ++ case COLOR_SPACE_YCBCR601: ++ case COLOR_SPACE_YCBCR709: ++ *black_color = black_color_format[BLACK_COLOR_FORMAT_YUV_CV]; ++ break; ++ ++ case COLOR_SPACE_N_MVPU_SUPER_AA: ++ /* In crossfire SuperAA mode, the slave overscan data is forced ++ * to 0 in the pixel mixer on the master. As a result, we need ++ * to adjust the blank color so that after blending the ++ * master+slave, it will appear black ++ */ ++ *black_color = ++ black_color_format[BLACK_COLOR_FORMAT_YUV_SUPER_AA]; ++ break; ++ ++ case COLOR_SPACE_SRGB_LIMITED_RANGE: ++ *black_color = ++ black_color_format[BLACK_COLOR_FORMAT_RGB_LIMITED]; ++ break; ++ ++ default: ++ /* fefault is sRGB black (full range). */ ++ *black_color = ++ black_color_format[BLACK_COLOR_FORMAT_RGB_FULLRANGE]; ++ /* default is sRGB black 0. */ ++ break; ++ } ++} ++ ++/** ++* apply_front_porch_workaround ++* ++* This is a workaround for a bug that has existed since R5xx and has not been ++* fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. ++*/ ++void dce110_timing_generator_apply_front_porch_workaround( ++ struct timing_generator *tg, ++ struct dc_crtc_timing *timing) ++{ ++ if (timing->flags.INTERLACE == 1) { ++ if (timing->v_front_porch < 2) ++ timing->v_front_porch = 2; ++ } else { ++ if (timing->v_front_porch < 1) ++ timing->v_front_porch = 1; ++ } ++} ++ ++int32_t dce110_timing_generator_get_vsynch_and_front_porch_size( ++ const struct dc_crtc_timing *timing) ++{ ++ return timing->v_sync_width + timing->v_front_porch; ++} ++ ++ ++void dce110_timing_generator_set_early_control( ++ struct timing_generator *tg, ++ uint32_t early_cntl) ++{ ++ uint32_t regval; ++ uint32_t address = tg->regs[IDX_CRTC_CONTROL]; ++ ++ regval = dal_read_reg(tg->ctx, address); ++ set_reg_field_value(regval, early_cntl, ++ CRTC_CONTROL, CRTC_HBLANK_EARLY_CONTROL); ++ dal_write_reg(tg->ctx, address, regval); ++} ++ ++/** ++ * Enable CRTC ++ * Enable CRTC - call ASIC Control Object to enable Timing generator. ++ */ ++bool dce110_timing_generator_enable_crtc(struct timing_generator *tg) ++{ ++ enum bp_result result; ++ ++ /* 0 value is needed by DRR and is also suggested default value for CZ ++ */ ++ uint32_t value; ++ ++ value = dal_read_reg(tg->ctx, ++ tg->regs[IDX_CRTC_MASTER_UPDATE_MODE]); ++ set_reg_field_value(value, 3, ++ CRTC_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); ++ dal_write_reg(tg->ctx, ++ tg->regs[IDX_CRTC_MASTER_UPDATE_MODE], value); ++ ++ result = dal_bios_parser_enable_crtc(tg->bp, tg->controller_id, true); ++ ++ return result == BP_RESULT_OK; ++} ++ ++void dce110_timing_generator_program_blank_color( ++ struct timing_generator *tg, ++ enum color_space color_space) ++{ ++ struct crtc_black_color black_color; ++ uint32_t addr = tg->regs[IDX_CRTC_BLACK_COLOR]; ++ uint32_t value = dal_read_reg(tg->ctx, addr); ++ ++ dce110_timing_generator_color_space_to_black_color( ++ color_space, ++ &black_color); ++ ++ set_reg_field_value( ++ value, ++ black_color.black_color_b_cb, ++ CRTC_BLACK_COLOR, ++ CRTC_BLACK_COLOR_B_CB); ++ set_reg_field_value( ++ value, ++ black_color.black_color_g_y, ++ CRTC_BLACK_COLOR, ++ CRTC_BLACK_COLOR_G_Y); ++ set_reg_field_value( ++ value, ++ black_color.black_color_r_cr, ++ CRTC_BLACK_COLOR, ++ CRTC_BLACK_COLOR_R_CR); ++ ++ dal_write_reg(tg->ctx, addr, value); ++} ++ ++/** ++ * blank_crtc ++ * Call ASIC Control Object to Blank CRTC. ++ */ ++ ++bool dce110_timing_generator_blank_crtc(struct timing_generator *tg) ++{ ++ uint32_t addr = tg->regs[IDX_CRTC_BLANK_CONTROL]; ++ uint32_t value = dal_read_reg(tg->ctx, addr); ++ uint8_t counter = 100; ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_BLANK_CONTROL, ++ CRTC_BLANK_DATA_EN); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_BLANK_CONTROL, ++ CRTC_BLANK_DE_MODE); ++ ++ dal_write_reg(tg->ctx, addr, value); ++ ++ while (counter > 0) { ++ value = dal_read_reg(tg->ctx, addr); ++ ++ if (get_reg_field_value( ++ value, ++ CRTC_BLANK_CONTROL, ++ CRTC_BLANK_DATA_EN) == 1 && ++ get_reg_field_value( ++ value, ++ CRTC_BLANK_CONTROL, ++ CRTC_CURRENT_BLANK_STATE) == 1) ++ break; ++ ++ dc_service_sleep_in_milliseconds(tg->ctx, 1); ++ counter--; ++ } ++ ++ if (!counter) { ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "timing generator %d blank timing out.\n", ++ tg->controller_id); ++ return false; ++ } ++ ++ return true; ++} ++ ++/** ++ * unblank_crtc ++ * Call ASIC Control Object to UnBlank CRTC. ++ */ ++bool dce110_timing_generator_unblank_crtc(struct timing_generator *tg) ++{ ++ uint32_t addr = tg->regs[IDX_CRTC_BLANK_CONTROL]; ++ uint32_t value = dal_read_reg(tg->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ CRTC_BLANK_CONTROL, ++ CRTC_BLANK_DATA_EN); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ CRTC_BLANK_CONTROL, ++ CRTC_BLANK_DE_MODE); ++ ++ dal_write_reg(tg->ctx, addr, value); ++ ++ return true; ++} ++ ++/** ++ ***************************************************************************** ++ * Function: is_in_vertical_blank ++ * ++ * @brief ++ * check the current status of CRTC to check if we are in Vertical Blank ++ * regioneased" state ++ * ++ * @return ++ * true if currently in blank region, false otherwise ++ * ++ ***************************************************************************** ++ */ ++bool dce110_timing_generator_is_in_vertical_blank(struct timing_generator *tg) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ uint32_t field = 0; ++ ++ addr = tg->regs[IDX_CRTC_STATUS]; ++ value = dal_read_reg(tg->ctx, addr); ++ field = get_reg_field_value(value, CRTC_STATUS, CRTC_V_BLANK); ++ return field == 1; ++} ++ ++/** ++ ***************************************************************************** ++ * Function: disable_stereo ++ * ++ * @brief ++ * Disables active stereo on controller ++ * Frame Packing need to be disabled in vBlank or when CRTC not running ++ ***************************************************************************** ++ */ ++#if 0 ++@TODOSTEREO ++static void disable_stereo(struct timing_generator *tg) ++{ ++ uint32_t addr = tg->regs[IDX_CRTC_3D_STRUCTURE_CONTROL]; ++ uint32_t value = 0; ++ uint32_t test = 0; ++ uint32_t field = 0; ++ uint32_t struc_en = 0; ++ uint32_t struc_stereo_sel_ovr = 0; ++ ++ value = dal_read_reg(tg->ctx, addr); ++ struc_en = get_reg_field_value( ++ value, ++ CRTC_3D_STRUCTURE_CONTROL, ++ CRTC_3D_STRUCTURE_EN); ++ ++ struc_stereo_sel_ovr = get_reg_field_value( ++ value, ++ CRTC_3D_STRUCTURE_CONTROL, ++ CRTC_3D_STRUCTURE_STEREO_SEL_OVR); ++ ++ /* ++ * When disabling Frame Packing in 2 step mode, we need to program both ++ * registers at the same frame ++ * Programming it in the beginning of VActive makes sure we are ok ++ */ ++ ++ if (struc_en != 0 && struc_stereo_sel_ovr == 0) { ++ tg->funcs->wait_for_vblank(tg); ++ tg->funcs->wait_for_vactive(tg); ++ } ++ ++ value = 0; ++ dal_write_reg(tg->ctx, addr, value); ++ ++ ++ addr = tg->regs[IDX_CRTC_STEREO_CONTROL]; ++ dal_write_reg(tg->ctx, addr, value); ++} ++#endif ++ ++/** ++ * disable_crtc - call ASIC Control Object to disable Timing generator. ++ */ ++bool dce110_timing_generator_disable_crtc(struct timing_generator *tg) ++{ ++ enum bp_result result; ++ ++ result = dal_bios_parser_enable_crtc(tg->bp, tg->controller_id, false); ++ ++ /* Need to make sure stereo is disabled according to the DCE5.0 spec */ ++ ++ /* ++ * @TODOSTEREO call this when adding stereo support ++ * tg->funcs->disable_stereo(tg); ++ */ ++ ++ return result == BP_RESULT_OK; ++} ++ ++/** ++* program_horz_count_by_2 ++* Programs DxCRTC_HORZ_COUNT_BY2_EN - 1 for DVI 30bpp mode, 0 otherwise ++* ++*/ ++static void program_horz_count_by_2( ++ struct timing_generator *tg, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t regval; ++ ++ regval = dal_read_reg(tg->ctx, ++ tg->regs[IDX_CRTC_COUNT_CONTROL]); ++ ++ set_reg_field_value(regval, 0, CRTC_COUNT_CONTROL, ++ CRTC_HORZ_COUNT_BY2_EN); ++ ++ if (timing->flags.HORZ_COUNT_BY_TWO) ++ set_reg_field_value(regval, 1, CRTC_COUNT_CONTROL, ++ CRTC_HORZ_COUNT_BY2_EN); ++ ++ dal_write_reg(tg->ctx, ++ tg->regs[IDX_CRTC_COUNT_CONTROL], regval); ++} ++ ++/** ++ * program_timing_generator ++ * Program CRTC Timing Registers - DxCRTC_H_*, DxCRTC_V_*, Pixel repetition. ++ * Call ASIC Control Object to program Timings. ++ */ ++bool dce110_timing_generator_program_timing_generator( ++ struct timing_generator *tg, ++ struct dc_crtc_timing *dc_crtc_timing) ++{ ++ enum bp_result result; ++ struct bp_hw_crtc_timing_parameters bp_params; ++ uint32_t regval; ++ ++ uint32_t vsync_offset = dc_crtc_timing->v_border_bottom + ++ dc_crtc_timing->v_front_porch; ++ uint32_t v_sync_start =dc_crtc_timing->v_addressable + vsync_offset; ++ ++ uint32_t hsync_offset = dc_crtc_timing->h_border_right + ++ dc_crtc_timing->h_front_porch; ++ uint32_t h_sync_start = dc_crtc_timing->h_addressable + hsync_offset; ++ ++ dc_service_memset(&bp_params, 0, sizeof(struct bp_hw_crtc_timing_parameters)); ++ ++ /* Due to an asic bug we need to apply the Front Porch workaround prior ++ * to programming the timing. ++ */ ++ dce110_timing_generator_apply_front_porch_workaround(tg, dc_crtc_timing); ++ ++ bp_params.controller_id = tg->controller_id; ++ ++ bp_params.h_total = dc_crtc_timing->h_total; ++ bp_params.h_addressable = ++ dc_crtc_timing->h_addressable; ++ bp_params.v_total = dc_crtc_timing->v_total; ++ bp_params.v_addressable = dc_crtc_timing->v_addressable; ++ ++ bp_params.h_sync_start = h_sync_start; ++ bp_params.h_sync_width = dc_crtc_timing->h_sync_width; ++ bp_params.v_sync_start = v_sync_start; ++ bp_params.v_sync_width = dc_crtc_timing->v_sync_width; ++ ++ /* Set overscan */ ++ bp_params.h_overscan_left = ++ dc_crtc_timing->h_border_left; ++ bp_params.h_overscan_right = ++ dc_crtc_timing->h_border_right; ++ bp_params.v_overscan_top = dc_crtc_timing->v_border_top; ++ bp_params.v_overscan_bottom = ++ dc_crtc_timing->v_border_bottom; ++ ++ /* Set flags */ ++ if (dc_crtc_timing->flags.HSYNC_POSITIVE_POLARITY == 1) ++ bp_params.flags.HSYNC_POSITIVE_POLARITY = 1; ++ ++ if (dc_crtc_timing->flags.VSYNC_POSITIVE_POLARITY == 1) ++ bp_params.flags.VSYNC_POSITIVE_POLARITY = 1; ++ ++ if (dc_crtc_timing->flags.INTERLACE == 1) ++ bp_params.flags.INTERLACE = 1; ++ ++ if (dc_crtc_timing->flags.HORZ_COUNT_BY_TWO == 1) ++ bp_params.flags.HORZ_COUNT_BY_TWO = 1; ++ ++ result = dal_bios_parser_program_crtc_timing(tg->bp, &bp_params); ++ ++ program_horz_count_by_2(tg, dc_crtc_timing); ++ ++ ++ regval = dal_read_reg(tg->ctx, ++ tg->regs[IDX_CRTC_START_LINE_CONTROL]); ++ ++ if (dce110_timing_generator_get_vsynch_and_front_porch_size(dc_crtc_timing) <= 3) { ++ set_reg_field_value(regval, 3, ++ CRTC_START_LINE_CONTROL, ++ CRTC_ADVANCED_START_LINE_POSITION); ++ ++ set_reg_field_value(regval, 0, ++ CRTC_START_LINE_CONTROL, ++ CRTC_PREFETCH_EN); ++ } else { ++ set_reg_field_value(regval, 4, ++ CRTC_START_LINE_CONTROL, ++ CRTC_ADVANCED_START_LINE_POSITION); ++ ++ set_reg_field_value(regval, 1, ++ CRTC_START_LINE_CONTROL, ++ CRTC_PREFETCH_EN); ++ } ++ dal_write_reg(tg->ctx, ++ tg->regs[IDX_CRTC_START_LINE_CONTROL], regval); ++ ++ /* Enable stereo - only when we need to pack 3D frame. Other types ++ * of stereo handled in explicit call */ ++ ++ /* TODOSTEREO ++ if (hw_crtc_timing->flags.PACK_3D_FRAME) { ++ struct crtc_stereo_parameters stereo_params = { false }; ++ stereo_params.PROGRAM_STEREO = true; ++ stereo_params.PROGRAM_POLARITY = true; ++ stereo_params.FRAME_PACKED = true; ++ stereo_params.RIGHT_EYE_POLARITY = ++ hw_crtc_timing->flags.RIGHT_EYE_3D_POLARITY; ++ tg->funcs->enable_stereo(tg, &stereo_params); ++ }*/ ++ ++ return result == BP_RESULT_OK; ++} ++ ++/** ++ ***************************************************************************** ++ * Function: program_drr ++ * ++ * @brief ++ * Program dynamic refresh rate registers m_DxCRTC_V_TOTAL_*. ++ * ++ * @param [in] pHwCrtcTiming: point to H ++ * wCrtcTiming struct ++ ***************************************************************************** ++ */ ++void dce110_timing_generator_program_drr( ++ struct timing_generator *tg, ++ const struct hw_ranged_timing *timing) ++{ ++ /* register values */ ++ uint32_t v_total_min = 0; ++ uint32_t v_total_max = 0; ++ uint32_t v_total_cntl = 0; ++ uint32_t static_screen_cntl = 0; ++ ++ uint32_t addr = 0; ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL_MIN]; ++ v_total_min = dal_read_reg(tg->ctx, addr); ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL_MAX]; ++ v_total_max = dal_read_reg(tg->ctx, addr); ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL_CONTROL]; ++ v_total_cntl = dal_read_reg(tg->ctx, addr); ++ ++ addr = tg->regs[IDX_CRTC_STATIC_SCREEN_CONTROL]; ++ static_screen_cntl = dal_read_reg(tg->ctx, addr); ++ ++ if (timing != NULL) { ++ /* Set Static Screen trigger events ++ * If CRTC_SET_V_TOTAL_MIN_MASK_EN is set, use legacy event mask ++ * register ++ */ ++ if (get_reg_field_value( ++ v_total_cntl, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_SET_V_TOTAL_MIN_MASK_EN)) { ++ set_reg_field_value(v_total_cntl, ++ /* TODO: add implementation ++ translate_to_dce_static_screen_events( ++ timing->control.event_mask.u_all), ++ */ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_SET_V_TOTAL_MIN_MASK); ++ } else { ++ set_reg_field_value(static_screen_cntl, ++ /* TODO: add implementation ++ translate_to_dce_static_screen_events( ++ timing->control.event_mask.u_all), ++ */ 0, ++ CRTC_STATIC_SCREEN_CONTROL, ++ CRTC_STATIC_SCREEN_EVENT_MASK); ++ } ++ ++ /* Number of consecutive static screen frames before interrupt ++ * is triggered. 0 is an invalid setting, which means we should ++ * leaving HW setting unchanged. */ ++ if (timing->control.static_frame_count != 0) { ++ set_reg_field_value( ++ static_screen_cntl, ++ timing->control.static_frame_count, ++ CRTC_STATIC_SCREEN_CONTROL, ++ CRTC_STATIC_SCREEN_FRAME_COUNT); ++ } ++ ++ /* This value is reduced by 1 based on the register definition ++ * of the VTOTAL value: ++ * CRTC_V_TOTAL should be set to Vertical total minus one. (E.g. ++ * for 525 lines, set to 524 = 0x20C) ++ */ ++ set_reg_field_value(v_total_min, ++ timing->vertical_total_min, ++ CRTC_V_TOTAL_MIN, ++ CRTC_V_TOTAL_MIN); ++ set_reg_field_value(v_total_max, ++ timing->vertical_total_max, ++ CRTC_V_TOTAL_MAX, ++ CRTC_V_TOTAL_MAX); ++ ++ /* set VTotalControl value according to ranged timing control. ++ */ ++ ++ if (timing->vertical_total_min != 0) { ++ set_reg_field_value(v_total_cntl, ++ 1, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_V_TOTAL_MIN_SEL); ++ } else { ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_V_TOTAL_MIN_SEL); ++ } ++ if (timing->vertical_total_max != 0) { ++ set_reg_field_value(v_total_cntl, ++ 1, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_V_TOTAL_MAX_SEL); ++ } else { ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_V_TOTAL_MAX_SEL); ++ } ++ set_reg_field_value(v_total_cntl, ++ timing->control.force_lock_on_event, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_FORCE_LOCK_ON_EVENT); ++ set_reg_field_value(v_total_cntl, ++ timing->control.lock_to_master_vsync, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_FORCE_LOCK_TO_MASTER_VSYNC); ++ } else { ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_SET_V_TOTAL_MIN_MASK); ++ set_reg_field_value(static_screen_cntl, ++ 0, ++ CRTC_STATIC_SCREEN_CONTROL, ++ CRTC_STATIC_SCREEN_EVENT_MASK); ++ set_reg_field_value(v_total_min, ++ 0, ++ CRTC_V_TOTAL_MIN, ++ CRTC_V_TOTAL_MIN); ++ set_reg_field_value(v_total_max, ++ 0, ++ CRTC_V_TOTAL_MAX, ++ CRTC_V_TOTAL_MAX); ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_V_TOTAL_MIN_SEL); ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_V_TOTAL_MAX_SEL); ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_FORCE_LOCK_ON_EVENT); ++ set_reg_field_value(v_total_cntl, ++ 0, ++ CRTC_V_TOTAL_CONTROL, ++ CRTC_FORCE_LOCK_TO_MASTER_VSYNC); ++ } ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL_MIN]; ++ dal_write_reg(tg->ctx, addr, v_total_min); ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL_MAX]; ++ dal_write_reg(tg->ctx, addr, v_total_max); ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL_CONTROL]; ++ dal_write_reg(tg->ctx, addr, v_total_cntl); ++ ++ addr = tg->regs[IDX_CRTC_STATIC_SCREEN_CONTROL]; ++ dal_write_reg(tg->ctx, addr, static_screen_cntl); ++} ++ ++/* ++ * get_vblank_counter ++ * ++ * @brief ++ * Get counter for vertical blanks. use register CRTC_STATUS_FRAME_COUNT which ++ * holds the counter of frames. ++ * ++ * @param ++ * struct timing_generator *tg - [in] timing generator which controls the ++ * desired CRTC ++ * ++ * @return ++ * Counter of frames, which should equal to number of vblanks. ++ */ ++uint32_t dce110_timing_generator_get_vblank_counter(struct timing_generator *tg) ++{ ++ uint32_t addr = tg->regs[IDX_CRTC_STATUS_FRAME_COUNT]; ++ uint32_t value = dal_read_reg(tg->ctx, addr); ++ uint32_t field = get_reg_field_value( ++ value, CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); ++ ++ return field; ++} ++ ++/** ++ ***************************************************************************** ++ * Function: dce110_get_crtc_positions ++ * ++ * @brief ++ * Returns CRTC vertical/horizontal counters ++ * ++ * @param [out] v_position, h_position ++ ***************************************************************************** ++ */ ++ ++void dce110_timing_generator_get_crtc_positions( ++ struct timing_generator *tg, ++ int32_t *h_position, ++ int32_t *v_position) ++{ ++ uint32_t value; ++ ++ value = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_STATUS_POSITION]); ++ ++ *h_position = get_reg_field_value( ++ value, ++ CRTC_STATUS_POSITION, ++ CRTC_HORZ_COUNT); ++ ++ *v_position = get_reg_field_value( ++ value, ++ CRTC_STATUS_POSITION, ++ CRTC_VERT_COUNT); ++} ++ ++/** ++ ***************************************************************************** ++ * Function: get_crtc_scanoutpos ++ * ++ * @brief ++ * Returns CRTC vertical/horizontal counters ++ * ++ * @param [out] vpos, hpos ++ ***************************************************************************** ++ */ ++uint32_t dce110_timing_generator_get_crtc_scanoutpos( ++ struct timing_generator *tg, ++ int32_t *vbl, ++ int32_t *position) ++{ ++ /* TODO 1: Update the implementation once caller is updated ++ * WARNING!! This function is returning the whole register value ++ * because the caller is expecting it instead of proper vertical and ++ * horizontal position. This should be a temporary implementation ++ * until the caller is updated. */ ++ ++ /* TODO 2: re-use dce110_timing_generator_get_crtc_positions() */ ++ ++ *vbl = dal_read_reg(tg->ctx, ++ tg->regs[IDX_CRTC_V_BLANK_START_END]); ++ ++ *position = dal_read_reg(tg->ctx, ++ tg->regs[IDX_CRTC_STATUS_POSITION]); ++ ++ /* @TODO: return value should indicate if current ++ * crtc is inside vblank*/ ++ return 0; ++} ++ ++/* TODO: is it safe to assume that mask/shift of Primary and Underlay ++ * are the same? ++ * For example: today CRTC_H_TOTAL == CRTCV_H_TOTAL but is it always ++ * guaranteed? */ ++void dce110_timing_generator_program_blanking( ++ struct timing_generator *tg, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t vsync_offset = timing->v_border_bottom + ++ timing->v_front_porch; ++ uint32_t v_sync_start =timing->v_addressable + vsync_offset; ++ ++ uint32_t hsync_offset = timing->h_border_right + ++ timing->h_front_porch; ++ uint32_t h_sync_start = timing->h_addressable + hsync_offset; ++ ++ struct dc_context *ctx = tg->ctx; ++ uint32_t value = 0; ++ uint32_t addr = 0; ++ uint32_t tmp = 0; ++ ++ addr = tg->regs[IDX_CRTC_H_TOTAL]; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ timing->h_total - 1, ++ CRTC_H_TOTAL, ++ CRTC_H_TOTAL); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = tg->regs[IDX_CRTC_V_TOTAL]; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ timing->v_total - 1, ++ CRTC_V_TOTAL, ++ CRTC_V_TOTAL); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = tg->regs[IDX_CRTC_H_BLANK_START_END]; ++ value = dal_read_reg(ctx, addr); ++ ++ tmp = timing->h_total - ++ (h_sync_start + timing->h_border_left); ++ ++ set_reg_field_value( ++ value, ++ tmp, ++ CRTC_H_BLANK_START_END, ++ CRTC_H_BLANK_END); ++ ++ tmp = tmp + timing->h_addressable + ++ timing->h_border_left + timing->h_border_right; ++ ++ set_reg_field_value( ++ value, ++ tmp, ++ CRTC_H_BLANK_START_END, ++ CRTC_H_BLANK_START); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ addr = tg->regs[IDX_CRTC_V_BLANK_START_END]; ++ value = dal_read_reg(ctx, addr); ++ ++ tmp = timing->v_total - (v_sync_start + timing->v_border_top); ++ ++ set_reg_field_value( ++ value, ++ tmp, ++ CRTC_V_BLANK_START_END, ++ CRTC_V_BLANK_END); ++ ++ tmp = tmp + timing->v_addressable + timing->v_border_top + ++ timing->v_border_bottom; ++ ++ set_reg_field_value( ++ value, ++ tmp, ++ CRTC_V_BLANK_START_END, ++ CRTC_V_BLANK_START); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++void dce110_timing_generator_set_test_pattern( ++ struct timing_generator *tg, ++ /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' ++ * because this is not DP-specific (which is probably somewhere in DP ++ * encoder) */ ++ enum controller_dp_test_pattern test_pattern, ++ enum dc_color_depth color_depth) ++{ ++ struct dc_context *ctx = tg->ctx; ++ uint32_t value; ++ uint32_t addr; ++ ++ /* TODO: add support for other test patterns */ ++ switch (test_pattern) { ++ default: ++ value = 0; ++ addr = tg->regs[IDX_CRTC_TEST_PATTERN_PARAMETERS]; ++ ++ set_reg_field_value( ++ value, ++ 6, ++ CRTC_TEST_PATTERN_PARAMETERS, ++ CRTC_TEST_PATTERN_VRES); ++ set_reg_field_value( ++ value, ++ 6, ++ CRTC_TEST_PATTERN_PARAMETERS, ++ CRTC_TEST_PATTERN_HRES); ++ ++ dal_write_reg(ctx, addr, value); ++ ++ addr = tg->regs[IDX_CRTC_TEST_PATTERN_CONTROL]; ++ value = 0; ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_TEST_PATTERN_CONTROL, ++ CRTC_TEST_PATTERN_EN); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ CRTC_TEST_PATTERN_CONTROL, ++ CRTC_TEST_PATTERN_MODE); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_TEST_PATTERN_CONTROL, ++ CRTC_TEST_PATTERN_DYNAMIC_RANGE); ++ /* add color depth translation here */ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_TEST_PATTERN_CONTROL, ++ CRTC_TEST_PATTERN_COLOR_FORMAT); ++ dal_write_reg(ctx, addr, value); ++ break; ++ } /* switch() */ ++} ++ ++/** ++* dce110_timing_generator_validate_timing ++* The timing generators support a maximum display size of is 8192 x 8192 pixels, ++* including both active display and blanking periods. Check H Total and V Total. ++*/ ++bool dce110_timing_generator_validate_timing( ++ struct timing_generator *tg, ++ const struct dc_crtc_timing *timing, ++ enum signal_type signal) ++{ ++ uint32_t h_blank; ++ uint32_t h_back_porch; ++ uint32_t hsync_offset = timing->h_border_right + ++ timing->h_front_porch; ++ uint32_t h_sync_start = timing->h_addressable + hsync_offset; ++ ++ ASSERT(timing != NULL); ++ ++ if (!timing) ++ return false; ++ ++ /* Check maximum number of pixels supported by Timing Generator ++ * (Currently will never fail, in order to fail needs display which ++ * needs more than 8192 horizontal and ++ * more than 8192 vertical total pixels) ++ */ ++ if (timing->h_total > tg->max_h_total || ++ timing->v_total > tg->max_v_total) ++ return false; ++ ++ h_blank = (timing->h_total - timing->h_addressable - ++ timing->h_border_right - ++ timing->h_border_left); ++ ++ if (h_blank < tg->min_h_blank) ++ return false; ++ ++ if (timing->h_front_porch < tg->min_h_front_porch) ++ return false; ++ ++ h_back_porch = h_blank - (h_sync_start - ++ timing->h_addressable - ++ timing->h_border_right - ++ timing->h_sync_width); ++ ++ if (h_back_porch < tg->min_h_back_porch) ++ return false; ++ ++ return true; ++} ++ ++/** ++* Wait till we are at the beginning of VBlank. ++*/ ++void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg) ++{ ++ /* We want to catch beginning of VBlank here, so if the first try are ++ * in VBlank, we might be very close to Active, in this case wait for ++ * another frame ++ */ ++ while (dce110_timing_generator_is_in_vertical_blank(tg)) { ++ if (!dce110_timing_generator_is_counter_moving(tg)) { ++ /* error - no point to wait if counter is not moving */ ++ break; ++ } ++ } ++ ++ while (!dce110_timing_generator_is_in_vertical_blank(tg)) { ++ if (!dce110_timing_generator_is_counter_moving(tg)) { ++ /* error - no point to wait if counter is not moving */ ++ break; ++ } ++ } ++} ++ ++/** ++* Wait till we are in VActive (anywhere in VActive) ++*/ ++void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg) ++{ ++ while (dce110_timing_generator_is_in_vertical_blank(tg)) { ++ if (!dce110_timing_generator_is_counter_moving(tg)) { ++ /* error - no point to wait if counter is not moving */ ++ break; ++ } ++ } ++} ++ ++void dce110_timing_generator_destroy(struct timing_generator **tg) ++{ ++ dc_service_free((*tg)->ctx, FROM_TIMING_GENERATOR(*tg)); ++ *tg = NULL; ++} ++ ++static bool timing_generator_dce110_construct(struct timing_generator *tg, ++ struct dc_context *ctx, ++ struct adapter_service *as, ++ enum controller_id id) ++{ ++ if (!as) ++ return false; ++ ++ switch (id) { ++ case CONTROLLER_ID_D0: ++ case CONTROLLER_ID_D1: ++ case CONTROLLER_ID_D2: ++ break; ++ default: ++ return false; ++ } ++ ++ if (!dce110_timing_generator_construct(tg, id)) ++ return false; ++ ++ tg->ctx = ctx; ++ tg->bp = dal_adapter_service_get_bios_parser(as); ++ tg->regs = tg_regs[id-1]; ++ ++ tg->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; ++ tg->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; ++ ++ tg->min_h_blank = 56; ++ tg->min_h_front_porch = 4; ++ tg->min_h_back_porch = 4; ++ ++ return true; ++} ++ ++struct timing_generator *dce110_timing_generator_create( ++ struct adapter_service *as, ++ struct dc_context *ctx, ++ enum controller_id id) ++{ ++ struct dce110_timing_generator *tg = ++ dc_service_alloc(ctx, sizeof(struct dce110_timing_generator)); ++ ++ if (!tg) ++ return NULL; ++ ++ if (timing_generator_dce110_construct(&tg->base, ctx, ++ as, id)) ++ return &tg->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, tg); ++ return NULL; ++} ++ ++/** ++ ***************************************************************************** ++ * Function: dce110_timing_generator_setup_global_swap_lock ++ * ++ * @brief ++ * Setups Global Swap Lock group for current pipe ++ * Pipe can join or leave GSL group, become a TimingServer or TimingClient ++ * ++ * @param [in] gsl_params: setup data ++ ***************************************************************************** ++ */ ++ ++void dce110_timing_generator_setup_global_swap_lock( ++ struct timing_generator *tg, ++ const struct dcp_gsl_params *gsl_params) ++{ ++ uint32_t value; ++ uint32_t address = tg->regs[IDX_DCP_GSL_CONTROL]; ++ uint32_t check_point = FLIP_READY_BACK_LOOKUP; ++ ++ value = dal_read_reg(tg->ctx, address); ++ ++ /* This pipe will belong to GSL Group zero. */ ++ set_reg_field_value(value, ++ 1, ++ DCP_GSL_CONTROL, ++ DCP_GSL0_EN); ++ ++ set_reg_field_value(value, ++ gsl_params->timing_server, ++ DCP_GSL_CONTROL, ++ DCP_GSL_MASTER_EN); ++ ++ set_reg_field_value(value, ++ HFLIP_READY_DELAY, ++ DCP_GSL_CONTROL, ++ DCP_GSL_HSYNC_FLIP_FORCE_DELAY); ++ ++ /* Keep signal low (pending high) during 6 lines. ++ * Also defines minimum interval before re-checking signal. */ ++ set_reg_field_value(value, ++ HFLIP_CHECK_DELAY, ++ DCP_GSL_CONTROL, ++ DCP_GSL_HSYNC_FLIP_CHECK_DELAY); ++ ++ /* DCP_GSL_PURPOSE_SURFACE_FLIP */ ++ { ++ uint32_t value_crtc_vtotal; ++ ++ value_crtc_vtotal = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_V_TOTAL]); ++ ++ set_reg_field_value(value, ++ gsl_params->gsl_purpose, ++ DCP_GSL_CONTROL, ++ DCP_GSL_SYNC_SOURCE); ++ ++ /* Checkpoint relative to end of frame */ ++ check_point = get_reg_field_value(value_crtc_vtotal, ++ CRTC_V_TOTAL, ++ CRTC_V_TOTAL); ++ ++ dal_write_reg(tg->ctx, tg->regs[IDX_CRTC_GSL_WINDOW], 0); ++ } ++ ++ set_reg_field_value(value, ++ 1, ++ DCP_GSL_CONTROL, ++ DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); ++ ++ dal_write_reg(tg->ctx, address, value); ++ ++ /********************************************************************/ ++ address = tg->regs[IDX_CRTC_GSL_CONTROL]; ++ ++ value = 0; ++ set_reg_field_value(value, ++ check_point - FLIP_READY_BACK_LOOKUP, ++ CRTC_GSL_CONTROL, ++ CRTC_GSL_CHECK_LINE_NUM); ++ ++ set_reg_field_value(value, ++ VFLIP_READY_DELAY, ++ CRTC_GSL_CONTROL, ++ CRTC_GSL_FORCE_DELAY); ++ ++ dal_write_reg(tg->ctx, address, value); ++} ++ ++ ++void dce110_timing_generator_tear_down_global_swap_lock( ++ struct timing_generator *tg) ++{ ++ /* Clear all the register writes done by ++ * dce110_timing_generator_setup_global_swap_lock ++ */ ++ ++ uint32_t value; ++ uint32_t address = tg->regs[IDX_DCP_GSL_CONTROL]; ++ ++ value = 0; ++ ++ /* This pipe will belong to GSL Group zero. */ ++ /* Settig HW default values from reg specs */ ++ set_reg_field_value(value, ++ 0, ++ DCP_GSL_CONTROL, ++ DCP_GSL0_EN); ++ ++ set_reg_field_value(value, ++ 0, ++ DCP_GSL_CONTROL, ++ DCP_GSL_MASTER_EN); ++ ++ set_reg_field_value(value, ++ 0x2, ++ DCP_GSL_CONTROL, ++ DCP_GSL_HSYNC_FLIP_FORCE_DELAY); ++ ++ ++ set_reg_field_value(value, ++ 0x6, ++ DCP_GSL_CONTROL, ++ DCP_GSL_HSYNC_FLIP_CHECK_DELAY); ++ ++ /* Restore DCP_GSL_PURPOSE_SURFACE_FLIP */ ++ { ++ uint32_t value_crtc_vtotal; ++ ++ value_crtc_vtotal = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_V_TOTAL]); ++ ++ set_reg_field_value(value, ++ 0, ++ DCP_GSL_CONTROL, ++ DCP_GSL_SYNC_SOURCE); ++ } ++ ++ set_reg_field_value(value, ++ 0, ++ DCP_GSL_CONTROL, ++ DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); ++ ++ dal_write_reg(tg->ctx, address, value); ++ ++ /********************************************************************/ ++ address = tg->regs[IDX_CRTC_GSL_CONTROL]; ++ ++ value = 0; ++ set_reg_field_value(value, ++ 0, ++ CRTC_GSL_CONTROL, ++ CRTC_GSL_CHECK_LINE_NUM); ++ ++ set_reg_field_value(value, ++ 0x2, ++ CRTC_GSL_CONTROL, ++ CRTC_GSL_FORCE_DELAY); ++ ++ dal_write_reg(tg->ctx, address, value); ++} ++/** ++ ***************************************************************************** ++ * Function: is_counter_moving ++ * ++ * @brief ++ * check if the timing generator is currently going ++ * ++ * @return ++ * true if currently going, false if currently paused or stopped. ++ * ++ ***************************************************************************** ++ */ ++ ++bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg) ++{ ++ uint32_t addr = 0; ++ uint32_t value_1 = 0; ++ uint32_t field_1 = 0; ++ uint32_t value_2 = 0; ++ uint32_t field_2 = 0; ++ ++ addr = tg->regs[IDX_CRTC_STATUS_POSITION]; ++ value_1 = dal_read_reg(tg->ctx, addr); ++ value_2 = dal_read_reg(tg->ctx, addr); ++ ++ field_1 = get_reg_field_value( ++ value_1, CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); ++ field_2 = get_reg_field_value( ++ value_2, CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); ++ ++ if (field_1 == field_2) { ++ field_1 = get_reg_field_value( ++ value_1, CRTC_STATUS_POSITION, CRTC_VERT_COUNT); ++ field_2 = get_reg_field_value( ++ value_2, CRTC_STATUS_POSITION, CRTC_VERT_COUNT); ++ return field_1 != field_2; ++ } ++ ++ return true; ++} ++ ++/*TODO: Figure out if we need this function. */ ++void dce110_timing_generator_enable_advanced_request( ++ struct timing_generator *tg, ++ bool enable, ++ const struct dc_crtc_timing *timing) ++{ ++ uint32_t addr = tg->regs[IDX_CRTC_START_LINE_CONTROL]; ++ uint32_t value = dal_read_reg(tg->ctx, addr); ++ ++ if (enable && FROM_TIMING_GENERATOR(tg)->advanced_request_enable) { ++ set_reg_field_value( ++ value, ++ 0, ++ CRTC_START_LINE_CONTROL, ++ CRTC_LEGACY_REQUESTOR_EN); ++ } else { ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_START_LINE_CONTROL, ++ CRTC_LEGACY_REQUESTOR_EN); ++ } ++ ++ if (dce110_timing_generator_get_vsynch_and_front_porch_size(timing) <= 3) { ++ set_reg_field_value( ++ value, ++ 3, ++ CRTC_START_LINE_CONTROL, ++ CRTC_ADVANCED_START_LINE_POSITION); ++ set_reg_field_value( ++ value, ++ 0, ++ CRTC_START_LINE_CONTROL, ++ CRTC_PREFETCH_EN); ++ } else { ++ set_reg_field_value( ++ value, ++ 4, ++ CRTC_START_LINE_CONTROL, ++ CRTC_ADVANCED_START_LINE_POSITION); ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_START_LINE_CONTROL, ++ CRTC_PREFETCH_EN); ++ } ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_START_LINE_CONTROL, ++ CRTC_PROGRESSIVE_START_LINE_EARLY); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTC_START_LINE_CONTROL, ++ CRTC_INTERLACE_START_LINE_EARLY); ++ ++ dal_write_reg(tg->ctx, addr, value); ++} ++ ++/*TODO: Figure out if we need this function. */ ++void dce110_timing_generator_set_lock_master(struct timing_generator *tg, ++ bool lock) ++{ ++ struct dc_context *ctx = tg->ctx; ++ uint32_t addr = tg->regs[IDX_CRTC_MASTER_UPDATE_LOCK]; ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ lock ? 1 : 0, ++ CRTC_MASTER_UPDATE_LOCK, ++ MASTER_UPDATE_LOCK); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++void dce110_timing_generator_enable_reset_trigger( ++ struct timing_generator *tg, ++ const struct trigger_params *trigger_params) ++{ ++ uint32_t value; ++ struct dc_context *dc_ctx = tg->ctx; ++ uint32_t rising_edge = 0; ++ uint32_t falling_edge = 0; ++ enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; ++ ++ /* Setup trigger edge */ ++ switch (trigger_params->edge) { ++ /* Default = based on current timing polarity */ ++ case TRIGGER_EDGE_DEFAULT: ++ { ++ uint32_t pol_value = dal_read_reg(tg->ctx, ++ tg->regs[IDX_CRTC_V_SYNC_A_CNTL]); ++ ++ /* Register spec has reversed definition: ++ * 0 for positive, 1 for negative */ ++ if (get_reg_field_value(pol_value, ++ CRTC_V_SYNC_A_CNTL, ++ CRTC_V_SYNC_A_POL) == 0) { ++ rising_edge = 1; ++ } else { ++ falling_edge = 1; ++ } ++ } ++ break; ++ case TRIGGER_EDGE_RISING: ++ rising_edge = 1; ++ break; ++ case TRIGGER_EDGE_FALLING: ++ falling_edge = 1; ++ break; ++ case TRIGGER_EDGE_BOTH: ++ rising_edge = 1; ++ falling_edge = 1; ++ break; ++ default: ++ DC_ERROR("Invalid Trigger Edge!\n"); ++ return; ++ } ++ ++ value = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_TRIGB_CNTL]); ++ ++ switch(trigger_params->source) { ++ /* Currently supporting only a single group, the group zero. */ ++ case SYNC_SOURCE_GSL_GROUP0: ++ trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; ++ break; ++ default: ++ DC_ERROR("Unsupported GSL Group!\n"); ++ return; ++ } ++ ++ set_reg_field_value(value, ++ trig_src_select, ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_SOURCE_SELECT); ++ ++ set_reg_field_value(value, ++ TRIGGER_POLARITY_SELECT_LOGIC_ZERO, ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_POLARITY_SELECT); ++ ++ set_reg_field_value(value, ++ rising_edge, ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_RISING_EDGE_DETECT_CNTL); ++ ++ set_reg_field_value(value, ++ falling_edge, ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL); ++ ++ set_reg_field_value(value, ++ 0, /* send every signal */ ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_FREQUENCY_SELECT); ++ ++ set_reg_field_value(value, ++ 0, /* no delay */ ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_DELAY); ++ ++ set_reg_field_value(value, ++ 1, /* clear trigger status */ ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_CLEAR); ++ ++ dal_write_reg(tg->ctx, tg->regs[IDX_CRTC_TRIGB_CNTL], value); ++ ++ /**************************************************************/ ++ ++ value = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_FORCE_COUNT_CNTL]); ++ ++ set_reg_field_value(value, ++ 2, /* force H count to H_TOTAL and V count to V_TOTAL */ ++ CRTC_FORCE_COUNT_NOW_CNTL, ++ CRTC_FORCE_COUNT_NOW_MODE); ++ ++ set_reg_field_value(value, ++ 1, /* TriggerB - we never use TriggerA */ ++ CRTC_FORCE_COUNT_NOW_CNTL, ++ CRTC_FORCE_COUNT_NOW_TRIG_SEL); ++ ++ set_reg_field_value(value, ++ 1, /* clear trigger status */ ++ CRTC_FORCE_COUNT_NOW_CNTL, ++ CRTC_FORCE_COUNT_NOW_CLEAR); ++ ++ dal_write_reg(tg->ctx, tg->regs[IDX_CRTC_FORCE_COUNT_CNTL], value); ++} ++ ++void dce110_timing_generator_disable_reset_trigger( ++ struct timing_generator *tg) ++{ ++ uint32_t value; ++ ++ value = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_FORCE_COUNT_CNTL]); ++ ++ set_reg_field_value(value, ++ 0, /* force counter now mode is disabled */ ++ CRTC_FORCE_COUNT_NOW_CNTL, ++ CRTC_FORCE_COUNT_NOW_MODE); ++ ++ set_reg_field_value(value, ++ 1, /* clear trigger status */ ++ CRTC_FORCE_COUNT_NOW_CNTL, ++ CRTC_FORCE_COUNT_NOW_CLEAR); ++ ++ dal_write_reg(tg->ctx, tg->regs[IDX_CRTC_FORCE_COUNT_CNTL], value); ++ ++ /********************************************************************/ ++ value = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_TRIGB_CNTL]); ++ ++ set_reg_field_value(value, ++ TRIGGER_SOURCE_SELECT_LOGIC_ZERO, ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_SOURCE_SELECT); ++ ++ set_reg_field_value(value, ++ TRIGGER_POLARITY_SELECT_LOGIC_ZERO, ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_POLARITY_SELECT); ++ ++ set_reg_field_value(value, ++ 1, /* clear trigger status */ ++ CRTC_TRIGB_CNTL, ++ CRTC_TRIGB_CLEAR); ++ ++ dal_write_reg(tg->ctx, tg->regs[IDX_CRTC_TRIGB_CNTL], value); ++} ++ ++/** ++ ***************************************************************************** ++ * @brief ++ * Checks whether CRTC triggered reset occurred ++ * ++ * @return ++ * true if triggered reset occurred, false otherwise ++ ***************************************************************************** ++ */ ++bool dce110_timing_generator_did_triggered_reset_occur( ++ struct timing_generator *tg) ++{ ++ uint32_t value = dal_read_reg(tg->ctx, tg->regs[IDX_CRTC_FORCE_COUNT_CNTL]); ++ ++ return get_reg_field_value(value, ++ CRTC_FORCE_COUNT_NOW_CNTL, ++ CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; ++} ++ ++/** ++ * dce110_timing_generator_disable_vga ++ * Turn OFF VGA Mode and Timing - DxVGA_CONTROL ++ * VGA Mode and VGA Timing is used by VBIOS on CRT Monitors; ++ */ ++void dce110_timing_generator_disable_vga( ++ struct timing_generator *tg) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ switch (tg->controller_id) { ++ case CONTROLLER_ID_D0: ++ addr = mmD1VGA_CONTROL; ++ break; ++ case CONTROLLER_ID_D1: ++ addr = mmD2VGA_CONTROL; ++ break; ++ case CONTROLLER_ID_D2: ++ addr = mmD3VGA_CONTROL; ++ break; ++ default: ++ break; ++ } ++ value = dal_read_reg(tg->ctx, addr); ++ ++ set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); ++ set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); ++ set_reg_field_value( ++ value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); ++ set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); ++ ++ dal_write_reg(tg->ctx, addr, value); ++} ++ ++ ++/** ++* set_overscan_color_black ++* ++* @param :black_color is one of the color space ++* :this routine will set overscan black color according to the color space. ++* @return none ++*/ ++ ++void dce110_timing_generator_set_overscan_color_black( ++ struct timing_generator *tg, ++ enum color_space black_color) ++{ ++ struct dc_context *ctx = tg->ctx; ++ uint32_t value = 0; ++ uint32_t addr; ++ ++ /* Overscan Color for YUV display modes: ++ * to achieve a black color for both the explicit and implicit overscan, ++ * the overscan color registers should be programmed to: */ ++ ++ switch (black_color) { ++ case COLOR_SPACE_YPBPR601: ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ case COLOR_SPACE_YPBPR709: ++ case COLOR_SPACE_YCBCR601: ++ case COLOR_SPACE_YCBCR709: ++ case COLOR_SPACE_YCBCR601_YONLY: ++ case COLOR_SPACE_YCBCR709_YONLY: ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ case COLOR_SPACE_N_MVPU_SUPER_AA: ++ /* In crossfire SuperAA mode, the slave overscan data is forced ++ * to 0 in the pixel mixer on the master. As a result, we need ++ * to adjust the blank color so that after blending the ++ * master+slave, it will appear black */ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ case COLOR_SPACE_SRGB_LIMITED_RANGE: ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE, ++ CRTC_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ default: ++ /* default is sRGB black 0. */ ++ break; ++ } ++ addr = tg->regs[IDX_CRTC_OVERSCAN_COLOR]; ++ dal_write_reg(ctx, addr, value); ++ addr = tg->regs[IDX_CRTC_BLACK_COLOR]; ++ dal_write_reg(ctx, addr, value); ++ /* This is desirable to have a constant DAC output voltage during the ++ * blank time that is higher than the 0 volt reference level that the ++ * DAC outputs when the NBLANK signal ++ * is asserted low, such as for output to an analog TV. */ ++ addr = tg->regs[IDX_CRTC_BLANK_DATA_COLOR]; ++ dal_write_reg(ctx, addr, value); ++ ++ /* TO DO we have to program EXT registers and we need to know LB DATA ++ * format because it is used when more 10 , i.e. 12 bits per color ++ * ++ * m_mmDxCRTC_OVERSCAN_COLOR_EXT ++ * m_mmDxCRTC_BLACK_COLOR_EXT ++ * m_mmDxCRTC_BLANK_DATA_COLOR_EXT ++ */ ++ ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h +new file mode 100644 +index 0000000..d95a2a0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h +@@ -0,0 +1,178 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_TIMING_GENERATOR_DCE110_H__ ++#define __DC_TIMING_GENERATOR_DCE110_H__ ++ ++ ++#include "../include/timing_generator_types.h" ++#include "../include/grph_object_id.h" ++ ++/* overscan in blank for YUV color space. For RGB, it is zero for black. */ ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV 0x1f4 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4CV 0x40 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV 0x1f4 ++ ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV 0x200 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV 0x40 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV 0x200 ++ ++/* overscan in blank for YUV color space when in SuperAA crossfire mode */ ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA 0x1a2 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA 0x20 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA 0x1a2 ++ ++/* OVERSCAN COLOR FOR RGB LIMITED RANGE ++ * (16~253) 16*4 (Multiple over 256 code leve) =64 (0x40) */ ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE 0x40 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE 0x40 ++#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE 0X40 ++ ++struct dce110_timing_generator { ++ struct timing_generator base; ++ enum sync_source cached_gsl_group; ++ bool advanced_request_enable; ++}; ++ ++struct timing_generator *dce110_timing_generator_create( ++ struct adapter_service *as, ++ struct dc_context *ctx, ++ enum controller_id id); ++ ++void dce110_timing_generator_destroy(struct timing_generator **tg); ++ ++bool dce110_timing_generator_construct( ++ struct timing_generator *tg, ++ enum controller_id id); ++ ++void dce110_timing_generator_program_blank_color( ++ struct timing_generator *tg, ++ enum color_space color_space); ++ ++bool dce110_timing_generator_blank_crtc(struct timing_generator *tg); ++ ++bool dce110_timing_generator_enable_crtc(struct timing_generator *tg); ++ ++bool dce110_timing_generator_disable_crtc(struct timing_generator *tg); ++ ++bool dce110_timing_generator_is_in_vertical_blank(struct timing_generator *tg); ++ ++void dce110_timing_generator_program_blanking( ++ struct timing_generator *tg, ++ const struct dc_crtc_timing *timing); ++ ++bool dce110_timing_generator_program_timing_generator( ++ struct timing_generator *tg, ++ struct dc_crtc_timing *dc_crtc_timing); ++ ++void dce110_timing_generator_set_early_control( ++ struct timing_generator *tg, ++ uint32_t early_cntl); ++ ++bool dce110_timing_generator_unblank_crtc(struct timing_generator *tg); ++ ++bool dce110_timing_generator_validate_timing( ++ struct timing_generator *tg, ++ const struct dc_crtc_timing *timing, ++ enum signal_type signal); ++ ++void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg); ++ ++void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg); ++ ++void dce110_timing_generator_set_test_pattern( ++ struct timing_generator *tg, ++ /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' ++ * because this is not DP-specific (which is probably somewhere in DP ++ * encoder) */ ++ enum controller_dp_test_pattern test_pattern, ++ enum dc_color_depth color_depth); ++ ++void dce110_timing_generator_program_drr( ++ struct timing_generator *tg, ++ const struct hw_ranged_timing *timing); ++ ++uint32_t dce110_timing_generator_get_crtc_scanoutpos( ++ struct timing_generator *tg, ++ int32_t *vbl, ++ int32_t *position); ++ ++uint32_t dce110_timing_generator_get_vblank_counter(struct timing_generator *tg); ++ ++void dce110_timing_generator_color_space_to_black_color( ++ enum color_space colorspace, ++ struct crtc_black_color *black_color); ++void dce110_timing_generator_apply_front_porch_workaround( ++ struct timing_generator *tg, ++ struct dc_crtc_timing *timing); ++int32_t dce110_timing_generator_get_vsynch_and_front_porch_size( ++ const struct dc_crtc_timing *timing); ++ ++void dce110_timing_generator_get_crtc_positions( ++ struct timing_generator *tg, ++ int32_t *h_position, ++ int32_t *v_position); ++ ++ ++/* TODO: Figure out if we need these functions*/ ++bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg); ++ ++void dce110_timing_generator_enable_advanced_request( ++ struct timing_generator *tg, ++ bool enable, ++ const struct dc_crtc_timing *timing); ++ ++void dce110_timing_generator_set_lock_master(struct timing_generator *tg, ++ bool lock); ++ ++void dce110_timing_generator_set_overscan_color_black( ++ struct timing_generator *tg, ++ enum color_space black_color); ++ ++ ++/**** Sync-related interfaces ****/ ++void dce110_timing_generator_setup_global_swap_lock( ++ struct timing_generator *tg, ++ const struct dcp_gsl_params *gsl_params); ++void dce110_timing_generator_tear_down_global_swap_lock( ++ struct timing_generator *tg); ++ ++ ++void dce110_timing_generator_enable_reset_trigger( ++ struct timing_generator *tg, ++ const struct trigger_params *trigger_params); ++ ++void dce110_timing_generator_disable_reset_trigger( ++ struct timing_generator *tg); ++ ++bool dce110_timing_generator_did_triggered_reset_occur( ++ struct timing_generator *tg); ++ ++void dce110_timing_generator_disable_vga( ++ struct timing_generator *tg); ++ ++/**** End-of-Sync-related interfaces ****/ ++ ++#endif /* __DC_TIMING_GENERATOR_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c +new file mode 100644 +index 0000000..f3b3630 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c +@@ -0,0 +1,116 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dc_types.h" ++#include "core_types.h" ++ ++#include "include/grph_object_id.h" ++#include "include/fixed31_32.h" ++#include "include/logger_interface.h" ++ ++#include "dce110_transform.h" ++#include "dce110_transform_bit_depth.h" ++ ++static const struct dce110_transform_reg_offsets reg_offsets[] = { ++{ ++ .scl_offset = (mmSCL0_SCL_CONTROL - mmSCL0_SCL_CONTROL), ++ .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .lb_offset = (mmLB0_LB_DATA_FORMAT - mmLB0_LB_DATA_FORMAT), ++}, ++{ .scl_offset = (mmSCL1_SCL_CONTROL - mmSCL0_SCL_CONTROL), ++ .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .lb_offset = (mmLB1_LB_DATA_FORMAT - mmLB0_LB_DATA_FORMAT), ++}, ++{ .scl_offset = (mmSCL2_SCL_CONTROL - mmSCL0_SCL_CONTROL), ++ .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++ .lb_offset = (mmLB2_LB_DATA_FORMAT - mmLB0_LB_DATA_FORMAT), ++} ++}; ++ ++/*****************************************/ ++/* Constructor, Destructor */ ++/*****************************************/ ++ ++bool dce110_transform_construct( ++ struct dce110_transform *xfm110, ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ if ((inst < 1) || (inst > ARRAY_SIZE(reg_offsets))) ++ return false; ++ ++ xfm110->base.ctx = ctx; ++ ++ xfm110->base.inst = inst; ++ ++ xfm110->offsets = reg_offsets[inst - 1]; ++ ++ xfm110->lb_pixel_depth_supported = ++ LB_PIXEL_DEPTH_18BPP | ++ LB_PIXEL_DEPTH_24BPP | ++ LB_PIXEL_DEPTH_30BPP; ++ ++ return true; ++} ++ ++void dce110_transform_destroy(struct transform **xfm) ++{ ++ dc_service_free((*xfm)->ctx, TO_DCE110_TRANSFORM(*xfm)); ++ *xfm = NULL; ++} ++ ++struct transform *dce110_transform_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct dce110_transform *transform = ++ dc_service_alloc(ctx, sizeof(struct dce110_transform)); ++ ++ if (!transform) ++ return NULL; ++ ++ if (dce110_transform_construct(transform, ++ ctx, inst)) ++ return &transform->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dc_service_free(ctx, transform); ++ return NULL; ++} ++ ++bool dce110_transform_power_up(struct transform *xfm) ++{ ++ return dce110_transform_power_up_line_buffer(xfm); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h +new file mode 100644 +index 0000000..edf016c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h +@@ -0,0 +1,91 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_TRANSFORM_DCE110_H__ ++#define __DAL_TRANSFORM_DCE110_H__ ++ ++#include "inc/transform.h" ++#include "include/grph_csc_types.h" ++ ++#define TO_DCE110_TRANSFORM(transform)\ ++ container_of(transform, struct dce110_transform, base) ++ ++struct dce110_transform_reg_offsets { ++ uint32_t scl_offset; ++ uint32_t dcfe_offset; ++ uint32_t dcp_offset; ++ uint32_t lb_offset; ++}; ++ ++struct dce110_transform { ++ struct transform base; ++ struct dce110_transform_reg_offsets offsets; ++ ++ uint32_t lb_pixel_depth_supported; ++}; ++ ++bool dce110_transform_construct(struct dce110_transform *xfm110, ++ struct dc_context *ctx, ++ uint32_t inst); ++ ++void dce110_transform_destroy(struct transform **xfm); ++ ++struct transform *dce110_transform_create( ++ struct dc_context *ctx, ++ uint32_t inst); ++ ++bool dce110_transform_power_up(struct transform *xfm); ++ ++/* SCALER RELATED */ ++bool dce110_transform_set_scaler( ++ struct transform *xfm, ++ const struct scaler_data *data); ++ ++void dce110_transform_set_scaler_bypass(struct transform *xfm); ++ ++bool dce110_transform_update_viewport( ++ struct transform *xfm, ++ const struct rect *view_port, ++ bool is_fbc_attached); ++ ++void dce110_transform_set_scaler_filter( ++ struct transform *xfm, ++ struct scaler_filter *filter); ++ ++/* GAMUT RELATED */ ++void dce110_transform_set_gamut_remap( ++ struct transform *xfm, ++ const struct grph_csc_adjustment *adjust); ++ ++/* BIT DEPTH RELATED */ ++bool dce110_transform_set_pixel_storage_depth( ++ struct transform *xfm, ++ enum lb_pixel_depth depth); ++ ++bool dce110_transform_get_current_pixel_storage_depth( ++ struct transform *xfm, ++ enum lb_pixel_depth *depth); ++ ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c +new file mode 100644 +index 0000000..747f2c7 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_transform.h" ++ ++#include "include/logger_interface.h" ++#include "include/fixed32_32.h" ++ ++#define DCP_REG(reg)\ ++ (reg + xfm110->offsets.dcp_offset) ++ ++#define LB_REG(reg)\ ++ (reg + xfm110->offsets.lb_offset) ++ ++#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 ++#define LB_BITS_PER_ENTRY 144 ++ ++enum dcp_out_trunc_round_mode { ++ DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, ++ DCP_OUT_TRUNC_ROUND_MODE_ROUND ++}; ++ ++enum dcp_out_trunc_round_depth { ++ DCP_OUT_TRUNC_ROUND_DEPTH_14BIT, ++ DCP_OUT_TRUNC_ROUND_DEPTH_13BIT, ++ DCP_OUT_TRUNC_ROUND_DEPTH_12BIT, ++ DCP_OUT_TRUNC_ROUND_DEPTH_11BIT, ++ DCP_OUT_TRUNC_ROUND_DEPTH_10BIT, ++ DCP_OUT_TRUNC_ROUND_DEPTH_9BIT, ++ DCP_OUT_TRUNC_ROUND_DEPTH_8BIT ++}; ++ ++/* defines the various methods of bit reduction available for use */ ++enum dcp_bit_depth_reduction_mode { ++ DCP_BIT_DEPTH_REDUCTION_MODE_DITHER, ++ DCP_BIT_DEPTH_REDUCTION_MODE_ROUND, ++ DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE, ++ DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED, ++ DCP_BIT_DEPTH_REDUCTION_MODE_INVALID ++}; ++ ++enum dcp_spatial_dither_mode { ++ DCP_SPATIAL_DITHER_MODE_AAAA, ++ DCP_SPATIAL_DITHER_MODE_A_AA_A, ++ DCP_SPATIAL_DITHER_MODE_AABBAABB, ++ DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC, ++ DCP_SPATIAL_DITHER_MODE_INVALID ++}; ++ ++enum dcp_spatial_dither_depth { ++ DCP_SPATIAL_DITHER_DEPTH_30BPP, ++ DCP_SPATIAL_DITHER_DEPTH_24BPP ++}; ++ ++static bool set_clamp( ++ struct dce110_transform *xfm110, ++ enum dc_color_depth depth); ++ ++static bool set_round( ++ struct dce110_transform *xfm110, ++ enum dcp_out_trunc_round_mode mode, ++ enum dcp_out_trunc_round_depth depth); ++ ++static bool set_dither( ++ struct dce110_transform *xfm110, ++ bool dither_enable, ++ enum dcp_spatial_dither_mode dither_mode, ++ enum dcp_spatial_dither_depth dither_depth, ++ bool frame_random_enable, ++ bool rgb_random_enable, ++ bool highpass_random_enable); ++ ++/** ++ ******************************************************************************* ++ * dce110_transform_bit_depth_reduction_program ++ * ++ * @brief ++ * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, ++ * Dither) for dce110 ++ * ++ * @param depth : bit depth to set the clamp to (should match denorm) ++ * ++ * @return ++ * true if succeeds. ++ ******************************************************************************* ++ */ ++static bool program_bit_depth_reduction( ++ struct dce110_transform *xfm110, ++ enum dc_color_depth depth) ++{ ++ enum dcp_bit_depth_reduction_mode depth_reduction_mode; ++ enum dcp_spatial_dither_mode spatial_dither_mode; ++ bool frame_random_enable; ++ bool rgb_random_enable; ++ bool highpass_random_enable; ++ ++ if (depth > COLOR_DEPTH_121212) { ++ ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ ++ return false; ++ } ++ ++ depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DITHER; ++ ++ spatial_dither_mode = DCP_SPATIAL_DITHER_MODE_A_AA_A; ++ ++ frame_random_enable = true; ++ rgb_random_enable = true; ++ highpass_random_enable = true; ++ ++ if (!set_clamp(xfm110, depth)) { ++ /* Failure in set_clamp() */ ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ switch (depth_reduction_mode) { ++ case DCP_BIT_DEPTH_REDUCTION_MODE_DITHER: ++ /* Spatial Dither: Set round/truncate to bypass (12bit), ++ * enable Dither (30bpp) */ ++ set_round(xfm110, ++ DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, ++ DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); ++ ++ set_dither(xfm110, true, spatial_dither_mode, ++ DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, ++ rgb_random_enable, highpass_random_enable); ++ break; ++ case DCP_BIT_DEPTH_REDUCTION_MODE_ROUND: ++ /* Round: Enable round (10bit), disable Dither */ ++ set_round(xfm110, ++ DCP_OUT_TRUNC_ROUND_MODE_ROUND, ++ DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); ++ ++ set_dither(xfm110, false, spatial_dither_mode, ++ DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, ++ rgb_random_enable, highpass_random_enable); ++ break; ++ case DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE: /* Truncate */ ++ /* Truncate: Enable truncate (10bit), disable Dither */ ++ set_round(xfm110, ++ DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, ++ DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); ++ ++ set_dither(xfm110, false, spatial_dither_mode, ++ DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, ++ rgb_random_enable, highpass_random_enable); ++ break; ++ ++ case DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED: /* Disabled */ ++ /* Truncate: Set round/truncate to bypass (12bit), ++ * disable Dither */ ++ set_round(xfm110, ++ DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, ++ DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); ++ ++ set_dither(xfm110, false, spatial_dither_mode, ++ DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, ++ rgb_random_enable, highpass_random_enable); ++ break; ++ default: ++ /* Invalid DCP Depth reduction mode */ ++ ASSERT_CRITICAL(false); ++ break; ++ } ++ ++ return true; ++} ++ ++/** ++ ******************************************************************************* ++ * set_clamp ++ * ++ * @param depth : bit depth to set the clamp to (should match denorm) ++ * ++ * @brief ++ * Programs clamp according to panel bit depth. ++ * ++ * @return ++ * true if succeeds ++ * ++ ******************************************************************************* ++ */ ++static bool set_clamp( ++ struct dce110_transform *xfm110, ++ enum dc_color_depth depth) ++{ ++ uint32_t clamp_max = 0; ++ ++ /* At the clamp block the data will be MSB aligned, so we set the max ++ * clamp accordingly. ++ * For example, the max value for 6 bits MSB aligned (14 bit bus) would ++ * be "11 1111 0000 0000" in binary, so 0x3F00. ++ */ ++ switch (depth) { ++ case COLOR_DEPTH_666: ++ /* 6bit MSB aligned on 14 bit bus '11 1111 0000 0000' */ ++ clamp_max = 0x3F00; ++ break; ++ case COLOR_DEPTH_888: ++ /* 8bit MSB aligned on 14 bit bus '11 1111 1100 0000' */ ++ clamp_max = 0x3FC0; ++ break; ++ case COLOR_DEPTH_101010: ++ /* 10bit MSB aligned on 14 bit bus '11 1111 1111 1100' */ ++ clamp_max = 0x3FFC; ++ break; ++ case COLOR_DEPTH_121212: ++ /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1111' */ ++ clamp_max = 0x3FFF; ++ break; ++ default: ++ ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ ++ return false; ++ } ++ ++ { ++ uint32_t value = 0; ++ /* always set min to 0 */ ++ set_reg_field_value( ++ value, ++ 0, ++ OUT_CLAMP_CONTROL_B_CB, ++ OUT_CLAMP_MIN_B_CB); ++ ++ set_reg_field_value( ++ value, ++ clamp_max, ++ OUT_CLAMP_CONTROL_B_CB, ++ OUT_CLAMP_MAX_B_CB); ++ ++ dal_write_reg(xfm110->base.ctx, ++ DCP_REG(mmOUT_CLAMP_CONTROL_B_CB), ++ value); ++ } ++ ++ { ++ uint32_t value = 0; ++ /* always set min to 0 */ ++ set_reg_field_value( ++ value, ++ 0, ++ OUT_CLAMP_CONTROL_G_Y, ++ OUT_CLAMP_MIN_G_Y); ++ ++ set_reg_field_value( ++ value, ++ clamp_max, ++ OUT_CLAMP_CONTROL_G_Y, ++ OUT_CLAMP_MAX_G_Y); ++ ++ dal_write_reg(xfm110->base.ctx, ++ DCP_REG(mmOUT_CLAMP_CONTROL_G_Y), ++ value); ++ } ++ ++ { ++ uint32_t value = 0; ++ /* always set min to 0 */ ++ set_reg_field_value( ++ value, ++ 0, ++ OUT_CLAMP_CONTROL_R_CR, ++ OUT_CLAMP_MIN_R_CR); ++ ++ set_reg_field_value( ++ value, ++ clamp_max, ++ OUT_CLAMP_CONTROL_R_CR, ++ OUT_CLAMP_MAX_R_CR); ++ ++ dal_write_reg(xfm110->base.ctx, ++ DCP_REG(mmOUT_CLAMP_CONTROL_R_CR), ++ value); ++ } ++ ++ return true; ++} ++ ++/** ++ ******************************************************************************* ++ * set_round ++ * ++ * @brief ++ * Programs Round/Truncate ++ * ++ * @param [in] mode :round or truncate ++ * @param [in] depth :bit depth to round/truncate to ++ OUT_ROUND_TRUNC_MODE 3:0 0xA Output data round or truncate mode ++ POSSIBLE VALUES: ++ 00 - truncate to u0.12 ++ 01 - truncate to u0.11 ++ 02 - truncate to u0.10 ++ 03 - truncate to u0.9 ++ 04 - truncate to u0.8 ++ 05 - reserved ++ 06 - truncate to u0.14 ++ 07 - truncate to u0.13 set_reg_field_value( ++ value, ++ clamp_max, ++ OUT_CLAMP_CONTROL_R_CR, ++ OUT_CLAMP_MAX_R_CR); ++ 08 - round to u0.12 ++ 09 - round to u0.11 ++ 10 - round to u0.10 ++ 11 - round to u0.9 ++ 12 - round to u0.8 ++ 13 - reserved ++ 14 - round to u0.14 ++ 15 - round to u0.13 ++ ++ * @return ++ * true if succeeds. ++ ******************************************************************************* ++ */ ++static bool set_round( ++ struct dce110_transform *xfm110, ++ enum dcp_out_trunc_round_mode mode, ++ enum dcp_out_trunc_round_depth depth) ++{ ++ uint32_t depth_bits = 0; ++ uint32_t mode_bit = 0; ++ /* zero out all bits */ ++ uint32_t value = 0; ++ ++ /* set up bit depth */ ++ switch (depth) { ++ case DCP_OUT_TRUNC_ROUND_DEPTH_14BIT: ++ depth_bits = 6; ++ break; ++ case DCP_OUT_TRUNC_ROUND_DEPTH_13BIT: ++ depth_bits = 7; ++ break; ++ case DCP_OUT_TRUNC_ROUND_DEPTH_12BIT: ++ depth_bits = 0; ++ break; ++ case DCP_OUT_TRUNC_ROUND_DEPTH_11BIT: ++ depth_bits = 1; ++ break; ++ case DCP_OUT_TRUNC_ROUND_DEPTH_10BIT: ++ depth_bits = 2; ++ break; ++ case DCP_OUT_TRUNC_ROUND_DEPTH_9BIT: ++ depth_bits = 3; ++ break; ++ case DCP_OUT_TRUNC_ROUND_DEPTH_8BIT: ++ depth_bits = 4; ++ break; ++ default: ++ /* Invalid dcp_out_trunc_round_depth */ ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ set_reg_field_value( ++ value, ++ depth_bits, ++ OUT_ROUND_CONTROL, ++ OUT_ROUND_TRUNC_MODE); ++ ++ /* set up round or truncate */ ++ switch (mode) { ++ case DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE: ++ mode_bit = 0; ++ break; ++ case DCP_OUT_TRUNC_ROUND_MODE_ROUND: ++ mode_bit = 1; ++ break; ++ default: ++ /* Invalid dcp_out_trunc_round_mode */ ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ depth_bits |= mode_bit << 3; ++ ++ set_reg_field_value( ++ value, ++ depth_bits, ++ OUT_ROUND_CONTROL, ++ OUT_ROUND_TRUNC_MODE); ++ ++ /* write the register */ ++ dal_write_reg(xfm110->base.ctx, ++ DCP_REG(mmOUT_ROUND_CONTROL), ++ value); ++ ++ return true; ++} ++ ++/** ++ ******************************************************************************* ++ * set_dither ++ * ++ * @brief ++ * Programs Dither ++ * ++ * @param [in] dither_enable : enable dither ++ * @param [in] dither_mode : dither mode to set ++ * @param [in] dither_depth : bit depth to dither to ++ * @param [in] frame_random_enable : enable frame random ++ * @param [in] rgb_random_enable : enable rgb random ++ * @param [in] highpass_random_enable : enable highpass random ++ * ++ * @return ++ * true if succeeds. ++ ******************************************************************************* ++ */ ++ ++static bool set_dither( ++ struct dce110_transform *xfm110, ++ bool dither_enable, ++ enum dcp_spatial_dither_mode dither_mode, ++ enum dcp_spatial_dither_depth dither_depth, ++ bool frame_random_enable, ++ bool rgb_random_enable, ++ bool highpass_random_enable) ++{ ++ uint32_t dither_depth_bits = 0; ++ uint32_t dither_mode_bits = 0; ++ /* zero out all bits */ ++ uint32_t value = 0; ++ ++ /* set up the fields */ ++ if (dither_enable) ++ set_reg_field_value( ++ value, ++ 1, ++ DCP_SPATIAL_DITHER_CNTL, ++ DCP_SPATIAL_DITHER_EN); ++ ++ switch (dither_mode) { ++ case DCP_SPATIAL_DITHER_MODE_AAAA: ++ dither_mode_bits = 0; ++ break; ++ case DCP_SPATIAL_DITHER_MODE_A_AA_A: ++ dither_mode_bits = 1; ++ break; ++ case DCP_SPATIAL_DITHER_MODE_AABBAABB: ++ dither_mode_bits = 2; ++ break; ++ case DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC: ++ dither_mode_bits = 3; ++ break; ++ default: ++ /* Invalid dcp_spatial_dither_mode */ ++ ASSERT_CRITICAL(false); ++ return false; ++ ++ } ++ set_reg_field_value( ++ value, ++ dither_mode_bits, ++ DCP_SPATIAL_DITHER_CNTL, ++ DCP_SPATIAL_DITHER_MODE); ++ ++ switch (dither_depth) { ++ case DCP_SPATIAL_DITHER_DEPTH_30BPP: ++ dither_depth_bits = 0; ++ break; ++ case DCP_SPATIAL_DITHER_DEPTH_24BPP: ++ dither_depth_bits = 1; ++ break; ++ default: ++ /* Invalid dcp_spatial_dither_depth */ ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ set_reg_field_value( ++ value, ++ dither_depth_bits, ++ DCP_SPATIAL_DITHER_CNTL, ++ DCP_SPATIAL_DITHER_DEPTH); ++ ++ if (frame_random_enable) ++ set_reg_field_value( ++ value, ++ 1, ++ DCP_SPATIAL_DITHER_CNTL, ++ DCP_FRAME_RANDOM_ENABLE); ++ ++ if (rgb_random_enable) ++ set_reg_field_value( ++ value, ++ 1, ++ DCP_SPATIAL_DITHER_CNTL, ++ DCP_RGB_RANDOM_ENABLE); ++ ++ if (highpass_random_enable) ++ set_reg_field_value( ++ value, ++ 1, ++ DCP_SPATIAL_DITHER_CNTL, ++ DCP_HIGHPASS_RANDOM_ENABLE); ++ ++ /* write the register */ ++ dal_write_reg(xfm110->base.ctx, ++ DCP_REG(mmDCP_SPATIAL_DITHER_CNTL), ++ value); ++ ++ return true; ++} ++ ++bool dce110_transform_get_max_num_of_supported_lines( ++ struct dce110_transform *xfm110, ++ enum lb_pixel_depth depth, ++ uint32_t pixel_width, ++ uint32_t *lines) ++{ ++ uint32_t pixels_per_entries = 0; ++ uint32_t max_pixels_supports = 0; ++ ++ if (pixel_width == 0) ++ return false; ++ ++ /* Find number of pixels that can fit into a single LB entry and ++ * take floor of the value since we cannot store a single pixel ++ * across multiple entries. */ ++ switch (depth) { ++ case LB_PIXEL_DEPTH_18BPP: ++ pixels_per_entries = LB_BITS_PER_ENTRY / 18; ++ break; ++ ++ case LB_PIXEL_DEPTH_24BPP: ++ pixels_per_entries = LB_BITS_PER_ENTRY / 24; ++ break; ++ ++ case LB_PIXEL_DEPTH_30BPP: ++ pixels_per_entries = LB_BITS_PER_ENTRY / 30; ++ break; ++ ++ case LB_PIXEL_DEPTH_36BPP: ++ pixels_per_entries = LB_BITS_PER_ENTRY / 36; ++ break; ++ ++ default: ++ dal_logger_write(xfm110->base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Invalid LB pixel depth", ++ __func__); ++ break; ++ } ++ ++ if (pixels_per_entries == 0) ++ return false; ++ ++ max_pixels_supports = pixels_per_entries * LB_TOTAL_NUMBER_OF_ENTRIES; ++ ++ *lines = max_pixels_supports / pixel_width; ++ return true; ++} ++ ++void dce110_transform_enable_alpha( ++ struct dce110_transform *xfm110, ++ bool enable) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t value; ++ uint32_t addr = LB_REG(mmLB_DATA_FORMAT); ++ ++ value = dal_read_reg(ctx, addr); ++ ++ if (enable == 1) ++ set_reg_field_value( ++ value, ++ 1, ++ LB_DATA_FORMAT, ++ ALPHA_EN); ++ else ++ set_reg_field_value( ++ value, ++ 0, ++ LB_DATA_FORMAT, ++ ALPHA_EN); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static enum lb_pixel_depth translate_display_bpp_to_lb_depth( ++ uint32_t display_bpp) ++{ ++ switch (display_bpp) { ++ case 18: ++ return LB_PIXEL_DEPTH_18BPP; ++ case 24: ++ return LB_PIXEL_DEPTH_24BPP; ++ case 36: ++ case 42: ++ case 48: ++ return LB_PIXEL_DEPTH_36BPP; ++ case 30: ++ default: ++ return LB_PIXEL_DEPTH_30BPP; ++ } ++} ++ ++bool dce110_transform_get_next_lower_pixel_storage_depth( ++ struct dce110_transform *xfm110, ++ uint32_t display_bpp, ++ enum lb_pixel_depth depth, ++ enum lb_pixel_depth *lower_depth) ++{ ++ enum lb_pixel_depth depth_req_by_display = ++ translate_display_bpp_to_lb_depth(display_bpp); ++ uint32_t current_required_depth = depth_req_by_display; ++ uint32_t current_depth = depth; ++ ++ /* if required display depth < current we could go down, for example ++ * from LB_PIXEL_DEPTH_30BPP to LB_PIXEL_DEPTH_24BPP ++ */ ++ if (current_required_depth < current_depth) { ++ current_depth = current_depth >> 1; ++ if (xfm110->lb_pixel_depth_supported & current_depth) { ++ *lower_depth = current_depth; ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool dce110_transform_is_prefetch_enabled( ++ struct dce110_transform *xfm110) ++{ ++ uint32_t value = dal_read_reg( ++ xfm110->base.ctx, LB_REG(mmLB_DATA_FORMAT)); ++ ++ if (get_reg_field_value(value, LB_DATA_FORMAT, PREFETCH) == 1) ++ return true; ++ ++ return false; ++} ++ ++bool dce110_transform_get_current_pixel_storage_depth( ++ struct transform *xfm, ++ enum lb_pixel_depth *depth) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ uint32_t value = 0; ++ ++ if (depth == NULL) ++ return false; ++ ++ value = dal_read_reg( ++ xfm->ctx, ++ LB_REG(mmLB_DATA_FORMAT)); ++ ++ switch (get_reg_field_value(value, LB_DATA_FORMAT, PIXEL_DEPTH)) { ++ case 0: ++ *depth = LB_PIXEL_DEPTH_30BPP; ++ break; ++ case 1: ++ *depth = LB_PIXEL_DEPTH_24BPP; ++ break; ++ case 2: ++ *depth = LB_PIXEL_DEPTH_18BPP; ++ break; ++ case 3: ++ *depth = LB_PIXEL_DEPTH_36BPP; ++ break; ++ default: ++ dal_logger_write(xfm->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Invalid LB pixel depth", ++ __func__); ++ *depth = LB_PIXEL_DEPTH_30BPP; ++ break; ++ } ++ return true; ++ ++} ++ ++static void set_denormalization( ++ struct dce110_transform *xfm110, ++ enum dc_color_depth depth) ++{ ++ uint32_t value = dal_read_reg(xfm110->base.ctx, ++ DCP_REG(mmDENORM_CONTROL)); ++ ++ switch (depth) { ++ case COLOR_DEPTH_666: ++ /* 63/64 for 6 bit output color depth */ ++ set_reg_field_value( ++ value, ++ 1, ++ DENORM_CONTROL, ++ DENORM_MODE); ++ break; ++ case COLOR_DEPTH_888: ++ /* Unity for 8 bit output color depth ++ * because prescale is disabled by default */ ++ set_reg_field_value( ++ value, ++ 0, ++ DENORM_CONTROL, ++ DENORM_MODE); ++ break; ++ case COLOR_DEPTH_101010: ++ /* 1023/1024 for 10 bit output color depth */ ++ set_reg_field_value( ++ value, ++ 3, ++ DENORM_CONTROL, ++ DENORM_MODE); ++ break; ++ case COLOR_DEPTH_121212: ++ /* 4095/4096 for 12 bit output color depth */ ++ set_reg_field_value( ++ value, ++ 5, ++ DENORM_CONTROL, ++ DENORM_MODE); ++ break; ++ case COLOR_DEPTH_141414: ++ case COLOR_DEPTH_161616: ++ default: ++ /* not valid used case! */ ++ break; ++ } ++ ++ dal_write_reg(xfm110->base.ctx, ++ DCP_REG(mmDENORM_CONTROL), ++ value); ++ ++} ++ ++bool dce110_transform_set_pixel_storage_depth( ++ struct transform *xfm, ++ enum lb_pixel_depth depth) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ bool ret = true; ++ uint32_t value; ++ enum dc_color_depth color_depth; ++ ++ value = dal_read_reg( ++ xfm->ctx, ++ LB_REG(mmLB_DATA_FORMAT)); ++ switch (depth) { ++ case LB_PIXEL_DEPTH_18BPP: ++ color_depth = COLOR_DEPTH_666; ++ set_reg_field_value(value, 2, LB_DATA_FORMAT, PIXEL_DEPTH); ++ set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); ++ break; ++ case LB_PIXEL_DEPTH_24BPP: ++ color_depth = COLOR_DEPTH_888; ++ set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_DEPTH); ++ set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); ++ break; ++ case LB_PIXEL_DEPTH_30BPP: ++ color_depth = COLOR_DEPTH_101010; ++ set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_DEPTH); ++ set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); ++ break; ++ case LB_PIXEL_DEPTH_36BPP: ++ color_depth = COLOR_DEPTH_121212; ++ set_reg_field_value(value, 3, LB_DATA_FORMAT, PIXEL_DEPTH); ++ set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); ++ break; ++ default: ++ ret = false; ++ break; ++ } ++ ++ if (ret == true) { ++ set_denormalization(xfm110, color_depth); ++ ret = program_bit_depth_reduction(xfm110, color_depth); ++ ++ set_reg_field_value(value, 0, LB_DATA_FORMAT, ALPHA_EN); ++ dal_write_reg( ++ xfm->ctx, LB_REG(mmLB_DATA_FORMAT), value); ++ if (!(xfm110->lb_pixel_depth_supported & depth)) { ++ /*we should use unsupported capabilities ++ * unless it is required by w/a*/ ++ dal_logger_write(xfm->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Capability not supported", ++ __func__); ++ } ++ } ++ ++ return ret; ++} ++ ++/* LB_MEMORY_CONFIG ++ * 00 - Use all three pieces of memory ++ * 01 - Use only one piece of memory of total 720x144 bits ++ * 10 - Use two pieces of memory of total 960x144 bits ++ * 11 - reserved ++ * ++ * LB_MEMORY_SIZE ++ * Total entries of LB memory. ++ * This number should be larger than 960. The default value is 1712(0x6B0) */ ++bool dce110_transform_power_up_line_buffer(struct transform *xfm) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ uint32_t value; ++ ++ value = dal_read_reg(xfm110->base.ctx, LB_REG(mmLB_MEMORY_CTRL)); ++ ++ /*Use all three pieces of memory always*/ ++ set_reg_field_value(value, 0, LB_MEMORY_CTRL, LB_MEMORY_CONFIG); ++ /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ ++ set_reg_field_value(value, LB_TOTAL_NUMBER_OF_ENTRIES, LB_MEMORY_CTRL, ++ LB_MEMORY_SIZE); ++ ++ dal_write_reg(xfm110->base.ctx, LB_REG(mmLB_MEMORY_CTRL), value); ++ ++ return true; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h +new file mode 100644 +index 0000000..ff100cc +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h +@@ -0,0 +1,51 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_TRANSFORM_BIT_DEPTH_DCE110_H__ ++#define __DC_TRANSFORM_BIT_DEPTH_DCE110_H__ ++ ++#include "dce110_transform.h" ++ ++bool dce110_transform_power_up_line_buffer(struct transform *xfm); ++ ++bool dce110_transform_get_max_num_of_supported_lines( ++ struct dce110_transform *xfm110, ++ enum lb_pixel_depth depth, ++ uint32_t pixel_width, ++ uint32_t *lines); ++ ++void dce110_transform_enable_alpha( ++ struct dce110_transform *xfm110, ++ bool enable); ++ ++bool dce110_transform_get_next_lower_pixel_storage_depth( ++ struct dce110_transform *xfm110, ++ uint32_t display_bpp, ++ enum lb_pixel_depth depth, ++ enum lb_pixel_depth *lower_depth); ++ ++bool dce110_transform_is_prefetch_enabled( ++ struct dce110_transform *xfm110); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c +new file mode 100644 +index 0000000..a5b5b01 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c +@@ -0,0 +1,297 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "dce110_transform.h" ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "include/fixed31_32.h" ++#include "include/hw_sequencer_types.h" ++#include "basics/conversion.h" ++#include "include/grph_object_id.h" ++ ++enum { ++ GAMUT_MATRIX_SIZE = 12 ++}; ++ ++#define DCP_REG(reg)\ ++ (reg + xfm110->offsets.dcp_offset) ++ ++#define DISP_BRIGHTNESS_DEFAULT_HW 0 ++#define DISP_BRIGHTNESS_MIN_HW -25 ++#define DISP_BRIGHTNESS_MAX_HW 25 ++#define DISP_BRIGHTNESS_STEP_HW 1 ++#define DISP_BRIGHTNESS_HW_DIVIDER 100 ++ ++#define DISP_HUE_DEFAULT_HW 0 ++#define DISP_HUE_MIN_HW -30 ++#define DISP_HUE_MAX_HW 30 ++#define DISP_HUE_STEP_HW 1 ++#define DISP_HUE_HW_DIVIDER 1 ++ ++#define DISP_CONTRAST_DEFAULT_HW 100 ++#define DISP_CONTRAST_MIN_HW 50 ++#define DISP_CONTRAST_MAX_HW 150 ++#define DISP_CONTRAST_STEP_HW 1 ++#define DISP_CONTRAST_HW_DIVIDER 100 ++ ++#define DISP_SATURATION_DEFAULT_HW 100 ++#define DISP_SATURATION_MIN_HW 0 ++#define DISP_SATURATION_MAX_HW 200 ++#define DISP_SATURATION_STEP_HW 1 ++#define DISP_SATURATION_HW_DIVIDER 100 ++ ++#define DISP_KELVIN_DEGRES_DEFAULT 6500 ++#define DISP_KELVIN_DEGRES_MIN 4000 ++#define DISP_KELVIN_DEGRES_MAX 10000 ++#define DISP_KELVIN_DEGRES_STEP 100 ++#define DISP_KELVIN_HW_DIVIDER 10000 ++ ++static void program_gamut_remap( ++ struct dce110_transform *xfm110, ++ const uint16_t *reg_val) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t value = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_CONTROL); ++ ++ /* the register controls ovl also */ ++ value = dal_read_reg(ctx, addr); ++ ++ if (reg_val) { ++ { ++ uint32_t reg_data = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_C11_C12); ++ ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[0], ++ GAMUT_REMAP_C11_C12, ++ GAMUT_REMAP_C11); ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[1], ++ GAMUT_REMAP_C11_C12, ++ GAMUT_REMAP_C12); ++ ++ dal_write_reg(ctx, addr, reg_data); ++ } ++ { ++ uint32_t reg_data = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_C13_C14); ++ ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[2], ++ GAMUT_REMAP_C13_C14, ++ GAMUT_REMAP_C13); ++ ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[3], ++ GAMUT_REMAP_C13_C14, ++ GAMUT_REMAP_C14); ++ ++ dal_write_reg(ctx, addr, reg_data); ++ } ++ { ++ uint32_t reg_data = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_C21_C22); ++ ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[4], ++ GAMUT_REMAP_C21_C22, ++ GAMUT_REMAP_C21); ++ ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[5], ++ GAMUT_REMAP_C21_C22, ++ GAMUT_REMAP_C22); ++ ++ dal_write_reg(ctx, addr, reg_data); ++ } ++ { ++ uint32_t reg_data = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_C23_C24); ++ ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[6], ++ GAMUT_REMAP_C23_C24, ++ GAMUT_REMAP_C23); ++ ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[7], ++ GAMUT_REMAP_C23_C24, ++ GAMUT_REMAP_C24); ++ ++ dal_write_reg(ctx, addr, reg_data); ++ } ++ { ++ uint32_t reg_data = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_C31_C32); ++ ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[8], ++ GAMUT_REMAP_C31_C32, ++ GAMUT_REMAP_C31); ++ ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[9], ++ GAMUT_REMAP_C31_C32, ++ GAMUT_REMAP_C32); ++ ++ dal_write_reg(ctx, addr, reg_data); ++ } ++ { ++ uint32_t reg_data = 0; ++ uint32_t addr = DCP_REG(mmGAMUT_REMAP_C33_C34); ++ ++ /* fixed S2.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[10], ++ GAMUT_REMAP_C33_C34, ++ GAMUT_REMAP_C33); ++ ++ /* fixed S0.13 format */ ++ set_reg_field_value( ++ reg_data, ++ reg_val[11], ++ GAMUT_REMAP_C33_C34, ++ GAMUT_REMAP_C34); ++ ++ dal_write_reg(ctx, addr, reg_data); ++ } ++ ++ set_reg_field_value( ++ value, ++ 1, ++ GAMUT_REMAP_CONTROL, ++ GRPH_GAMUT_REMAP_MODE); ++ ++ } else ++ set_reg_field_value( ++ value, ++ 0, ++ GAMUT_REMAP_CONTROL, ++ GRPH_GAMUT_REMAP_MODE); ++ ++ addr = DCP_REG(mmGAMUT_REMAP_CONTROL); ++ dal_write_reg(ctx, addr, value); ++ ++} ++ ++/** ++ ***************************************************************************** ++ * Function: dal_transform_wide_gamut_set_gamut_remap ++ * ++ * @param [in] const struct grph_csc_adjustment *adjust ++ * ++ * @return ++ * void ++ * ++ * @note calculate and apply color temperature adjustment to in Rgb color space ++ * ++ * @see ++ * ++ ***************************************************************************** ++ */ ++void dce110_transform_set_gamut_remap( ++ struct transform *xfm, ++ const struct grph_csc_adjustment *adjust) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ ++ if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW || ++ adjust->temperature_divider == 0) ++ program_gamut_remap(xfm110, NULL); ++ else { ++ struct fixed31_32 arr_matrix[GAMUT_MATRIX_SIZE]; ++ uint16_t arr_reg_val[GAMUT_MATRIX_SIZE]; ++ ++ arr_matrix[0] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[0], ++ adjust->temperature_divider); ++ arr_matrix[1] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[1], ++ adjust->temperature_divider); ++ arr_matrix[2] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[2], ++ adjust->temperature_divider); ++ arr_matrix[3] = dal_fixed31_32_zero; ++ ++ arr_matrix[4] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[3], ++ adjust->temperature_divider); ++ arr_matrix[5] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[4], ++ adjust->temperature_divider); ++ arr_matrix[6] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[5], ++ adjust->temperature_divider); ++ arr_matrix[7] = dal_fixed31_32_zero; ++ ++ arr_matrix[8] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[6], ++ adjust->temperature_divider); ++ arr_matrix[9] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[7], ++ adjust->temperature_divider); ++ arr_matrix[10] = ++ dal_fixed31_32_from_fraction( ++ adjust->temperature_matrix[8], ++ adjust->temperature_divider); ++ arr_matrix[11] = dal_fixed31_32_zero; ++ ++ convert_float_matrix( ++ arr_reg_val, arr_matrix, GAMUT_MATRIX_SIZE); ++ ++ program_gamut_remap(xfm110, arr_reg_val); ++ } ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c +new file mode 100644 +index 0000000..f313d2c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c +@@ -0,0 +1,818 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* include DCE11 register header files */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_transform.h" ++ ++#define UP_SCALER_RATIO_MAX 16000 ++#define DOWN_SCALER_RATIO_MAX 250 ++#define SCALER_RATIO_DIVIDER 1000 ++ ++#define SCL_REG(reg)\ ++ (reg + xfm110->offsets.scl_offset) ++ ++#define DCFE_REG(reg)\ ++ (reg + xfm110->offsets.dcfe_offset) ++ ++static void disable_enhanced_sharpness(struct dce110_transform *xfm110) ++{ ++ uint32_t value; ++ ++ value = dal_read_reg(xfm110->base.ctx, ++ SCL_REG(mmSCL_F_SHARP_CONTROL)); ++ ++ set_reg_field_value(value, 0, ++ SCL_F_SHARP_CONTROL, SCL_HF_SHARP_EN); ++ ++ set_reg_field_value(value, 0, ++ SCL_F_SHARP_CONTROL, SCL_VF_SHARP_EN); ++ ++ set_reg_field_value(value, 0, ++ SCL_F_SHARP_CONTROL, SCL_HF_SHARP_SCALE_FACTOR); ++ ++ set_reg_field_value(value, 0, ++ SCL_F_SHARP_CONTROL, SCL_VF_SHARP_SCALE_FACTOR); ++ ++ dal_write_reg(xfm110->base.ctx, ++ SCL_REG(mmSCL_F_SHARP_CONTROL), value); ++} ++ ++/** ++* Function: ++* void setup_scaling_configuration ++* ++* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps ++* Input: data ++* ++* Output: ++ void ++*/ ++static bool setup_scaling_configuration( ++ struct dce110_transform *xfm110, ++ const struct scaler_data *data) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t addr; ++ uint32_t value; ++ ++ if (data->taps.h_taps + data->taps.v_taps <= 2) { ++ dce110_transform_set_scaler_bypass(&xfm110->base); ++ return false; ++ } ++ ++ { ++ addr = SCL_REG(mmSCL_MODE); ++ value = dal_read_reg(ctx, addr); ++ ++ if (data->dal_pixel_format <= PIXEL_FORMAT_GRPH_END) ++ set_reg_field_value(value, 1, SCL_MODE, SCL_MODE); ++ else ++ set_reg_field_value(value, 2, SCL_MODE, SCL_MODE); ++ ++ set_reg_field_value(value, 1, SCL_MODE, SCL_PSCL_EN); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ addr = SCL_REG(mmSCL_TAP_CONTROL); ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value(value, data->taps.h_taps - 1, ++ SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS); ++ ++ set_reg_field_value(value, data->taps.v_taps - 1, ++ SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ { ++ addr = SCL_REG(mmSCL_CONTROL); ++ value = dal_read_reg(ctx, addr); ++ /* 1 - Replaced out of bound pixels with edge */ ++ set_reg_field_value(value, 1, SCL_CONTROL, SCL_BOUNDARY_MODE); ++ ++ /* 1 - Replaced out of bound pixels with the edge pixel. */ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ return true; ++} ++ ++/** ++* Function: ++* void program_overscan ++* ++* Purpose: Programs overscan border ++* Input: overscan ++* ++* Output: ++ void ++*/ ++static void program_overscan( ++ struct dce110_transform *xfm110, ++ const struct overscan_info *overscan) ++{ ++ uint32_t overscan_left_right = 0; ++ uint32_t overscan_top_bottom = 0; ++ ++ set_reg_field_value(overscan_left_right, overscan->left, ++ EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); ++ ++ set_reg_field_value(overscan_left_right, overscan->right, ++ EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); ++ ++ set_reg_field_value(overscan_top_bottom, overscan->top, ++ EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); ++ ++ set_reg_field_value(overscan_top_bottom, overscan->bottom, ++ EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); ++ ++ dal_write_reg(xfm110->base.ctx, ++ SCL_REG(mmEXT_OVERSCAN_LEFT_RIGHT), ++ overscan_left_right); ++ ++ dal_write_reg(xfm110->base.ctx, ++ SCL_REG(mmEXT_OVERSCAN_TOP_BOTTOM), ++ overscan_top_bottom); ++} ++ ++static void program_two_taps_filter( ++ struct dce110_transform *xfm110, ++ bool enable, ++ bool vertical) ++{ ++ uint32_t addr; ++ uint32_t value; ++ /* 1: Hard coded 2 tap filter ++ * 0: Programmable 2 tap filter from coefficient RAM ++ */ ++ if (vertical) { ++ addr = SCL_REG(mmSCL_VERT_FILTER_CONTROL); ++ value = dal_read_reg(xfm110->base.ctx, addr); ++ set_reg_field_value( ++ value, ++ enable ? 1 : 0, ++ SCL_VERT_FILTER_CONTROL, ++ SCL_V_2TAP_HARDCODE_COEF_EN); ++ ++ } else { ++ addr = SCL_REG(mmSCL_HORZ_FILTER_CONTROL); ++ value = dal_read_reg(xfm110->base.ctx, addr); ++ set_reg_field_value( ++ value, ++ enable ? 1 : 0, ++ SCL_HORZ_FILTER_CONTROL, ++ SCL_H_2TAP_HARDCODE_COEF_EN); ++ } ++ ++ dal_write_reg(xfm110->base.ctx, addr, value); ++} ++ ++static void set_coeff_update_complete(struct dce110_transform *xfm110) ++{ ++ uint32_t value; ++ uint32_t addr = SCL_REG(mmSCL_UPDATE); ++ ++ value = dal_read_reg(xfm110->base.ctx, addr); ++ set_reg_field_value(value, 1, ++ SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++} ++ ++static void program_filter( ++ struct dce110_transform *xfm110, ++ enum ram_filter_type filter_type, ++ struct scaler_filter_params *scl_filter_params, ++ uint32_t *coeffs, ++ uint32_t coeffs_num) ++{ ++ uint32_t phase = 0; ++ uint32_t array_idx = 0; ++ uint32_t pair = 0; ++ ++ uint32_t taps_pairs = (scl_filter_params->taps + 1) / 2; ++ uint32_t phases_to_program = scl_filter_params->phases / 2 + 1; ++ ++ uint32_t i; ++ uint32_t addr; ++ uint32_t select_addr; ++ uint32_t select; ++ uint32_t data; ++ /* We need to disable power gating on coeff memory to do programming */ ++ ++ uint32_t pwr_ctrl_orig; ++ uint32_t pwr_ctrl_off; ++ ++ addr = DCFE_REG(mmDCFE_MEM_PWR_CTRL); ++ pwr_ctrl_orig = dal_read_reg(xfm110->base.ctx, addr); ++ pwr_ctrl_off = pwr_ctrl_orig; ++ set_reg_field_value( ++ pwr_ctrl_off, ++ 1, ++ DCFE_MEM_PWR_CTRL, ++ SCL_COEFF_MEM_PWR_DIS); ++ dal_write_reg(xfm110->base.ctx, addr, pwr_ctrl_off); ++ ++ addr = DCFE_REG(mmDCFE_MEM_PWR_STATUS); ++ /* Wait to disable gating: */ ++ for (i = 0; ++ i < 10 && ++ get_reg_field_value( ++ dal_read_reg(xfm110->base.ctx, addr), ++ DCFE_MEM_PWR_STATUS, ++ SCL_COEFF_MEM_PWR_STATE); ++ i++) ++ dc_service_delay_in_microseconds(xfm110->base.ctx, 1); ++ ++ ASSERT(i < 10); ++ ++ select_addr = SCL_REG(mmSCL_COEF_RAM_SELECT); ++ select = dal_read_reg(xfm110->base.ctx, select_addr); ++ ++ set_reg_field_value( ++ select, ++ filter_type, ++ SCL_COEF_RAM_SELECT, ++ SCL_C_RAM_FILTER_TYPE); ++ set_reg_field_value( ++ select, ++ 0, ++ SCL_COEF_RAM_SELECT, ++ SCL_C_RAM_TAP_PAIR_IDX); ++ set_reg_field_value( ++ select, ++ 0, ++ SCL_COEF_RAM_SELECT, ++ SCL_C_RAM_PHASE); ++ ++ data = 0; ++ ++ for (phase = 0; phase < phases_to_program; phase++) { ++ /* we always program N/2 + 1 phases, total phases N, but N/2-1 ++ * are just mirror phase 0 is unique and phase N/2 is unique ++ * if N is even ++ */ ++ ++ set_reg_field_value( ++ select, ++ phase, ++ SCL_COEF_RAM_SELECT, ++ SCL_C_RAM_PHASE); ++ ++ for (pair = 0; pair < taps_pairs; pair++) { ++ set_reg_field_value( ++ select, ++ pair, ++ SCL_COEF_RAM_SELECT, ++ SCL_C_RAM_TAP_PAIR_IDX); ++ dal_write_reg(xfm110->base.ctx, select_addr, select); ++ ++ /* even tap write enable */ ++ set_reg_field_value( ++ data, ++ 1, ++ SCL_COEF_RAM_TAP_DATA, ++ SCL_C_RAM_EVEN_TAP_COEF_EN); ++ /* even tap data */ ++ set_reg_field_value( ++ data, ++ coeffs[array_idx], ++ SCL_COEF_RAM_TAP_DATA, ++ SCL_C_RAM_EVEN_TAP_COEF); ++ ++ /* if we have odd number of taps and the last pair is ++ * here then we do not need to program ++ */ ++ if (scl_filter_params->taps % 2 && ++ pair == taps_pairs - 1) { ++ /* odd tap write disable */ ++ set_reg_field_value( ++ data, ++ 0, ++ SCL_COEF_RAM_TAP_DATA, ++ SCL_C_RAM_ODD_TAP_COEF_EN); ++ set_reg_field_value( ++ data, ++ 0, ++ SCL_COEF_RAM_TAP_DATA, ++ SCL_C_RAM_ODD_TAP_COEF); ++ array_idx += 1; ++ } else { ++ /* odd tap write enable */ ++ set_reg_field_value( ++ data, ++ 1, ++ SCL_COEF_RAM_TAP_DATA, ++ SCL_C_RAM_ODD_TAP_COEF_EN); ++ /* dbg_val: 0x1000 / sclFilterParams->taps; */ ++ set_reg_field_value( ++ data, ++ coeffs[array_idx + 1], ++ SCL_COEF_RAM_TAP_DATA, ++ SCL_C_RAM_ODD_TAP_COEF); ++ ++ array_idx += 2; ++ } ++ ++ dal_write_reg( ++ xfm110->base.ctx, ++ SCL_REG(mmSCL_COEF_RAM_TAP_DATA), ++ data); ++ } ++ } ++ ++ ASSERT(coeffs_num == array_idx); ++ ++ /* reset the power gating register */ ++ dal_write_reg( ++ xfm110->base.ctx, ++ DCFE_REG(mmDCFE_MEM_PWR_CTRL), ++ pwr_ctrl_orig); ++ ++ set_coeff_update_complete(xfm110); ++} ++ ++/* ++ * ++ * Populates an array with filter coefficients in 1.1.12 fixed point form ++*/ ++static bool get_filter_coefficients( ++ struct dce110_transform *xfm110, ++ uint32_t taps, ++ uint32_t **data_tab, ++ uint32_t *data_size) ++{ ++ uint32_t num = 0; ++ uint32_t i; ++ const struct fixed31_32 *filter = ++ dal_scaler_filter_get( ++ xfm110->base.filter, ++ data_tab, ++ &num); ++ uint32_t *data_row; ++ ++ if (!filter) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ data_row = *data_tab; ++ ++ for (i = 0; i < num; ++i) { ++ /* req. format sign fixed 1.1.12, the values are always between ++ * [-1; 1] ++ * ++ * Each phase is mirrored as follows : ++ * 0 : Phase 0 ++ * 1 : Phase 1 or Phase 64 - 1 / 128 - 1 ++ * N : Phase N or Phase 64 - N / 128 - N ++ * ++ * Convert from Fixed31_32 to 1.1.12 by using floor on value ++ * shifted by number of required fractional bits(12) ++ */ ++ struct fixed31_32 value = filter[i]; ++ ++ data_row[i] = ++ dal_fixed31_32_floor(dal_fixed31_32_shl(value, 12)) & ++ 0x3FFC; ++ } ++ *data_size = num; ++ ++ return true; ++} ++ ++static bool program_multi_taps_filter( ++ struct dce110_transform *xfm110, ++ const struct scaler_data *data, ++ bool horizontal) ++{ ++ struct scaler_filter_params filter_params; ++ enum ram_filter_type filter_type; ++ uint32_t src_size; ++ uint32_t dst_size; ++ ++ uint32_t *filter_data = NULL; ++ uint32_t filter_data_size = 0; ++ ++ /* 16 phases total for DCE11 */ ++ filter_params.phases = 16; ++ ++ if (horizontal) { ++ filter_params.taps = data->taps.h_taps; ++ filter_params.sharpness = data->h_sharpness; ++ filter_params.flags.bits.HORIZONTAL = 1; ++ ++ src_size = data->viewport.width; ++ dst_size = ++ dal_fixed31_32_floor( ++ dal_fixed31_32_div( ++ dal_fixed31_32_from_int( ++ data->viewport.width), ++ data->ratios->horz)); ++ ++ filter_type = FILTER_TYPE_RGB_Y_HORIZONTAL; ++ } else { ++ filter_params.taps = data->taps.v_taps; ++ filter_params.sharpness = data->v_sharpness; ++ filter_params.flags.bits.HORIZONTAL = 0; ++ ++ src_size = data->viewport.height; ++ dst_size = ++ dal_fixed31_32_floor( ++ dal_fixed31_32_div( ++ dal_fixed31_32_from_int( ++ data->viewport.height), ++ data->ratios->vert)); ++ ++ filter_type = FILTER_TYPE_RGB_Y_VERTICAL; ++ } ++ ++ /* 1. Generate the coefficients */ ++ if (!dal_scaler_filter_generate( ++ xfm110->base.filter, ++ &filter_params, ++ src_size, ++ dst_size)) ++ return false; ++ ++ /* 2. Convert coefficients to fixed point format 1.12 (note coeff. ++ * could be negative(!) and range is [ from -1 to 1 ]) */ ++ if (!get_filter_coefficients( ++ xfm110, ++ filter_params.taps, ++ &filter_data, ++ &filter_data_size)) ++ return false; ++ ++ /* 3. Program the filter */ ++ program_filter( ++ xfm110, ++ filter_type, ++ &filter_params, ++ filter_data, ++ filter_data_size); ++ ++ /* 4. Program the alpha if necessary */ ++ if (data->flags.bits.SHOULD_PROGRAM_ALPHA) { ++ if (horizontal) ++ filter_type = FILTER_TYPE_ALPHA_HORIZONTAL; ++ else ++ filter_type = FILTER_TYPE_ALPHA_VERTICAL; ++ ++ program_filter( ++ xfm110, ++ filter_type, ++ &filter_params, ++ filter_data, ++ filter_data_size); ++ } ++ ++ return true; ++} ++ ++static void program_viewport( ++ struct dce110_transform *xfm110, ++ const struct rect *view_port) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t value = 0; ++ uint32_t addr = 0; ++ ++ addr = SCL_REG(mmVIEWPORT_START); ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ view_port->x, ++ VIEWPORT_START, ++ VIEWPORT_X_START); ++ set_reg_field_value( ++ value, ++ view_port->y, ++ VIEWPORT_START, ++ VIEWPORT_Y_START); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = SCL_REG(mmVIEWPORT_SIZE); ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ view_port->height, ++ VIEWPORT_SIZE, ++ VIEWPORT_HEIGHT); ++ set_reg_field_value( ++ value, ++ view_port->width, ++ VIEWPORT_SIZE, ++ VIEWPORT_WIDTH); ++ dal_write_reg(ctx, addr, value); ++ ++ /* TODO: add stereo support */ ++} ++ ++static void calculate_inits( ++ struct dce110_transform *xfm110, ++ const struct scaler_data *data, ++ struct scl_ratios_inits *inits) ++{ ++ struct fixed31_32 h_init; ++ struct fixed31_32 v_init; ++ struct fixed31_32 v_init_bot; ++ ++ inits->bottom_enable = 0; ++ inits->h_int_scale_ratio = ++ dal_fixed31_32_u2d19(data->ratios->horz) << 5; ++ inits->v_int_scale_ratio = ++ dal_fixed31_32_u2d19(data->ratios->vert) << 5; ++ ++ h_init = ++ dal_fixed31_32_div_int( ++ dal_fixed31_32_add( ++ data->ratios->horz, ++ dal_fixed31_32_from_int(data->taps.h_taps + 1)), ++ 2); ++ inits->h_init.integer = dal_fixed31_32_floor(h_init); ++ inits->h_init.fraction = dal_fixed31_32_u0d19(h_init) << 5; ++ ++ v_init = ++ dal_fixed31_32_div_int( ++ dal_fixed31_32_add( ++ data->ratios->vert, ++ dal_fixed31_32_from_int(data->taps.v_taps + 1)), ++ 2); ++ inits->v_init.integer = dal_fixed31_32_floor(v_init); ++ inits->v_init.fraction = dal_fixed31_32_u0d19(v_init) << 5; ++ ++ if (data->flags.bits.INTERLACED) { ++ v_init_bot = ++ dal_fixed31_32_add( ++ dal_fixed31_32_div_int( ++ dal_fixed31_32_add( ++ data->ratios->vert, ++ dal_fixed31_32_from_int( ++ data->taps.v_taps + 1)), ++ 2), ++ data->ratios->vert); ++ inits->v_init_bottom.integer = dal_fixed31_32_floor(v_init_bot); ++ inits->v_init_bottom.fraction = ++ dal_fixed31_32_u0d19(v_init_bot) << 5; ++ ++ inits->bottom_enable = 1; ++ } ++} ++ ++static void program_scl_ratios_inits( ++ struct dce110_transform *xfm110, ++ struct scl_ratios_inits *inits) ++{ ++ uint32_t addr = SCL_REG(mmSCL_HORZ_FILTER_SCALE_RATIO); ++ uint32_t value = 0; ++ ++ set_reg_field_value( ++ value, ++ inits->h_int_scale_ratio, ++ SCL_HORZ_FILTER_SCALE_RATIO, ++ SCL_H_SCALE_RATIO); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++ ++ addr = SCL_REG(mmSCL_VERT_FILTER_SCALE_RATIO); ++ value = 0; ++ set_reg_field_value( ++ value, ++ inits->v_int_scale_ratio, ++ SCL_VERT_FILTER_SCALE_RATIO, ++ SCL_V_SCALE_RATIO); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++ ++ addr = SCL_REG(mmSCL_HORZ_FILTER_INIT); ++ value = 0; ++ set_reg_field_value( ++ value, ++ inits->h_init.integer, ++ SCL_HORZ_FILTER_INIT, ++ SCL_H_INIT_INT); ++ set_reg_field_value( ++ value, ++ inits->h_init.fraction, ++ SCL_HORZ_FILTER_INIT, ++ SCL_H_INIT_FRAC); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++ ++ addr = SCL_REG(mmSCL_VERT_FILTER_INIT); ++ value = 0; ++ set_reg_field_value( ++ value, ++ inits->v_init.integer, ++ SCL_VERT_FILTER_INIT, ++ SCL_V_INIT_INT); ++ set_reg_field_value( ++ value, ++ inits->v_init.fraction, ++ SCL_VERT_FILTER_INIT, ++ SCL_V_INIT_FRAC); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++ ++ if (inits->bottom_enable) { ++ addr = SCL_REG(mmSCL_VERT_FILTER_INIT_BOT); ++ value = 0; ++ set_reg_field_value( ++ value, ++ inits->v_init_bottom.integer, ++ SCL_VERT_FILTER_INIT_BOT, ++ SCL_V_INIT_INT_BOT); ++ set_reg_field_value( ++ value, ++ inits->v_init_bottom.fraction, ++ SCL_VERT_FILTER_INIT_BOT, ++ SCL_V_INIT_FRAC_BOT); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++ } ++ ++ addr = SCL_REG(mmSCL_AUTOMATIC_MODE_CONTROL); ++ value = 0; ++ set_reg_field_value( ++ value, ++ 0, ++ SCL_AUTOMATIC_MODE_CONTROL, ++ SCL_V_CALC_AUTO_RATIO_EN); ++ set_reg_field_value( ++ value, ++ 0, ++ SCL_AUTOMATIC_MODE_CONTROL, ++ SCL_H_CALC_AUTO_RATIO_EN); ++ dal_write_reg(xfm110->base.ctx, addr, value); ++} ++ ++static void get_viewport( ++ struct dce110_transform *xfm110, ++ struct rect *current_view_port) ++{ ++ uint32_t value_start; ++ uint32_t value_size; ++ ++ if (current_view_port == NULL) ++ return; ++ ++ value_start = dal_read_reg(xfm110->base.ctx, SCL_REG(mmVIEWPORT_START)); ++ value_size = dal_read_reg(xfm110->base.ctx, SCL_REG(mmVIEWPORT_SIZE)); ++ ++ current_view_port->x = get_reg_field_value( ++ value_start, ++ VIEWPORT_START, ++ VIEWPORT_X_START); ++ current_view_port->y = get_reg_field_value( ++ value_start, ++ VIEWPORT_START, ++ VIEWPORT_Y_START); ++ current_view_port->height = get_reg_field_value( ++ value_size, ++ VIEWPORT_SIZE, ++ VIEWPORT_HEIGHT); ++ current_view_port->width = get_reg_field_value( ++ value_size, ++ VIEWPORT_SIZE, ++ VIEWPORT_WIDTH); ++} ++ ++ ++bool dce110_transform_set_scaler( ++ struct transform *xfm, ++ const struct scaler_data *data) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ bool is_scaling_required; ++ struct dc_context *ctx = xfm->ctx; ++ ++ { ++ uint32_t addr = SCL_REG(mmSCL_BYPASS_CONTROL); ++ uint32_t value = dal_read_reg(xfm->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ SCL_BYPASS_CONTROL, ++ SCL_BYPASS_MODE); ++ dal_write_reg(xfm->ctx, addr, value); ++ } ++ ++ disable_enhanced_sharpness(xfm110); ++ ++ /* 3. Program overscan */ ++ program_overscan(xfm110, &data->overscan); ++ ++ /* 4. Program taps and configuration */ ++ is_scaling_required = setup_scaling_configuration(xfm110, data); ++ if (is_scaling_required) { ++ /* 5. Calculate and program ratio, filter initialization */ ++ struct scl_ratios_inits inits = { 0 }; ++ ++ calculate_inits(xfm110, data, &inits); ++ ++ program_scl_ratios_inits(xfm110, &inits); ++ ++ /* 6. Program vertical filters */ ++ if (data->taps.v_taps > 2) { ++ program_two_taps_filter(xfm110, false, true); ++ ++ if (!program_multi_taps_filter(xfm110, data, false)) { ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_DCP, ++ LOG_MINOR_DCP_SCALER, ++ "Failed vertical taps programming\n"); ++ return false; ++ } ++ } else ++ program_two_taps_filter(xfm110, true, true); ++ ++ /* 7. Program horizontal filters */ ++ if (data->taps.h_taps > 2) { ++ program_two_taps_filter(xfm110, false, false); ++ ++ if (!program_multi_taps_filter(xfm110, data, true)) { ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_DCP, ++ LOG_MINOR_DCP_SCALER, ++ "Failed horizontal taps programming\n"); ++ return false; ++ } ++ } else ++ program_two_taps_filter(xfm110, true, false); ++ } ++ ++ return true; ++} ++ ++void dce110_transform_set_scaler_bypass(struct transform *xfm) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ uint32_t sclv_mode; ++ ++ disable_enhanced_sharpness(xfm110); ++ ++ sclv_mode = dal_read_reg(xfm->ctx, SCL_REG(mmSCL_MODE)); ++ set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_MODE); ++ set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_PSCL_EN); ++ dal_write_reg(xfm->ctx, SCL_REG(mmSCL_MODE), sclv_mode); ++} ++ ++bool dce110_transform_update_viewport( ++ struct transform *xfm, ++ const struct rect *view_port, ++ bool is_fbc_attached) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ bool program_req = false; ++ struct rect current_view_port; ++ ++ if (view_port == NULL) ++ return program_req; ++ ++ get_viewport(xfm110, ¤t_view_port); ++ ++ if (current_view_port.x != view_port->x || ++ current_view_port.y != view_port->y || ++ current_view_port.height != view_port->height || ++ current_view_port.width != view_port->width) ++ program_req = true; ++ ++ if (program_req) { ++ /*underlay viewport is programmed with scaler ++ *program_viewport function pointer is not exposed*/ ++ program_viewport(xfm110, view_port); ++ } ++ ++ return program_req; ++} ++ ++void dce110_transform_set_scaler_filter( ++ struct transform *xfm, ++ struct scaler_filter *filter) ++{ ++ xfm->filter = filter; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c +new file mode 100644 +index 0000000..bcf20bb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c +@@ -0,0 +1,531 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "dce110_transform.h" ++ ++#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_CONTROLLER,\ ++ "TRANSFORM SCALER:%s()\n", __func__) ++ ++/* ++***************************************************************************** ++* Function: calculateViewport ++* ++* @brief ++* Calculates all of the data required to set the viewport ++* ++* @param [in] pData: scaler settings data ++* @param [out] pLumaVp: luma viewport information ++* @param [out] pChromaVp: chroma viewport information ++* @param [out] srcResCx2: source chroma resolution times 2 - for multi-taps ++* ++***************************************************************************** ++*/ ++static void calculate_viewport( ++ const struct scaler_data *scl_data, ++ struct rect *luma_viewport, ++ struct rect *chroma_viewport) ++{ ++ /*Do not set chroma vp for rgb444 pixel format*/ ++ luma_viewport->x = scl_data->viewport.x - scl_data->viewport.x % 2; ++ luma_viewport->y = scl_data->viewport.y - scl_data->viewport.y % 2; ++ luma_viewport->width = ++ scl_data->viewport.width - scl_data->viewport.width % 2; ++ luma_viewport->height = ++ scl_data->viewport.height - scl_data->viewport.height % 2; ++ ++ ++ if (scl_data->dal_pixel_format == PIXEL_FORMAT_422BPP16) { ++ luma_viewport->width += luma_viewport->width % 2; ++ ++ chroma_viewport->x = luma_viewport->x / 2; ++ chroma_viewport->width = luma_viewport->width / 2; ++ } else if (scl_data->dal_pixel_format == PIXEL_FORMAT_420BPP12) { ++ luma_viewport->height += luma_viewport->height % 2; ++ luma_viewport->width += luma_viewport->width % 2; ++ /*for 420 video chroma is 1/4 the area of luma, scaled ++ *vertically and horizontally ++ */ ++ chroma_viewport->x = luma_viewport->x / 2; ++ chroma_viewport->y = luma_viewport->y / 2; ++ chroma_viewport->height = luma_viewport->height / 2; ++ chroma_viewport->width = luma_viewport->width / 2; ++ } ++} ++ ++ ++static void program_viewport( ++ struct dce110_transform *xfm110, ++ struct rect *luma_view_port, ++ struct rect *chroma_view_port) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t value = 0; ++ uint32_t addr = 0; ++ ++ if (luma_view_port->width != 0 && luma_view_port->height != 0) { ++ addr = mmSCLV_VIEWPORT_START; ++ value = 0; ++ set_reg_field_value( ++ value, ++ luma_view_port->x, ++ SCLV_VIEWPORT_START, ++ VIEWPORT_X_START); ++ set_reg_field_value( ++ value, ++ luma_view_port->y, ++ SCLV_VIEWPORT_START, ++ VIEWPORT_Y_START); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = mmSCLV_VIEWPORT_SIZE; ++ value = 0; ++ set_reg_field_value( ++ value, ++ luma_view_port->height, ++ SCLV_VIEWPORT_SIZE, ++ VIEWPORT_HEIGHT); ++ set_reg_field_value( ++ value, ++ luma_view_port->width, ++ SCLV_VIEWPORT_SIZE, ++ VIEWPORT_WIDTH); ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ if (chroma_view_port->width != 0 && chroma_view_port->height != 0) { ++ addr = mmSCLV_VIEWPORT_START_C; ++ value = 0; ++ set_reg_field_value( ++ value, ++ chroma_view_port->x, ++ SCLV_VIEWPORT_START_C, ++ VIEWPORT_X_START_C); ++ set_reg_field_value( ++ value, ++ chroma_view_port->y, ++ SCLV_VIEWPORT_START_C, ++ VIEWPORT_Y_START_C); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = mmSCLV_VIEWPORT_SIZE_C; ++ value = 0; ++ set_reg_field_value( ++ value, ++ chroma_view_port->height, ++ SCLV_VIEWPORT_SIZE_C, ++ VIEWPORT_HEIGHT_C); ++ set_reg_field_value( ++ value, ++ chroma_view_port->width, ++ SCLV_VIEWPORT_SIZE_C, ++ VIEWPORT_WIDTH_C); ++ dal_write_reg(ctx, addr, value); ++ } ++ /* TODO: add stereo support */ ++} ++ ++ ++/* Until and For MPO video play story, to reduce time for implementation, ++ * below limits are applied for now: 2_TAPS only ++ * Use auto-calculated filter values ++ * Following routines will be empty for now: ++ * ++ * programSclRatiosInits -- calcualate scaler ratio manually ++ * calculateInits --- calcualate scaler ratio manually ++ * programFilter -- multi-taps ++ * GetOptimalNumberOfTaps -- will hard coded to 2 TAPS ++ * GetNextLowerNumberOfTaps -- will hard coded to 2TAPS ++ * validateRequestedScaleRatio - used by GetOptimalNumberOfTaps internally ++ */ ++ ++/** ++* Function: ++* void setup_scaling_configuration ++* ++* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps ++* Input: data ++* ++* Output: ++ void ++*/ ++static bool setup_scaling_configuration( ++ struct dce110_transform *xfm110, ++ const struct scaler_data *data) ++{ ++ bool is_scaling_needed = false; ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t value = 0; ++ ++ if (data->taps.h_taps + data->taps.v_taps > 2) { ++ set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE); ++ set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN); ++ is_scaling_needed = true; ++ } else { ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); ++ } ++ ++ if (data->taps.h_taps_c + data->taps.v_taps_c > 2) { ++ set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE_C); ++ set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN_C); ++ is_scaling_needed = true; ++ } else if (data->dal_pixel_format != PIXEL_FORMAT_420BPP12 && ++ data->dal_pixel_format != PIXEL_FORMAT_422BPP16) { ++ set_reg_field_value( ++ value, ++ get_reg_field_value(value, SCLV_MODE, SCL_MODE), ++ SCLV_MODE, ++ SCL_MODE_C); ++ set_reg_field_value( ++ value, ++ get_reg_field_value(value, SCLV_MODE, SCL_PSCL_EN), ++ SCLV_MODE, ++ SCL_PSCL_EN_C); ++ } else { ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); ++ } ++ dal_write_reg(ctx, mmSCLV_MODE, value); ++ ++ { ++ value = dal_read_reg(ctx, mmSCLV_TAP_CONTROL); ++ ++ set_reg_field_value(value, data->taps.h_taps - 1, ++ SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS); ++ ++ set_reg_field_value(value, data->taps.v_taps - 1, ++ SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS); ++ ++ set_reg_field_value(value, data->taps.h_taps_c - 1, ++ SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS_C); ++ ++ set_reg_field_value(value, data->taps.v_taps_c - 1, ++ SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS_C); ++ ++ dal_write_reg(ctx, mmSCLV_TAP_CONTROL, value); ++ } ++ ++ { ++ /* we can ignore this register because we are ok with hw ++ * default 0 -- change to 1 according to dal2 code*/ ++ value = dal_read_reg(ctx, mmSCLV_CONTROL); ++ /* 0 - Replaced out of bound pixels with black pixel ++ * (or any other required color) */ ++ set_reg_field_value(value, 1, SCLV_CONTROL, SCL_BOUNDARY_MODE); ++ ++ /* 1 - Replaced out of bound pixels with the edge pixel. */ ++ dal_write_reg(ctx, mmSCLV_CONTROL, value); ++ } ++ ++ return is_scaling_needed; ++} ++ ++/** ++* Function: ++* void program_overscan ++* ++* Purpose: Programs overscan border ++* Input: overscan ++* ++* Output: ++ void ++*/ ++static void program_overscan( ++ struct dce110_transform *xfm110, ++ const struct overscan_info *overscan) ++{ ++ uint32_t overscan_left_right = 0; ++ uint32_t overscan_top_bottom = 0; ++ ++ set_reg_field_value(overscan_left_right, overscan->left, ++ SCLV_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); ++ ++ set_reg_field_value(overscan_left_right, overscan->right, ++ SCLV_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); ++ ++ set_reg_field_value(overscan_top_bottom, overscan->top, ++ SCLV_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); ++ ++ set_reg_field_value(overscan_top_bottom, overscan->bottom, ++ SCLV_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); ++ ++ dal_write_reg(xfm110->base.ctx, ++ mmSCLV_EXT_OVERSCAN_LEFT_RIGHT, ++ overscan_left_right); ++ ++ dal_write_reg(xfm110->base.ctx, ++ mmSCLV_EXT_OVERSCAN_TOP_BOTTOM, ++ overscan_top_bottom); ++} ++/* ++static void setup_auto_scaling(struct dce110_transform *xfm110) ++{ ++ uint32_t value = 0; ++ set_reg_field_value(value, 1, SCLV_AUTOMATIC_MODE_CONTROL, ++ SCL_V_CALC_AUTO_RATIO_EN); ++ set_reg_field_value(value, 1, SCLV_AUTOMATIC_MODE_CONTROL, ++ SCL_H_CALC_AUTO_RATIO_EN); ++ dal_write_reg(xfm->ctx, ++ xfm->regs[IDX_SCL_AUTOMATIC_MODE_CONTROL], ++ value); ++} ++*/ ++ ++static void program_two_taps_filter_horz( ++ struct dce110_transform *xfm110, ++ bool hardcode_coff) ++{ ++ uint32_t value = 0; ++ ++ if (hardcode_coff) ++ set_reg_field_value( ++ value, ++ 1, ++ SCLV_HORZ_FILTER_CONTROL, ++ SCL_H_2TAP_HARDCODE_COEF_EN); ++ ++ dal_write_reg(xfm110->base.ctx, ++ mmSCLV_HORZ_FILTER_CONTROL, ++ value); ++} ++ ++static void program_two_taps_filter_vert( ++ struct dce110_transform *xfm110, ++ bool hardcode_coff) ++{ ++ uint32_t value = 0; ++ ++ if (hardcode_coff) ++ set_reg_field_value(value, 1, SCLV_VERT_FILTER_CONTROL, ++ SCL_V_2TAP_HARDCODE_COEF_EN); ++ ++ dal_write_reg(xfm110->base.ctx, ++ mmSCLV_VERT_FILTER_CONTROL, ++ value); ++} ++ ++static void set_coeff_update_complete( ++ struct dce110_transform *xfm110) ++{ ++ /*TODO: Until now, only scaler bypass, up-scaler 2 -TAPS coeff auto ++ * calculation are implemented. Coefficient RAM is not used ++ * Do not check this flag yet ++ */ ++ ++ /*uint32_t value; ++ uint32_t addr = xfm->regs[IDX_SCL_UPDATE]; ++ ++ value = dal_read_reg(xfm->ctx, addr); ++ set_reg_field_value(value, 0, ++ SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); ++ dal_write_reg(xfm->ctx, addr, value);*/ ++} ++ ++static bool program_multi_taps_filter( ++ struct dce110_transform *xfm110, ++ const struct scaler_data *data, ++ bool horizontal) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ ++ NOT_IMPLEMENTED(); ++ return false; ++} ++ ++static void calculate_inits( ++ struct dce110_transform *xfm110, ++ const struct scaler_data *data, ++ struct sclv_ratios_inits *inits, ++ struct rect *luma_viewport, ++ struct rect *chroma_viewport) ++{ ++ if (data->dal_pixel_format == PIXEL_FORMAT_420BPP12 || ++ data->dal_pixel_format == PIXEL_FORMAT_422BPP16) ++ inits->chroma_enable = true; ++ ++ /* TODO: implement rest of this function properly */ ++ if (inits->chroma_enable) { ++ inits->h_int_scale_ratio_luma = 0x1000000; ++ inits->v_int_scale_ratio_luma = 0x1000000; ++ inits->h_int_scale_ratio_chroma = 0x800000; ++ inits->v_int_scale_ratio_chroma = 0x800000; ++ } ++} ++ ++static void program_scl_ratios_inits( ++ struct dce110_transform *xfm110, ++ struct sclv_ratios_inits *inits) ++{ ++ struct dc_context *ctx = xfm110->base.ctx; ++ uint32_t addr = mmSCLV_HORZ_FILTER_SCALE_RATIO; ++ uint32_t value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ inits->h_int_scale_ratio_luma, ++ SCLV_HORZ_FILTER_SCALE_RATIO, ++ SCL_H_SCALE_RATIO); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = mmSCLV_VERT_FILTER_SCALE_RATIO; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ inits->v_int_scale_ratio_luma, ++ SCLV_VERT_FILTER_SCALE_RATIO, ++ SCL_V_SCALE_RATIO); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = mmSCLV_HORZ_FILTER_SCALE_RATIO_C; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ inits->h_int_scale_ratio_chroma, ++ SCLV_HORZ_FILTER_SCALE_RATIO_C, ++ SCL_H_SCALE_RATIO_C); ++ dal_write_reg(ctx, addr, value); ++ ++ addr = mmSCLV_VERT_FILTER_SCALE_RATIO_C; ++ value = dal_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ inits->v_int_scale_ratio_chroma, ++ SCLV_VERT_FILTER_SCALE_RATIO_C, ++ SCL_V_SCALE_RATIO_C); ++ dal_write_reg(ctx, addr, value); ++} ++ ++void dce110_transform_underlay_set_scalerv_bypass(struct transform *xfm) ++{ ++ uint32_t addr = mmSCLV_MODE; ++ uint32_t value = dal_read_reg(xfm->ctx, addr); ++ ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); ++ set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); ++ dal_write_reg(xfm->ctx, addr, value); ++} ++ ++bool dce110_transform_underlay_is_scaling_enabled(struct transform *xfm) ++{ ++ uint32_t value = dal_read_reg(xfm->ctx, mmSCLV_MODE); ++ uint8_t scl_mode = get_reg_field_value(value, SCLV_MODE, SCL_MODE); ++ ++ return scl_mode == 0; ++} ++ ++/* TODO: sync this one with DAL2 */ ++bool dce110_transform_underlay_set_scaler( ++ struct transform *xfm, ++ const struct scaler_data *data) ++{ ++ struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); ++ bool is_scaling_required; ++ struct rect luma_viewport = {0}; ++ struct rect chroma_viewport = {0}; ++ struct dc_context *ctx = xfm->ctx; ++ ++ /* 1. Lock Scaler TODO: enable?*/ ++ /*set_scaler_update_lock(xfm, true);*/ ++ ++ /* 2. Calculate viewport, viewport programming should happen after init ++ * calculations as they may require an adjustment in the viewport. ++ */ ++ ++ calculate_viewport(data, &luma_viewport, &chroma_viewport); ++ ++ /* 3. Program overscan */ ++ program_overscan(xfm110, &data->overscan); ++ ++ /* 4. Program taps and configuration */ ++ is_scaling_required = setup_scaling_configuration(xfm110, data); ++ ++ if (is_scaling_required) { ++ /* 5. Calculate and program ratio, filter initialization */ ++ ++ struct sclv_ratios_inits inits = { 0 }; ++ ++ calculate_inits( ++ xfm110, ++ data, ++ &inits, ++ &luma_viewport, ++ &chroma_viewport); ++ ++ program_scl_ratios_inits(xfm110, &inits); ++ ++ /*scaler coeff of 2-TAPS use hardware auto calculated value*/ ++ ++ /* 6. Program vertical filters */ ++ if (data->taps.v_taps > 2) { ++ program_two_taps_filter_vert(xfm110, false); ++ ++ if (!program_multi_taps_filter(xfm110, data, false)) { ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_DCP, ++ LOG_MINOR_DCP_SCALER, ++ "Failed vertical taps programming\n"); ++ return false; ++ } ++ } else ++ program_two_taps_filter_vert(xfm110, true); ++ ++ /* 7. Program horizontal filters */ ++ if (data->taps.h_taps > 2) { ++ program_two_taps_filter_horz(xfm110, false); ++ ++ if (!program_multi_taps_filter(xfm110, data, true)) { ++ dal_logger_write(ctx->logger, ++ LOG_MAJOR_DCP, ++ LOG_MINOR_DCP_SCALER, ++ "Failed horizontal taps programming\n"); ++ return false; ++ } ++ } else ++ program_two_taps_filter_horz(xfm110, true); ++ } ++ ++ /* 8. Program the viewport */ ++ if (data->flags.bits.SHOULD_PROGRAM_VIEWPORT) ++ program_viewport(xfm110, &luma_viewport, &chroma_viewport); ++ ++ /* 9. Unlock the Scaler TODO: enable?*/ ++ /* Every call to "set_scaler_update_lock(xfm, TRUE)" ++ * must have a corresponding call to ++ * "set_scaler_update_lock(xfm, FALSE)" */ ++ /*set_scaler_update_lock(xfm, false);*/ ++ ++ /* TODO: investigate purpose/need of SHOULD_UNLOCK */ ++ if (data->flags.bits.SHOULD_UNLOCK == false) ++ set_coeff_update_complete(xfm110); ++ ++ return true; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dcs/Makefile b/drivers/gpu/drm/amd/dal/dc/dcs/Makefile +new file mode 100644 +index 0000000..a266942 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dcs/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the 'gpu' sub-component of DAL. ++# It provides the control and status of HW adapter resources, ++# that are global for the ASIC and sharable between pipes. ++ ++DCS = ddc_service.o ddc_i2caux_helper.o ++ ++AMD_DAL_DCS = $(addprefix $(AMDDALPATH)/dc/dcs/,$(DCS)) ++ ++AMD_DAL_FILES += $(AMD_DAL_DCS) +diff --git a/drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.c b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.c +new file mode 100644 +index 0000000..a4442d6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.c +@@ -0,0 +1,159 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "ddc_i2caux_helper.h" ++#include "include/ddc_service_types.h" ++#include "include/vector.h" ++ ++struct i2c_payloads { ++ struct vector payloads; ++}; ++ ++struct aux_payloads { ++ struct vector payloads; ++}; ++ ++struct i2c_payloads *dal_ddc_i2c_payloads_create(struct dc_context *ctx, uint32_t count) ++{ ++ struct i2c_payloads *payloads; ++ ++ payloads = dc_service_alloc(ctx, sizeof(struct i2c_payloads)); ++ ++ if (!payloads) ++ return NULL; ++ ++ if (dal_vector_construct( ++ &payloads->payloads, ctx, count, sizeof(struct i2c_payload))) ++ return payloads; ++ ++ dc_service_free(ctx, payloads); ++ return NULL; ++ ++} ++ ++struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p) ++{ ++ return (struct i2c_payload *)p->payloads.container; ++} ++ ++uint32_t dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p) ++{ ++ return p->payloads.count; ++} ++ ++void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p) ++{ ++ if (!p || !*p) ++ return; ++ dal_vector_destruct(&(*p)->payloads); ++ dc_service_free((*p)->payloads.ctx, *p); ++ *p = NULL; ++ ++} ++ ++struct aux_payloads *dal_ddc_aux_payloads_create(struct dc_context *ctx, uint32_t count) ++{ ++ struct aux_payloads *payloads; ++ ++ payloads = dc_service_alloc(ctx, sizeof(struct aux_payloads)); ++ ++ if (!payloads) ++ return NULL; ++ ++ if (dal_vector_construct( ++ &payloads->payloads, ctx, count, sizeof(struct aux_payloads))) ++ return payloads; ++ ++ dc_service_free(ctx, payloads); ++ return NULL; ++} ++ ++struct aux_payload *dal_ddc_aux_payloads_get(struct aux_payloads *p) ++{ ++ return (struct aux_payload *)p->payloads.container; ++} ++ ++uint32_t dal_ddc_aux_payloads_get_count(struct aux_payloads *p) ++{ ++ return p->payloads.count; ++} ++ ++ ++void dal_ddc_aux_payloads_destroy(struct aux_payloads **p) ++{ ++ if (!p || !*p) ++ return; ++ ++ dal_vector_destruct(&(*p)->payloads); ++ dc_service_free((*p)->payloads.ctx, *p); ++ *p = NULL; ++} ++ ++#define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b)) ++ ++void dal_ddc_i2c_payloads_add( ++ struct i2c_payloads *payloads, ++ uint32_t address, ++ uint32_t len, ++ uint8_t *data, ++ bool write) ++{ ++ uint32_t payload_size = EDID_SEGMENT_SIZE; ++ uint32_t pos; ++ ++ for (pos = 0; pos < len; pos += payload_size) { ++ struct i2c_payload payload = { ++ .write = write, ++ .address = address, ++ .length = DDC_MIN(payload_size, len - pos), ++ .data = data + pos }; ++ dal_vector_append(&payloads->payloads, &payload); ++ } ++ ++} ++ ++void dal_ddc_aux_payloads_add( ++ struct aux_payloads *payloads, ++ uint32_t address, ++ uint32_t len, ++ uint8_t *data, ++ bool write) ++{ ++ uint32_t payload_size = DEFAULT_AUX_MAX_DATA_SIZE; ++ uint32_t pos; ++ ++ for (pos = 0; pos < len; pos += payload_size) { ++ struct aux_payload payload = { ++ .i2c_over_aux = true, ++ .write = write, ++ .address = address, ++ .length = DDC_MIN(payload_size, len - pos), ++ .data = data + pos }; ++ dal_vector_append(&payloads->payloads, &payload); ++ } ++} ++ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.h b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.h +new file mode 100644 +index 0000000..bb628cd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_i2caux_helper.h +@@ -0,0 +1,60 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2CAUX_HELPER_H__ ++#define __DAL_I2CAUX_HELPER_H__ ++ ++#include "include/i2caux_interface.h" ++ ++#define EDID_SEGMENT_SIZE 256 ++ ++struct i2c_payloads; ++struct aux_payloads; ++ ++struct i2c_payloads *dal_ddc_i2c_payloads_create(struct dc_context *ctx, uint32_t count); ++struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p); ++uint32_t dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p); ++void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p); ++ ++struct aux_payloads *dal_ddc_aux_payloads_create(struct dc_context *ctx, uint32_t count); ++struct aux_payload *dal_ddc_aux_payloads_get(struct aux_payloads *p); ++uint32_t dal_ddc_aux_payloads_get_count(struct aux_payloads *p); ++void dal_ddc_aux_payloads_destroy(struct aux_payloads **p); ++ ++void dal_ddc_i2c_payloads_add( ++ struct i2c_payloads *payloads, ++ uint32_t address, ++ uint32_t len, ++ uint8_t *data, ++ bool write); ++ ++void dal_ddc_aux_payloads_add( ++ struct aux_payloads *payloads, ++ uint32_t address, ++ uint32_t len, ++ uint8_t *data, ++ bool write); ++ ++#endif /* __DAL_I2CAUX_HELPER_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.c b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.c +new file mode 100644 +index 0000000..5436704 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.c +@@ -0,0 +1,1034 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/adapter_service_interface.h" ++#include "include/i2caux_interface.h" ++#include "include/ddc_service_interface.h" ++#include "include/ddc_service_types.h" ++#include "include/grph_object_id.h" ++#include "include/dpcd_defs.h" ++#include "include/logger_interface.h" ++#include "ddc_i2caux_helper.h" ++#include "ddc_service.h" ++#include "dal_services_types.h" ++ ++#define AUX_POWER_UP_WA_DELAY 500 ++#define I2C_OVER_AUX_DEFER_WA_DELAY 70 ++ ++/* CV smart dongle slave address for retrieving supported HDTV modes*/ ++#define CV_SMART_DONGLE_ADDRESS 0x20 ++/* DVI-HDMI dongle slave address for retrieving dongle signature*/ ++#define DVI_HDMI_DONGLE_ADDRESS 0x68 ++static const int8_t dvi_hdmi_dongle_signature_str[] = "6140063500G"; ++struct dvi_hdmi_dongle_signature_data { ++ int8_t vendor[3];/* "AMD" */ ++ uint8_t version[2]; ++ uint8_t size; ++ int8_t id[11];/* "6140063500G"*/ ++}; ++/* DP-HDMI dongle slave address for retrieving dongle signature*/ ++#define DP_HDMI_DONGLE_ADDRESS 0x40 ++static const uint8_t dp_hdmi_dongle_signature_str[] = "DP-HDMI ADAPTOR"; ++#define DP_HDMI_DONGLE_SIGNATURE_EOT 0x04 ++ ++struct dp_hdmi_dongle_signature_data { ++ int8_t id[15];/* "DP-HDMI ADAPTOR"*/ ++ uint8_t eot;/* end of transmition '\x4' */ ++}; ++ ++/* Address range from 0x00 to 0x1F.*/ ++#define DP_ADAPTOR_TYPE2_SIZE 0x20 ++#define DP_ADAPTOR_TYPE2_REG_ID 0x10 ++#define DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK 0x1D ++/* Identifies adaptor as Dual-mode adaptor */ ++#define DP_ADAPTOR_TYPE2_ID 0xA0 ++/* MHz*/ ++#define DP_ADAPTOR_TYPE2_MAX_TMDS_CLK 600 ++/* MHz*/ ++#define DP_ADAPTOR_TYPE2_MIN_TMDS_CLK 25 ++/* kHZ*/ ++#define DP_ADAPTOR_DVI_MAX_TMDS_CLK 165000 ++/* kHZ*/ ++#define DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK 165000 ++ ++#define DDC_I2C_COMMAND_ENGINE I2C_COMMAND_ENGINE_SW ++ ++enum edid_read_result { ++ EDID_READ_RESULT_EDID_MATCH = 0, ++ EDID_READ_RESULT_EDID_MISMATCH, ++ EDID_READ_RESULT_CHECKSUM_READ_ERR, ++ EDID_READ_RESULT_VENDOR_READ_ERR ++}; ++ ++/* SCDC Address defines (HDMI 2.0)*/ ++#define HDMI_SCDC_WRITE_UPDATE_0_ARRAY 3 ++#define HDMI_SCDC_ADDRESS 0x54 ++#define HDMI_SCDC_SINK_VERSION 0x01 ++#define HDMI_SCDC_SOURCE_VERSION 0x02 ++#define HDMI_SCDC_UPDATE_0 0x10 ++#define HDMI_SCDC_TMDS_CONFIG 0x20 ++#define HDMI_SCDC_SCRAMBLER_STATUS 0x21 ++#define HDMI_SCDC_CONFIG_0 0x30 ++#define HDMI_SCDC_STATUS_FLAGS 0x40 ++#define HDMI_SCDC_ERR_DETECT 0x50 ++#define HDMI_SCDC_TEST_CONFIG 0xC0 ++ ++ ++union hdmi_scdc_update_read_data ++{ ++ uint8_t byte[2]; ++ struct ++ { ++ uint8_t STATUS_UPDATE:1; ++ uint8_t CED_UPDATE:1; ++ uint8_t RR_TEST:1; ++ uint8_t RESERVED:5; ++ uint8_t RESERVED2:8; ++ } fields; ++}; ++ ++union hdmi_scdc_status_flags_data ++{ ++ uint8_t byte[2]; ++ struct ++ { ++ uint8_t CLOCK_DETECTED:1; ++ uint8_t CH0_LOCKED:1; ++ uint8_t CH1_LOCKED:1; ++ uint8_t CH2_LOCKED:1; ++ uint8_t RESERVED:4; ++ uint8_t RESERVED2:8; ++ } fields; ++}; ++ ++union hdmi_scdc_ced_data ++{ ++ uint8_t byte[7]; ++ struct ++ { ++ uint8_t CH0_8LOW:8; ++ uint8_t CH0_7HIGH:7; ++ uint8_t CH0_VALID:1; ++ uint8_t CH1_8LOW:8; ++ uint8_t CH1_7HIGH:7; ++ uint8_t CH1_VALID:1; ++ uint8_t CH2_8LOW:8; ++ uint8_t CH2_7HIGH:7; ++ uint8_t CH2_VALID:1; ++ uint8_t CHECKSUM:8; ++ } fields; ++}; ++ ++union hdmi_scdc_test_config_Data ++{ ++ uint8_t byte; ++ struct ++ { ++ uint8_t TEST_READ_REQUEST_DELAY:7; ++ uint8_t TEST_READ_REQUEST: 1; ++ } fields; ++}; ++ ++ ++ ++union ddc_wa { ++ struct { ++ uint32_t DP_SKIP_POWER_OFF:1; ++ uint32_t DP_AUX_POWER_UP_WA_DELAY:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++struct ddc_flags { ++ uint8_t EDID_QUERY_DONE_ONCE:1; ++ uint8_t IS_INTERNAL_DISPLAY:1; ++ uint8_t FORCE_READ_REPEATED_START:1; ++ uint8_t EDID_STRESS_READ:1; ++ ++}; ++ ++struct ddc_service { ++ struct ddc *ddc_pin; ++ struct ddc_flags flags; ++ union ddc_wa wa; ++ enum ddc_transaction_type transaction_type; ++ enum display_dongle_type dongle_type; ++ struct dp_receiver_id_info dp_receiver_id_info; ++ struct adapter_service *as; ++ struct dc_context *ctx; ++ ++ uint32_t address; ++ uint32_t edid_buf_len; ++ uint8_t edid_buf[MAX_EDID_BUFFER_SIZE]; ++}; ++ ++static bool construct( ++ struct ddc_service *ddc_service, ++ struct ddc_service_init_data *init_data) ++{ ++ enum connector_id connector_id = ++ dal_graphics_object_id_get_connector_id(init_data->id); ++ ++ ddc_service->ctx = init_data->ctx; ++ ddc_service->as = init_data->as; ++ ddc_service->ddc_pin = dal_adapter_service_obtain_ddc( ++ init_data->as, init_data->id); ++ ++ ddc_service->flags.EDID_QUERY_DONE_ONCE = false; ++ ++ ddc_service->flags.FORCE_READ_REPEATED_START = ++ dal_adapter_service_is_feature_supported( ++ FEATURE_DDC_READ_FORCE_REPEATED_START); ++ ++ ddc_service->flags.EDID_STRESS_READ = ++ dal_adapter_service_is_feature_supported( ++ FEATURE_EDID_STRESS_READ); ++ ++ ++ ddc_service->flags.IS_INTERNAL_DISPLAY = ++ connector_id == CONNECTOR_ID_EDP || ++ connector_id == CONNECTOR_ID_LVDS; ++ ++ ddc_service->wa.raw = 0; ++ return true; ++} ++ ++struct ddc_service *dal_ddc_service_create( ++ struct ddc_service_init_data *init_data) ++{ ++ struct ddc_service *ddc_service; ++ ++ ddc_service = dc_service_alloc(init_data->ctx, sizeof(struct ddc_service)); ++ ++ if (!ddc_service) ++ return NULL; ++ ++ if (construct(ddc_service, init_data)) ++ return ddc_service; ++ ++ dc_service_free(init_data->ctx, ddc_service); ++ return NULL; ++} ++ ++static void destruct(struct ddc_service *ddc) ++{ ++ if (ddc->ddc_pin) ++ dal_adapter_service_release_ddc(ddc->as, ddc->ddc_pin); ++} ++ ++void dal_ddc_service_destroy(struct ddc_service **ddc) ++{ ++ if (!ddc || !*ddc) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ destruct(*ddc); ++ dc_service_free((*ddc)->ctx, *ddc); ++ *ddc = NULL; ++} ++ ++enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc) ++{ ++ return DDC_SERVICE_TYPE_CONNECTOR; ++} ++ ++void dal_ddc_service_set_transaction_type( ++ struct ddc_service *ddc, ++ enum ddc_transaction_type type) ++{ ++ ddc->transaction_type = type; ++} ++ ++bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc) ++{ ++ switch (ddc->transaction_type) { ++ case DDC_TRANSACTION_TYPE_I2C_OVER_AUX: ++ case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER: ++ case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER: ++ return true; ++ default: ++ break; ++ } ++ return false; ++} ++ ++void ddc_service_set_dongle_type(struct ddc_service *ddc, ++ enum display_dongle_type dongle_type) ++{ ++ ddc->dongle_type = dongle_type; ++} ++ ++static uint32_t defer_delay_converter_wa( ++ struct ddc_service *ddc, ++ uint32_t defer_delay) ++{ ++ struct dp_receiver_id_info dp_rec_info = {0}; ++ ++ if (dal_ddc_service_get_dp_receiver_id_info(ddc, &dp_rec_info) && ++ (dp_rec_info.branch_id == DP_BRANCH_DEVICE_ID_4) && ++ !dal_strncmp(dp_rec_info.branch_name, ++ DP_DVI_CONVERTER_ID_4, ++ sizeof(dp_rec_info.branch_name))) ++ return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ? ++ defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY; ++ ++ return defer_delay; ++ ++} ++ ++#define DP_TRANSLATOR_DELAY 5 ++ ++static uint32_t get_defer_delay(struct ddc_service *ddc) ++{ ++ uint32_t defer_delay = 0; ++ ++ switch (ddc->transaction_type) { ++ case DDC_TRANSACTION_TYPE_I2C_OVER_AUX: ++ if ((DISPLAY_DONGLE_DP_VGA_CONVERTER == ddc->dongle_type) || ++ (DISPLAY_DONGLE_DP_DVI_CONVERTER == ddc->dongle_type) || ++ (DISPLAY_DONGLE_DP_HDMI_CONVERTER == ++ ddc->dongle_type)) { ++ ++ defer_delay = DP_TRANSLATOR_DELAY; ++ ++ defer_delay = ++ defer_delay_converter_wa(ddc, defer_delay); ++ ++ } else /*sink has a delay different from an Active Converter*/ ++ defer_delay = 0; ++ break; ++ case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER: ++ defer_delay = DP_TRANSLATOR_DELAY; ++ break; ++ default: ++ break; ++ } ++ return defer_delay; ++} ++ ++static bool i2c_read( ++ struct ddc_service *ddc, ++ uint32_t address, ++ uint8_t *buffer, ++ uint32_t len) ++{ ++ uint8_t offs_data = 0; ++ struct i2c_payload payloads[2] = { ++ { ++ .write = true, ++ .address = address, ++ .length = 1, ++ .data = &offs_data }, ++ { ++ .write = false, ++ .address = address, ++ .length = len, ++ .data = buffer } }; ++ ++ struct i2c_command command = { ++ .payloads = payloads, ++ .number_of_payloads = 2, ++ .engine = DDC_I2C_COMMAND_ENGINE, ++ .speed = dal_adapter_service_get_sw_i2c_speed(ddc->as) }; ++ ++ return dal_i2caux_submit_i2c_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &command); ++} ++ ++static uint8_t aux_read_edid_block( ++ struct ddc_service *ddc, ++ uint8_t address, ++ uint8_t index, ++ uint8_t *buf) ++{ ++ struct aux_command cmd = { ++ .payloads = NULL, ++ .number_of_payloads = 0, ++ .defer_delay = get_defer_delay(ddc), ++ .max_defer_write_retry = 0 }; ++ ++ uint8_t retrieved = 0; ++ uint8_t base_offset = ++ (index % DDC_EDID_BLOCKS_PER_SEGMENT) * DDC_EDID_BLOCK_SIZE; ++ uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT; ++ ++ for (retrieved = 0; retrieved < DDC_EDID_BLOCK_SIZE; ++ retrieved += DEFAULT_AUX_MAX_DATA_SIZE) { ++ ++ uint8_t offset = base_offset + retrieved; ++ ++ struct aux_payload payloads[3] = { ++ { ++ .i2c_over_aux = true, ++ .write = true, ++ .address = DDC_EDID_SEGMENT_ADDRESS, ++ .length = 1, ++ .data = &segment }, ++ { ++ .i2c_over_aux = true, ++ .write = true, ++ .address = address, ++ .length = 1, ++ .data = &offset }, ++ { ++ .i2c_over_aux = true, ++ .write = false, ++ .address = address, ++ .length = DEFAULT_AUX_MAX_DATA_SIZE, ++ .data = &buf[retrieved] } }; ++ ++ if (segment == 0) { ++ cmd.payloads = &payloads[1]; ++ cmd.number_of_payloads = 2; ++ } else { ++ cmd.payloads = payloads; ++ cmd.number_of_payloads = 3; ++ } ++ ++ if (!dal_i2caux_submit_aux_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &cmd)) ++ /* cannot read, break*/ ++ break; ++ } ++ ++ /* Reset segment to 0. Needed by some panels */ ++ if (0 != segment) { ++ struct aux_payload payloads[1] = { { ++ .i2c_over_aux = true, ++ .write = true, ++ .address = DDC_EDID_SEGMENT_ADDRESS, ++ .length = 1, ++ .data = &segment } }; ++ bool result = false; ++ ++ segment = 0; ++ ++ cmd.number_of_payloads = ARRAY_SIZE(payloads); ++ cmd.payloads = payloads; ++ ++ result = dal_i2caux_submit_aux_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &cmd); ++ ++ if (false == result) ++ dal_logger_write( ++ ddc->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, ++ "%s: Writing of EDID Segment (0x30) failed!\n", ++ __func__); ++ } ++ ++ return retrieved; ++} ++ ++static uint8_t i2c_read_edid_block( ++ struct ddc_service *ddc, ++ uint8_t address, ++ uint8_t index, ++ uint8_t *buf) ++{ ++ bool ret = false; ++ uint8_t offset = (index % DDC_EDID_BLOCKS_PER_SEGMENT) * ++ DDC_EDID_BLOCK_SIZE; ++ uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT; ++ ++ struct i2c_command cmd = { ++ .payloads = NULL, ++ .number_of_payloads = 0, ++ .engine = DDC_I2C_COMMAND_ENGINE, ++ .speed = dal_adapter_service_get_sw_i2c_speed(ddc->as) }; ++ ++ struct i2c_payload payloads[3] = { ++ { ++ .write = true, ++ .address = DDC_EDID_SEGMENT_ADDRESS, ++ .length = 1, ++ .data = &segment }, ++ { ++ .write = true, ++ .address = address, ++ .length = 1, ++ .data = &offset }, ++ { ++ .write = false, ++ .address = address, ++ .length = DDC_EDID_BLOCK_SIZE, ++ .data = buf } }; ++/* ++ * Some I2C engines don't handle stop/start between write-offset and read-data ++ * commands properly. For those displays, we have to force the newer E-DDC ++ * behavior of repeated-start which can be enabled by runtime parameter. */ ++/* Originally implemented for OnLive using NXP receiver chip */ ++ ++ if (index == 0 && !ddc->flags.FORCE_READ_REPEATED_START) { ++ /* base block, use use DDC2B, submit as 2 commands */ ++ cmd.payloads = &payloads[1]; ++ cmd.number_of_payloads = 1; ++ ++ if (dal_i2caux_submit_i2c_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &cmd)) { ++ ++ cmd.payloads = &payloads[2]; ++ cmd.number_of_payloads = 1; ++ ++ ret = dal_i2caux_submit_i2c_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &cmd); ++ } ++ ++ } else { ++ /* ++ * extension block use E-DDC, submit as 1 command ++ * or if repeated-start is forced by runtime parameter ++ */ ++ if (segment != 0) { ++ /* include segment offset in command*/ ++ cmd.payloads = payloads; ++ cmd.number_of_payloads = 3; ++ } else { ++ /* we are reading first segment, ++ * segment offset is not required */ ++ cmd.payloads = &payloads[1]; ++ cmd.number_of_payloads = 2; ++ } ++ ++ ret = dal_i2caux_submit_i2c_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &cmd); ++ } ++ ++ return ret ? DDC_EDID_BLOCK_SIZE : 0; ++} ++ ++static uint32_t query_edid_block( ++ struct ddc_service *ddc, ++ uint8_t address, ++ uint8_t index, ++ uint8_t *buf, ++ uint32_t size) ++{ ++ uint32_t size_retrieved = 0; ++ ++ if (size < DDC_EDID_BLOCK_SIZE) ++ return 0; ++ ++ if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) { ++ ++ ASSERT(index < 2); ++ size_retrieved = ++ aux_read_edid_block(ddc, address, index, buf); ++ } else { ++ size_retrieved = ++ i2c_read_edid_block(ddc, address, index, buf); ++ } ++ ++ return size_retrieved; ++} ++ ++#define DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS 0x261 ++#define DDC_TEST_ACK_ADDRESS 0x260 ++#define DDC_DPCD_EDID_TEST_ACK 0x04 ++#define DDC_DPCD_EDID_TEST_MASK 0x04 ++#define DDC_DPCD_TEST_REQUEST_ADDRESS 0x218 ++ ++static void write_dp_edid_checksum( ++ struct ddc_service *ddc, ++ uint8_t checksum) ++{ ++ uint8_t dpcd_data; ++ ++ dal_ddc_service_read_dpcd_data( ++ ddc, ++ DDC_DPCD_TEST_REQUEST_ADDRESS, ++ &dpcd_data, ++ 1); ++ ++ if (dpcd_data & DDC_DPCD_EDID_TEST_MASK) { ++ ++ dal_ddc_service_write_dpcd_data( ++ ddc, ++ DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS, ++ &checksum, ++ 1); ++ ++ dpcd_data = DDC_DPCD_EDID_TEST_ACK; ++ ++ dal_ddc_service_write_dpcd_data( ++ ddc, ++ DDC_TEST_ACK_ADDRESS, ++ &dpcd_data, ++ 1); ++ } ++} ++ ++uint32_t dal_ddc_service_edid_query(struct ddc_service *ddc) ++{ ++ uint32_t bytes_read = 0; ++ uint32_t ext_cnt = 0; ++ ++ uint8_t address; ++ uint32_t i; ++ ++ for (address = DDC_EDID_ADDRESS_START; ++ address <= DDC_EDID_ADDRESS_END; ++address) { ++ ++ bytes_read = query_edid_block( ++ ddc, ++ address, ++ 0, ++ ddc->edid_buf, ++ sizeof(ddc->edid_buf) - bytes_read); ++ ++ if (bytes_read != DDC_EDID_BLOCK_SIZE) ++ continue; ++ ++ /* get the number of ext blocks*/ ++ ext_cnt = ddc->edid_buf[DDC_EDID_EXT_COUNT_OFFSET]; ++ ++ /* EDID 2.0, need to read 1 more block because EDID2.0 is ++ * 256 byte in size*/ ++ if (ddc->edid_buf[DDC_EDID_20_SIGNATURE_OFFSET] == ++ DDC_EDID_20_SIGNATURE) ++ ext_cnt = 1; ++ ++ for (i = 0; i < ext_cnt; i++) { ++ /* read additional ext blocks accordingly */ ++ bytes_read += query_edid_block( ++ ddc, ++ address, ++ i+1, ++ &ddc->edid_buf[bytes_read], ++ sizeof(ddc->edid_buf) - bytes_read); ++ } ++ ++ /*this is special code path for DP compliance*/ ++ if (DDC_TRANSACTION_TYPE_I2C_OVER_AUX == ddc->transaction_type) ++ write_dp_edid_checksum( ++ ddc, ++ ddc->edid_buf[(ext_cnt * DDC_EDID_BLOCK_SIZE) + ++ DDC_EDID1X_CHECKSUM_OFFSET]); ++ ++ /*remembers the address where we fetch the EDID from ++ * for later signature check use */ ++ ddc->address = address; ++ ++ break;/* already read edid, done*/ ++ } ++ ++ ddc->edid_buf_len = bytes_read; ++ return bytes_read; ++} ++ ++uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc) ++{ ++ return ddc->edid_buf_len; ++} ++ ++void dal_ddc_service_get_edid_buf(struct ddc_service *ddc, uint8_t *edid_buf) ++{ ++ dc_service_memmove(edid_buf, ++ ddc->edid_buf, ddc->edid_buf_len); ++} ++ ++void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( ++ struct ddc_service *ddc, ++ struct display_sink_capability *sink_cap) ++{ ++ uint8_t i; ++ bool is_valid_hdmi_signature; ++ enum display_dongle_type *dongle = &sink_cap->dongle_type; ++ uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE]; ++ bool is_type2_dongle = false; ++ struct dp_hdmi_dongle_signature_data *dongle_signature; ++ ++ /* Assume we have no valid DP passive dongle connected */ ++ *dongle = DISPLAY_DONGLE_NONE; ++ sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK; ++ ++ /* Read DP-HDMI dongle I2c (no response interpreted as DP-DVI dongle)*/ ++ if (!i2c_read( ++ ddc, ++ DP_HDMI_DONGLE_ADDRESS, ++ type2_dongle_buf, ++ sizeof(type2_dongle_buf))) { ++ dal_logger_write(ddc->ctx->logger, ++ LOG_MAJOR_DCS, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ "Detected DP-DVI dongle.\n"); ++ *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; ++ sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK; ++ return; ++ } ++ ++ /* Check if Type 2 dongle.*/ ++ if (type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_ID] == DP_ADAPTOR_TYPE2_ID) ++ is_type2_dongle = true; ++ ++ dongle_signature = ++ (struct dp_hdmi_dongle_signature_data *)type2_dongle_buf; ++ ++ is_valid_hdmi_signature = true; ++ ++ /* Check EOT */ ++ if (dongle_signature->eot != DP_HDMI_DONGLE_SIGNATURE_EOT) { ++ is_valid_hdmi_signature = false; ++ } ++ ++ /* Check signature */ ++ for (i = 0; i < sizeof(dongle_signature->id); ++i) { ++ /* If its not the right signature, ++ * skip mismatch in subversion byte.*/ ++ if (dongle_signature->id[i] != ++ dp_hdmi_dongle_signature_str[i] && i != 3) { ++ ++ if (is_type2_dongle) { ++ is_valid_hdmi_signature = false; ++ break; ++ } ++ ++ } ++ } ++ ++ if (is_type2_dongle) { ++ uint32_t max_tmds_clk = ++ type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK]; ++ ++ max_tmds_clk = max_tmds_clk * 2 + max_tmds_clk / 2; ++ ++ if (0 == max_tmds_clk || ++ max_tmds_clk < DP_ADAPTOR_TYPE2_MIN_TMDS_CLK || ++ max_tmds_clk > DP_ADAPTOR_TYPE2_MAX_TMDS_CLK) { ++ dal_logger_write(ddc->ctx->logger, ++ LOG_MAJOR_DCS, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ "Invalid Maximum TMDS clock"); ++ *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; ++ } else { ++ if (is_valid_hdmi_signature == true) { ++ *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; ++ dal_logger_write(ddc->ctx->logger, ++ LOG_MAJOR_DCS, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ "Detected Type 2 DP-HDMI Maximum TMDS " ++ "clock, max TMDS clock: %d MHz", ++ max_tmds_clk); ++ } else { ++ *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; ++ dal_logger_write(ddc->ctx->logger, ++ LOG_MAJOR_DCS, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ "Detected Type 2 DP-HDMI (no valid HDMI" ++ " signature) Maximum TMDS clock, max " ++ "TMDS clock: %d MHz", ++ max_tmds_clk); ++ } ++ ++ /* Multiply by 1000 to convert to kHz. */ ++ sink_cap->max_hdmi_pixel_clock = ++ max_tmds_clk * 1000; ++ } ++ ++ } else { ++ if (is_valid_hdmi_signature == true) { ++ dal_logger_write(ddc->ctx->logger, ++ LOG_MAJOR_DCS, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ "Detected Type 1 DP-HDMI dongle.\n"); ++ *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; ++ } ++ else { ++ dal_logger_write(ddc->ctx->logger, ++ LOG_MAJOR_DCS, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ "Detected Type 1 DP-HDMI dongle (no valid HDMI " ++ "signature).\n"); ++ ++ *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; ++ } ++ } ++ ++ return; ++} ++ ++enum { ++ DP_SINK_CAP_SIZE = ++ DPCD_ADDRESS_EDP_CONFIG_CAP - DPCD_ADDRESS_DPCD_REV + 1 ++}; ++ ++bool dal_ddc_service_query_ddc_data( ++ struct ddc_service *ddc, ++ uint32_t address, ++ uint8_t *write_buf, ++ uint32_t write_size, ++ uint8_t *read_buf, ++ uint32_t read_size) ++{ ++ bool ret; ++ uint32_t payload_size = ++ dal_ddc_service_is_in_aux_transaction_mode(ddc) ? ++ DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE; ++ ++ uint32_t write_payloads = ++ (write_size + payload_size - 1) / payload_size; ++ ++ uint32_t read_payloads = ++ (read_size + payload_size - 1) / payload_size; ++ ++ uint32_t payloads_num = write_payloads + read_payloads; ++ ++ if (write_size > EDID_SEGMENT_SIZE || read_size > EDID_SEGMENT_SIZE) ++ return false; ++ ++ /*TODO: len of payload data for i2c and aux is uint8!!!!, ++ * but we want to read 256 over i2c!!!!*/ ++ if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) { ++ ++ struct aux_payloads *payloads = ++ dal_ddc_aux_payloads_create(ddc->ctx, payloads_num); ++ ++ struct aux_command command = { ++ .payloads = dal_ddc_aux_payloads_get(payloads), ++ .number_of_payloads = 0, ++ .defer_delay = get_defer_delay(ddc), ++ .max_defer_write_retry = 0 }; ++ ++ dal_ddc_aux_payloads_add( ++ payloads, address, write_size, write_buf, true); ++ ++ dal_ddc_aux_payloads_add( ++ payloads, address, read_size, read_buf, false); ++ ++ command.number_of_payloads = ++ dal_ddc_aux_payloads_get_count(payloads); ++ ++ ret = dal_i2caux_submit_aux_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &command); ++ ++ dal_ddc_aux_payloads_destroy(&payloads); ++ ++ } else { ++ struct i2c_payloads *payloads = ++ dal_ddc_i2c_payloads_create(ddc->ctx, payloads_num); ++ ++ struct i2c_command command = { ++ .payloads = dal_ddc_i2c_payloads_get(payloads), ++ .number_of_payloads = 0, ++ .engine = DDC_I2C_COMMAND_ENGINE, ++ .speed = ++ dal_adapter_service_get_sw_i2c_speed(ddc->as) }; ++ ++ dal_ddc_i2c_payloads_add( ++ payloads, address, write_size, write_buf, true); ++ ++ dal_ddc_i2c_payloads_add( ++ payloads, address, read_size, read_buf, false); ++ ++ command.number_of_payloads = ++ dal_ddc_i2c_payloads_get_count(payloads); ++ ++ ret = dal_i2caux_submit_i2c_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &command); ++ ++ dal_ddc_i2c_payloads_destroy(&payloads); ++ } ++ ++ return ret; ++} ++ ++bool dal_ddc_service_get_dp_receiver_id_info( ++ struct ddc_service *ddc, ++ struct dp_receiver_id_info *info) ++{ ++ if (!info) ++ return false; ++ ++ *info = ddc->dp_receiver_id_info; ++ return true; ++} ++ ++enum ddc_result dal_ddc_service_read_dpcd_data( ++ struct ddc_service *ddc, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t len) ++{ ++ struct aux_payload read_payload = { ++ .i2c_over_aux = false, ++ .write = false, ++ .address = address, ++ .length = len, ++ .data = data, ++ }; ++ struct aux_command command = { ++ .payloads = &read_payload, ++ .number_of_payloads = 1, ++ .defer_delay = 0, ++ .max_defer_write_retry = 0, ++ }; ++ ++ if (len > DEFAULT_AUX_MAX_DATA_SIZE) { ++ BREAK_TO_DEBUGGER(); ++ return DDC_RESULT_FAILED_INVALID_OPERATION; ++ } ++ ++ if (dal_i2caux_submit_aux_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &command)) ++ return DDC_RESULT_SUCESSFULL; ++ ++ return DDC_RESULT_FAILED_OPERATION; ++} ++ ++enum ddc_result dal_ddc_service_write_dpcd_data( ++ struct ddc_service *ddc, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t len) ++{ ++ struct aux_payload write_payload = { ++ .i2c_over_aux = false, ++ .write = true, ++ .address = address, ++ .length = len, ++ .data = (uint8_t *)data, ++ }; ++ struct aux_command command = { ++ .payloads = &write_payload, ++ .number_of_payloads = 1, ++ .defer_delay = 0, ++ .max_defer_write_retry = 0, ++ }; ++ ++ if (len > DEFAULT_AUX_MAX_DATA_SIZE) { ++ BREAK_TO_DEBUGGER(); ++ return DDC_RESULT_FAILED_INVALID_OPERATION; ++ } ++ ++ if (dal_i2caux_submit_aux_command( ++ dal_adapter_service_get_i2caux(ddc->as), ++ ddc->ddc_pin, ++ &command)) ++ return DDC_RESULT_SUCESSFULL; ++ ++ return DDC_RESULT_FAILED_OPERATION; ++} ++ ++/*test only function*/ ++void dal_ddc_service_set_ddc_pin( ++ struct ddc_service *ddc_service, ++ struct ddc *ddc) ++{ ++ ddc_service->ddc_pin = ddc; ++} ++ ++struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service) ++{ ++ return ddc_service->ddc_pin; ++} ++ ++ ++void dal_ddc_service_reset_dp_receiver_id_info(struct ddc_service *ddc_service) ++{ ++ dc_service_memset(&ddc_service->dp_receiver_id_info, ++ 0, sizeof(struct dp_receiver_id_info)); ++} ++ ++void dal_ddc_service_write_scdc_data(struct ddc_service *ddc_service, ++ uint32_t pix_clk, ++ bool lte_340_scramble) ++{ ++ bool over_340_mhz = pix_clk > 340000 ? 1 : 0; ++ uint8_t slave_address = HDMI_SCDC_ADDRESS; ++ uint8_t offset = HDMI_SCDC_SINK_VERSION; ++ uint8_t sink_version = 0; ++ uint8_t write_buffer[2] = {0}; ++ /*Lower than 340 Scramble bit from SCDC caps*/ ++ ++ dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset, ++ sizeof(offset), &sink_version, sizeof(sink_version)); ++ if (sink_version == 1) { ++ /*Source Version = 1*/ ++ write_buffer[0] = HDMI_SCDC_SOURCE_VERSION; ++ write_buffer[1] = 1; ++ dal_ddc_service_query_ddc_data(ddc_service, slave_address, ++ write_buffer, sizeof(write_buffer), NULL, 0); ++ /*Read Request from SCDC caps*/ ++ } ++ write_buffer[0] = HDMI_SCDC_TMDS_CONFIG; ++ ++ if (over_340_mhz) ++ { ++ write_buffer[1] = 3; ++ } ++ else if (lte_340_scramble) ++ { ++ write_buffer[1] = 1; ++ } ++ else ++ { ++ write_buffer[1] = 0; ++ } ++ dal_ddc_service_query_ddc_data(ddc_service, slave_address, write_buffer, ++ sizeof(write_buffer), NULL, 0); ++} ++ ++void dal_ddc_service_read_scdc_data(struct ddc_service *ddc_service) ++{ ++ uint8_t slave_address = HDMI_SCDC_ADDRESS; ++ uint8_t offset = HDMI_SCDC_TMDS_CONFIG; ++ uint8_t tmds_config = 0; ++ ++ dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset, ++ sizeof(offset), &tmds_config, sizeof(tmds_config)); ++ if (tmds_config & 0x1){ ++ union hdmi_scdc_status_flags_data status_data = {{0}}; ++ uint8_t scramble_status = 0; ++ ++ offset = HDMI_SCDC_SCRAMBLER_STATUS; ++ dal_ddc_service_query_ddc_data(ddc_service, slave_address, ++ &offset, sizeof(offset),&scramble_status, ++ sizeof(scramble_status)); ++ offset = HDMI_SCDC_STATUS_FLAGS; ++ dal_ddc_service_query_ddc_data(ddc_service, slave_address, ++ &offset, sizeof(offset),status_data.byte, ++ sizeof(status_data.byte)); ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.h b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.h +new file mode 100644 +index 0000000..e5217b7 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dcs/ddc_service.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DDC_SERVICE_H__ ++#define __DAL_DDC_SERVICE_H__ ++ ++#include "include/ddc_service_types.h" ++ ++void dal_ddc_service_set_ddc_pin( ++ struct ddc_service *ddc_service, ++ struct ddc *ddc); ++ ++struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service); ++void dal_ddc_service_reset_dp_receiver_id_info(struct ddc_service *ddc_service); ++ ++#endif /* __DAL_DDC_SERVICE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/Makefile b/drivers/gpu/drm/amd/dal/dc/gpio/Makefile +new file mode 100644 +index 0000000..7380910 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/Makefile +@@ -0,0 +1,24 @@ ++# ++# Makefile for the 'gpio' sub-component of DAL. ++# It provides the control and status of HW GPIO pins. ++ ++GPIO = ddc.o dvo.o gpio_base.o gpio_service.o hw_ddc.o hw_dvo.o hw_factory.o \ ++ hw_gpio.o hw_gpio_pad.o hw_gpio_pin.o hw_hpd.o hw_translate.o irq.o ++ ++AMD_DAL_GPIO = $(addprefix $(AMDDALPATH)/dc/gpio/,$(GPIO)) ++ ++AMD_DAL_FILES += $(AMD_DAL_GPIO) ++ ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++GPIO_DCE110 = hw_translate_dce110.o hw_factory_dce110.o hw_hpd_dce110.o \ ++ hw_ddc_dce110.o ++ ++AMD_DAL_GPIO_DCE110 = $(addprefix $(AMDDALPATH)/dc/gpio/dce110/,$(GPIO_DCE110)) ++ ++AMD_DAL_FILES += $(AMD_DAL_GPIO_DCE110) ++endif ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.c +new file mode 100644 +index 0000000..f026464 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.c +@@ -0,0 +1,883 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "../hw_gpio_pin.h" ++#include "../hw_gpio.h" ++#include "../hw_ddc.h" ++ ++/* ++ * Header of this unit ++ */ ++#include "hw_ddc_dce110.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#define ADDR_DDC_SETUP pin->addr.dc_i2c_ddc_setup ++/* ++ * This unit ++ */ ++static void destruct( ++ struct hw_ddc_dce110 *pin) ++{ ++ dal_hw_ddc_destruct(&pin->base); ++} ++ ++static void destroy( ++ struct hw_gpio_pin **ptr) ++{ ++ struct hw_ddc_dce110 *pin = DDC_DCE110_FROM_BASE(*ptr); ++ ++ destruct(pin); ++ ++ dc_service_free((*ptr)->ctx, pin); ++ ++ *ptr = NULL; ++} ++ ++struct hw_ddc_dce110_init { ++ struct hw_gpio_pin_reg hw_gpio_data_reg; ++ struct hw_ddc_mask hw_ddc_mask; ++ struct hw_ddc_dce110_addr hw_ddc_dce110_addr; ++}; ++ ++static const struct hw_ddc_dce110_init ++ hw_ddc_dce110_init_data[GPIO_DDC_LINE_COUNT] = { ++ /* GPIO_DDC_LINE_DDC1 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC1_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC1_A, ++ DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC1_EN, ++ DC_GPIO_DDC1_EN__DC_GPIO_DDC1DATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC1_Y, ++ DC_GPIO_DDC1_Y__DC_GPIO_DDC1DATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_MASK_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_PD_EN_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_RECV_MASK, ++ DC_GPIO_DDC1_MASK__AUX_PAD1_MODE_MASK, ++ DC_GPIO_DDC1_MASK__AUX1_POL_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC1_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC2 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC2_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC2_A, ++ DC_GPIO_DDC2_A__DC_GPIO_DDC2DATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC2_EN, ++ DC_GPIO_DDC2_EN__DC_GPIO_DDC2DATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC2_Y, ++ DC_GPIO_DDC2_Y__DC_GPIO_DDC2DATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_MASK_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_PD_EN_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_RECV_MASK, ++ DC_GPIO_DDC2_MASK__AUX_PAD2_MODE_MASK, ++ DC_GPIO_DDC2_MASK__AUX2_POL_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC2_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC3 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC3_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC3_A, ++ DC_GPIO_DDC3_A__DC_GPIO_DDC3DATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC3_EN, ++ DC_GPIO_DDC3_EN__DC_GPIO_DDC3DATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC3_Y, ++ DC_GPIO_DDC3_Y__DC_GPIO_DDC3DATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_MASK_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_PD_EN_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_RECV_MASK, ++ DC_GPIO_DDC3_MASK__AUX_PAD3_MODE_MASK, ++ DC_GPIO_DDC3_MASK__AUX3_POL_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC3_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC4 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC4_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC4_A, ++ DC_GPIO_DDC4_A__DC_GPIO_DDC4DATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC4_EN, ++ DC_GPIO_DDC4_EN__DC_GPIO_DDC4DATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC4_Y, ++ DC_GPIO_DDC4_Y__DC_GPIO_DDC4DATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_MASK_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_PD_EN_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_RECV_MASK, ++ DC_GPIO_DDC4_MASK__AUX_PAD4_MODE_MASK, ++ DC_GPIO_DDC4_MASK__AUX4_POL_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC4_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC5 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC5_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC5_A, ++ DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC5_EN, ++ DC_GPIO_DDC5_EN__DC_GPIO_DDC5DATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC5_Y, ++ DC_GPIO_DDC5_Y__DC_GPIO_DDC5DATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_MASK_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_PD_EN_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_RECV_MASK, ++ DC_GPIO_DDC5_MASK__AUX_PAD5_MODE_MASK, ++ DC_GPIO_DDC5_MASK__AUX5_POL_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC5_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC6 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC6_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC6_A, ++ DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC6_EN, ++ DC_GPIO_DDC6_EN__DC_GPIO_DDC6DATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC6_Y, ++ DC_GPIO_DDC6_Y__DC_GPIO_DDC6DATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_MASK_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_PD_EN_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_RECV_MASK, ++ DC_GPIO_DDC6_MASK__AUX_PAD6_MODE_MASK, ++ DC_GPIO_DDC6_MASK__AUX6_POL_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC6_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC_VGA */ ++ { ++ { ++ { ++ mmDC_GPIO_DDCVGA_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDCVGA_A, ++ DC_GPIO_DDCVGA_A__DC_GPIO_DDCVGADATA_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDCVGA_EN, ++ DC_GPIO_DDCVGA_EN__DC_GPIO_DDCVGADATA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDCVGA_Y, ++ DC_GPIO_DDCVGA_Y__DC_GPIO_DDCVGADATA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_MASK_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_PD_EN_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_RECV_MASK, ++ DC_GPIO_DDCVGA_MASK__AUX_PADVGA_MODE_MASK, ++ DC_GPIO_DDCVGA_MASK__AUXVGA_POL_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDCVGA_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_I2CPAD */ ++ { ++ { ++ { ++ mmDC_GPIO_I2CPAD_MASK, ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_I2CPAD_A, ++ DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK ++ }, ++ { ++ mmDC_GPIO_I2CPAD_EN, ++ DC_GPIO_I2CPAD_EN__DC_GPIO_SDA_EN_MASK ++ }, ++ { ++ mmDC_GPIO_I2CPAD_Y, ++ DC_GPIO_I2CPAD_Y__DC_GPIO_SDA_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_MASK_MASK, ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_PD_DIS_MASK, ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_RECV_MASK, ++ 0, ++ 0, ++ 0 ++ }, ++ { ++ 0 ++ } ++ } ++}; ++ ++static const struct hw_ddc_dce110_init ++ hw_ddc_dce110_init_clock[GPIO_DDC_LINE_COUNT] = { ++ /* GPIO_DDC_LINE_DDC1 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC1_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC1_A, ++ DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC1_EN, ++ DC_GPIO_DDC1_EN__DC_GPIO_DDC1CLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC1_Y, ++ DC_GPIO_DDC1_Y__DC_GPIO_DDC1CLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_MASK_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_PD_EN_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_RECV_MASK, ++ DC_GPIO_DDC1_MASK__AUX_PAD1_MODE_MASK, ++ DC_GPIO_DDC1_MASK__AUX1_POL_MASK, ++ DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC1_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC2 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC2_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC2_A, ++ DC_GPIO_DDC2_A__DC_GPIO_DDC2CLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC2_EN, ++ DC_GPIO_DDC2_EN__DC_GPIO_DDC2CLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC2_Y, ++ DC_GPIO_DDC2_Y__DC_GPIO_DDC2CLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_MASK_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_PD_EN_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_RECV_MASK, ++ DC_GPIO_DDC2_MASK__AUX_PAD2_MODE_MASK, ++ DC_GPIO_DDC2_MASK__AUX2_POL_MASK, ++ DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC2_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC3 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC3_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC3_A, ++ DC_GPIO_DDC3_A__DC_GPIO_DDC3CLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC3_EN, ++ DC_GPIO_DDC3_EN__DC_GPIO_DDC3CLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC3_Y, ++ DC_GPIO_DDC3_Y__DC_GPIO_DDC3CLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_MASK_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_PD_EN_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_RECV_MASK, ++ DC_GPIO_DDC3_MASK__AUX_PAD3_MODE_MASK, ++ DC_GPIO_DDC3_MASK__AUX3_POL_MASK, ++ DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC3_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC4 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC4_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC4_A, ++ DC_GPIO_DDC4_A__DC_GPIO_DDC4CLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC4_EN, ++ DC_GPIO_DDC4_EN__DC_GPIO_DDC4CLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC4_Y, ++ DC_GPIO_DDC4_Y__DC_GPIO_DDC4CLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_MASK_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_PD_EN_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_RECV_MASK, ++ DC_GPIO_DDC4_MASK__AUX_PAD4_MODE_MASK, ++ DC_GPIO_DDC4_MASK__AUX4_POL_MASK, ++ DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC4_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC5 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC5_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC5_A, ++ DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC5_EN, ++ DC_GPIO_DDC5_EN__DC_GPIO_DDC5CLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC5_Y, ++ DC_GPIO_DDC5_Y__DC_GPIO_DDC5CLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_MASK_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_PD_EN_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_RECV_MASK, ++ DC_GPIO_DDC5_MASK__AUX_PAD5_MODE_MASK, ++ DC_GPIO_DDC5_MASK__AUX5_POL_MASK, ++ DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC5_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC6 */ ++ { ++ { ++ { ++ mmDC_GPIO_DDC6_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDC6_A, ++ DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDC6_EN, ++ DC_GPIO_DDC6_EN__DC_GPIO_DDC6CLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDC6_Y, ++ DC_GPIO_DDC6_Y__DC_GPIO_DDC6CLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_MASK_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_PD_EN_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_RECV_MASK, ++ DC_GPIO_DDC6_MASK__AUX_PAD6_MODE_MASK, ++ DC_GPIO_DDC6_MASK__AUX6_POL_MASK, ++ DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDC6_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_DDC_VGA */ ++ { ++ { ++ { ++ mmDC_GPIO_DDCVGA_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_DDCVGA_A, ++ DC_GPIO_DDCVGA_A__DC_GPIO_DDCVGACLK_A_MASK ++ }, ++ { ++ mmDC_GPIO_DDCVGA_EN, ++ DC_GPIO_DDCVGA_EN__DC_GPIO_DDCVGACLK_EN_MASK ++ }, ++ { ++ mmDC_GPIO_DDCVGA_Y, ++ DC_GPIO_DDCVGA_Y__DC_GPIO_DDCVGACLK_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_MASK_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_PD_EN_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_RECV_MASK, ++ DC_GPIO_DDCVGA_MASK__AUX_PADVGA_MODE_MASK, ++ DC_GPIO_DDCVGA_MASK__AUXVGA_POL_MASK, ++ DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_STR_MASK ++ }, ++ { ++ mmDC_I2C_DDCVGA_SETUP ++ } ++ }, ++ /* GPIO_DDC_LINE_I2CPAD */ ++ { ++ { ++ { ++ mmDC_GPIO_I2CPAD_MASK, ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_I2CPAD_A, ++ DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK ++ }, ++ { ++ mmDC_GPIO_I2CPAD_EN, ++ DC_GPIO_I2CPAD_EN__DC_GPIO_SCL_EN_MASK ++ }, ++ { ++ mmDC_GPIO_I2CPAD_Y, ++ DC_GPIO_I2CPAD_Y__DC_GPIO_SCL_Y_MASK ++ } ++ }, ++ { ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_MASK_MASK, ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_PD_DIS_MASK, ++ DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_RECV_MASK, ++ 0, ++ 0, ++ 0 ++ }, ++ { ++ 0 ++ } ++ } ++}; ++ ++static void setup_i2c_polling( ++ struct dc_context *ctx, ++ const uint32_t addr, ++ bool enable_detect, ++ bool detect_mode) ++{ ++ uint32_t value; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ enable_detect, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_ENABLE); ++ ++ set_reg_field_value( ++ value, ++ enable_detect, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_EDID_DETECT_ENABLE); ++ ++ if (enable_detect) ++ set_reg_field_value( ++ value, ++ detect_mode, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_EDID_DETECT_MODE); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static enum gpio_result set_config( ++ struct hw_gpio_pin *ptr, ++ const struct gpio_config_data *config_data) ++{ ++ struct hw_ddc_dce110 *pin = DDC_DCE110_FROM_BASE(ptr); ++ struct hw_gpio *hw_gpio = NULL; ++ uint32_t addr; ++ uint32_t regval; ++ uint32_t ddc_data_pd_en = 0; ++ uint32_t ddc_clk_pd_en = 0; ++ uint32_t aux_pad_mode = 0; ++ ++ hw_gpio = &pin->base.base; ++ ++ if (hw_gpio == NULL) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_NULL_HANDLE; ++ } ++ ++ /* switch dual mode GPIO to I2C/AUX mode */ ++ ++ addr = hw_gpio->pin_reg.DC_GPIO_DATA_MASK.addr; ++ ++ regval = dal_read_reg(ptr->ctx, addr); ++ ++ ddc_data_pd_en = get_reg_field_value( ++ regval, ++ DC_GPIO_DDC1_MASK, ++ DC_GPIO_DDC1DATA_PD_EN); ++ ++ ddc_clk_pd_en = get_reg_field_value( ++ regval, ++ DC_GPIO_DDC1_MASK, ++ DC_GPIO_DDC1CLK_PD_EN); ++ ++ aux_pad_mode = get_reg_field_value( ++ regval, ++ DC_GPIO_DDC1_MASK, ++ AUX_PAD1_MODE); ++ ++ switch (config_data->config.ddc.type) { ++ case GPIO_DDC_CONFIG_TYPE_MODE_I2C: ++ /* On plug-in, there is a transient level on the pad ++ * which must be discharged through the internal pull-down. ++ * Enable internal pull-down, 2.5msec discharge time ++ * is required for detection of AUX mode */ ++ if (hw_gpio->base.en != GPIO_DDC_LINE_VIP_PAD) { ++ if (!ddc_data_pd_en || !ddc_clk_pd_en) { ++ set_reg_field_value( ++ regval, ++ 1, ++ DC_GPIO_DDC1_MASK, ++ DC_GPIO_DDC1DATA_PD_EN); ++ ++ set_reg_field_value( ++ regval, ++ 1, ++ DC_GPIO_DDC1_MASK, ++ DC_GPIO_DDC1CLK_PD_EN); ++ ++ dal_write_reg(ptr->ctx, addr, regval); ++ ++ if (config_data->type == ++ GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) ++ /* should not affect normal I2C R/W */ ++ /* [anaumov] in DAL2, there was ++ * dc_service_delay_in_microseconds(2500); */ ++ dc_service_sleep_in_milliseconds(ptr->ctx, 3); ++ } ++ } else { ++ uint32_t reg2 = regval; ++ uint32_t sda_pd_dis = 0; ++ uint32_t scl_pd_dis = 0; ++ ++ sda_pd_dis = get_reg_field_value( ++ reg2, ++ DC_GPIO_I2CPAD_MASK, ++ DC_GPIO_SDA_PD_DIS); ++ ++ scl_pd_dis = get_reg_field_value( ++ reg2, ++ DC_GPIO_I2CPAD_MASK, ++ DC_GPIO_SCL_PD_DIS); ++ ++ if (sda_pd_dis) { ++ sda_pd_dis = 0; ++ ++ dal_write_reg(ptr->ctx, addr, reg2); ++ ++ if (config_data->type == ++ GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) ++ /* should not affect normal I2C R/W */ ++ /* [anaumov] in DAL2, there was ++ * dc_service_delay_in_microseconds(2500); */ ++ dc_service_sleep_in_milliseconds(ptr->ctx, 3); ++ } ++ ++ if (!scl_pd_dis) { ++ scl_pd_dis = 1; ++ ++ dal_write_reg(ptr->ctx, addr, reg2); ++ ++ if (config_data->type == ++ GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) ++ /* should not affect normal I2C R/W */ ++ /* [anaumov] in DAL2, there was ++ * dc_service_delay_in_microseconds(2500); */ ++ dc_service_sleep_in_milliseconds(ptr->ctx, 3); ++ } ++ } ++ ++ if (aux_pad_mode) { ++ /* let pins to get de-asserted ++ * before setting pad to I2C mode */ ++ if (config_data->config.ddc.data_en_bit_present || ++ config_data->config.ddc.clock_en_bit_present) ++ /* [anaumov] in DAL2, there was ++ * dc_service_delay_in_microseconds(2000); */ ++ dc_service_sleep_in_milliseconds(ptr->ctx, 2); ++ ++ /* set the I2C pad mode */ ++ /* read the register again, ++ * some bits may have been changed */ ++ regval = dal_read_reg(ptr->ctx, addr); ++ ++ set_reg_field_value( ++ regval, ++ 0, ++ DC_GPIO_DDC1_MASK, ++ AUX_PAD1_MODE); ++ ++ dal_write_reg(ptr->ctx, addr, regval); ++ } ++ ++ return GPIO_RESULT_OK; ++ case GPIO_DDC_CONFIG_TYPE_MODE_AUX: ++ /* set the AUX pad mode */ ++ if (!aux_pad_mode) { ++ set_reg_field_value( ++ regval, ++ 1, ++ DC_GPIO_DDC1_MASK, ++ AUX_PAD1_MODE); ++ ++ dal_write_reg(ptr->ctx, addr, regval); ++ } ++ ++ return GPIO_RESULT_OK; ++ case GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT: ++ if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && ++ (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { ++ setup_i2c_polling( ++ ptr->ctx, ADDR_DDC_SETUP, 1, 0); ++ return GPIO_RESULT_OK; ++ } ++ break; ++ case GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT: ++ if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && ++ (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { ++ setup_i2c_polling( ++ ptr->ctx, ADDR_DDC_SETUP, 1, 1); ++ return GPIO_RESULT_OK; ++ } ++ break; ++ case GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING: ++ if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && ++ (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { ++ setup_i2c_polling( ++ ptr->ctx, ADDR_DDC_SETUP, 0, 0); ++ return GPIO_RESULT_OK; ++ } ++ break; ++ } ++ ++ BREAK_TO_DEBUGGER(); ++ ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++} ++ ++static const struct hw_gpio_pin_funcs funcs = { ++ .destroy = destroy, ++ .open = dal_hw_ddc_open, ++ .get_value = dal_hw_gpio_get_value, ++ .set_value = dal_hw_gpio_set_value, ++ .set_config = set_config, ++ .change_mode = dal_hw_gpio_change_mode, ++ .close = dal_hw_gpio_close, ++}; ++ ++ ++static bool construct( ++ struct hw_ddc_dce110 *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ const struct hw_ddc_dce110_init *init; ++ ++ if ((en < GPIO_DDC_LINE_MIN) || (en > GPIO_DDC_LINE_MAX)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if (!dal_hw_ddc_construct(&pin->base, id, en, ctx)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ pin->base.base.base.funcs = &funcs; ++ ++ switch (id) { ++ case GPIO_ID_DDC_DATA: ++ init = hw_ddc_dce110_init_data + en; ++ ++ pin->base.base.pin_reg = init->hw_gpio_data_reg; ++ pin->base.mask = init->hw_ddc_mask; ++ pin->addr = init->hw_ddc_dce110_addr; ++ ++ return true; ++ case GPIO_ID_DDC_CLOCK: ++ init = hw_ddc_dce110_init_clock + en; ++ ++ pin->base.base.pin_reg = init->hw_gpio_data_reg; ++ pin->base.mask = init->hw_ddc_mask; ++ pin->addr = init->hw_ddc_dce110_addr; ++ ++ return true; ++ default: ++ ASSERT_CRITICAL(false); ++ } ++ ++ return false; ++} ++ ++struct hw_gpio_pin *dal_hw_ddc_dce110_create( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ struct hw_ddc_dce110 *pin = dc_service_alloc(ctx, sizeof(struct hw_ddc_dce110)); ++ ++ if (!pin) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(pin, id, en, ctx)) ++ return &pin->base.base.base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(ctx, pin); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.h +new file mode 100644 +index 0000000..6830369 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_ddc_dce110.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_DDC_DCE110_H__ ++#define __DAL_HW_DDC_DCE110_H__ ++ ++struct hw_ddc_dce110_addr { ++ uint32_t dc_i2c_ddc_setup; ++}; ++ ++struct hw_ddc_dce110 { ++ struct hw_ddc base; ++ struct hw_ddc_dce110_addr addr; ++}; ++ ++#define DDC_DCE110_FROM_BASE(ddc_base) \ ++ container_of((HW_DDC_FROM_BASE(ddc_base)), struct hw_ddc_dce110, base) ++ ++struct hw_gpio_pin *dal_hw_ddc_dce110_create( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.c +new file mode 100644 +index 0000000..85644c5 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.c +@@ -0,0 +1,84 @@ ++/* ++ * Copyright 2013-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "dal_services.h" ++#include "include/gpio_types.h" ++#include "../hw_factory.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "../hw_gpio_pin.h" ++#include "../hw_gpio.h" ++#include "../hw_ddc.h" ++#include "../hw_hpd.h" ++ ++#include "hw_factory_dce110.h" ++#include "hw_hpd_dce110.h" ++#include "hw_ddc_dce110.h" ++ ++/* fucntion table */ ++static const struct hw_factory_funcs funcs = { ++ .create_dvo = NULL, ++ .create_ddc_data = dal_hw_ddc_dce110_create, ++ .create_ddc_clock = dal_hw_ddc_dce110_create, ++ .create_generic = NULL, ++ .create_hpd = dal_hw_hpd_dce110_create, ++ .create_gpio_pad = NULL, ++ .create_sync = NULL, ++ .create_gsl = NULL, ++}; ++ ++/* ++ * dal_hw_factory_dce110_init ++ * ++ * @brief ++ * Initialize HW factory function pointers and pin info ++ * ++ * @param ++ * struct hw_factory *factory - [out] struct of function pointers ++ */ ++void dal_hw_factory_dce110_init(struct hw_factory *factory) ++{ ++ /*TODO check ASIC CAPs*/ ++ factory->number_of_pins[GPIO_ID_DVO1] = 24; ++ factory->number_of_pins[GPIO_ID_DVO12] = 2; ++ factory->number_of_pins[GPIO_ID_DVO24] = 1; ++ factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; ++ factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; ++ factory->number_of_pins[GPIO_ID_GENERIC] = 7; ++ factory->number_of_pins[GPIO_ID_HPD] = 6; ++ factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; ++ factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; ++ factory->number_of_pins[GPIO_ID_SYNC] = 2; ++ factory->number_of_pins[GPIO_ID_GSL] = 4; ++ ++ factory->funcs = &funcs; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.h +new file mode 100644 +index 0000000..ecf06ed +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_factory_dce110.h +@@ -0,0 +1,32 @@ ++/* ++ * Copyright 2013-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_FACTORY_DCE110_H__ ++#define __DAL_HW_FACTORY_DCE110_H__ ++ ++/* Initialize HW factory function pointers and pin info */ ++void dal_hw_factory_dce110_init(struct hw_factory *factory); ++ ++#endif /* __DAL_HW_FACTORY_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.c +new file mode 100644 +index 0000000..34405e9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.c +@@ -0,0 +1,367 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "../hw_gpio_pin.h" ++#include "../hw_gpio.h" ++#include "../hw_hpd.h" ++ ++/* ++ * Header of this unit ++ */ ++#include "hw_hpd_dce110.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++/* ++ * This unit ++ */ ++ ++static void destruct( ++ struct hw_hpd_dce110 *pin) ++{ ++ dal_hw_hpd_destruct(&pin->base); ++} ++ ++static void destroy( ++ struct hw_gpio_pin **ptr) ++{ ++ struct hw_hpd_dce110 *pin = HPD_DCE110_FROM_BASE(*ptr); ++ ++ destruct(pin); ++ ++ dc_service_free((*ptr)->ctx, pin); ++ ++ *ptr = NULL; ++} ++ ++struct hw_gpio_generic_dce110_init { ++ struct hw_gpio_pin_reg hw_gpio_data_reg; ++ struct hw_hpd_dce110_addr addr; ++}; ++ ++static const struct hw_gpio_generic_dce110_init ++ hw_gpio_generic_dce110_init[GPIO_HPD_COUNT] = { ++ /* GPIO_HPD_1 */ ++ { ++ { ++ { ++ mmDC_GPIO_HPD_MASK, ++ DC_GPIO_HPD_MASK__DC_GPIO_HPD1_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_A, ++ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_EN, ++ DC_GPIO_HPD_EN__DC_GPIO_HPD1_EN_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_Y, ++ DC_GPIO_HPD_Y__DC_GPIO_HPD1_Y_MASK ++ } ++ }, ++ { ++ mmHPD0_DC_HPD_INT_STATUS, ++ mmHPD0_DC_HPD_TOGGLE_FILT_CNTL ++ } ++ }, ++ /* GPIO_HPD_2 */ ++ { ++ { ++ { ++ mmDC_GPIO_HPD_MASK, ++ DC_GPIO_HPD_MASK__DC_GPIO_HPD2_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_A, ++ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_EN, ++ DC_GPIO_HPD_EN__DC_GPIO_HPD2_EN_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_Y, ++ DC_GPIO_HPD_Y__DC_GPIO_HPD2_Y_MASK ++ } ++ }, ++ { ++ mmHPD1_DC_HPD_INT_STATUS, ++ mmHPD1_DC_HPD_TOGGLE_FILT_CNTL ++ } ++ }, ++ /* GPIO_HPD_3 */ ++ { ++ { ++ { ++ mmDC_GPIO_HPD_MASK, ++ DC_GPIO_HPD_MASK__DC_GPIO_HPD3_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_A, ++ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_EN, ++ DC_GPIO_HPD_EN__DC_GPIO_HPD3_EN_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_Y, ++ DC_GPIO_HPD_Y__DC_GPIO_HPD3_Y_MASK ++ } ++ }, ++ { ++ mmHPD2_DC_HPD_INT_STATUS, ++ mmHPD2_DC_HPD_TOGGLE_FILT_CNTL ++ } ++ }, ++ /* GPIO_HPD_4 */ ++ { ++ { ++ { ++ mmDC_GPIO_HPD_MASK, ++ DC_GPIO_HPD_MASK__DC_GPIO_HPD4_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_A, ++ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_EN, ++ DC_GPIO_HPD_EN__DC_GPIO_HPD4_EN_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_Y, ++ DC_GPIO_HPD_Y__DC_GPIO_HPD4_Y_MASK ++ } ++ }, ++ { ++ mmHPD3_DC_HPD_INT_STATUS, ++ mmHPD3_DC_HPD_TOGGLE_FILT_CNTL ++ } ++ }, ++ /* GPIO_HPD_5 */ ++ { ++ { ++ { ++ mmDC_GPIO_HPD_MASK, ++ DC_GPIO_HPD_MASK__DC_GPIO_HPD5_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_A, ++ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_EN, ++ DC_GPIO_HPD_EN__DC_GPIO_HPD5_EN_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_Y, ++ DC_GPIO_HPD_Y__DC_GPIO_HPD5_Y_MASK ++ } ++ }, ++ { ++ mmHPD4_DC_HPD_INT_STATUS, ++ mmHPD4_DC_HPD_TOGGLE_FILT_CNTL ++ } ++ }, ++ /* GPIO_HPD_6 */ ++ { ++ { ++ { ++ mmDC_GPIO_HPD_MASK, ++ DC_GPIO_HPD_MASK__DC_GPIO_HPD6_MASK_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_A, ++ DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_EN, ++ DC_GPIO_HPD_EN__DC_GPIO_HPD6_EN_MASK ++ }, ++ { ++ mmDC_GPIO_HPD_Y, ++ DC_GPIO_HPD_Y__DC_GPIO_HPD6_Y_MASK ++ } ++ }, ++ { ++ mmHPD5_DC_HPD_INT_STATUS, ++ mmHPD5_DC_HPD_TOGGLE_FILT_CNTL ++ } ++ } ++}; ++ ++static enum gpio_result get_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t *value) ++{ ++ struct hw_hpd_dce110 *pin = HPD_DCE110_FROM_BASE(ptr); ++ ++ /* in Interrupt mode we ask for SENSE bit */ ++ ++ if (ptr->mode == GPIO_MODE_INTERRUPT) { ++ uint32_t regval; ++ uint32_t hpd_delayed = 0; ++ uint32_t hpd_sense = 0; ++ ++ regval = dal_read_reg( ++ ptr->ctx, ++ pin->addr.DC_HPD_INT_STATUS); ++ ++ hpd_delayed = get_reg_field_value( ++ regval, ++ DC_HPD_INT_STATUS, ++ DC_HPD_SENSE_DELAYED); ++ ++ hpd_sense = get_reg_field_value( ++ regval, ++ DC_HPD_INT_STATUS, ++ DC_HPD_SENSE); ++ ++ *value = hpd_delayed; ++ return GPIO_RESULT_OK; ++ } ++ ++ /* in any other modes, operate as normal GPIO */ ++ ++ return dal_hw_gpio_get_value(ptr, value); ++} ++ ++static enum gpio_result set_config( ++ struct hw_gpio_pin *ptr, ++ const struct gpio_config_data *config_data) ++{ ++ struct hw_hpd_dce110 *pin = HPD_DCE110_FROM_BASE(ptr); ++ ++ if (!config_data) ++ return GPIO_RESULT_INVALID_DATA; ++ ++ { ++ uint32_t value; ++ ++ value = dal_read_reg( ++ ptr->ctx, ++ pin->addr.DC_HPD_TOGGLE_FILT_CNTL); ++ ++ set_reg_field_value( ++ value, ++ config_data->config.hpd.delay_on_connect / 10, ++ DC_HPD_TOGGLE_FILT_CNTL, ++ DC_HPD_CONNECT_INT_DELAY); ++ ++ set_reg_field_value( ++ value, ++ config_data->config.hpd.delay_on_disconnect / 10, ++ DC_HPD_TOGGLE_FILT_CNTL, ++ DC_HPD_DISCONNECT_INT_DELAY); ++ ++ dal_write_reg( ++ ptr->ctx, ++ pin->addr.DC_HPD_TOGGLE_FILT_CNTL, ++ value); ++ ++ } ++ ++ return GPIO_RESULT_OK; ++} ++ ++static const struct hw_gpio_pin_funcs funcs = { ++ .destroy = destroy, ++ .open = dal_hw_gpio_open, ++ .get_value = get_value, ++ .set_value = dal_hw_gpio_set_value, ++ .set_config = set_config, ++ .change_mode = dal_hw_gpio_change_mode, ++ .close = dal_hw_gpio_close, ++}; ++ ++static bool construct( ++ struct hw_hpd_dce110 *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ const struct hw_gpio_generic_dce110_init *init; ++ ++ if (id != GPIO_ID_HPD) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if ((en < GPIO_HPD_MIN) || (en > GPIO_HPD_MAX)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if (!dal_hw_hpd_construct(&pin->base, id, en, ctx)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ pin->base.base.base.funcs = &funcs; ++ ++ init = hw_gpio_generic_dce110_init + en; ++ ++ pin->base.base.pin_reg = init->hw_gpio_data_reg; ++ ++ pin->addr = init->addr; ++ ++ return true; ++} ++ ++struct hw_gpio_pin *dal_hw_hpd_dce110_create( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ struct hw_hpd_dce110 *pin = dc_service_alloc(ctx, sizeof(struct hw_hpd_dce110)); ++ ++ if (!pin) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(pin, id, en, ctx)) ++ return &pin->base.base.base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(ctx, pin); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.h +new file mode 100644 +index 0000000..d032f4b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_hpd_dce110.h +@@ -0,0 +1,47 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_HPD_DCE110_H__ ++#define __DAL_HW_HPD_DCE110_H__ ++ ++struct hw_hpd_dce110_addr { ++ uint32_t DC_HPD_INT_STATUS; ++ uint32_t DC_HPD_TOGGLE_FILT_CNTL; ++}; ++ ++struct hw_hpd_dce110 { ++ struct hw_hpd base; ++ struct hw_hpd_dce110_addr addr; ++}; ++ ++#define HPD_DCE110_FROM_BASE(hpd_base) \ ++ container_of((HW_HPD_FROM_BASE(hpd_base)), struct hw_hpd_dce110, base) ++ ++struct hw_gpio_pin *dal_hw_hpd_dce110_create( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ ++#endif /*__DAL_HW_HPD_DCE110_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.c +new file mode 100644 +index 0000000..05ac0b2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.c +@@ -0,0 +1,440 @@ ++/* ++ * Copyright 2013-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "dal_services.h" ++#include "include/gpio_types.h" ++#include "../hw_translate.h" ++ ++/* ++ * Header of this unit ++ */ ++#include "hw_translate_dce110.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++#include "../hw_gpio_pin.h" ++#include "../hw_dvo.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++static bool offset_to_id( ++ uint32_t offset, ++ uint32_t mask, ++ enum gpio_id *id, ++ uint32_t *en) ++{ ++ switch (offset) { ++ /* DVO */ ++ case mmDC_GPIO_DVODATA_A: ++ switch (mask) { ++ case BUNDLE_A_MASK: ++ *id = GPIO_ID_DVO12; ++ *en = GPIO_DVO12_A; ++ return true; ++ case BUNDLE_B_MASK: ++ *id = GPIO_ID_DVO12; ++ *en = GPIO_DVO12_B; ++ return true; ++ case DC_GPIO_DVODATA_A__DC_GPIO_DVODATA_A_MASK: ++ *id = GPIO_ID_DVO24; ++ *en = 0; ++ return true; ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ break; ++ /* GENERIC */ ++ case mmDC_GPIO_GENERIC_A: ++ *id = GPIO_ID_GENERIC; ++ switch (mask) { ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: ++ *en = GPIO_GENERIC_A; ++ return true; ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: ++ *en = GPIO_GENERIC_B; ++ return true; ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: ++ *en = GPIO_GENERIC_C; ++ return true; ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: ++ *en = GPIO_GENERIC_D; ++ return true; ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: ++ *en = GPIO_GENERIC_E; ++ return true; ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: ++ *en = GPIO_GENERIC_F; ++ return true; ++ case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: ++ *en = GPIO_GENERIC_G; ++ return true; ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ break; ++ /* HPD */ ++ case mmDC_GPIO_HPD_A: ++ *id = GPIO_ID_HPD; ++ switch (mask) { ++ case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: ++ *en = GPIO_HPD_1; ++ return true; ++ case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: ++ *en = GPIO_HPD_2; ++ return true; ++ case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: ++ *en = GPIO_HPD_3; ++ return true; ++ case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: ++ *en = GPIO_HPD_4; ++ return true; ++ case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: ++ *en = GPIO_HPD_5; ++ return true; ++ case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: ++ *en = GPIO_HPD_6; ++ return true; ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ break; ++ /* SYNCA */ ++ case mmDC_GPIO_SYNCA_A: ++ *id = GPIO_ID_SYNC; ++ switch (mask) { ++ case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: ++ *en = GPIO_SYNC_HSYNC_A; ++ return true; ++ case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: ++ *en = GPIO_SYNC_VSYNC_A; ++ return true; ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ break; ++ /* mmDC_GPIO_GENLK_MASK */ ++ case mmDC_GPIO_GENLK_A: ++ *id = GPIO_ID_GSL; ++ switch (mask) { ++ case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: ++ *en = GPIO_GSL_GENLOCK_CLOCK; ++ return true; ++ case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: ++ *en = GPIO_GSL_GENLOCK_VSYNC; ++ return true; ++ case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: ++ *en = GPIO_GSL_SWAPLOCK_A; ++ return true; ++ case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: ++ *en = GPIO_GSL_SWAPLOCK_B; ++ return true; ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ break; ++ /* DDC */ ++ /* we don't care about the GPIO_ID for DDC ++ * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK ++ * directly in the create method */ ++ case mmDC_GPIO_DDC1_A: ++ *en = GPIO_DDC_LINE_DDC1; ++ return true; ++ case mmDC_GPIO_DDC2_A: ++ *en = GPIO_DDC_LINE_DDC2; ++ return true; ++ case mmDC_GPIO_DDC3_A: ++ *en = GPIO_DDC_LINE_DDC3; ++ return true; ++ case mmDC_GPIO_DDC4_A: ++ *en = GPIO_DDC_LINE_DDC4; ++ return true; ++ case mmDC_GPIO_DDC5_A: ++ *en = GPIO_DDC_LINE_DDC5; ++ return true; ++ case mmDC_GPIO_DDC6_A: ++ *en = GPIO_DDC_LINE_DDC6; ++ return true; ++ case mmDC_GPIO_DDCVGA_A: ++ *en = GPIO_DDC_LINE_DDC_VGA; ++ return true; ++ /* GPIO_I2CPAD */ ++ case mmDC_GPIO_I2CPAD_A: ++ *en = GPIO_DDC_LINE_I2C_PAD; ++ return true; ++ /* Not implemented */ ++ case mmDC_GPIO_PWRSEQ_A: ++ case mmDC_GPIO_PAD_STRENGTH_1: ++ case mmDC_GPIO_PAD_STRENGTH_2: ++ case mmDC_GPIO_DEBUG: ++ return false; ++ /* UNEXPECTED */ ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++} ++ ++static bool id_to_offset( ++ enum gpio_id id, ++ uint32_t en, ++ struct gpio_pin_info *info) ++{ ++ bool result = true; ++ ++ switch (id) { ++ case GPIO_ID_DVO12: ++ info->offset = mmDC_GPIO_DVODATA_A; ++ switch (en) { ++ case GPIO_DVO12_A: ++ info->mask = BUNDLE_A_MASK; ++ break; ++ case GPIO_DVO12_B: ++ info->mask = BUNDLE_B_MASK; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_DDC_DATA: ++ info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; ++ switch (en) { ++ case GPIO_DDC_LINE_DDC1: ++ info->offset = mmDC_GPIO_DDC1_A; ++ break; ++ case GPIO_DDC_LINE_DDC2: ++ info->offset = mmDC_GPIO_DDC2_A; ++ break; ++ case GPIO_DDC_LINE_DDC3: ++ info->offset = mmDC_GPIO_DDC3_A; ++ break; ++ case GPIO_DDC_LINE_DDC4: ++ info->offset = mmDC_GPIO_DDC4_A; ++ break; ++ case GPIO_DDC_LINE_DDC5: ++ info->offset = mmDC_GPIO_DDC5_A; ++ break; ++ case GPIO_DDC_LINE_DDC6: ++ info->offset = mmDC_GPIO_DDC6_A; ++ break; ++ case GPIO_DDC_LINE_DDC_VGA: ++ info->offset = mmDC_GPIO_DDCVGA_A; ++ break; ++ case GPIO_DDC_LINE_I2C_PAD: ++ info->offset = mmDC_GPIO_I2CPAD_A; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_DDC_CLOCK: ++ info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; ++ switch (en) { ++ case GPIO_DDC_LINE_DDC1: ++ info->offset = mmDC_GPIO_DDC1_A; ++ break; ++ case GPIO_DDC_LINE_DDC2: ++ info->offset = mmDC_GPIO_DDC2_A; ++ break; ++ case GPIO_DDC_LINE_DDC3: ++ info->offset = mmDC_GPIO_DDC3_A; ++ break; ++ case GPIO_DDC_LINE_DDC4: ++ info->offset = mmDC_GPIO_DDC4_A; ++ break; ++ case GPIO_DDC_LINE_DDC5: ++ info->offset = mmDC_GPIO_DDC5_A; ++ break; ++ case GPIO_DDC_LINE_DDC6: ++ info->offset = mmDC_GPIO_DDC6_A; ++ break; ++ case GPIO_DDC_LINE_DDC_VGA: ++ info->offset = mmDC_GPIO_DDCVGA_A; ++ break; ++ case GPIO_DDC_LINE_I2C_PAD: ++ info->offset = mmDC_GPIO_I2CPAD_A; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_GENERIC: ++ info->offset = mmDC_GPIO_GENERIC_A; ++ switch (en) { ++ case GPIO_GENERIC_A: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; ++ break; ++ case GPIO_GENERIC_B: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; ++ break; ++ case GPIO_GENERIC_C: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; ++ break; ++ case GPIO_GENERIC_D: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; ++ break; ++ case GPIO_GENERIC_E: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; ++ break; ++ case GPIO_GENERIC_F: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; ++ break; ++ case GPIO_GENERIC_G: ++ info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_HPD: ++ info->offset = mmDC_GPIO_HPD_A; ++ switch (en) { ++ case GPIO_HPD_1: ++ info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; ++ break; ++ case GPIO_HPD_2: ++ info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; ++ break; ++ case GPIO_HPD_3: ++ info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; ++ break; ++ case GPIO_HPD_4: ++ info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; ++ break; ++ case GPIO_HPD_5: ++ info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; ++ break; ++ case GPIO_HPD_6: ++ info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_SYNC: ++ switch (en) { ++ case GPIO_SYNC_HSYNC_A: ++ info->offset = mmDC_GPIO_SYNCA_A; ++ info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; ++ break; ++ case GPIO_SYNC_VSYNC_A: ++ info->offset = mmDC_GPIO_SYNCA_A; ++ info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; ++ break; ++ case GPIO_SYNC_HSYNC_B: ++ case GPIO_SYNC_VSYNC_B: ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_GSL: ++ switch (en) { ++ case GPIO_GSL_GENLOCK_CLOCK: ++ info->offset = mmDC_GPIO_GENLK_A; ++ info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; ++ break; ++ case GPIO_GSL_GENLOCK_VSYNC: ++ info->offset = mmDC_GPIO_GENLK_A; ++ info->mask = ++ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; ++ break; ++ case GPIO_GSL_SWAPLOCK_A: ++ info->offset = mmDC_GPIO_GENLK_A; ++ info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; ++ break; ++ case GPIO_GSL_SWAPLOCK_B: ++ info->offset = mmDC_GPIO_GENLK_A; ++ info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ break; ++ case GPIO_ID_DVO24: ++ info->offset = mmDC_GPIO_DVODATA_A; ++ info->mask = DC_GPIO_DVODATA_A__DC_GPIO_DVODATA_A_MASK; ++ break; ++ case GPIO_ID_DVO1: ++ case GPIO_ID_VIP_PAD: ++ default: ++ ASSERT_CRITICAL(false); ++ result = false; ++ } ++ ++ if (result) { ++ info->offset_y = info->offset + 2; ++ info->offset_en = info->offset + 1; ++ info->offset_mask = info->offset - 1; ++ ++ info->mask_y = info->mask; ++ info->mask_en = info->mask; ++ info->mask_mask = info->mask; ++ } ++ ++ return result; ++} ++ ++/* function table */ ++static const struct hw_translate_funcs funcs = { ++ .offset_to_id = offset_to_id, ++ .id_to_offset = id_to_offset, ++}; ++ ++/* ++ * dal_hw_translate_dce110_init ++ * ++ * @brief ++ * Initialize Hw translate function pointers. ++ * ++ * @param ++ * struct hw_translate *tr - [out] struct of function pointers ++ * ++ */ ++void dal_hw_translate_dce110_init(struct hw_translate *tr) ++{ ++ tr->funcs = &funcs; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.h +new file mode 100644 +index 0000000..4d16e09 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce110/hw_translate_dce110.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright 2013-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_TRANSLATE_DCE110_H__ ++#define __DAL_HW_TRANSLATE_DCE110_H__ ++ ++struct hw_translate; ++ ++/* Initialize Hw translate function pointers */ ++void dal_hw_translate_dce110_init(struct hw_translate *tr); ++ ++#endif /* __DAL_HW_TRANSLATE_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/ddc.c b/drivers/gpu/drm/amd/dal/dc/gpio/ddc.c +new file mode 100644 +index 0000000..548b1cf +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/ddc.c +@@ -0,0 +1,290 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/gpio_interface.h" ++#include "include/ddc_interface.h" ++#include "include/gpio_service_interface.h" ++#include "hw_gpio_pin.h" ++#include "hw_translate.h" ++#include "hw_factory.h" ++#include "gpio_service.h" ++#include "gpio.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "ddc.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++enum gpio_result dal_ddc_open( ++ struct ddc *ddc, ++ enum gpio_mode mode, ++ enum gpio_ddc_config_type config_type) ++{ ++ enum gpio_result result; ++ ++ struct gpio_ddc_open_options data_options; ++ struct gpio_ddc_open_options clock_options; ++ struct gpio_config_data config_data; ++ ++ result = dal_gpio_open_ex(ddc->pin_data, mode, &data_options); ++ ++ if (result != GPIO_RESULT_OK) { ++ BREAK_TO_DEBUGGER(); ++ return result; ++ } ++ ++ result = dal_gpio_open_ex(ddc->pin_clock, mode, &clock_options); ++ ++ if (result != GPIO_RESULT_OK) { ++ BREAK_TO_DEBUGGER(); ++ goto failure; ++ } ++ ++ /* DDC clock and data pins should belong ++ * to the same DDC block id, ++ * we use the data pin to set the pad mode. */ ++ ++ if (mode == GPIO_MODE_INPUT) ++ /* this is from detect_sink_type, ++ * we need extra delay there */ ++ config_data.type = GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE; ++ else ++ config_data.type = GPIO_CONFIG_TYPE_DDC; ++ ++ config_data.config.ddc.type = config_type; ++ config_data.config.ddc.data_en_bit_present = ++ data_options.en_bit_present; ++ config_data.config.ddc.clock_en_bit_present = ++ clock_options.en_bit_present; ++ ++ result = dal_gpio_set_config(ddc->pin_data, &config_data); ++ ++ if (result == GPIO_RESULT_OK) ++ return result; ++ ++ BREAK_TO_DEBUGGER(); ++ ++ dal_gpio_close(ddc->pin_clock); ++ ++failure: ++ dal_gpio_close(ddc->pin_data); ++ ++ return result; ++} ++ ++enum gpio_result dal_ddc_get_clock( ++ const struct ddc *ddc, ++ uint32_t *value) ++{ ++ return dal_gpio_get_value(ddc->pin_clock, value); ++} ++ ++enum gpio_result dal_ddc_set_clock( ++ const struct ddc *ddc, ++ uint32_t value) ++{ ++ return dal_gpio_set_value(ddc->pin_clock, value); ++} ++ ++enum gpio_result dal_ddc_get_data( ++ const struct ddc *ddc, ++ uint32_t *value) ++{ ++ return dal_gpio_get_value(ddc->pin_data, value); ++} ++ ++enum gpio_result dal_ddc_set_data( ++ const struct ddc *ddc, ++ uint32_t value) ++{ ++ return dal_gpio_set_value(ddc->pin_data, value); ++} ++ ++enum gpio_result dal_ddc_change_mode( ++ struct ddc *ddc, ++ enum gpio_mode mode) ++{ ++ enum gpio_result result; ++ ++ enum gpio_mode original_mode = ++ dal_gpio_get_mode(ddc->pin_data); ++ ++ result = dal_gpio_change_mode(ddc->pin_data, mode); ++ ++ /* [anaumov] DAL2 code returns GPIO_RESULT_NON_SPECIFIC_ERROR ++ * in case of failures; ++ * set_mode() is so that, in case of failure, ++ * we must explicitly set original mode */ ++ ++ if (result != GPIO_RESULT_OK) ++ goto failure; ++ ++ result = dal_gpio_change_mode(ddc->pin_clock, mode); ++ ++ if (result == GPIO_RESULT_OK) ++ return result; ++ ++ dal_gpio_change_mode(ddc->pin_clock, original_mode); ++ ++failure: ++ dal_gpio_change_mode(ddc->pin_data, original_mode); ++ ++ return result; ++} ++ ++bool dal_ddc_is_hw_supported( ++ const struct ddc *ddc) ++{ ++ return ddc->hw_info.hw_supported; ++} ++ ++enum gpio_ddc_line dal_ddc_get_line( ++ const struct ddc *ddc) ++{ ++ return (enum gpio_ddc_line)dal_gpio_get_enum(ddc->pin_data); ++} ++ ++bool dal_ddc_check_line_aborted( ++ const struct ddc *self) ++{ ++ /* No arbitration with VBIOS is performed since DCE 6.0 */ ++ ++ return false; ++} ++ ++enum gpio_result dal_ddc_set_config( ++ struct ddc *ddc, ++ enum gpio_ddc_config_type config_type) ++{ ++ struct gpio_config_data config_data; ++ ++ config_data.type = GPIO_CONFIG_TYPE_DDC; ++ ++ config_data.config.ddc.type = config_type; ++ config_data.config.ddc.data_en_bit_present = false; ++ config_data.config.ddc.clock_en_bit_present = false; ++ ++ return dal_gpio_set_config(ddc->pin_data, &config_data); ++} ++ ++void dal_ddc_close( ++ struct ddc *ddc) ++{ ++ dal_gpio_close(ddc->pin_clock); ++ dal_gpio_close(ddc->pin_data); ++} ++ ++/* ++ * @brief ++ * Creation and destruction ++ */ ++ ++struct ddc *dal_gpio_create_ddc( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask, ++ struct gpio_ddc_hw_info *info) ++{ ++ enum gpio_id id; ++ uint32_t en; ++ struct ddc *ddc; ++ ++ if (!service->translate.funcs->offset_to_id(offset, mask, &id, &en)) ++ return NULL; ++ ++ ddc = dc_service_alloc(service->ctx, sizeof(struct ddc)); ++ ++ if (!ddc) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ ddc->pin_data = dal_gpio_service_create_gpio_ex( ++ service, GPIO_ID_DDC_DATA, en, GPIO_PIN_OUTPUT_STATE_DEFAULT); ++ ++ if (!ddc->pin_data) { ++ BREAK_TO_DEBUGGER(); ++ goto failure_1; ++ } ++ ++ ddc->pin_clock = dal_gpio_service_create_gpio_ex( ++ service, GPIO_ID_DDC_CLOCK, en, GPIO_PIN_OUTPUT_STATE_DEFAULT); ++ ++ if (!ddc->pin_clock) { ++ BREAK_TO_DEBUGGER(); ++ goto failure_2; ++ } ++ ++ ddc->hw_info = *info; ++ ++ ddc->ctx = service->ctx; ++ ++ return ddc; ++ ++failure_2: ++ dal_gpio_service_destroy_gpio(&ddc->pin_data); ++ ++failure_1: ++ dc_service_free(service->ctx, ddc); ++ ++ return NULL; ++} ++ ++static void destruct(struct ddc *ddc) ++{ ++ dal_ddc_close(ddc); ++ dal_gpio_service_destroy_gpio(&ddc->pin_data); ++ dal_gpio_service_destroy_gpio(&ddc->pin_clock); ++ ++} ++ ++void dal_gpio_destroy_ddc( ++ struct ddc **ddc) ++{ ++ if (!ddc || !*ddc) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ destruct(*ddc); ++ dc_service_free((*ddc)->ctx, *ddc); ++ ++ *ddc = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/ddc.h b/drivers/gpu/drm/amd/dal/dc/gpio/ddc.h +new file mode 100644 +index 0000000..2631571 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/ddc.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DDC_H__ ++#define __DAL_DDC_H__ ++ ++struct ddc { ++ struct gpio *pin_data; ++ struct gpio *pin_clock; ++ struct gpio_ddc_hw_info hw_info; ++ struct dc_context *ctx; ++}; ++ ++struct ddc *dal_gpio_create_ddc( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask, ++ struct gpio_ddc_hw_info *info); ++ ++void dal_gpio_destroy_ddc( ++ struct ddc **ddc); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dvo.c b/drivers/gpu/drm/amd/dal/dc/gpio/dvo.c +new file mode 100644 +index 0000000..a237d25 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dvo.c +@@ -0,0 +1,138 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_interface.h" ++#include "include/dvo_interface.h" ++#include "include/gpio_service_interface.h" ++#include "hw_gpio_pin.h" ++#include "hw_translate.h" ++#include "hw_factory.h" ++#include "gpio_service.h" ++#include "gpio.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "dvo.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++enum gpio_result dal_dvo_open( ++ struct dvo *dvo, ++ enum gpio_mode mode) ++{ ++ return dal_gpio_open(dvo->pin, mode); ++} ++ ++enum gpio_result dal_dvo_get_value( ++ const struct dvo *dvo, ++ uint32_t *value) ++{ ++ return dal_gpio_get_value(dvo->pin, value); ++} ++ ++enum gpio_result dal_dvo_set_value( ++ const struct dvo *dvo, ++ uint32_t value) ++{ ++ return dal_gpio_set_value(dvo->pin, value); ++} ++ ++void dal_dvo_close( ++ struct dvo *dvo) ++{ ++ dal_gpio_close(dvo->pin); ++} ++ ++/* ++ * @brief ++ * Creation and destruction ++ */ ++ ++struct dvo *dal_dvo_create( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ struct dvo *dvo; ++ ++ switch (id) { ++ case GPIO_ID_DVO12: ++ if ((en < GPIO_DVO12_MIN) || (en > GPIO_DVO12_MAX)) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ break; ++ case GPIO_ID_DVO24: ++ if ((en < GPIO_DVO24_MIN) || (en > GPIO_DVO24_MAX)) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ break; ++ default: ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ dvo = dc_service_alloc(service->ctx, sizeof(struct dvo)); ++ ++ if (!dvo) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ dvo->pin = NULL; ++ dvo->ctx = service->ctx; ++ ++ return dvo; ++} ++ ++void dal_dvo_destroy( ++ struct dvo **dvo) ++{ ++ if (!dvo || !*dvo) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ dal_dvo_close(*dvo); ++ ++ dc_service_free((*dvo)->ctx, *dvo); ++ ++ *dvo = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dvo.h b/drivers/gpu/drm/amd/dal/dc/gpio/dvo.h +new file mode 100644 +index 0000000..0d98b51 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/dvo.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DVO_H__ ++#define __DAL_DVO_H__ ++ ++struct dvo { ++ struct gpio *pin; ++ struct dc_context *ctx; ++}; ++ ++struct dvo *dal_dvo_create( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en); ++ ++void dal_dvo_destroy( ++ struct dvo **dvo); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/gpio.h b/drivers/gpu/drm/amd/dal/dc/gpio/gpio.h +new file mode 100644 +index 0000000..7fcbb69 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/gpio.h +@@ -0,0 +1,48 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPIO_H__ ++#define __DAL_GPIO_H__ ++ ++struct gpio { ++ struct gpio_service *service; ++ struct hw_gpio_pin *pin; ++ enum gpio_id id; ++ uint32_t en; ++ enum gpio_mode mode; ++ /* when GPIO comes from VBIOS, it has defined output state */ ++ enum gpio_pin_output_state output_state; ++}; ++ ++struct gpio *dal_gpio_create( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en, ++ enum gpio_pin_output_state output_state); ++ ++void dal_gpio_destroy( ++ struct gpio **ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/gpio_base.c b/drivers/gpu/drm/amd/dal/dc/gpio/gpio_base.c +new file mode 100644 +index 0000000..6115f59 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/gpio_base.c +@@ -0,0 +1,279 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/gpio_interface.h" ++#include "include/gpio_service_interface.h" ++#include "hw_gpio_pin.h" ++#include "hw_translate.h" ++#include "hw_factory.h" ++#include "gpio_service.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "gpio.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++/* ++ * @brief ++ * Public API ++ */ ++ ++enum gpio_result dal_gpio_open( ++ struct gpio *gpio, ++ enum gpio_mode mode) ++{ ++ return dal_gpio_open_ex(gpio, mode, NULL); ++} ++ ++enum gpio_result dal_gpio_open_ex( ++ struct gpio *gpio, ++ enum gpio_mode mode, ++ void *options) ++{ ++ if (gpio->pin) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_ALREADY_OPENED; ++ } ++ ++ gpio->mode = mode; ++ ++ return dal_gpio_service_open( ++ gpio->service, gpio->id, gpio->en, mode, options, &gpio->pin); ++} ++ ++enum gpio_result dal_gpio_get_value( ++ const struct gpio *gpio, ++ uint32_t *value) ++{ ++ if (!gpio->pin) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_NULL_HANDLE; ++ } ++ ++ return gpio->pin->funcs->get_value(gpio->pin, value); ++} ++ ++enum gpio_result dal_gpio_set_value( ++ const struct gpio *gpio, ++ uint32_t value) ++{ ++ if (!gpio->pin) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_NULL_HANDLE; ++ } ++ ++ return gpio->pin->funcs->set_value(gpio->pin, value); ++} ++ ++enum gpio_mode dal_gpio_get_mode( ++ const struct gpio *gpio) ++{ ++ return gpio->mode; ++} ++ ++enum gpio_result dal_gpio_change_mode( ++ struct gpio *gpio, ++ enum gpio_mode mode) ++{ ++ if (!gpio->pin) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_NULL_HANDLE; ++ } ++ ++ return gpio->pin->funcs->change_mode(gpio->pin, mode); ++} ++ ++enum gpio_id dal_gpio_get_id( ++ const struct gpio *gpio) ++{ ++ return gpio->id; ++} ++ ++uint32_t dal_gpio_get_enum( ++ const struct gpio *gpio) ++{ ++ return gpio->en; ++} ++ ++enum gpio_result dal_gpio_set_config( ++ struct gpio *gpio, ++ const struct gpio_config_data *config_data) ++{ ++ if (!gpio->pin) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_NULL_HANDLE; ++ } ++ ++ return gpio->pin->funcs->set_config(gpio->pin, config_data); ++} ++ ++enum gpio_result dal_gpio_get_pin_info( ++ const struct gpio *gpio, ++ struct gpio_pin_info *pin_info) ++{ ++ return gpio->service->translate.funcs->id_to_offset( ++ gpio->id, gpio->en, pin_info) ? ++ GPIO_RESULT_OK : GPIO_RESULT_INVALID_DATA; ++} ++ ++enum sync_source dal_gpio_get_sync_source( ++ const struct gpio *gpio) ++{ ++ switch (gpio->id) { ++ case GPIO_ID_GENERIC: ++ switch (gpio->en) { ++ case GPIO_GENERIC_A: ++ return SYNC_SOURCE_IO_GENERIC_A; ++ case GPIO_GENERIC_B: ++ return SYNC_SOURCE_IO_GENERIC_B; ++ case GPIO_GENERIC_C: ++ return SYNC_SOURCE_IO_GENERIC_C; ++ case GPIO_GENERIC_D: ++ return SYNC_SOURCE_IO_GENERIC_D; ++ case GPIO_GENERIC_E: ++ return SYNC_SOURCE_IO_GENERIC_E; ++ case GPIO_GENERIC_F: ++ return SYNC_SOURCE_IO_GENERIC_F; ++ default: ++ return SYNC_SOURCE_NONE; ++ } ++ break; ++ case GPIO_ID_SYNC: ++ switch (gpio->en) { ++ case GPIO_SYNC_HSYNC_A: ++ return SYNC_SOURCE_IO_HSYNC_A; ++ case GPIO_SYNC_VSYNC_A: ++ return SYNC_SOURCE_IO_VSYNC_A; ++ case GPIO_SYNC_HSYNC_B: ++ return SYNC_SOURCE_IO_HSYNC_B; ++ case GPIO_SYNC_VSYNC_B: ++ return SYNC_SOURCE_IO_VSYNC_B; ++ default: ++ return SYNC_SOURCE_NONE; ++ } ++ break; ++ case GPIO_ID_HPD: ++ switch (gpio->en) { ++ case GPIO_HPD_1: ++ return SYNC_SOURCE_IO_HPD1; ++ case GPIO_HPD_2: ++ return SYNC_SOURCE_IO_HPD2; ++ default: ++ return SYNC_SOURCE_NONE; ++ } ++ break; ++ case GPIO_ID_GSL: ++ switch (gpio->en) { ++ case GPIO_GSL_GENLOCK_CLOCK: ++ return SYNC_SOURCE_GSL_IO_GENLOCK_CLOCK; ++ case GPIO_GSL_GENLOCK_VSYNC: ++ return SYNC_SOURCE_GSL_IO_GENLOCK_VSYNC; ++ case GPIO_GSL_SWAPLOCK_A: ++ return SYNC_SOURCE_GSL_IO_SWAPLOCK_A; ++ case GPIO_GSL_SWAPLOCK_B: ++ return SYNC_SOURCE_GSL_IO_SWAPLOCK_B; ++ default: ++ return SYNC_SOURCE_NONE; ++ } ++ break; ++ default: ++ return SYNC_SOURCE_NONE; ++ } ++} ++ ++enum gpio_pin_output_state dal_gpio_get_output_state( ++ const struct gpio *gpio) ++{ ++ return gpio->output_state; ++} ++ ++void dal_gpio_close( ++ struct gpio *gpio) ++{ ++ if (!gpio) ++ return; ++ ++ dal_gpio_service_close(gpio->service, &gpio->pin); ++ ++ gpio->mode = GPIO_MODE_UNKNOWN; ++} ++ ++/* ++ * @brief ++ * Creation and destruction ++ */ ++ ++struct gpio *dal_gpio_create( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en, ++ enum gpio_pin_output_state output_state) ++{ ++ struct gpio *gpio = dc_service_alloc(service->ctx, sizeof(struct gpio)); ++ ++ if (!gpio) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ gpio->service = service; ++ gpio->pin = NULL; ++ gpio->id = id; ++ gpio->en = en; ++ gpio->mode = GPIO_MODE_UNKNOWN; ++ gpio->output_state = output_state; ++ ++ return gpio; ++} ++ ++void dal_gpio_destroy( ++ struct gpio **gpio) ++{ ++ if (!gpio || !*gpio) { ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ dal_gpio_close(*gpio); ++ ++ dc_service_free((*gpio)->service->ctx, *gpio); ++ ++ *gpio = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.c b/drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.c +new file mode 100644 +index 0000000..08e046b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.c +@@ -0,0 +1,470 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "dal_services.h" ++#include "include/gpio_interface.h" ++#include "include/ddc_interface.h" ++#include "include/dvo_interface.h" ++#include "include/irq_interface.h" ++#include "include/gpio_service_interface.h" ++#include "hw_translate.h" ++#include "hw_factory.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "gpio_service.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++#include "hw_gpio_pin.h" ++#include "gpio.h" ++#include "dvo.h" ++#include "ddc.h" ++#include "irq.h" ++ ++/* ++ * This unit ++ */ ++ ++/* ++ * @brief ++ * Public API. ++ */ ++ ++struct gpio_service *dal_gpio_service_create( ++ enum dce_version dce_version, ++ struct dc_context *ctx) ++{ ++ struct gpio_service *service; ++ ++ uint32_t index_of_id; ++ ++ service = dc_service_alloc(ctx, sizeof(struct gpio_service)); ++ ++ if (!service) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ if (!dal_hw_translate_init(&service->translate, dce_version)) { ++ BREAK_TO_DEBUGGER(); ++ goto failure_1; ++ } ++ ++ if (!dal_hw_factory_init(&service->factory, dce_version)) { ++ BREAK_TO_DEBUGGER(); ++ goto failure_1; ++ } ++ ++ /* allocate and initialize business storage */ ++ { ++ const uint32_t bits_per_uint = sizeof(uint32_t) << 3; ++ ++ index_of_id = 0; ++ service->ctx = ctx; ++ ++ do { ++ uint32_t number_of_bits = ++ service->factory.number_of_pins[index_of_id]; ++ ++ uint32_t number_of_uints = ++ (number_of_bits + bits_per_uint - 1) / ++ bits_per_uint; ++ ++ uint32_t *slot; ++ ++ if (number_of_bits) { ++ uint32_t index_of_uint = 0; ++ ++ slot = dc_service_alloc( ++ ctx, ++ number_of_uints * sizeof(uint32_t)); ++ ++ if (!slot) { ++ BREAK_TO_DEBUGGER(); ++ goto failure_2; ++ } ++ ++ do { ++ slot[index_of_uint] = 0; ++ ++ ++index_of_uint; ++ } while (index_of_uint < number_of_uints); ++ } else ++ slot = NULL; ++ ++ service->busyness[index_of_id] = slot; ++ ++ ++index_of_id; ++ } while (index_of_id < GPIO_ID_COUNT); ++ } ++ ++ return service; ++ ++failure_2: ++ while (index_of_id) { ++ uint32_t *slot; ++ ++ --index_of_id; ++ ++ slot = service->busyness[index_of_id]; ++ ++ if (slot) ++ dc_service_free(ctx, slot); ++ }; ++ ++failure_1: ++ dc_service_free(ctx, service); ++ ++ return NULL; ++} ++ ++struct gpio *dal_gpio_service_create_gpio( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask, ++ enum gpio_pin_output_state output_state) ++{ ++ enum gpio_id id; ++ uint32_t en; ++ ++ if (!service->translate.funcs->offset_to_id(offset, mask, &id, &en)) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ return dal_gpio_create(service, id, en, output_state); ++} ++ ++struct gpio *dal_gpio_service_create_gpio_ex( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en, ++ enum gpio_pin_output_state output_state) ++{ ++ return dal_gpio_create(service, id, en, output_state); ++} ++ ++void dal_gpio_service_destroy_gpio( ++ struct gpio **gpio) ++{ ++ dal_gpio_destroy(gpio); ++} ++ ++struct ddc *dal_gpio_service_create_ddc( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask, ++ struct gpio_ddc_hw_info *info) ++{ ++ return dal_gpio_create_ddc(service, offset, mask, info); ++} ++ ++void dal_gpio_service_destroy_ddc( ++ struct ddc **ddc) ++{ ++ dal_gpio_destroy_ddc(ddc); ++} ++ ++struct dvo *dal_gpio_service_create_dvo( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask) ++{ ++ enum gpio_id id; ++ uint32_t en; ++ ++ if (!service->translate.funcs->offset_to_id(offset, mask, &id, &en)) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ return dal_dvo_create(service, id, en); ++} ++ ++struct dvo *dal_gpio_service_create_dvo_ex( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ return dal_dvo_create(service, id, en); ++} ++ ++void dal_gpio_service_destroy_dvo( ++ struct dvo **dvo) ++{ ++ dal_dvo_destroy(dvo); ++} ++ ++struct irq *dal_gpio_service_create_irq( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask) ++{ ++ enum gpio_id id; ++ uint32_t en; ++ ++ if (!service->translate.funcs->offset_to_id(offset, mask, &id, &en)) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ return dal_gpio_create_irq(service, id, en); ++} ++ ++struct irq *dal_gpio_service_create_irq_ex( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ return dal_gpio_create_irq(service, id, en); ++} ++ ++void dal_gpio_service_destroy_irq( ++ struct irq **irq) ++{ ++ dal_gpio_destroy_irq(irq); ++} ++ ++void dal_gpio_service_destroy( ++ struct gpio_service **ptr) ++{ ++ if (!ptr || !*ptr) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ /* free business storage */ ++ { ++ uint32_t index_of_id = 0; ++ ++ do { ++ uint32_t *slot = (*ptr)->busyness[index_of_id]; ++ ++ if (slot) ++ dc_service_free((*ptr)->ctx, slot); ++ ++ ++index_of_id; ++ } while (index_of_id < GPIO_ID_COUNT); ++ } ++ ++ dc_service_free((*ptr)->ctx, *ptr); ++ ++ *ptr = NULL; ++} ++ ++/* ++ * @brief ++ * Private API. ++ */ ++ ++static bool is_pin_busy( ++ const struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ const uint32_t bits_per_uint = sizeof(uint32_t) << 3; ++ ++ const uint32_t *slot = service->busyness[id] + (en / bits_per_uint); ++ ++ return 0 != (*slot & (1 << (en % bits_per_uint))); ++} ++ ++static bool is_some_pin_busy( ++ const struct gpio_service *service, ++ enum gpio_id id) ++{ ++ const uint32_t bits_per_uint = sizeof(uint32_t) << 3; ++ ++ uint32_t index_of_uint = 0; ++ ++ uint32_t number_of_uints = ++ service->factory.number_of_pins[id]; ++ ++ number_of_uints = (number_of_uints + bits_per_uint - 1) / bits_per_uint; ++ ++ while (index_of_uint < number_of_uints) { ++ if (service->busyness[id][index_of_uint]) ++ return true; ++ ++ ++index_of_uint; ++ }; ++ ++ return false; ++} ++ ++static void set_pin_busy( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ const uint32_t bits_per_uint = sizeof(uint32_t) << 3; ++ ++ service->busyness[id][en / bits_per_uint] |= ++ (1 << (en % bits_per_uint)); ++} ++ ++static void set_pin_free( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ const uint32_t bits_per_uint = sizeof(uint32_t) << 3; ++ ++ service->busyness[id][en / bits_per_uint] &= ++ ~(1 << (en % bits_per_uint)); ++} ++ ++enum gpio_result dal_gpio_service_open( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en, ++ enum gpio_mode mode, ++ void *options, ++ struct hw_gpio_pin **ptr) ++{ ++ struct hw_gpio_pin *pin; ++ ++ if (!service->busyness[id]) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_OPEN_FAILED; ++ } ++ ++ if (is_pin_busy(service, id, en)) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_DEVICE_BUSY; ++ } ++ ++ switch (id) { ++ case GPIO_ID_DVO1: ++ /* [anaumov] not implemented, commented with "to do" */ ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ case GPIO_ID_DVO12: ++ if (!service->busyness[GPIO_ID_DVO24]) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_OPEN_FAILED; ++ } ++ ++ if (is_some_pin_busy(service, GPIO_ID_DVO24)) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_DEVICE_BUSY; ++ } ++ ++ pin = service->factory.funcs->create_dvo( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_DVO24: ++ if (!service->busyness[GPIO_ID_DVO12]) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_OPEN_FAILED; ++ } ++ ++ if (is_some_pin_busy(service, GPIO_ID_DVO12)) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_DEVICE_BUSY; ++ } ++ ++ pin = service->factory.funcs->create_dvo( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_DDC_DATA: ++ pin = service->factory.funcs->create_ddc_data( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_DDC_CLOCK: ++ pin = service->factory.funcs->create_ddc_clock( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_GENERIC: ++ pin = service->factory.funcs->create_generic( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_HPD: ++ pin = service->factory.funcs->create_hpd( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_GPIO_PAD: ++ pin = service->factory.funcs->create_gpio_pad( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_SYNC: ++ pin = service->factory.funcs->create_sync( ++ service->ctx, id, en); ++ break; ++ case GPIO_ID_GSL: ++ pin = service->factory.funcs->create_gsl( ++ service->ctx, id, en); ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ } ++ ++ if (!pin) { ++ ASSERT_CRITICAL(false); ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ } ++ ++ if (!pin->funcs->open(pin, mode, options)) { ++ ASSERT_CRITICAL(false); ++ dal_gpio_service_close(service, &pin); ++ return GPIO_RESULT_OPEN_FAILED; ++ } ++ ++ set_pin_busy(service, id, en); ++ *ptr = pin; ++ return GPIO_RESULT_OK; ++} ++ ++void dal_gpio_service_close( ++ struct gpio_service *service, ++ struct hw_gpio_pin **ptr) ++{ ++ struct hw_gpio_pin *pin; ++ ++ if (!ptr) { ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ pin = *ptr; ++ ++ if (pin) { ++ set_pin_free(service, pin->id, pin->en); ++ ++ pin->funcs->close(pin); ++ ++ pin->funcs->destroy(ptr); ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.h b/drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.h +new file mode 100644 +index 0000000..a17c438 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/gpio_service.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPIO_SERVICE_H__ ++#define __DAL_GPIO_SERVICE_H__ ++ ++struct hw_translate; ++struct hw_factory; ++ ++struct gpio_service { ++ struct dc_context *ctx; ++ struct hw_translate translate; ++ struct hw_factory factory; ++ /* ++ * @brief ++ * Business storage. ++ * For each member of 'enum gpio_id', ++ * store array of bits (packed into uint32_t slots), ++ * index individual bit by 'en' value */ ++ uint32_t *busyness[GPIO_ID_COUNT]; ++}; ++ ++enum gpio_result dal_gpio_service_open( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en, ++ enum gpio_mode mode, ++ void *options, ++ struct hw_gpio_pin **ptr); ++ ++void dal_gpio_service_close( ++ struct gpio_service *service, ++ struct hw_gpio_pin **ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.c +new file mode 100644 +index 0000000..0608f16 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.c +@@ -0,0 +1,105 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "hw_gpio_pin.h" ++#include "hw_gpio.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_ddc.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++#define FROM_HW_GPIO(ptr) \ ++ container_of((ptr), struct hw_ddc, base) ++ ++#define FROM_HW_GPIO_PIN(ptr) \ ++ FROM_HW_GPIO(container_of((ptr), struct hw_gpio, base)) ++ ++bool dal_hw_ddc_open( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode, ++ void *options) ++{ ++ struct hw_ddc *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ uint32_t en; ++ ++ if (!options) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ /* get the EN bit before overwriting it */ ++ ++ dal_hw_gpio_get_reg_value( ++ ptr->ctx, ++ &pin->base.pin_reg.DC_GPIO_DATA_EN, ++ &en); ++ ++ ((struct gpio_ddc_open_options *)options)->en_bit_present = (en != 0); ++ ++ return dal_hw_gpio_open(ptr, mode, options); ++} ++ ++bool dal_hw_ddc_construct( ++ struct hw_ddc *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ if (!dal_hw_gpio_construct(&pin->base, id, en, ctx)) ++ return false; ++ ++ pin->mask.DC_GPIO_DDC_MASK_MASK = 0; ++ pin->mask.DC_GPIO_DDC_PD_EN_MASK = 0; ++ pin->mask.DC_GPIO_DDC_RECV_MASK = 0; ++ pin->mask.AUX_PAD_MODE_MASK = 0; ++ pin->mask.AUX_POL_MASK = 0; ++ pin->mask.DC_GPIO_DDCCLK_STR_MASK = 0; ++ ++ return true; ++} ++ ++void dal_hw_ddc_destruct( ++ struct hw_ddc *pin) ++{ ++ dal_hw_gpio_destruct(&pin->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.h +new file mode 100644 +index 0000000..a3a727c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_ddc.h +@@ -0,0 +1,60 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_DDC_H__ ++#define __DAL_HW_DDC_H__ ++ ++struct hw_ddc_mask { ++ uint32_t DC_GPIO_DDC_MASK_MASK; ++ uint32_t DC_GPIO_DDC_PD_EN_MASK; ++ uint32_t DC_GPIO_DDC_RECV_MASK; ++ uint32_t AUX_PAD_MODE_MASK; ++ uint32_t AUX_POL_MASK; ++ uint32_t DC_GPIO_DDCCLK_STR_MASK; ++}; ++ ++struct hw_ddc { ++ struct hw_gpio base; ++ struct hw_ddc_mask mask; ++}; ++ ++#define HW_DDC_FROM_BASE(hw_gpio) \ ++ container_of((HW_GPIO_FROM_BASE(hw_gpio)), struct hw_ddc, base) ++ ++bool dal_hw_ddc_construct( ++ struct hw_ddc *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx); ++ ++void dal_hw_ddc_destruct( ++ struct hw_ddc *pin); ++ ++bool dal_hw_ddc_open( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode, ++ void *options); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.c +new file mode 100644 +index 0000000..a5a07f0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.c +@@ -0,0 +1,318 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "hw_gpio_pin.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_dvo.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++#define FROM_HW_GPIO_PIN(ptr) \ ++ container_of((ptr), struct hw_dvo, base) ++ ++static void store_dvo_registers( ++ struct hw_dvo *pin) ++{ ++ pin->store.dvo_mask = dal_read_reg( ++ pin->base.ctx, pin->addr.DC_GPIO_DVODATA_MASK); ++ pin->store.dvo_en = dal_read_reg( ++ pin->base.ctx, pin->addr.DC_GPIO_DVODATA_EN); ++ pin->store.dvo_data_a = dal_read_reg( ++ pin->base.ctx, pin->addr.DC_GPIO_DVODATA_A); ++} ++ ++static void restore_dvo_registers( ++ struct hw_dvo *pin) ++{ ++ { ++ const uint32_t addr = pin->addr.DC_GPIO_DVODATA_MASK; ++ ++ uint32_t data = dal_read_reg(pin->base.ctx, addr); ++ ++ data &= ~pin->dvo_mask; ++ data |= pin->store.dvo_mask & pin->dvo_mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++ } ++ ++ { ++ const uint32_t addr = pin->addr.DC_GPIO_DVODATA_EN; ++ ++ uint32_t data = dal_read_reg(pin->base.ctx, addr); ++ ++ data &= ~pin->dvo_mask; ++ data |= pin->store.dvo_en & pin->dvo_mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++ } ++ ++ { ++ const uint32_t addr = pin->addr.DC_GPIO_DVODATA_A; ++ ++ uint32_t data = dal_read_reg(pin->base.ctx, addr); ++ ++ data &= ~pin->dvo_mask; ++ data |= pin->store.dvo_data_a & pin->dvo_mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++ } ++} ++ ++static void program_dvo( ++ struct hw_dvo *pin, ++ bool output) ++{ ++ /* Turn on Mask bits for the requested channel, ++ * this will enable the channel for software control. */ ++ { ++ const uint32_t addr = pin->addr.DC_GPIO_DVODATA_MASK; ++ ++ uint32_t mask = dal_read_reg(pin->base.ctx, addr); ++ ++ uint32_t data = pin->dvo_mask | mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++ } ++ ++ /* Turn off/on the Enable bits on the requested channel, ++ * this will set it to Input/Output mode. */ ++ { ++ const uint32_t addr = pin->addr.DC_GPIO_DVODATA_EN; ++ ++ uint32_t enable = dal_read_reg(pin->base.ctx, addr); ++ ++ uint32_t data = output ? ++ (pin->dvo_mask | enable) : ++ (~pin->dvo_mask & enable); ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++ } ++} ++ ++static void program_dvo_strength( ++ struct hw_dvo *pin) ++{ ++ const uint32_t addr = pin->addr.DVO_STRENGTH_CONTROL; ++ ++ uint32_t data = dal_read_reg(pin->base.ctx, addr); ++ ++ data &= ~pin->dvo_strength_mask; ++ data |= pin->dvo_strength & pin->dvo_strength_mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++} ++ ++static void disable_on_chip_terminators( ++ struct hw_dvo *pin) ++{ ++ const uint32_t addr = pin->addr.D1CRTC_MVP_CONTROL1; ++ ++ uint32_t data = dal_read_reg(pin->base.ctx, addr); ++ ++ pin->store.mvp_terminator_state = (data & pin->mvp_termination_mask); ++ ++ data &= ~pin->mvp_termination_mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++} ++ ++static void restore_on_chip_terminators( ++ struct hw_dvo *pin) ++{ ++ const uint32_t addr = pin->addr.D1CRTC_MVP_CONTROL1; ++ ++ uint32_t data = dal_read_reg(pin->base.ctx, addr); ++ ++ data &= ~pin->mvp_termination_mask; ++ ++ if (pin->store.mvp_terminator_state) ++ data |= pin->mvp_termination_mask; ++ ++ dal_write_reg(pin->base.ctx, addr, data); ++} ++ ++bool dal_hw_dvo_open( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode, ++ void *options) ++{ ++ struct hw_dvo *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ store_dvo_registers(pin); ++ ++ ptr->mode = mode; ++ ++ switch (mode) { ++ case GPIO_MODE_INPUT: ++ program_dvo_strength(pin); ++ disable_on_chip_terminators(pin); ++ program_dvo(pin, false); ++ ++ ptr->opened = true; ++ break; ++ case GPIO_MODE_OUTPUT: ++ program_dvo_strength(pin); ++ disable_on_chip_terminators(pin); ++ program_dvo(pin, true); ++ ++ ptr->opened = true; ++ break; ++ default: ++ /* unsupported mode */ ++ BREAK_TO_DEBUGGER(); ++ ++ ptr->opened = false; ++ } ++ ++ return ptr->opened; ++} ++ ++enum gpio_result dal_hw_dvo_get_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t *value) ++{ ++ const struct hw_dvo *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ if (ptr->mode != GPIO_MODE_INPUT) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ *value = dal_read_reg(ptr->ctx, pin->addr.DC_GPIO_DVODATA_Y); ++ ++ *value &= pin->dvo_mask; ++ *value >>= pin->dvo_shift; ++ ++ return GPIO_RESULT_OK; ++} ++ ++enum gpio_result dal_hw_dvo_set_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t value) ++{ ++ const struct hw_dvo *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ uint32_t masked_value; ++ ++ if (ptr->mode != GPIO_MODE_OUTPUT) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ } ++ ++ /* Ensure there is no overflow of the value written. ++ * Value cannot be more than 12 bits for a 12-bit channel. */ ++ ++ masked_value = value << pin->dvo_shift; ++ ++ if (masked_value != (masked_value & pin->dvo_mask)) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_INVALID_DATA; ++ } ++ ++ masked_value &= pin->dvo_mask; ++ ++ /* read the DataA register ++ * mask off the Bundle that we want to write to ++ * or the data into the register */ ++ { ++ const uint32_t addr = pin->addr.DC_GPIO_DVODATA_A; ++ ++ uint32_t data = dal_read_reg(ptr->ctx, addr); ++ ++ data &= ~pin->dvo_mask; ++ data |= masked_value; ++ ++ dal_write_reg(ptr->ctx, addr, data); ++ } ++ ++ return GPIO_RESULT_OK; ++} ++ ++void dal_hw_dvo_close( ++ struct hw_gpio_pin *ptr) ++{ ++ struct hw_dvo *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ restore_dvo_registers(pin); ++ restore_on_chip_terminators(pin); ++ ++ ptr->mode = GPIO_MODE_UNKNOWN; ++ ++ ptr->opened = false; ++} ++ ++bool dal_hw_dvo_construct( ++ struct hw_dvo *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ struct hw_gpio_pin *base = &pin->base; ++ ++ if (!dal_hw_gpio_pin_construct(base, id, en, ctx)) ++ return false; ++ ++ pin->addr.DC_GPIO_DVODATA_MASK = 0; ++ pin->addr.DC_GPIO_DVODATA_EN = 0; ++ pin->addr.DC_GPIO_DVODATA_A = 0; ++ pin->addr.DC_GPIO_DVODATA_Y = 0; ++ pin->addr.DVO_STRENGTH_CONTROL = 0; ++ pin->addr.D1CRTC_MVP_CONTROL1 = 0; ++ ++ pin->dvo_mask = 0; ++ pin->dvo_shift = 0; ++ pin->dvo_strength_mask = 0; ++ pin->mvp_termination_mask = 0; ++ ++ pin->dvo_strength = 0; ++ ++ pin->store.dvo_mask = 0; ++ pin->store.dvo_en = 0; ++ pin->store.dvo_data_a = 0; ++ pin->store.mvp_terminator_state = false; ++ ++ return true; ++} ++ ++void dal_hw_dvo_destruct( ++ struct hw_dvo *pin) ++{ ++ dal_hw_gpio_pin_destruct(&pin->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.h +new file mode 100644 +index 0000000..5a120c2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_dvo.h +@@ -0,0 +1,89 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_DVO_H__ ++#define __DAL_HW_DVO_H__ ++ ++#define BUNDLE_A_SHIFT 12L ++#define BUNDLE_B_SHIFT 0L ++ ++struct hw_dvo { ++ struct hw_gpio_pin base; ++ /* Register indices are represented by member variables, ++ * are to be filled in by derived classes. ++ * These members permit the use of common code ++ * for programming registers where the sequence is the same ++ * but the register sets are different */ ++ struct { ++ uint32_t DC_GPIO_DVODATA_MASK; ++ uint32_t DC_GPIO_DVODATA_EN; ++ uint32_t DC_GPIO_DVODATA_A; ++ uint32_t DC_GPIO_DVODATA_Y; ++ uint32_t DVO_STRENGTH_CONTROL; ++ uint32_t D1CRTC_MVP_CONTROL1; ++ } addr; ++ ++ /* Mask and shift differentiates between Bundle A and Bundle B */ ++ uint32_t dvo_mask; ++ uint32_t dvo_shift; ++ uint32_t dvo_strength_mask; ++ uint32_t mvp_termination_mask; ++ ++ uint32_t dvo_strength; ++ ++ struct { ++ uint32_t dvo_mask; ++ uint32_t dvo_en; ++ uint32_t dvo_data_a; ++ bool mvp_terminator_state; ++ } store; ++}; ++ ++bool dal_hw_dvo_construct( ++ struct hw_dvo *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx); ++ ++void dal_hw_dvo_destruct( ++ struct hw_dvo *pin); ++ ++bool dal_hw_dvo_open( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode, ++ void *options); ++ ++enum gpio_result dal_hw_dvo_get_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t *value); ++ ++enum gpio_result dal_hw_dvo_set_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t value); ++ ++void dal_hw_dvo_close( ++ struct hw_gpio_pin *ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c +new file mode 100644 +index 0000000..d1b6b7e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c +@@ -0,0 +1,80 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_factory.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/hw_factory_dce110.h" ++#endif ++/* ++ * This unit ++ */ ++ ++bool dal_hw_factory_init( ++ struct hw_factory *factory, ++ enum dce_version dce_version) ++{ ++ switch (dce_version) { ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ dal_hw_factory_dce110_init(factory); ++ return true; ++#endif ++ default: ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++} ++ ++void dal_hw_factory_destroy( ++ struct dc_context *ctx, ++ struct hw_factory **factory) ++{ ++ if (!factory || !*factory) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ dc_service_free(ctx, *factory); ++ ++ *factory = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.h +new file mode 100644 +index 0000000..f16678c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.h +@@ -0,0 +1,74 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_FACTORY_H__ ++#define __DAL_HW_FACTORY_H__ ++ ++struct hw_gpio_pin; ++ ++struct hw_factory { ++ uint32_t number_of_pins[GPIO_ID_COUNT]; ++ ++ const struct hw_factory_funcs { ++ struct hw_gpio_pin *(*create_dvo)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_ddc_data)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_ddc_clock)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_generic)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_hpd)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_gpio_pad)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_sync)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ struct hw_gpio_pin *(*create_gsl)( ++ struct dc_context *ctx, ++ enum gpio_id id, ++ uint32_t en); ++ } *funcs; ++}; ++ ++bool dal_hw_factory_init( ++ struct hw_factory *factory, ++ enum dce_version dce_version); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.c +new file mode 100644 +index 0000000..2964d5d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.c +@@ -0,0 +1,408 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "hw_gpio_pin.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_gpio.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++enum gpio_result dal_hw_gpio_get_reg_value( ++ struct dc_context *ctx, ++ const struct addr_mask *reg, ++ uint32_t *value) ++{ ++ *value = dal_read_reg(ctx, reg->addr); ++ ++ *value &= reg->mask; ++ ++ return GPIO_RESULT_OK; ++} ++ ++enum gpio_result dal_hw_gpio_set_reg_value( ++ struct dc_context *ctx, ++ const struct addr_mask *reg, ++ uint32_t value) ++{ ++ uint32_t prev_value; ++ ++ if ((value & reg->mask) != value) { ++ BREAK_TO_DEBUGGER(); ++ return GPIO_RESULT_INVALID_DATA; ++ } ++ ++ prev_value = dal_read_reg(ctx, reg->addr); ++ ++ prev_value &= ~reg->mask; ++ prev_value |= (value & reg->mask); ++ ++ dal_write_reg(ctx, reg->addr, prev_value); ++ ++ return GPIO_RESULT_OK; ++} ++ ++uint32_t dal_hw_gpio_get_shift_from_mask( ++ uint32_t mask) ++{ ++ uint32_t result = 0; ++ ++ if (!mask) ++ return 32; ++ ++ do { ++ if ((1 << result) & mask) ++ break; ++ ++ ++result; ++ } while (result < 32); ++ ++ return result; ++} ++ ++#define FROM_HW_GPIO_PIN(ptr) \ ++ container_of((ptr), struct hw_gpio, base) ++ ++static void store_registers( ++ struct hw_gpio *pin) ++{ ++ dal_hw_gpio_get_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ &pin->store.mask); ++ dal_hw_gpio_get_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_A, ++ &pin->store.a); ++ dal_hw_gpio_get_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_EN, ++ &pin->store.en); ++ ++ if (pin->mux_supported) ++ dal_hw_gpio_get_reg_value( ++ pin->base.ctx, ++ &pin->mux_reg.GPIO_MUX_CONTROL, ++ &pin->store.mux); ++} ++ ++static void restore_registers( ++ struct hw_gpio *pin) ++{ ++ dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ pin->store.mask); ++ dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_A, ++ pin->store.a); ++ dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_EN, ++ pin->store.en); ++ ++ if (pin->mux_supported) ++ dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->mux_reg.GPIO_MUX_CONTROL, ++ pin->store.mux); ++} ++ ++bool dal_hw_gpio_open( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode, ++ void *options) ++{ ++ struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ store_registers(pin); ++ ++ ptr->opened = (pin->funcs->config_mode(pin, mode) == GPIO_RESULT_OK); ++ ++ return ptr->opened; ++} ++ ++enum gpio_result dal_hw_gpio_get_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t *value) ++{ ++ const struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ enum gpio_result result; ++ ++ switch (ptr->mode) { ++ case GPIO_MODE_INPUT: ++ case GPIO_MODE_OUTPUT: ++ case GPIO_MODE_HARDWARE: ++ case GPIO_MODE_FAST_OUTPUT: ++ result = dal_hw_gpio_get_reg_value( ++ ptr->ctx, ++ &pin->pin_reg.DC_GPIO_DATA_Y, ++ value); ++ /* Clients does not know that the value ++ * comes from register and is shifted. */ ++ if (result == GPIO_RESULT_OK) ++ *value >>= dal_hw_gpio_get_shift_from_mask( ++ pin->pin_reg.DC_GPIO_DATA_Y.mask); ++ break; ++ default: ++ result = GPIO_RESULT_NON_SPECIFIC_ERROR; ++ } ++ ++ return result; ++} ++ ++enum gpio_result dal_hw_gpio_set_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t value) ++{ ++ struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ /* This is the public interface ++ * where the input comes from client, not shifted yet ++ * (because client does not know the shifts). */ ++ ++ switch (ptr->mode) { ++ case GPIO_MODE_OUTPUT: ++ return dal_hw_gpio_set_reg_value( ++ ptr->ctx, ++ &pin->pin_reg.DC_GPIO_DATA_A, ++ value << dal_hw_gpio_get_shift_from_mask( ++ pin->pin_reg.DC_GPIO_DATA_A.mask)); ++ case GPIO_MODE_FAST_OUTPUT: ++ /* We use (EN) to faster switch (used in DDC GPIO). ++ * So (A) is grounded, output is driven by (EN = 0) ++ * to pull the line down (output == 0) and (EN=1) ++ * then output is tri-state */ ++ return dal_hw_gpio_set_reg_value( ++ ptr->ctx, ++ &pin->pin_reg.DC_GPIO_DATA_EN, ++ pin->pin_reg.DC_GPIO_DATA_EN.mask & ++ ~(value << dal_hw_gpio_get_shift_from_mask( ++ pin->pin_reg.DC_GPIO_DATA_EN.mask))); ++ default: ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ } ++} ++ ++enum gpio_result dal_hw_gpio_change_mode( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode) ++{ ++ struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ return pin->funcs->config_mode(pin, mode); ++} ++ ++void dal_hw_gpio_close( ++ struct hw_gpio_pin *ptr) ++{ ++ struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ restore_registers(pin); ++ ++ ptr->mode = GPIO_MODE_UNKNOWN; ++ ptr->opened = false; ++} ++ ++static enum gpio_result config_mode_input( ++ struct hw_gpio *pin) ++{ ++ enum gpio_result result; ++ ++ /* turn off output enable, act as input pin; ++ * program the pin as GPIO, mask out signal driven by HW */ ++ ++ result = dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_EN, ++ 0); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ result = dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ pin->pin_reg.DC_GPIO_DATA_MASK.mask); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ return GPIO_RESULT_OK; ++} ++ ++static enum gpio_result config_mode_output( ++ struct hw_gpio *pin) ++{ ++ enum gpio_result result; ++ ++ /* turn on output enable, act as output pin; ++ * program the pin as GPIO, mask out signal driven by HW */ ++ ++ result = dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_EN, ++ pin->pin_reg.DC_GPIO_DATA_EN.mask); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ result = dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ pin->pin_reg.DC_GPIO_DATA_MASK.mask); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ return GPIO_RESULT_OK; ++} ++ ++static enum gpio_result config_mode_fast_output( ++ struct hw_gpio *pin) ++{ ++ enum gpio_result result; ++ ++ /* grounding the A register then use the EN register bit ++ * will have faster effect on the rise time */ ++ ++ result = dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_A, 0); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ result = dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ pin->pin_reg.DC_GPIO_DATA_MASK.mask); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ return GPIO_RESULT_OK; ++} ++ ++static enum gpio_result config_mode_hardware( ++ struct hw_gpio *pin) ++{ ++ /* program the pin as tri-state, pin is driven by HW */ ++ ++ enum gpio_result result = ++ dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ 0); ++ ++ if (result != GPIO_RESULT_OK) ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ ++ return GPIO_RESULT_OK; ++} ++ ++enum gpio_result dal_hw_gpio_config_mode( ++ struct hw_gpio *pin, ++ enum gpio_mode mode) ++{ ++ pin->base.mode = mode; ++ ++ switch (mode) { ++ case GPIO_MODE_INPUT: ++ return config_mode_input(pin); ++ case GPIO_MODE_OUTPUT: ++ return config_mode_output(pin); ++ case GPIO_MODE_FAST_OUTPUT: ++ return config_mode_fast_output(pin); ++ case GPIO_MODE_HARDWARE: ++ return config_mode_hardware(pin); ++ default: ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++ } ++} ++ ++const struct hw_gpio_funcs func = { ++ .config_mode = dal_hw_gpio_config_mode, ++}; ++ ++bool dal_hw_gpio_construct( ++ struct hw_gpio *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ struct hw_gpio_pin *base = &pin->base; ++ ++ if (!dal_hw_gpio_pin_construct(base, id, en, ctx)) ++ return false; ++ ++ pin->funcs = &func; ++ ++ pin->pin_reg.DC_GPIO_DATA_MASK.addr = 0; ++ pin->pin_reg.DC_GPIO_DATA_MASK.mask = 0; ++ pin->pin_reg.DC_GPIO_DATA_A.addr = 0; ++ pin->pin_reg.DC_GPIO_DATA_A.mask = 0; ++ pin->pin_reg.DC_GPIO_DATA_EN.addr = 0; ++ pin->pin_reg.DC_GPIO_DATA_EN.mask = 0; ++ pin->pin_reg.DC_GPIO_DATA_Y.addr = 0; ++ pin->pin_reg.DC_GPIO_DATA_Y.mask = 0; ++ pin->mux_reg.GPIO_MUX_CONTROL.addr = 0; ++ pin->mux_reg.GPIO_MUX_CONTROL.mask = 0; ++ pin->mux_reg.GPIO_MUX_STEREO_SEL.addr = 0; ++ pin->mux_reg.GPIO_MUX_STEREO_SEL.mask = 0; ++ ++ pin->store.mask = 0; ++ pin->store.a = 0; ++ pin->store.en = 0; ++ pin->store.mux = 0; ++ ++ pin->mux_supported = false; ++ ++ return true; ++} ++ ++void dal_hw_gpio_destruct( ++ struct hw_gpio *pin) ++{ ++ dal_hw_gpio_pin_destruct(&pin->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.h +new file mode 100644 +index 0000000..44eb86e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio.h +@@ -0,0 +1,129 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_GPIO_H__ ++#define __DAL_HW_GPIO_H__ ++ ++struct addr_mask { ++ uint32_t addr; ++ uint32_t mask; ++}; ++ ++enum gpio_result dal_hw_gpio_get_reg_value( ++ struct dc_context *ctx, ++ const struct addr_mask *reg, ++ uint32_t *value); ++ ++enum gpio_result dal_hw_gpio_set_reg_value( ++ struct dc_context *ctx, ++ const struct addr_mask *reg, ++ uint32_t value); ++ ++uint32_t dal_hw_gpio_get_shift_from_mask( ++ uint32_t mask); ++ ++struct hw_gpio; ++ ++struct hw_gpio_funcs { ++ enum gpio_result (*config_mode)( ++ struct hw_gpio *pin, ++ enum gpio_mode mode); ++}; ++ ++/* Register indices are represented by member variables ++ * and are to be filled in by constructors of derived classes. ++ * These members permit the use of common code ++ * for programming registers, where the sequence is the same ++ * but register sets are different. ++ * Some GPIOs have HW mux which allows to choose ++ * what is the source of the signal in HW mode */ ++ ++struct hw_gpio_pin_reg { ++ struct addr_mask DC_GPIO_DATA_MASK; ++ struct addr_mask DC_GPIO_DATA_A; ++ struct addr_mask DC_GPIO_DATA_EN; ++ struct addr_mask DC_GPIO_DATA_Y; ++}; ++ ++struct hw_gpio_mux_reg { ++ struct addr_mask GPIO_MUX_CONTROL; ++ struct addr_mask GPIO_MUX_STEREO_SEL; ++}; ++ ++struct hw_gpio { ++ struct hw_gpio_pin base; ++ const struct hw_gpio_funcs *funcs; ++ struct hw_gpio_pin_reg pin_reg; ++ struct hw_gpio_mux_reg mux_reg; ++ ++ /* variables to save register value */ ++ struct { ++ uint32_t mask; ++ uint32_t a; ++ uint32_t en; ++ uint32_t mux; ++ } store; ++ ++ /* GPIO MUX support */ ++ bool mux_supported; ++}; ++ ++#define HW_GPIO_FROM_BASE(hw_gpio_pin) \ ++ container_of((hw_gpio_pin), struct hw_gpio, base) ++ ++bool dal_hw_gpio_construct( ++ struct hw_gpio *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx); ++ ++bool dal_hw_gpio_open( ++ struct hw_gpio_pin *pin, ++ enum gpio_mode mode, ++ void *options); ++ ++enum gpio_result dal_hw_gpio_get_value( ++ const struct hw_gpio_pin *pin, ++ uint32_t *value); ++ ++enum gpio_result dal_hw_gpio_config_mode( ++ struct hw_gpio *pin, ++ enum gpio_mode mode); ++ ++void dal_hw_gpio_destruct( ++ struct hw_gpio *pin); ++ ++enum gpio_result dal_hw_gpio_set_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t value); ++ ++enum gpio_result dal_hw_gpio_change_mode( ++ struct hw_gpio_pin *ptr, ++ enum gpio_mode mode); ++ ++void dal_hw_gpio_close( ++ struct hw_gpio_pin *ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.c +new file mode 100644 +index 0000000..057c439 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.c +@@ -0,0 +1,93 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "hw_gpio_pin.h" ++#include "hw_gpio.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_gpio_pad.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++#define FROM_HW_GPIO(ptr) \ ++ container_of((ptr), struct hw_gpio_pad, base) ++ ++#define FROM_HW_GPIO_PIN(ptr) \ ++ FROM_HW_GPIO(container_of((ptr), struct hw_gpio, base)) ++ ++enum gpio_result dal_hw_gpio_pad_get_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t *value) ++{ ++ const struct hw_gpio_pad *pin = FROM_HW_GPIO_PIN(ptr); ++ ++ if (ptr->mode == GPIO_MODE_INTERRUPT) ++ /* in Interrupt mode, ask for interrupt status bit */ ++ return dal_hw_gpio_get_reg_value( ++ ptr->ctx, ++ &pin->gpiopad_int_status, ++ value); ++ else ++ /* for any mode other than Interrupt, ++ * gpio_pad operates as normal GPIO */ ++ return dal_hw_gpio_get_value(ptr, value); ++} ++ ++bool dal_hw_gpio_pad_construct( ++ struct hw_gpio_pad *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ if (!dal_hw_gpio_construct(&pin->base, id, en, ctx)) ++ return false; ++ ++ pin->gpiopad_int_status.addr = 0; ++ pin->gpiopad_int_status.mask = 0; ++ ++ return true; ++} ++ ++void dal_hw_gpio_pad_destruct( ++ struct hw_gpio_pad *pin) ++{ ++ dal_hw_gpio_destruct(&pin->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.h +new file mode 100644 +index 0000000..34b470a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pad.h +@@ -0,0 +1,47 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_GPIO_PAD_H__ ++#define __DAL_HW_GPIO_PAD_H__ ++ ++struct hw_gpio_pad { ++ struct hw_gpio base; ++ struct addr_mask gpiopad_int_status; ++}; ++ ++bool dal_hw_gpio_pad_construct( ++ struct hw_gpio_pad *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx); ++ ++void dal_hw_gpio_pad_destruct( ++ struct hw_gpio_pad *pin); ++ ++enum gpio_result dal_hw_gpio_pad_get_value( ++ const struct hw_gpio_pin *ptr, ++ uint32_t *value); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.c +new file mode 100644 +index 0000000..4ab1848 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.c +@@ -0,0 +1,86 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_gpio_pin.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++enum gpio_result dal_hw_gpio_pin_set_config( ++ struct hw_gpio_pin *pin, ++ const struct gpio_config_data *config_data) ++{ ++ /* Attention! ++ * You must override this method in derived class */ ++ ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++} ++ ++enum gpio_result dal_hw_gpio_pin_change_mode( ++ struct hw_gpio_pin *pin, ++ enum gpio_mode mode) ++{ ++ /* Attention! ++ * You must override this method in derived class */ ++ ++ return GPIO_RESULT_NON_SPECIFIC_ERROR; ++} ++ ++bool dal_hw_gpio_pin_construct( ++ struct hw_gpio_pin *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ pin->ctx = ctx; ++ pin->id = id; ++ pin->en = en; ++ pin->mode = GPIO_MODE_UNKNOWN; ++ pin->opened = false; ++ ++ return true; ++} ++ ++void dal_hw_gpio_pin_destruct( ++ struct hw_gpio_pin *pin) ++{ ++ ASSERT(!pin->opened); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.h +new file mode 100644 +index 0000000..d1f2f27 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_gpio_pin.h +@@ -0,0 +1,79 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_GPIO_PIN_H__ ++#define __DAL_HW_GPIO_PIN_H__ ++ ++struct hw_gpio_pin; ++ ++struct hw_gpio_pin_funcs { ++ void (*destroy)( ++ struct hw_gpio_pin **ptr); ++ bool (*open)( ++ struct hw_gpio_pin *pin, ++ enum gpio_mode mode, ++ void *options); ++ enum gpio_result (*get_value)( ++ const struct hw_gpio_pin *pin, ++ uint32_t *value); ++ enum gpio_result (*set_value)( ++ const struct hw_gpio_pin *pin, ++ uint32_t value); ++ enum gpio_result (*set_config)( ++ struct hw_gpio_pin *pin, ++ const struct gpio_config_data *config_data); ++ enum gpio_result (*change_mode)( ++ struct hw_gpio_pin *pin, ++ enum gpio_mode mode); ++ void (*close)( ++ struct hw_gpio_pin *pin); ++}; ++ ++struct hw_gpio_pin { ++ const struct hw_gpio_pin_funcs *funcs; ++ enum gpio_id id; ++ uint32_t en; ++ enum gpio_mode mode; ++ bool opened; ++ struct dc_context *ctx; ++}; ++ ++bool dal_hw_gpio_pin_construct( ++ struct hw_gpio_pin *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx); ++ ++void dal_hw_gpio_pin_destruct( ++ struct hw_gpio_pin *pin); ++ ++enum gpio_result dal_hw_gpio_pin_change_mode( ++ struct hw_gpio_pin *pin, ++ enum gpio_mode mode); ++ ++enum gpio_result dal_hw_gpio_pin_set_config( ++ struct hw_gpio_pin *pin, ++ const struct gpio_config_data *config_data); ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.c +new file mode 100644 +index 0000000..c09d74c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.c +@@ -0,0 +1,88 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++#include "hw_gpio_pin.h" ++#include "hw_gpio.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_hpd.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++static enum gpio_result config_mode( ++ struct hw_gpio *pin, ++ enum gpio_mode mode) ++{ ++ if (mode == GPIO_MODE_INTERRUPT) { ++ /* Interrupt mode supported only by HPD (IrqGpio) pins. */ ++ pin->base.mode = mode; ++ ++ return dal_hw_gpio_set_reg_value( ++ pin->base.ctx, ++ &pin->pin_reg.DC_GPIO_DATA_MASK, ++ 0); ++ } else ++ /* For any mode other than Interrupt, ++ * act as normal GPIO. */ ++ return dal_hw_gpio_config_mode(pin, mode); ++} ++ ++const struct hw_gpio_funcs hw_hpd_func = { ++ .config_mode = config_mode, ++}; ++ ++bool dal_hw_hpd_construct( ++ struct hw_hpd *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx) ++{ ++ if (!dal_hw_gpio_construct(&pin->base, id, en, ctx)) ++ return false; ++ pin->base.funcs = &hw_hpd_func; ++ return true; ++} ++ ++void dal_hw_hpd_destruct( ++ struct hw_hpd *pin) ++{ ++ dal_hw_gpio_destruct(&pin->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.h +new file mode 100644 +index 0000000..3fb82df +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_hpd.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_HPD_H__ ++#define __DAL_HW_HPD_H__ ++ ++struct hw_hpd { ++ struct hw_gpio base; ++}; ++ ++#define HW_HPD_FROM_BASE(hw_gpio) \ ++ container_of((HW_GPIO_FROM_BASE(hw_gpio)), struct hw_hpd, base) ++ ++bool dal_hw_hpd_construct( ++ struct hw_hpd *pin, ++ enum gpio_id id, ++ uint32_t en, ++ struct dc_context *ctx); ++ ++void dal_hw_hpd_destruct( ++ struct hw_hpd *pin); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c +new file mode 100644 +index 0000000..96e135f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c +@@ -0,0 +1,67 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_types.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "hw_translate.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/hw_translate_dce110.h" ++#endif ++ ++/* ++ * This unit ++ */ ++ ++bool dal_hw_translate_init( ++ struct hw_translate *translate, ++ enum dce_version dce_version) ++{ ++ switch (dce_version) { ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ dal_hw_translate_dce110_init(translate); ++ return true; ++#endif ++ default: ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.h b/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.h +new file mode 100644 +index 0000000..d5740ac +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.h +@@ -0,0 +1,49 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_TRANSLATE_H__ ++#define __DAL_HW_TRANSLATE_H__ ++ ++struct hw_translate_funcs { ++ bool (*offset_to_id)( ++ uint32_t offset, ++ uint32_t mask, ++ enum gpio_id *id, ++ uint32_t *en); ++ bool (*id_to_offset)( ++ enum gpio_id id, ++ uint32_t en, ++ struct gpio_pin_info *info); ++}; ++ ++struct hw_translate { ++ const struct hw_translate_funcs *funcs; ++}; ++ ++bool dal_hw_translate_init( ++ struct hw_translate *translate, ++ enum dce_version dce_version); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/irq.c b/drivers/gpu/drm/amd/dal/dc/gpio/irq.c +new file mode 100644 +index 0000000..382b89f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/irq.c +@@ -0,0 +1,181 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/gpio_interface.h" ++#include "include/irq_interface.h" ++#include "include/gpio_service_interface.h" ++#include "hw_gpio_pin.h" ++#include "hw_translate.h" ++#include "hw_factory.h" ++#include "gpio_service.h" ++#include "gpio.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "irq.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++enum gpio_result dal_irq_open( ++ struct irq *irq) ++{ ++ return dal_gpio_open(irq->pin, GPIO_MODE_INTERRUPT); ++} ++ ++enum gpio_result dal_irq_get_value( ++ const struct irq *irq, ++ uint32_t *value) ++{ ++ return dal_gpio_get_value(irq->pin, value); ++} ++ ++enum dc_irq_source dal_irq_get_source( ++ const struct irq *irq) ++{ ++ enum gpio_id id = dal_gpio_get_id(irq->pin); ++ ++ switch (id) { ++ case GPIO_ID_HPD: ++ return (enum dc_irq_source)(DC_IRQ_SOURCE_HPD1 + ++ dal_gpio_get_enum(irq->pin)); ++ case GPIO_ID_GPIO_PAD: ++ return (enum dc_irq_source)(DC_IRQ_SOURCE_GPIOPAD0 + ++ dal_gpio_get_enum(irq->pin)); ++ default: ++ return DC_IRQ_SOURCE_INVALID; ++ } ++} ++ ++enum dc_irq_source dal_irq_get_rx_source( ++ const struct irq *irq) ++{ ++ enum gpio_id id = dal_gpio_get_id(irq->pin); ++ ++ switch (id) { ++ case GPIO_ID_HPD: ++ return (enum dc_irq_source)(DC_IRQ_SOURCE_HPD1RX + ++ dal_gpio_get_enum(irq->pin)); ++ default: ++ return DC_IRQ_SOURCE_INVALID; ++ } ++} ++ ++enum gpio_result dal_irq_setup_hpd_filter( ++ struct irq *irq, ++ struct gpio_hpd_config *config) ++{ ++ struct gpio_config_data config_data; ++ ++ if (!config) ++ return GPIO_RESULT_INVALID_DATA; ++ ++ config_data.type = GPIO_CONFIG_TYPE_HPD; ++ config_data.config.hpd = *config; ++ ++ return dal_gpio_set_config(irq->pin, &config_data); ++} ++ ++void dal_irq_close( ++ struct irq *irq) ++{ ++ dal_gpio_close(irq->pin); ++} ++ ++/* ++ * @brief ++ * Creation and destruction ++ */ ++ ++struct irq *dal_gpio_create_irq( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en) ++{ ++ struct irq *irq; ++ ++ switch (id) { ++ case GPIO_ID_HPD: ++ case GPIO_ID_GPIO_PAD: ++ break; ++ default: ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ irq = dc_service_alloc(service->ctx, sizeof(struct irq)); ++ ++ if (!irq) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ irq->pin = dal_gpio_service_create_gpio_ex( ++ service, id, en, GPIO_PIN_OUTPUT_STATE_DEFAULT); ++ irq->ctx = service->ctx; ++ ++ if (irq->pin) ++ return irq; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(service->ctx, irq); ++ ++ return NULL; ++} ++ ++static void destruct(struct irq *irq) ++{ ++ dal_irq_close(irq); ++ dal_gpio_service_destroy_gpio(&irq->pin); ++ ++} ++ ++void dal_gpio_destroy_irq( ++ struct irq **irq) ++{ ++ if (!irq || !*irq) { ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ ++ destruct(*irq); ++ dc_service_free((*irq)->ctx, *irq); ++ ++ *irq = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/irq.h b/drivers/gpu/drm/amd/dal/dc/gpio/irq.h +new file mode 100644 +index 0000000..b69375c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/irq.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IRQ_H__ ++#define __DAL_IRQ_H__ ++ ++struct irq { ++ struct gpio *pin; ++ struct dc_context *ctx; ++}; ++ ++struct irq *dal_gpio_create_irq( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en); ++ ++void dal_gpio_destroy_irq( ++ struct irq **ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/Makefile b/drivers/gpu/drm/amd/dal/dc/gpu/Makefile +new file mode 100644 +index 0000000..d3d6faf +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/Makefile +@@ -0,0 +1,26 @@ ++# ++# Makefile for the 'gpu' sub-component of DAL. ++# It provides the control and status of HW adapter resources, ++# that are global for the ASIC and sharable between pipes. ++ ++GPU = calc_pll_clock_source.o clock_source.o \ ++dc_clock_generator.o display_clock.o divider_range.o \ ++ext_clock_source.o pll_clock_source.o ++ ++AMD_DAL_GPU = $(addprefix $(AMDDALPATH)/dc/gpu/,$(GPU)) ++ ++AMD_DAL_FILES += $(AMD_DAL_GPU) ++ ++ ++############################################################################### ++# DCE 110 family ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++GPU_DCE110 = display_clock_dce110.o \ ++ pll_clock_source_dce110.o ext_clock_source_dce110.o \ ++ vce_clock_source_dce110.o dc_clock_gating_dce110.o ++ ++AMD_DAL_GPU_DCE110 = $(addprefix $(AMDDALPATH)/dc/gpu/dce110/,$(GPU_DCE110)) ++ ++AMD_DAL_FILES += $(AMD_DAL_GPU_DCE110) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.c b/drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.c +new file mode 100644 +index 0000000..7c94733 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.c +@@ -0,0 +1,407 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "calc_pll_clock_source.h" ++#include "include/bios_parser_interface.h" ++#include "include/logger_interface.h" ++ ++/** ++* Function: calculate_fb_and_fractional_fb_divider ++* ++* * DESCRIPTION: Calculates feedback and fractional feedback dividers values ++* ++*PARAMETERS: ++* targetPixelClock Desired frequency in 10 KHz ++* ref_divider Reference divider (already known) ++* postDivider Post Divider (already known) ++* feedback_divider_param Pointer where to store ++* calculated feedback divider value ++* fract_feedback_divider_param Pointer where to store ++* calculated fract feedback divider value ++* ++*RETURNS: ++* It fills the locations pointed by feedback_divider_param ++* and fract_feedback_divider_param ++* It returns - true if feedback divider not 0 ++* - false should never happen) ++*/ ++static bool calculate_fb_and_fractional_fb_divider( ++ struct calc_pll_clock_source *calc_pll_cs, ++ uint32_t target_pix_clk_khz, ++ uint32_t ref_divider, ++ uint32_t post_divider, ++ uint32_t *feedback_divider_param, ++ uint32_t *fract_feedback_divider_param) ++{ ++ uint64_t feedback_divider; ++ ++ feedback_divider = ++ (uint64_t)(target_pix_clk_khz * ref_divider * post_divider); ++ feedback_divider *= 10; ++ /* additional factor, since we divide by 10 afterwards */ ++ feedback_divider *= (uint64_t)(calc_pll_cs->fract_fb_divider_factor); ++ feedback_divider = div_u64(feedback_divider, calc_pll_cs->ref_freq_khz); ++ ++/*Round to the number of precision ++ * The following code replace the old code (ullfeedbackDivider + 5)/10 ++ * for example if the difference between the number ++ * of fractional feedback decimal point and the fractional FB Divider precision ++ * is 2 then the equation becomes (ullfeedbackDivider + 5*100) / (10*100))*/ ++ ++ feedback_divider += (uint64_t) ++ (5 * calc_pll_cs->fract_fb_divider_precision_factor); ++ feedback_divider = ++ div_u64(feedback_divider, ++ calc_pll_cs->fract_fb_divider_precision_factor * 10); ++ feedback_divider *= (uint64_t) ++ (calc_pll_cs->fract_fb_divider_precision_factor); ++ ++ *feedback_divider_param = ++ div_u64_rem( ++ feedback_divider, ++ calc_pll_cs->fract_fb_divider_factor, ++ fract_feedback_divider_param); ++ ++ if (*feedback_divider_param != 0) ++ return true; ++ return false; ++} ++ ++/** ++*calc_fb_divider_checking_tolerance ++* ++*DESCRIPTION: Calculates Feedback and Fractional Feedback divider values ++* for passed Reference and Post divider, checking for tolerance. ++*PARAMETERS: ++* pll_settings Pointer to structure ++* ref_divider Reference divider (already known) ++* postDivider Post Divider (already known) ++* tolerance Tolerance for Calculated Pixel Clock to be within ++* ++*RETURNS: ++* It fills the PLLSettings structure with PLL Dividers values ++* if calculated values are within required tolerance ++* It returns - true if eror is within tolerance ++* - false if eror is not within tolerance ++*/ ++static bool calc_fb_divider_checking_tolerance( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct pll_settings *pll_settings, ++ uint32_t ref_divider, ++ uint32_t post_divider, ++ uint32_t tolerance) ++{ ++ uint32_t feedback_divider; ++ uint32_t fract_feedback_divider; ++ uint32_t actual_calculated_clock_khz; ++ uint32_t abs_err; ++ uint64_t actual_calc_clk_khz; ++ ++ calculate_fb_and_fractional_fb_divider( ++ calc_pll_cs, ++ pll_settings->adjusted_pix_clk, ++ ref_divider, ++ post_divider, ++ &feedback_divider, ++ &fract_feedback_divider); ++ ++ /*Actual calculated value*/ ++ actual_calc_clk_khz = (uint64_t)(feedback_divider * ++ calc_pll_cs->fract_fb_divider_factor) + ++ fract_feedback_divider; ++ actual_calc_clk_khz *= calc_pll_cs->ref_freq_khz; ++ actual_calc_clk_khz = ++ div_u64(actual_calc_clk_khz, ++ ref_divider * post_divider * ++ calc_pll_cs->fract_fb_divider_factor); ++ ++ actual_calculated_clock_khz = (uint32_t)(actual_calc_clk_khz); ++ ++ abs_err = (actual_calculated_clock_khz > ++ pll_settings->adjusted_pix_clk) ++ ? actual_calculated_clock_khz - ++ pll_settings->adjusted_pix_clk ++ : pll_settings->adjusted_pix_clk - ++ actual_calculated_clock_khz; ++ ++ if (abs_err <= tolerance) { ++ /*found good values*/ ++ pll_settings->reference_freq = calc_pll_cs->ref_freq_khz; ++ pll_settings->reference_divider = ref_divider; ++ pll_settings->feedback_divider = feedback_divider; ++ pll_settings->fract_feedback_divider = fract_feedback_divider; ++ pll_settings->pix_clk_post_divider = post_divider; ++ pll_settings->calculated_pix_clk = ++ actual_calculated_clock_khz; ++ pll_settings->vco_freq = ++ actual_calculated_clock_khz * post_divider; ++ return true; ++ } ++ return false; ++} ++ ++static bool calc_pll_dividers_in_range( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct pll_settings *pll_settings, ++ uint32_t min_ref_divider, ++ uint32_t max_ref_divider, ++ uint32_t min_post_divider, ++ uint32_t max_post_divider, ++ uint32_t err_tolerance) ++{ ++ uint32_t ref_divider; ++ uint32_t post_divider; ++ uint32_t tolerance; ++ ++/* This is err_tolerance / 10000 = 0.0025 - acceptable error of 0.25% ++ * This is errorTolerance / 10000 = 0.0001 - acceptable error of 0.01%*/ ++ tolerance = (pll_settings->adjusted_pix_clk * err_tolerance) / ++ 10000; ++ if (tolerance < CALC_PLL_CLK_SRC_ERR_TOLERANCE) ++ tolerance = CALC_PLL_CLK_SRC_ERR_TOLERANCE; ++ ++ for ( ++ post_divider = max_post_divider; ++ post_divider >= min_post_divider; ++ --post_divider) { ++ for ( ++ ref_divider = min_ref_divider; ++ ref_divider <= max_ref_divider; ++ ++ref_divider) { ++ if (calc_fb_divider_checking_tolerance( ++ calc_pll_cs, ++ pll_settings, ++ ref_divider, ++ post_divider, ++ tolerance)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++uint32_t dal_clock_source_calculate_pixel_clock_pll_dividers( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct pll_settings *pll_settings) ++{ ++ uint32_t err_tolerance; ++ uint32_t min_post_divider; ++ uint32_t max_post_divider; ++ uint32_t min_ref_divider; ++ uint32_t max_ref_divider; ++ ++ if (pll_settings->adjusted_pix_clk == 0) { ++ dal_logger_write(calc_pll_cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s Bad requested pixel clock", __func__); ++ return MAX_PLL_CALC_ERROR; ++ } ++ ++/* 1) Find Post divider ranges */ ++ if (pll_settings->pix_clk_post_divider) { ++ min_post_divider = pll_settings->pix_clk_post_divider; ++ max_post_divider = pll_settings->pix_clk_post_divider; ++ } else { ++ min_post_divider = calc_pll_cs->min_pix_clock_pll_post_divider; ++ if (min_post_divider * pll_settings->adjusted_pix_clk < ++ calc_pll_cs->min_vco_khz) { ++ min_post_divider = calc_pll_cs->min_vco_khz / ++ pll_settings->adjusted_pix_clk; ++ if ((min_post_divider * ++ pll_settings->adjusted_pix_clk) < ++ calc_pll_cs->min_vco_khz) ++ min_post_divider++; ++ } ++ ++ max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider; ++ if (max_post_divider * pll_settings->adjusted_pix_clk ++ > calc_pll_cs->max_vco_khz) ++ max_post_divider = calc_pll_cs->max_vco_khz / ++ pll_settings->adjusted_pix_clk; ++ } ++ ++/* 2) Find Reference divider ranges ++ * When SS is enabled, or for Display Port even without SS, ++ * pll_settings->referenceDivider is not zero. ++ * So calculate PPLL FB and fractional FB divider ++ * using the passed reference divider*/ ++ ++ if (pll_settings->reference_divider) { ++ min_ref_divider = pll_settings->reference_divider; ++ max_ref_divider = pll_settings->reference_divider; ++ } else { ++ min_ref_divider = ((calc_pll_cs->ref_freq_khz ++ / calc_pll_cs->max_pll_input_freq_khz) ++ > calc_pll_cs->min_pll_ref_divider) ++ ? calc_pll_cs->ref_freq_khz ++ / calc_pll_cs->max_pll_input_freq_khz ++ : calc_pll_cs->min_pll_ref_divider; ++ ++ max_ref_divider = ((calc_pll_cs->ref_freq_khz ++ / calc_pll_cs->min_pll_input_freq_khz) ++ < calc_pll_cs->max_pll_ref_divider) ++ ? calc_pll_cs->ref_freq_khz / ++ calc_pll_cs->min_pll_input_freq_khz ++ : calc_pll_cs->max_pll_ref_divider; ++ } ++ ++/* If some parameters are invalid we could have scenario when "min">"max" ++ * which produced endless loop later. ++ * We should investigate why we get the wrong parameters. ++ * But to follow the similar logic when "adjustedPixelClock" is set to be 0 ++ * it is better to return here than cause system hang/watchdog timeout later. ++ * ## SVS Wed 15 Jul 2009 */ ++ ++ if (min_post_divider > max_post_divider) { ++ dal_logger_write(calc_pll_cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s Post divider range is invalid", __func__); ++ return MAX_PLL_CALC_ERROR; ++ } ++ ++ if (min_ref_divider > max_ref_divider) { ++ dal_logger_write(calc_pll_cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s Reference divider range is invalid", __func__); ++ return MAX_PLL_CALC_ERROR; ++ } ++ ++/* 3) Try to find PLL dividers given ranges ++ * starting with minimal error tolerance. ++ * Increase error tolerance until PLL dividers found*/ ++ err_tolerance = MAX_PLL_CALC_ERROR; ++ ++ while (!calc_pll_dividers_in_range( ++ calc_pll_cs, ++ pll_settings, ++ min_ref_divider, ++ max_ref_divider, ++ min_post_divider, ++ max_post_divider, ++ err_tolerance)) ++ err_tolerance += (err_tolerance > 10) ++ ? (err_tolerance / 10) ++ : 1; ++ ++ return err_tolerance; ++} ++ ++static bool calc_pll_clock_source_max_vco_construct( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct calc_pll_clock_source_init_data *init_data) ++{ ++ ++ uint32_t i; ++ struct firmware_info fw_info = { { 0 } }; ++ if (calc_pll_cs == NULL || ++ init_data == NULL || ++ init_data->bp == NULL) ++ return false; ++ ++ if (dal_bios_parser_get_firmware_info( ++ init_data->bp, ++ &fw_info) != BP_RESULT_OK) ++ return false; ++ ++ calc_pll_cs->ctx = init_data->ctx; ++ calc_pll_cs->ref_freq_khz = fw_info.pll_info.crystal_frequency; ++ calc_pll_cs->min_vco_khz = ++ fw_info.pll_info.min_output_pxl_clk_pll_frequency; ++ calc_pll_cs->max_vco_khz = ++ fw_info.pll_info.max_output_pxl_clk_pll_frequency; ++ ++ if (init_data->max_override_input_pxl_clk_pll_freq_khz != 0) ++ calc_pll_cs->max_pll_input_freq_khz = ++ init_data->max_override_input_pxl_clk_pll_freq_khz; ++ else ++ calc_pll_cs->max_pll_input_freq_khz = ++ fw_info.pll_info.max_input_pxl_clk_pll_frequency; ++ ++ if (init_data->min_override_input_pxl_clk_pll_freq_khz != 0) ++ calc_pll_cs->min_pll_input_freq_khz = ++ init_data->min_override_input_pxl_clk_pll_freq_khz; ++ else ++ calc_pll_cs->min_pll_input_freq_khz = ++ fw_info.pll_info.min_input_pxl_clk_pll_frequency; ++ ++ calc_pll_cs->min_pix_clock_pll_post_divider = ++ init_data->min_pix_clk_pll_post_divider; ++ calc_pll_cs->max_pix_clock_pll_post_divider = ++ init_data->max_pix_clk_pll_post_divider; ++ calc_pll_cs->min_pll_ref_divider = ++ init_data->min_pll_ref_divider; ++ calc_pll_cs->max_pll_ref_divider = ++ init_data->max_pll_ref_divider; ++ ++ if (init_data->num_fract_fb_divider_decimal_point == 0 || ++ init_data->num_fract_fb_divider_decimal_point_precision > ++ init_data->num_fract_fb_divider_decimal_point) { ++ dal_logger_write(calc_pll_cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "The dec point num or precision is incorrect!"); ++ return false; ++ } ++ if (init_data->num_fract_fb_divider_decimal_point_precision == 0) { ++ dal_logger_write(calc_pll_cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "Incorrect fract feedback divider precision num!"); ++ return false; ++ } ++ ++ calc_pll_cs->fract_fb_divider_decimal_points_num = ++ init_data->num_fract_fb_divider_decimal_point; ++ calc_pll_cs->fract_fb_divider_precision = ++ init_data->num_fract_fb_divider_decimal_point_precision; ++ calc_pll_cs->fract_fb_divider_factor = 1; ++ for (i = 0; i < calc_pll_cs->fract_fb_divider_decimal_points_num; ++i) ++ calc_pll_cs->fract_fb_divider_factor *= 10; ++ ++ calc_pll_cs->fract_fb_divider_precision_factor = 1; ++ for ( ++ i = 0; ++ i < (calc_pll_cs->fract_fb_divider_decimal_points_num - ++ calc_pll_cs->fract_fb_divider_precision); ++ ++i) ++ calc_pll_cs->fract_fb_divider_precision_factor *= 10; ++ ++ return true; ++} ++ ++bool dal_calc_pll_clock_source_max_vco_init( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct calc_pll_clock_source_init_data *init_data) ++{ ++ return calc_pll_clock_source_max_vco_construct( ++ calc_pll_cs, init_data); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.h b/drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.h +new file mode 100644 +index 0000000..be44d06 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/calc_pll_clock_source.h +@@ -0,0 +1,79 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_CALC_PLL_CLOCK_SOURCE_H__ ++#define __DAL_CALC_PLL_CLOCK_SOURCE_H__ ++ ++#include "include/clock_source_types.h" ++ ++struct calc_pll_clock_source_init_data { ++ struct bios_parser *bp; ++ uint32_t min_pix_clk_pll_post_divider; ++ uint32_t max_pix_clk_pll_post_divider; ++ uint32_t min_pll_ref_divider; ++ uint32_t max_pll_ref_divider; ++ uint32_t min_override_input_pxl_clk_pll_freq_khz; ++/* if not 0, override the firmware info */ ++ ++ uint32_t max_override_input_pxl_clk_pll_freq_khz; ++/* if not 0, override the firmware info */ ++ ++ uint32_t num_fract_fb_divider_decimal_point; ++/* number of decimal point for fractional feedback divider value */ ++ ++ uint32_t num_fract_fb_divider_decimal_point_precision; ++/* number of decimal point to round off for fractional feedback divider value*/ ++ struct dc_context *ctx; ++ ++}; ++#define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1 ++struct calc_pll_clock_source { ++ uint32_t ref_freq_khz; ++ uint32_t min_pix_clock_pll_post_divider; ++ uint32_t max_pix_clock_pll_post_divider; ++ uint32_t min_pll_ref_divider; ++ uint32_t max_pll_ref_divider; ++ ++ uint32_t max_vco_khz; ++ uint32_t min_vco_khz; ++ uint32_t min_pll_input_freq_khz; ++ uint32_t max_pll_input_freq_khz; ++ ++ uint32_t fract_fb_divider_decimal_points_num; ++ uint32_t fract_fb_divider_factor; ++ uint32_t fract_fb_divider_precision; ++ uint32_t fract_fb_divider_precision_factor; ++ struct dc_context *ctx; ++}; ++ ++ ++bool dal_calc_pll_clock_source_max_vco_init( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct calc_pll_clock_source_init_data *init_data); ++ ++uint32_t dal_clock_source_calculate_pixel_clock_pll_dividers( ++ struct calc_pll_clock_source *calc_pll_cs, ++ struct pll_settings *pll_settings); ++ ++ ++#endif /*__DAL_CALC_PLL_CLOCK_SOURCE_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/clock_source.c b/drivers/gpu/drm/amd/dal/dc/gpu/clock_source.c +new file mode 100644 +index 0000000..8e700ea +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/clock_source.c +@@ -0,0 +1,649 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/adapter_service_interface.h" ++#include "include/bios_parser_interface.h" ++#include "include/grph_object_id.h" ++#include "include/clock_source_interface.h" ++#include "include/logger_interface.h" ++ ++#include "clock_source.h" ++#include "pll_clock_source.h" ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/ext_clock_source_dce110.h" ++#include "dce110/pll_clock_source_dce110.h" ++#include "dce110/vce_clock_source_dce110.h" ++#endif ++ ++ ++struct clock_source *dal_clock_source_create( ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ enum dce_version dce_ver = ++ dal_adapter_service_get_dce_version(clk_src_init_data->as); ++ enum clock_source_id clk_src_id = ++ dal_graphics_object_id_get_clock_source_id( ++ clk_src_init_data->clk_src_id); ++ switch (dce_ver) { ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ break; ++ case DCE_VERSION_11_0: ++ { ++ switch (clk_src_id) { ++ case CLOCK_SOURCE_ID_PLL0: ++ /* fall through */ ++ case CLOCK_SOURCE_ID_PLL1: ++ /* fall through */ ++ case CLOCK_SOURCE_ID_PLL2: ++ return dal_pll_clock_source_dce110_create( ++ clk_src_init_data); ++ case CLOCK_SOURCE_ID_EXTERNAL: ++ return dal_ext_clock_source_dce110_create( ++ clk_src_init_data); ++ case CLOCK_SOURCE_ID_VCE: ++ return dal_vce_clock_source_dce110_create( ++ clk_src_init_data); ++ default: ++ return NULL; ++ } ++ } ++ break; ++#endif ++ default: ++ dal_logger_write(clk_src_init_data->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "Clock Source (id %d): not supported DCE version %d", ++ clk_src_id, ++ dce_ver); ++ ASSERT_CRITICAL(false); ++ break; ++ } ++ return NULL; ++} ++ ++const struct spread_spectrum_data *dal_clock_source_get_ss_data_entry( ++ struct clock_source *clk_src, ++ enum signal_type signal, ++ uint32_t pix_clk_khz) ++{ ++ ++ uint32_t entrys_num; ++ uint32_t i; ++ struct spread_spectrum_data *ss_parm = NULL; ++ struct spread_spectrum_data *ret = NULL; ++ ++ switch (signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ ss_parm = clk_src->dvi_ss_params; ++ entrys_num = clk_src->dvi_ss_params_cnt; ++ break; ++ ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ ss_parm = clk_src->hdmi_ss_params; ++ entrys_num = clk_src->hdmi_ss_params_cnt; ++ break; ++ ++ case SIGNAL_TYPE_LVDS: ++ ss_parm = clk_src->ep_ss_params; ++ entrys_num = clk_src->ep_ss_params_cnt; ++ break; ++ ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ ss_parm = clk_src->dp_ss_params; ++ entrys_num = clk_src->dp_ss_params_cnt; ++ break; ++ ++ default: ++ ss_parm = NULL; ++ entrys_num = 0; ++ break; ++ } ++ ++ if (ss_parm == NULL) ++ return ret; ++ ++ for (i = 0; i < entrys_num; ++i, ++ss_parm) { ++ if (ss_parm->freq_range_khz >= pix_clk_khz) { ++ ret = ss_parm; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++bool dal_clock_source_base_adjust_dto_pix_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requested_pix_clk_hz) ++{ ++ return false; ++} ++ ++/* Adjust clock to match given pixel rate (SS/DeepColor compensated)*/ ++bool dal_clock_source_base_adjust_pll_pixel_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requestedPixelClockInHz) ++{ ++ return false; ++} ++ ++static uint32_t retrieve_raw_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params) ++{ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ return clk_src->funcs->retrieve_dto_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ else ++ return clk_src->funcs->retrieve_pll_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++} ++ ++ ++ ++bool dal_clock_source_adjust_pxl_clk_by_pxl_amount( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ int32_t pix_num) ++{ ++ ++ uint32_t cur_pix_rate_hz; ++ uint32_t reqested_pix_rate_hz; ++ bool success = false; ++ ++ if (pix_clk_params == NULL) ++ return false; ++ ++ cur_pix_rate_hz = retrieve_raw_pix_rate_hz(clk_src, pix_clk_params); ++ reqested_pix_rate_hz = cur_pix_rate_hz + pix_num; ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[start]: Current(Raw): %u,%03u,%03uHz, Requested(Raw): %u,%03u,%03uHz\n", ++ __func__, ++ (cur_pix_rate_hz / 1000000), ++ (cur_pix_rate_hz / 1000) % 1000, ++ (cur_pix_rate_hz % 1000), ++ (reqested_pix_rate_hz / 1000000), ++ (reqested_pix_rate_hz / 1000) % 1000, ++ (reqested_pix_rate_hz % 1000)); ++ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ success = clk_src->funcs->adjust_dto_pixel_rate(clk_src, ++ pix_clk_params, ++ reqested_pix_rate_hz); ++ else ++ success = clk_src->funcs->adjust_pll_pixel_rate( ++ clk_src, ++ pix_clk_params, ++ reqested_pix_rate_hz); ++ ++ cur_pix_rate_hz = retrieve_raw_pix_rate_hz(clk_src, pix_clk_params); ++ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[end]: Current(Raw): %u,%03u,%03uHz, Requested(Raw): %u,%03u,%03uHz\n\n", ++ __func__, ++ (cur_pix_rate_hz / 1000000), ++ (cur_pix_rate_hz / 1000) % 1000, ++ (cur_pix_rate_hz % 1000), ++ (reqested_pix_rate_hz / 1000000), ++ (reqested_pix_rate_hz / 1000) % 1000, ++ (reqested_pix_rate_hz % 1000)); ++ ++ return success; ++} ++ ++/***************************/ ++/* private methods section */ ++/***************************/ ++ ++void dal_clock_source_get_ss_info_from_atombios( ++ struct clock_source *clk_src, ++ enum as_signal_type as_signal, ++ struct spread_spectrum_data *spread_spectrum_data[], ++ uint32_t *ss_entries_num) ++{ ++ enum bp_result bp_result = BP_RESULT_FAILURE; ++ struct spread_spectrum_info *ss_info; ++ struct spread_spectrum_data *ss_data; ++ struct spread_spectrum_info *ss_info_cur; ++ struct spread_spectrum_data *ss_data_cur; ++ uint32_t i; ++ ++ if (ss_entries_num == NULL) { ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "Invalid entry !!!\n"); ++ return; ++ } ++ if (spread_spectrum_data == NULL) { ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "Invalid array pointer!!!\n"); ++ return; ++ } ++ ++ spread_spectrum_data[0] = NULL; ++ *ss_entries_num = 0; ++ ++ *ss_entries_num = dal_bios_parser_get_ss_entry_number( ++ clk_src->bios_parser, ++ as_signal); ++ if (*ss_entries_num == 0) ++ return; ++ ++ ss_info = dc_service_alloc(clk_src->ctx, sizeof(struct spread_spectrum_info) ++ * (*ss_entries_num)); ++ ss_info_cur = ss_info; ++ if (ss_info == NULL) ++ return; ++ ++ ss_data = dc_service_alloc(clk_src->ctx, sizeof(struct spread_spectrum_data) * ++ (*ss_entries_num)); ++ if (ss_data == NULL) ++ goto out_free_info; ++ ++ for (i = 0, ss_info_cur = ss_info; ++ i < (*ss_entries_num); ++ ++i, ++ss_info_cur) { ++ bp_result = dal_bios_parser_get_spread_spectrum_info( ++ clk_src->bios_parser, ++ as_signal, ++ i, ++ ss_info_cur); ++ if (bp_result != BP_RESULT_OK) ++ goto out_free_data; ++ } ++ ++ for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data; ++ i < (*ss_entries_num); ++ ++i, ++ss_info_cur, ++ss_data_cur) { ++ ++ if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) { ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "Invalid ATOMBIOS SS Table!!!\n"); ++ goto out_free_data; ++ } ++ ++ /* for HDMI check SS percentage, ++ * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/ ++ if (as_signal == AS_SIGNAL_TYPE_HDMI ++ && ss_info_cur->spread_spectrum_percentage > 6){ ++ /* invalid input, do nothing */ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "Invalid SS percentage "); ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "for HDMI in ATOMBIOS info Table!!!\n"); ++ continue; ++ } ++ if (ss_info_cur->spread_percentage_divider == 1000) { ++ /* Keep previous precision from ATOMBIOS for these ++ * in case new precision set by ATOMBIOS for these ++ * (otherwise all code in DCE specific classes ++ * for all previous ASICs would need ++ * to be updated for SS calculations, ++ * Audio SS compensation and DP DTO SS compensation ++ * which assumes fixed SS percentage Divider = 100)*/ ++ ss_info_cur->spread_spectrum_percentage /= 10; ++ ss_info_cur->spread_percentage_divider = 100; ++ } ++ ++ ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range; ++ ss_data_cur->percentage = ++ ss_info_cur->spread_spectrum_percentage; ++ ss_data_cur->percentage_divider = ++ ss_info_cur->spread_percentage_divider; ++ ss_data_cur->modulation_freq_hz = ++ ss_info_cur->spread_spectrum_range; ++ ++ if (ss_info_cur->type.CENTER_MODE) ++ ss_data_cur->flags.CENTER_SPREAD = 1; ++ ++ if (ss_info_cur->type.EXTERNAL) ++ ss_data_cur->flags.EXTERNAL_SS = 1; ++ ++ } ++ ++ *spread_spectrum_data = ss_data; ++ dc_service_free(clk_src->ctx, ss_info); ++ return; ++ ++out_free_data: ++ dc_service_free(clk_src->ctx, ss_data); ++ *ss_entries_num = 0; ++out_free_info: ++ dc_service_free(clk_src->ctx, ss_info); ++} ++ ++uint32_t dal_clock_source_base_retrieve_dto_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params) ++{ ++ return 0; ++} ++ ++ ++uint32_t dal_clock_source_base_retrieve_pll_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params) ++{ ++ return 0; ++} ++ ++/*****************************/ ++/* interface methods section */ ++/*****************************/ ++ ++enum clock_source_id dal_clock_source_get_id( ++ const struct clock_source *clk_src) ++{ ++ return clk_src->clk_src_id; ++} ++ ++bool dal_clock_source_is_clk_src_with_fixed_freq( ++ const struct clock_source *clk_src) ++{ ++ return clk_src->is_clock_source_with_fixed_freq; ++} ++ ++const struct graphics_object_id dal_clock_source_get_graphics_object_id( ++ const struct clock_source *clk_src) ++{ ++ return clk_src->id; ++} ++ ++enum clock_sharing_level dal_clock_souce_get_clk_sharing_lvl( ++ const struct clock_source *clk_src) ++{ ++ return clk_src->clk_sharing_lvl; ++} ++ ++uint32_t dal_clock_source_get_pix_clk_dividers( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ return clk_src->funcs-> ++ get_pix_clk_dividers(clk_src, pix_clk_params, pll_settings); ++} ++ ++bool dal_clock_source_program_pix_clk( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ return clk_src->funcs-> ++ program_pix_clk(clk_src, pix_clk_params, pll_settings); ++} ++ ++/* TODO save/restore FP was here */ ++bool dal_clock_source_adjust_pxl_clk_by_ref_pixel_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t pix_rate_hz) ++{ ++ uint32_t current_pix_rate_hz = 0; ++ uint32_t raw_cur_pix_rate_hz = 0; ++ uint32_t raw_pix_rate_hz = pix_rate_hz; ++ bool success = false; ++ ++ if (pix_clk_params == NULL || pix_rate_hz == 0) ++ return false; ++ ++ current_pix_rate_hz = retrieve_raw_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ raw_cur_pix_rate_hz = current_pix_rate_hz; ++ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[start]: Current: %u,%03u,%03uHz, Requested: %u,%03u,%03uHz\n", ++ __func__, ++ (current_pix_rate_hz / 1000000), ++ (current_pix_rate_hz / 1000) % 1000, ++ (current_pix_rate_hz % 1000), ++ (pix_rate_hz / 1000000), ++ (pix_rate_hz / 1000) % 1000, ++ (pix_rate_hz % 1000)); ++ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ success = clk_src->funcs->adjust_dto_pixel_rate( ++ clk_src, ++ pix_clk_params, ++ raw_pix_rate_hz); ++ else ++ success = clk_src->funcs->adjust_pll_pixel_rate( ++ clk_src, ++ pix_clk_params, ++ raw_pix_rate_hz); ++ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ raw_cur_pix_rate_hz = clk_src->funcs-> ++ retrieve_dto_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ else ++ raw_cur_pix_rate_hz = clk_src->funcs-> ++ retrieve_pll_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ ++ current_pix_rate_hz = raw_cur_pix_rate_hz; ++ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[end]: Current: %u,%03u,%03uHz, Requested: %u,%03u,%03uHz\n", ++ __func__, ++ (current_pix_rate_hz / 1000000), ++ (current_pix_rate_hz / 1000) % 1000, ++ (current_pix_rate_hz % 1000), ++ (pix_rate_hz / 1000000), ++ (pix_rate_hz / 1000) % 1000, ++ (pix_rate_hz % 1000)); ++ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[end]: Current(Raw): %u,%03u,%03uHz, Requested(Raw): %u,%03u,%03uHz\n\n", ++ __func__, ++ (raw_cur_pix_rate_hz / 1000000), ++ (raw_cur_pix_rate_hz / 1000) % 1000, ++ (raw_cur_pix_rate_hz % 1000), ++ (raw_pix_rate_hz / 1000000), ++ (raw_pix_rate_hz / 1000) % 1000, ++ (raw_pix_rate_hz % 1000)); ++ ++ return success; ++} ++ ++/* TODO store/restore FP was here*/ ++bool dal_clock_source_adjust_pxl_clk_by_pix_amount( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ int32_t pix_num) ++{ ++ bool success = false; ++ uint32_t requested_pix_rate_hz; ++ uint32_t cur_pix_rate_hz = retrieve_raw_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ requested_pix_rate_hz = cur_pix_rate_hz + pix_num; ++ ++ if (pix_clk_params == NULL) ++ return false; ++ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[start]: Current(Raw): %u,%03u,%03uHz, Requested(Raw): %u,%03u,%03uHz\n", ++ __func__, ++ (cur_pix_rate_hz / 1000000), ++ (cur_pix_rate_hz / 1000) % 1000, ++ (cur_pix_rate_hz % 1000), ++ (requested_pix_rate_hz / 1000000), ++ (requested_pix_rate_hz / 1000) % 1000, ++ (requested_pix_rate_hz % 1000)); ++ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ success = clk_src->funcs->adjust_dto_pixel_rate( ++ clk_src, ++ pix_clk_params, ++ requested_pix_rate_hz); ++ else ++ success = clk_src->funcs->adjust_pll_pixel_rate( ++ clk_src, ++ pix_clk_params, ++ requested_pix_rate_hz); ++ ++ cur_pix_rate_hz = retrieve_raw_pix_rate_hz(clk_src, pix_clk_params); ++ ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_SYNC, ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST, ++ "%s[end]: Current(Raw): %u,%03u,%03uHz,Requested(Raw): %u,%03u,%03uHz\n\n", ++ __func__, ++ (cur_pix_rate_hz / 1000000), ++ (cur_pix_rate_hz / 1000) % 1000, ++ (cur_pix_rate_hz % 1000), ++ (requested_pix_rate_hz / 1000000), ++ (requested_pix_rate_hz / 1000) % 1000, ++ (requested_pix_rate_hz % 1000)); ++ ++ return success; ++} ++ ++/* TODO save/restore FP was here*/ ++uint32_t dal_clock_source_retrieve_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params) ++{ ++ uint32_t pixel_rate_hz = 0; ++ ++ if (pix_clk_params == NULL) ++ return pixel_rate_hz; ++ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ pixel_rate_hz = clk_src->funcs->retrieve_dto_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ else ++ pixel_rate_hz = clk_src->funcs->retrieve_pll_pix_rate_hz( ++ clk_src, ++ pix_clk_params); ++ ++ ++ return pixel_rate_hz; ++} ++ ++bool dal_clock_source_construct( ++ struct clock_source *clk_src, ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ if (clk_src_init_data == NULL || clk_src_init_data->as == NULL) ++ return false; ++ clk_src->ctx = clk_src_init_data->ctx; ++ clk_src->id = clk_src_init_data->clk_src_id; ++ clk_src->adapter_service = clk_src_init_data->as; ++ clk_src->bios_parser = dal_adapter_service_get_bios_parser( ++ clk_src_init_data->as); ++ clk_src->turn_off_ds = false; ++ clk_src->clk_src_id = dal_graphics_object_id_get_clock_source_id( ++ clk_src_init_data->clk_src_id); ++ clk_src->is_gen_lock_capable = true; ++/*NOTE is_gen_lock_capable is false only for ext clock source dce80 */ ++ ++ clk_src->ep_ss_params = NULL; ++ clk_src->dp_ss_params = NULL; ++ clk_src->hdmi_ss_params = NULL; ++ clk_src->hdmi_ss_params = NULL; ++ clk_src->ep_ss_params_cnt = 0; ++ clk_src->dp_ss_params_cnt = 0; ++ clk_src->hdmi_ss_params_cnt = 0; ++ clk_src->dvi_ss_params_cnt = 0; ++ clk_src->output_signals = SIGNAL_TYPE_ALL; ++ clk_src->input_signals = SIGNAL_TYPE_ALL; ++ ++ return true; ++} ++ ++void dal_clock_source_destroy(struct clock_source **clk_src) ++{ ++ if (!clk_src || !(*clk_src)) ++ return; ++ ++ (*clk_src)->funcs->destroy(clk_src); ++ ++ *clk_src = NULL; ++} ++ ++bool dal_clock_source_is_input_signal_supported( ++ struct clock_source *clk_src, ++ enum signal_type signal_type) ++{ ++ /* TODO do we need this in clock_source ?? */ ++ return (clk_src->input_signals & signal_type) != 0; ++} ++ ++bool dal_clock_source_is_output_signal_supported( ++ const struct clock_source *clk_src, ++ enum signal_type signal_type) ++{ ++ return (clk_src->output_signals & signal_type) != 0; ++} ++ ++bool dal_clock_source_is_gen_lock_capable(struct clock_source *clk_src) ++{ ++ return clk_src->is_gen_lock_capable; ++} ++ ++bool dal_clock_source_power_down_pll(struct clock_source *clk_src, ++ enum controller_id controller_id) ++{ ++ return clk_src->funcs->power_down_pll(clk_src, controller_id); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/clock_source.h b/drivers/gpu/drm/amd/dal/dc/gpu/clock_source.h +new file mode 100644 +index 0000000..0a83874 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/clock_source.h +@@ -0,0 +1,136 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CLOCK_SOURCE_H__ ++#define __DAL_CLOCK_SOURCE_H__ ++ ++#include "include/adapter_service_types.h" ++#include "include/bios_parser_types.h" ++#include "include/clock_source_interface.h" ++#include "include/clock_source_types.h" ++ ++struct spread_spectrum_data { ++ uint32_t percentage; /*> In unit of 0.01% or 0.001%*/ ++ uint32_t percentage_divider; /*> 100 or 1000 */ ++ uint32_t freq_range_khz; ++ uint32_t modulation_freq_hz; ++ ++ struct spread_spectrum_flags flags; ++}; ++ ++struct clock_source_impl { ++ bool (*switch_dp_clock_source)( ++ struct clock_source *clk_src, ++ enum controller_id, ++ enum clock_source_id); ++ bool (*adjust_pll_pixel_rate)( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requested_pix_clk_hz); ++ bool (*adjust_dto_pixel_rate)( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requested_clk_freq_hz); ++ uint32_t (*retrieve_dto_pix_rate_hz)( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params); ++ uint32_t (*retrieve_pll_pix_rate_hz)( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params); ++ ++ uint32_t (*get_pix_clk_dividers)(struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++ bool (*program_pix_clk)(struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++ bool (*power_down_pll)(struct clock_source *clk_src, ++ enum controller_id); ++ void (*destroy)(struct clock_source **clk_src); ++}; ++ ++void dal_clock_source_get_ss_info_from_atombios( ++ struct clock_source *clk_src, ++ enum as_signal_type as_signal, ++ struct spread_spectrum_data *ss_data[], ++ uint32_t *ss_entries_num); ++uint32_t dal_clock_source_base_retrieve_dto_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params); ++const struct spread_spectrum_data *dal_clock_source_get_ss_data_entry( ++ struct clock_source *clk_src, ++ enum signal_type signal, ++ uint32_t pix_clk_khz); ++/* for PLL and EXT clock sources */ ++struct registers { ++ uint32_t dp_dtox_phase; ++ uint32_t dp_dtox_modulo; ++ uint32_t crtcx_pixel_rate_cntl; ++}; ++ ++struct clock_source { ++ const struct clock_source_impl *funcs; ++ struct graphics_object_id id; ++ enum clock_source_id clk_src_id; ++ struct adapter_service *adapter_service; ++ struct bios_parser *bios_parser; ++ ++ struct spread_spectrum_data *ep_ss_params; ++ uint32_t ep_ss_params_cnt; ++ struct spread_spectrum_data *dp_ss_params; ++ uint32_t dp_ss_params_cnt; ++ ++ struct spread_spectrum_data *hdmi_ss_params; ++ uint32_t hdmi_ss_params_cnt; ++ ++ struct spread_spectrum_data *dvi_ss_params; ++ uint32_t dvi_ss_params_cnt; ++ ++ uint32_t output_signals; ++ uint32_t input_signals; ++ ++ bool turn_off_ds; ++ bool is_gen_lock_capable; /*replacement for virtual method*/ ++ bool is_clock_source_with_fixed_freq; /*replacement for virtual method*/ ++ enum clock_sharing_level clk_sharing_lvl; ++ struct dc_context *ctx; ++}; ++ ++bool dal_clock_source_construct( ++ struct clock_source *clk_src, ++ struct clock_source_init_data *clk_src_init_data); ++bool dal_clock_source_base_adjust_pll_pixel_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requested_pix_clk_hz); ++bool dal_clock_source_base_adjust_dto_pix_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requested_pix_clk_hz); ++uint32_t dal_clock_source_base_retrieve_pll_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.c b/drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.c +new file mode 100644 +index 0000000..f124dba +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.c +@@ -0,0 +1,92 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dc_clock_generator.h" ++ ++void dal_dc_clock_generator_destroy(struct dc_clock_generator **dc) ++{ ++ if (dc == NULL || *dc == NULL) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ (*dc)->funcs->destroy(dc); ++ ++ *dc = NULL; ++} ++ ++void dal_dc_clock_generator_set_display_pipe_mapping( ++ struct dc_clock_generator *dc_clk_gen, ++ struct dccg_mapping_params *params) ++{ ++ dc_clk_gen->funcs->set_display_pipe_mapping(dc_clk_gen, params); ++} ++ ++bool dal_dc_clock_generator_get_dp_ref_clk_ds_params( ++ struct dc_clock_generator *dc_clk_gen, ++ struct dccg_dp_ref_clk_ds_params *params) ++{ ++ return dc_clk_gen->funcs->get_dp_ref_clk_ds_params(dc_clk_gen, params); ++} ++ ++bool dal_dc_clock_generator_enable_gtc_counter( ++ struct dc_clock_generator *dc_clk_gen, ++ uint32_t dprefclk) ++{ ++ return dc_clk_gen->funcs->enable_gtc_counter(dc_clk_gen, dprefclk); ++} ++ ++void dal_dc_clock_generator_disable_gtc_counter( ++ struct dc_clock_generator *dc_clk_gen) ++{ ++ dc_clk_gen->funcs->disable_gtc_counter(dc_clk_gen); ++} ++ ++void dal_dc_clock_generator_set_gtc_group_offset( ++ struct dc_clock_generator *dc_clk_gen, ++ enum gtc_group group_num, ++ uint32_t offset) ++{ ++ dc_clk_gen->funcs->set_gtc_group_offset(dc_clk_gen, group_num, offset); ++} ++ ++void dal_dc_clock_generator_base_set_display_pipe_mapping( ++ struct dc_clock_generator *base, ++ struct dccg_mapping_params *params) ++{ ++ ++} ++ ++bool dal_dc_clock_generator_construct_base( ++ struct dc_clock_generator *base, ++ struct dc_context *ctx ++) ++{ ++ base->ctx = ctx; ++ return true; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.h b/drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.h +new file mode 100644 +index 0000000..d1bf1af +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dc_clock_generator.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DC_CLOCK_GENERATOR_H__ ++#define __DAL_DC_CLOCK_GENERATOR_H__ ++ ++#include "include/dc_clock_generator_interface.h" ++ ++struct dc_clock_generator_funcs { ++ void (*destroy)(struct dc_clock_generator **to_destroy); ++ ++ void (*set_display_pipe_mapping)( ++ struct dc_clock_generator *dc_clk_gen, ++ struct dccg_mapping_params *params); ++ ++ bool (*get_dp_ref_clk_ds_params)( ++ struct dc_clock_generator *dc_clk_gen, ++ struct dccg_dp_ref_clk_ds_params *params); ++ bool (*enable_gtc_counter)( ++ struct dc_clock_generator *dc_clk_gen, ++ uint32_t dprefclk); ++ void (*disable_gtc_counter)( ++ struct dc_clock_generator *dc_clk_gen); ++ void (*set_gtc_group_offset)( ++ struct dc_clock_generator *dc_clk_gen, ++ enum gtc_group group_num, ++ uint32_t offset); ++}; ++struct dc_clock_generator { ++ const struct dc_clock_generator_funcs *funcs; ++ struct dc_context *ctx; ++}; ++bool dal_dc_clock_generator_construct_base( ++ struct dc_clock_generator *base, ++ struct dc_context *ctx ++); ++void dal_dc_clock_generator_base_set_display_pipe_mapping( ++ struct dc_clock_generator *base, ++ struct dccg_mapping_params *params); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.c +new file mode 100644 +index 0000000..e2d4228 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.c +@@ -0,0 +1,90 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/logger_interface.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "dc_clock_gating_dce110.h" ++ ++/****************************************************************************** ++ * Macro definitions ++ *****************************************************************************/ ++ ++#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_GPU, \ ++ "%s:%s()\n", __FILE__, __func__) ++ ++/****************************************************************************** ++ * static functions ++ *****************************************************************************/ ++static void force_hw_base_light_sleep(struct dc_context *ctx) ++{ ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ ++ ++ addr = mmDC_MEM_GLOBAL_PWR_REQ_CNTL; ++ /* Read the mmDC_MEM_GLOBAL_PWR_REQ_CNTL to get the currently ++ * programmed DC_MEM_GLOBAL_PWR_REQ_DIS*/ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_MEM_GLOBAL_PWR_REQ_CNTL, ++ DC_MEM_GLOBAL_PWR_REQ_DIS); ++ ++ dal_write_reg(ctx, addr, value); ++ ++} ++ ++static void enable_hw_base_light_sleep(struct dc_context *ctx) ++{ ++ NOT_IMPLEMENTED(); ++} ++ ++static void disable_sw_manual_control_light_sleep( ++ struct dc_context *ctx) ++{ ++ NOT_IMPLEMENTED(); ++} ++ ++/****************************************************************************** ++ * public functions ++ *****************************************************************************/ ++ ++void dal_dc_clock_gating_dce110_power_up( ++ struct dc_context *ctx, ++ bool enable) ++{ ++ if (enable) { ++ enable_hw_base_light_sleep(ctx); ++ disable_sw_manual_control_light_sleep(ctx); ++ } else { ++ force_hw_base_light_sleep(ctx); ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.h +new file mode 100644 +index 0000000..1bfd75a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/dc_clock_gating_dce110.h +@@ -0,0 +1,33 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DC_CLOCK_GATING_DCE110_H__ ++#define __DAL_DC_CLOCK_GATING_DCE110_H__ ++ ++void dal_dc_clock_gating_dce110_power_up( ++ struct dc_context *ctx, ++ bool enable); ++ ++#endif /* __DAL_DC_CLOCK_GATING_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.c +new file mode 100644 +index 0000000..e582ba0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.c +@@ -0,0 +1,958 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "include/adapter_service_interface.h" ++#include "include/bios_parser_interface.h" ++#include "include/fixed32_32.h" ++#include "include/logger_interface.h" ++ ++#include "../divider_range.h" ++ ++#include "display_clock_dce110.h" ++ ++#define FROM_DISPLAY_CLOCK(base) \ ++ container_of(base, struct display_clock_dce110, disp_clk_base) ++ ++static struct state_dependent_clocks max_clks_by_state[] = { ++/*( dvo not exist in KV)*/ ++/*ClocksStateInvalid - should not be used*/ ++{ .display_clk_khz = 0, .pixel_clk_khz = 0, .dvo_clk_khz = 0 }, ++/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ ++{ .display_clk_khz = 352000, .pixel_clk_khz = 330000, .dvo_clk_khz = 0 }, ++/*ClocksStateLow*/ ++{ .display_clk_khz = 352000, .pixel_clk_khz = 330000, .dvo_clk_khz = 0 }, ++/*ClocksStateNominal*/ ++{ .display_clk_khz = 467000, .pixel_clk_khz = 400000, .dvo_clk_khz = 0 }, ++/*ClocksStatePerformance*/ ++{ .display_clk_khz = 643000, .pixel_clk_khz = 400000, .dvo_clk_khz = 0 } }; ++ ++ ++/* Starting point for each divider range.*/ ++enum divider_range_start { ++ DIVIDER_RANGE_01_START = 200, /* 2.00*/ ++ DIVIDER_RANGE_02_START = 1600, /* 16.00*/ ++ DIVIDER_RANGE_03_START = 3200, /* 32.00*/ ++ DIVIDER_RANGE_SCALE_FACTOR = 100 /* Results are scaled up by 100.*/ ++}; ++ ++/* Array identifiers and count for the divider ranges.*/ ++enum divider_range_count { ++ DIVIDER_RANGE_01 = 0, ++ DIVIDER_RANGE_02, ++ DIVIDER_RANGE_03, ++ DIVIDER_RANGE_MAX /* == 3*/ ++}; ++ ++/* Ranges for divider identifiers (Divider ID or DID) ++ mmDENTIST_DISPCLK_CNTL.DENTIST_DISPCLK_WDIVIDER*/ ++enum divider_id_register_setting { ++ DIVIDER_RANGE_01_BASE_DIVIDER_ID = 0X08, ++ DIVIDER_RANGE_02_BASE_DIVIDER_ID = 0X40, ++ DIVIDER_RANGE_03_BASE_DIVIDER_ID = 0X60, ++ DIVIDER_RANGE_MAX_DIVIDER_ID = 0X80 ++}; ++ ++/* Step size between each divider within a range. ++ Incrementing the DENTIST_DISPCLK_WDIVIDER by one ++ will increment the divider by this much.*/ ++enum divider_range_step_size { ++ DIVIDER_RANGE_01_STEP_SIZE = 25, /* 0.25*/ ++ DIVIDER_RANGE_02_STEP_SIZE = 50, /* 0.50*/ ++ DIVIDER_RANGE_03_STEP_SIZE = 100 /* 1.00 */ ++}; ++ ++static struct divider_range divider_ranges[DIVIDER_RANGE_MAX]; ++ ++#define DCE110_DFS_BYPASS_THRESHOLD_KHZ 400000 ++/***************************************************************************** ++ * static functions ++ *****************************************************************************/ ++ ++/* ++ * store_max_clocks_state ++ * ++ * @brief ++ * Cache the clock state ++ * ++ * @param ++ * struct display_clock *base - [out] cach the state in this structure ++ * enum clocks_state max_clocks_state - [in] state to be stored ++ */ ++static void store_max_clocks_state( ++ struct display_clock *base, ++ enum clocks_state max_clocks_state) ++{ ++ struct display_clock_dce110 *dc = DCLCK110_FROM_BASE(base); ++ ++ switch (max_clocks_state) { ++ case CLOCKS_STATE_LOW: ++ case CLOCKS_STATE_NOMINAL: ++ case CLOCKS_STATE_PERFORMANCE: ++ case CLOCKS_STATE_ULTRA_LOW: ++ dc->max_clks_state = max_clocks_state; ++ break; ++ ++ case CLOCKS_STATE_INVALID: ++ default: ++ /*Invalid Clocks State!*/ ++ ASSERT_CRITICAL(false); ++ break; ++ } ++} ++ ++static enum clocks_state get_min_clocks_state(struct display_clock *base) ++{ ++ return base->cur_min_clks_state; ++} ++ ++static bool set_min_clocks_state( ++ struct display_clock *base, ++ enum clocks_state clocks_state) ++{ ++ struct display_clock_dce110 *dc = DCLCK110_FROM_BASE(base); ++ ++ if (clocks_state > dc->max_clks_state) { ++ /*Requested state exceeds max supported state.*/ ++ dal_logger_write(base->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "Requested state exceeds max supported state"); ++ return false; ++ } else if (clocks_state == base->cur_min_clks_state) { ++ /*if we're trying to set the same state, we can just return ++ * since nothing needs to be done*/ ++ return true; ++ } ++ ++ base->cur_min_clks_state = clocks_state; ++ ++ return true; ++} ++ ++static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc) ++{ ++ uint32_t dispclk_cntl_value; ++ uint32_t dp_ref_clk_cntl_value; ++ uint32_t dp_ref_clk_cntl_src_sel_value; ++ uint32_t dp_ref_clk_khz = 600000; ++ uint32_t target_div = INVALID_DIVIDER; ++ struct display_clock_dce110 *disp_clk = FROM_DISPLAY_CLOCK(dc); ++ ++ /* ASSERT DP Reference Clock source is from DFS*/ ++ dp_ref_clk_cntl_value = dal_read_reg(dc->ctx, ++ mmDPREFCLK_CNTL); ++ ++ dp_ref_clk_cntl_src_sel_value = ++ get_reg_field_value( ++ dp_ref_clk_cntl_value, ++ DPREFCLK_CNTL, DPREFCLK_SRC_SEL); ++ ++ ASSERT(dp_ref_clk_cntl_src_sel_value == 0); ++ ++ /* Read the mmDENTIST_DISPCLK_CNTL to get the currently ++ * programmed DID DENTIST_DPREFCLK_WDIVIDER*/ ++ dispclk_cntl_value = dal_read_reg(dc->ctx, ++ mmDENTIST_DISPCLK_CNTL); ++ ++ /* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/ ++ target_div = dal_divider_range_get_divider( ++ divider_ranges, ++ DIVIDER_RANGE_MAX, ++ get_reg_field_value(dispclk_cntl_value, ++ DENTIST_DISPCLK_CNTL, ++ DENTIST_DPREFCLK_WDIVIDER)); ++ ++ ++ if (target_div != INVALID_DIVIDER) { ++ /* Calculate the current DFS clock, in kHz.*/ ++ dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR ++ * disp_clk->dentist_vco_freq_khz) / target_div; ++ } ++ ++ /* SW will adjust DP REF Clock average value for all purposes ++ * (DP DTO / DP Audio DTO and DP GTC) ++ if clock is spread for all cases: ++ -if SS enabled on DP Ref clock and HW de-spreading enabled with SW ++ calculations for DS_INCR/DS_MODULO (this is planned to be default case) ++ -if SS enabled on DP Ref clock and HW de-spreading enabled with HW ++ calculations (not planned to be used, but average clock should still ++ be valid) ++ -if SS enabled on DP Ref clock and HW de-spreading disabled ++ (should not be case with CIK) then SW should program all rates ++ generated according to average value (case as with previous ASICs) ++ */ ++ if ((disp_clk->ss_on_gpu_pll) && (disp_clk->gpu_pll_ss_divider != 0)) { ++ struct fixed32_32 ss_percentage = dal_fixed32_32_div_int( ++ dal_fixed32_32_from_fraction( ++ disp_clk->gpu_pll_ss_percentage, ++ disp_clk->gpu_pll_ss_divider), 200); ++ struct fixed32_32 adj_dp_ref_clk_khz; ++ ++ ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one, ++ ss_percentage); ++ adj_dp_ref_clk_khz = ++ dal_fixed32_32_mul_int( ++ ss_percentage, ++ dp_ref_clk_khz); ++ dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz); ++ } ++ ++ return dp_ref_clk_khz; ++} ++ ++ ++static void destroy(struct display_clock **base) ++{ ++ struct display_clock_dce110 *dc110; ++ ++ dc110 = DCLCK110_FROM_BASE(*base); ++ ++ dc_service_free((*base)->ctx, dc110); ++ ++ *base = NULL; ++} ++ ++static uint32_t get_validation_clock(struct display_clock *dc) ++{ ++ uint32_t clk = 0; ++ struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc); ++ ++ switch (disp_clk->max_clks_state) { ++ case CLOCKS_STATE_ULTRA_LOW: ++ /*Currently not supported, it has 0 in table entry*/ ++ case CLOCKS_STATE_LOW: ++ clk = max_clks_by_state[CLOCKS_STATE_LOW]. ++ display_clk_khz; ++ break; ++ ++ case CLOCKS_STATE_NOMINAL: ++ clk = max_clks_by_state[CLOCKS_STATE_NOMINAL]. ++ display_clk_khz; ++ break; ++ ++ case CLOCKS_STATE_PERFORMANCE: ++ clk = max_clks_by_state[CLOCKS_STATE_PERFORMANCE]. ++ display_clk_khz; ++ break; ++ ++ case CLOCKS_STATE_INVALID: ++ default: ++ /*Invalid Clocks State*/ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "Invalid clock state"); ++ /* just return the display engine clock for ++ * lowest supported state*/ ++ clk = max_clks_by_state[CLOCKS_STATE_LOW]. ++ display_clk_khz; ++ break; ++ } ++ return clk; ++} ++ ++static struct fixed32_32 get_deep_color_factor(struct min_clock_params *params) ++{ ++ /* DeepColorFactor = IF (HDMI = True, bpp / 24, 1)*/ ++ struct fixed32_32 deep_color_factor = dal_fixed32_32_from_int(1); ++ ++ if (params->signal_type != SIGNAL_TYPE_HDMI_TYPE_A) ++ return deep_color_factor; ++ ++ switch (params->deep_color_depth) { ++ case COLOR_DEPTH_101010: ++ /*deep color ratio for 30bpp is 30/24 = 1.25*/ ++ deep_color_factor = dal_fixed32_32_from_fraction(30, 24); ++ break; ++ ++ case COLOR_DEPTH_121212: ++ /* deep color ratio for 36bpp is 36/24 = 1.5*/ ++ deep_color_factor = dal_fixed32_32_from_fraction(36, 24); ++ break; ++ ++ case COLOR_DEPTH_161616: ++ /* deep color ratio for 48bpp is 48/24 = 2.0 */ ++ deep_color_factor = dal_fixed32_32_from_fraction(48, 24); ++ break; ++ default: ++ break; ++ } ++ return deep_color_factor; ++} ++ ++static struct fixed32_32 get_scaler_efficiency( ++ struct dc_context *ctx, ++ struct min_clock_params *params) ++{ ++ struct fixed32_32 scaler_efficiency = dal_fixed32_32_from_int(3); ++ ++ if (params->scaler_efficiency == V_SCALER_EFFICIENCY_LB18BPP) { ++ scaler_efficiency = ++ dal_fixed32_32_add( ++ dal_fixed32_32_from_fraction(35555, 10000), ++ dal_fixed32_32_from_fraction( ++ 55556, ++ 100000 * 10000)); ++ } else if (params->scaler_efficiency == V_SCALER_EFFICIENCY_LB24BPP) { ++ scaler_efficiency = ++ dal_fixed32_32_add( ++ dal_fixed32_32_from_fraction(34285, 10000), ++ dal_fixed32_32_from_fraction( ++ 71429, ++ 100000 * 10000)); ++ } else if (params->scaler_efficiency == V_SCALER_EFFICIENCY_LB30BPP) ++ scaler_efficiency = dal_fixed32_32_from_fraction(32, 10); ++ ++ return scaler_efficiency; ++} ++ ++static struct fixed32_32 get_lb_lines_in_per_line_out( ++ struct min_clock_params *params, ++ struct fixed32_32 v_scale_ratio) ++{ ++ struct fixed32_32 two = dal_fixed32_32_from_int(2); ++ struct fixed32_32 four = dal_fixed32_32_from_int(4); ++ struct fixed32_32 f4_to_3 = dal_fixed32_32_from_fraction(4, 3); ++ struct fixed32_32 f6_to_4 = dal_fixed32_32_from_fraction(6, 4); ++ ++ if (params->line_buffer_prefetch_enabled) ++ return dal_fixed32_32_max(v_scale_ratio, dal_fixed32_32_one); ++ else if (dal_fixed32_32_le(v_scale_ratio, dal_fixed32_32_one)) ++ return dal_fixed32_32_one; ++ else if (dal_fixed32_32_le(v_scale_ratio, f4_to_3)) ++ return f4_to_3; ++ else if (dal_fixed32_32_le(v_scale_ratio, f6_to_4)) ++ return f6_to_4; ++ else if (dal_fixed32_32_le(v_scale_ratio, two)) ++ return two; ++ else if (dal_fixed32_32_le(v_scale_ratio, dal_fixed32_32_from_int(3))) ++ return four; ++ else ++ return dal_fixed32_32_zero; ++} ++ ++static uint32_t get_actual_required_display_clk( ++ struct display_clock_dce110 *disp_clk, ++ uint32_t target_clk_khz) ++{ ++ uint32_t disp_clk_khz = target_clk_khz; ++ uint32_t div = INVALID_DIVIDER; ++ uint32_t did = INVALID_DID; ++ uint32_t scaled_vco = ++ disp_clk->dentist_vco_freq_khz * DIVIDER_RANGE_SCALE_FACTOR; ++ ++ ASSERT_CRITICAL(!!disp_clk_khz); ++ ++ if (disp_clk_khz) ++ div = scaled_vco / disp_clk_khz; ++ ++ did = dal_divider_range_get_did(divider_ranges, DIVIDER_RANGE_MAX, div); ++ ++ if (did != INVALID_DID) { ++ div = dal_divider_range_get_divider( ++ divider_ranges, DIVIDER_RANGE_MAX, did); ++ ++ if ((div != INVALID_DIVIDER) && ++ (did > DIVIDER_RANGE_01_BASE_DIVIDER_ID)) ++ if (disp_clk_khz > (scaled_vco / div)) ++ div = dal_divider_range_get_divider( ++ divider_ranges, DIVIDER_RANGE_MAX, ++ did - 1); ++ ++ if (div != INVALID_DIVIDER) ++ disp_clk_khz = scaled_vco / div; ++ ++ } ++ /* We need to add 10KHz to this value because the accuracy in VBIOS is ++ in 10KHz units. So we need to always round the last digit up in order ++ to reach the next div level.*/ ++ return disp_clk_khz + 10; ++} ++ ++static uint32_t calc_single_display_min_clks( ++ struct display_clock *base, ++ struct min_clock_params *params, ++ bool set_clk) ++{ ++ struct fixed32_32 h_scale_ratio = dal_fixed32_32_one; ++ struct fixed32_32 v_scale_ratio = dal_fixed32_32_one; ++ uint32_t pix_clk_khz = 0; ++ uint32_t lb_source_width = 0; ++ struct fixed32_32 deep_color_factor; ++ struct fixed32_32 scaler_efficiency; ++ struct fixed32_32 v_filter_init; ++ uint32_t v_filter_init_trunc; ++ uint32_t num_lines_at_frame_start = 3; ++ struct fixed32_32 v_filter_init_ceil; ++ struct fixed32_32 lines_per_lines_out_at_frame_start; ++ struct fixed32_32 lb_lines_in_per_line_out; /* in middle of the frame*/ ++ uint32_t src_wdth_rnd_to_chunks; ++ struct fixed32_32 scaling_coeff; ++ struct fixed32_32 h_blank_granularity_factor = ++ dal_fixed32_32_one; ++ struct fixed32_32 fx_disp_clk_mhz; ++ struct fixed32_32 line_time; ++ struct fixed32_32 disp_pipe_pix_throughput; ++ struct fixed32_32 fx_alt_disp_clk_mhz; ++ uint32_t disp_clk_khz; ++ uint32_t alt_disp_clk_khz; ++ struct display_clock_dce110 *disp_clk_110 = DCLCK110_FROM_BASE(base); ++ uint32_t max_clk_khz = get_validation_clock(base); ++ bool panning_allowed = false; /* TODO: receive this value from AS */ ++ ++ if (params == NULL) { ++ dal_logger_write(base->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "Invalid input parameter in %s", ++ __func__); ++ return 0; ++ } ++ ++ deep_color_factor = get_deep_color_factor(params); ++ scaler_efficiency = get_scaler_efficiency(base->ctx, params); ++ pix_clk_khz = params->requested_pixel_clock; ++ lb_source_width = params->source_view.width; ++ ++ if (0 != params->dest_view.height && 0 != params->dest_view.width) { ++ ++ h_scale_ratio = dal_fixed32_32_from_fraction( ++ params->source_view.width, ++ params->dest_view.width); ++ v_scale_ratio = dal_fixed32_32_from_fraction( ++ params->source_view.height, ++ params->dest_view.height); ++ } else { ++ dal_logger_write(base->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "Destination height or width is 0!\n"); ++ } ++ ++ v_filter_init = ++ dal_fixed32_32_add( ++ v_scale_ratio, ++ dal_fixed32_32_add_int( ++ dal_fixed32_32_div_int( ++ dal_fixed32_32_mul_int( ++ v_scale_ratio, ++ params->timing_info.INTERLACED), ++ 2), ++ params->scaling_info.v_taps + 1)); ++ v_filter_init = dal_fixed32_32_div_int(v_filter_init, 2); ++ ++ v_filter_init_trunc = dal_fixed32_32_floor(v_filter_init); ++ ++ v_filter_init_ceil = dal_fixed32_32_from_fraction( ++ v_filter_init_trunc, 2); ++ v_filter_init_ceil = dal_fixed32_32_from_int( ++ dal_fixed32_32_ceil(v_filter_init_ceil)); ++ v_filter_init_ceil = dal_fixed32_32_mul_int(v_filter_init_ceil, 2); ++ ++ lines_per_lines_out_at_frame_start = ++ dal_fixed32_32_div_int(v_filter_init_ceil, ++ num_lines_at_frame_start); ++ lb_lines_in_per_line_out = ++ get_lb_lines_in_per_line_out(params, v_scale_ratio); ++ ++ if (panning_allowed) ++ src_wdth_rnd_to_chunks = ++ ((lb_source_width - 1) / 128) * 128 + 256; ++ else ++ src_wdth_rnd_to_chunks = ++ ((lb_source_width + 127) / 128) * 128; ++ ++ scaling_coeff = ++ dal_fixed32_32_div( ++ dal_fixed32_32_from_int(params->scaling_info.v_taps), ++ scaler_efficiency); ++ ++ if (dal_fixed32_32_le(h_scale_ratio, dal_fixed32_32_one)) ++ scaling_coeff = dal_fixed32_32_max( ++ dal_fixed32_32_from_int( ++ dal_fixed32_32_ceil( ++ dal_fixed32_32_from_fraction( ++ params->scaling_info.h_taps, ++ 4))), ++ dal_fixed32_32_max( ++ dal_fixed32_32_mul( ++ scaling_coeff, ++ h_scale_ratio), ++ dal_fixed32_32_one)); ++ ++ if (!params->line_buffer_prefetch_enabled && ++ dal_fixed32_32_floor(lb_lines_in_per_line_out) != 2 && ++ dal_fixed32_32_floor(lb_lines_in_per_line_out) != 4) { ++ uint32_t line_total_pixel = ++ params->timing_info.h_total + lb_source_width - 256; ++ h_blank_granularity_factor = dal_fixed32_32_div( ++ dal_fixed32_32_from_int(params->timing_info.h_total), ++ dal_fixed32_32_div( ++ dal_fixed32_32_from_fraction( ++ line_total_pixel, 2), ++ h_scale_ratio)); ++ } ++ ++ /* Calculate display clock with ramping. Ramping factor is 1.1*/ ++ fx_disp_clk_mhz = ++ dal_fixed32_32_div_int( ++ dal_fixed32_32_mul_int(scaling_coeff, 11), ++ 10); ++ line_time = dal_fixed32_32_from_fraction( ++ params->timing_info.h_total * 1000, pix_clk_khz); ++ ++ disp_pipe_pix_throughput = dal_fixed32_32_mul( ++ lb_lines_in_per_line_out, h_blank_granularity_factor); ++ disp_pipe_pix_throughput = dal_fixed32_32_max( ++ disp_pipe_pix_throughput, ++ lines_per_lines_out_at_frame_start); ++ disp_pipe_pix_throughput = dal_fixed32_32_div(dal_fixed32_32_mul_int( ++ disp_pipe_pix_throughput, src_wdth_rnd_to_chunks), ++ line_time); ++ ++ if (0 != params->timing_info.h_total) { ++ fx_disp_clk_mhz = ++ dal_fixed32_32_max( ++ dal_fixed32_32_div_int( ++ dal_fixed32_32_mul_int( ++ scaling_coeff, pix_clk_khz), ++ 1000), ++ disp_pipe_pix_throughput); ++ fx_disp_clk_mhz = ++ dal_fixed32_32_mul( ++ fx_disp_clk_mhz, ++ dal_fixed32_32_from_fraction(11, 10)); ++ } ++ ++ fx_disp_clk_mhz = dal_fixed32_32_max(fx_disp_clk_mhz, ++ dal_fixed32_32_mul(deep_color_factor, ++ dal_fixed32_32_from_fraction(11, 10))); ++ ++ /* Calculate display clock without ramping */ ++ fx_alt_disp_clk_mhz = scaling_coeff; ++ ++ if (0 != params->timing_info.h_total) { ++ fx_alt_disp_clk_mhz = dal_fixed32_32_max( ++ dal_fixed32_32_div_int(dal_fixed32_32_mul_int( ++ scaling_coeff, pix_clk_khz), ++ 1000), ++ dal_fixed32_32_div_int(dal_fixed32_32_mul_int( ++ disp_pipe_pix_throughput, 105), ++ 100)); ++ } ++ ++ if (set_clk && disp_clk_110->ss_on_gpu_pll && ++ disp_clk_110->gpu_pll_ss_divider) ++ fx_alt_disp_clk_mhz = dal_fixed32_32_mul(fx_alt_disp_clk_mhz, ++ dal_fixed32_32_add_int( ++ dal_fixed32_32_div_int( ++ dal_fixed32_32_div_int( ++ dal_fixed32_32_from_fraction( ++ disp_clk_110->gpu_pll_ss_percentage, ++ disp_clk_110->gpu_pll_ss_divider), 100), ++ 2), ++ 1)); ++ ++ /* convert to integer */ ++ disp_clk_khz = dal_fixed32_32_round( ++ dal_fixed32_32_mul_int(fx_disp_clk_mhz, 1000)); ++ alt_disp_clk_khz = dal_fixed32_32_round( ++ dal_fixed32_32_mul_int(fx_alt_disp_clk_mhz, 1000)); ++ ++ if ((disp_clk_khz > max_clk_khz) && (alt_disp_clk_khz <= max_clk_khz)) ++ disp_clk_khz = alt_disp_clk_khz; ++ ++ if (set_clk) { /* only compensate clock if we are going to set it.*/ ++ disp_clk_khz = get_actual_required_display_clk( ++ disp_clk_110, disp_clk_khz); ++ } ++ ++ disp_clk_khz = disp_clk_khz > max_clk_khz ? max_clk_khz : disp_clk_khz; ++ ++ return disp_clk_khz; ++} ++ ++static uint32_t calculate_min_clock( ++ struct display_clock *base, ++ uint32_t path_num, ++ struct min_clock_params *params) ++{ ++ uint32_t i; ++ uint32_t validation_clk_khz = ++ get_validation_clock(base); ++ uint32_t min_clk_khz = validation_clk_khz; ++ uint32_t max_clk_khz = 0; ++ struct display_clock_dce110 *dc = DCLCK110_FROM_BASE(base); ++ ++ if (dc->use_max_disp_clk) ++ return min_clk_khz; ++ ++ if (params != NULL) { ++ uint32_t disp_clk_khz = 0; ++ ++ for (i = 0; i < path_num; ++i) { ++ ++ disp_clk_khz = calc_single_display_min_clks( ++ base, params, true); ++ ++ /* update the max required clock found*/ ++ if (disp_clk_khz > max_clk_khz) ++ max_clk_khz = disp_clk_khz; ++ ++ params++; ++ } ++ } ++ ++ min_clk_khz = max_clk_khz; ++ ++ if (min_clk_khz > validation_clk_khz) ++ min_clk_khz = validation_clk_khz; ++ else if (min_clk_khz < base->min_display_clk_threshold_khz) ++ min_clk_khz = base->min_display_clk_threshold_khz; ++ ++ if (dc->use_max_disp_clk) ++ min_clk_khz = get_validation_clock(base); ++ ++ return min_clk_khz; ++} ++ ++static bool display_clock_integrated_info_construct( ++ struct display_clock_dce110 *disp_clk, ++ struct adapter_service *as) ++{ ++ struct integrated_info info; ++ uint32_t i; ++ ++ struct display_clock *base = &disp_clk->disp_clk_base; ++ ++ if (!dal_adapter_service_get_integrated_info(as, &info)) ++ return false; ++ ++ disp_clk->dentist_vco_freq_khz = info.dentist_vco_freq; ++ if (disp_clk->dentist_vco_freq_khz == 0) ++ disp_clk->dentist_vco_freq_khz = 3600000; ++ ++ base->min_display_clk_threshold_khz = ++ disp_clk->dentist_vco_freq_khz / 64; ++ ++ /*update the maximum display clock for each power state*/ ++ for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { ++ enum clocks_state clk_state = CLOCKS_STATE_INVALID; ++ ++ switch (i) { ++ case 0: ++ clk_state = CLOCKS_STATE_ULTRA_LOW; ++ break; ++ ++ case 1: ++ clk_state = CLOCKS_STATE_LOW; ++ break; ++ ++ case 2: ++ clk_state = CLOCKS_STATE_NOMINAL; ++ break; ++ ++ case 3: ++ clk_state = CLOCKS_STATE_PERFORMANCE; ++ break; ++ ++ default: ++ clk_state = CLOCKS_STATE_INVALID; ++ break; ++ } ++ ++ /*Do not allow bad VBIOS/SBIOS to override with invalid values, ++ * check for > 100MHz*/ ++ if (info.disp_clk_voltage[i].max_supported_clk >= 100000) { ++ max_clks_by_state[clk_state].display_clk_khz = ++ info.disp_clk_voltage[i].max_supported_clk; ++ } ++ } ++ disp_clk->dfs_bypass_enabled = ++ dal_adapter_service_is_dfs_bypass_enabled(as); ++ disp_clk->use_max_disp_clk = ++ dal_adapter_service_is_feature_supported( ++ FEATURE_USE_MAX_DISPLAY_CLK); ++ ++ return true; ++} ++ ++static uint32_t get_clock(struct display_clock *dc) ++{ ++ uint32_t disp_clock = get_validation_clock(dc); ++ uint32_t target_div = INVALID_DIVIDER; ++ uint32_t addr = mmDENTIST_DISPCLK_CNTL; ++ uint32_t value = 0; ++ uint32_t field = 0; ++ struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc); ++ ++ if (disp_clk->dfs_bypass_enabled && disp_clk->dfs_bypass_disp_clk) ++ return disp_clk->dfs_bypass_disp_clk; ++ ++ /* Read the mmDENTIST_DISPCLK_CNTL to get the currently programmed ++ DID DENTIST_DISPCLK_WDIVIDER.*/ ++ value = dal_read_reg(dc->ctx, addr); ++ field = get_reg_field_value( ++ value, DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_WDIVIDER); ++ ++ /* Convert DENTIST_DISPCLK_WDIVIDER to actual divider*/ ++ target_div = dal_divider_range_get_divider( ++ divider_ranges, ++ DIVIDER_RANGE_MAX, ++ field); ++ ++ if (target_div != INVALID_DIVIDER) ++ /* Calculate the current DFS clock in KHz. ++ Should be okay up to 42.9 THz before overflowing.*/ ++ disp_clock = (DIVIDER_RANGE_SCALE_FACTOR ++ * disp_clk->dentist_vco_freq_khz) / target_div; ++ return disp_clock; ++} ++ ++static enum clocks_state get_required_clocks_state( ++ struct display_clock *dc, ++ struct state_dependent_clocks *req_clocks) ++{ ++ int32_t i; ++ struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc); ++ enum clocks_state low_req_clk = disp_clk->max_clks_state; ++ ++ if (!req_clocks) { ++ /* NULL pointer*/ ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Invalid parameter", ++ __func__); ++ return CLOCKS_STATE_INVALID; ++ } ++ ++ /* Iterate from highest supported to lowest valid state, and update ++ * lowest RequiredState with the lowest state that satisfies ++ * all required clocks ++ */ ++ for (i = disp_clk->max_clks_state; i >= CLOCKS_STATE_ULTRA_LOW; --i) { ++ if ((req_clocks->display_clk_khz <= ++ max_clks_by_state[i].display_clk_khz) && ++ (req_clocks->pixel_clk_khz <= ++ max_clks_by_state[i].pixel_clk_khz)) ++ low_req_clk = i; ++ } ++ return low_req_clk; ++} ++ ++static void set_clock( ++ struct display_clock *base, ++ uint32_t requested_clk_khz) ++{ ++ struct bp_pixel_clock_parameters pxl_clk_params; ++ struct display_clock_dce110 *dc = DCLCK110_FROM_BASE(base); ++ struct bios_parser *bp = dal_adapter_service_get_bios_parser(base->as); ++ ++ /* Prepare to program display clock*/ ++ dc_service_memset(&pxl_clk_params, 0, sizeof(pxl_clk_params)); ++ ++ pxl_clk_params.target_pixel_clock = requested_clk_khz; ++ pxl_clk_params.pll_id = base->id; ++ ++ dal_bios_parser_program_display_engine_pll(bp, &pxl_clk_params); ++ ++ if (dc->dfs_bypass_enabled) { ++ ++ /* Cache the fixed display clock*/ ++ dc->dfs_bypass_disp_clk = ++ pxl_clk_params.dfs_bypass_display_clock; ++ } ++ ++ /* from power down, we need mark the clock state as ClocksStateNominal ++ * from HWReset, so when resume we will call pplib voltage regulator.*/ ++ if (requested_clk_khz == 0) ++ base->cur_min_clks_state = CLOCKS_STATE_NOMINAL; ++} ++ ++static void set_clock_state( ++ struct display_clock *dc, ++ struct display_clock_state clk_state) ++{ ++ struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc); ++ ++ disp_clk->clock_state = clk_state; ++} ++ ++static struct display_clock_state get_clock_state( ++ struct display_clock *dc) ++{ ++ struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc); ++ ++ return disp_clk->clock_state; ++} ++ ++static uint32_t get_dfs_bypass_threshold(struct display_clock *dc) ++{ ++ return DCE110_DFS_BYPASS_THRESHOLD_KHZ; ++} ++ ++static const struct display_clock_funcs funcs = { ++ .destroy = destroy, ++ .calculate_min_clock = calculate_min_clock, ++ .get_clock = get_clock, ++ .get_clock_state = get_clock_state, ++ .get_dfs_bypass_threshold = get_dfs_bypass_threshold, ++ .get_dp_ref_clk_frequency = get_dp_ref_clk_frequency, ++ .get_min_clocks_state = get_min_clocks_state, ++ .get_required_clocks_state = get_required_clocks_state, ++ .get_validation_clock = get_validation_clock, ++ .set_clock = set_clock, ++ .set_clock_state = set_clock_state, ++ .set_dp_ref_clock_source = NULL, ++ .set_min_clocks_state = set_min_clocks_state, ++ .store_max_clocks_state = store_max_clocks_state, ++ .validate = NULL, ++}; ++ ++static bool dal_display_clock_dce110_construct( ++ struct display_clock_dce110 *dc110, ++ struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ struct display_clock *dc_base = &dc110->disp_clk_base; ++ ++ if (NULL == as) ++ return false; ++ ++ if (!dal_display_clock_construct_base(dc_base, ctx, as)) ++ return false; ++ ++ dc_base->funcs = &funcs; ++ ++ dc110->dfs_bypass_disp_clk = 0; ++ ++ if (!display_clock_integrated_info_construct(dc110, as)) ++ dal_logger_write(dc_base->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "Cannot obtain VBIOS integrated info\n"); ++ ++ dc110->gpu_pll_ss_percentage = 0; ++ dc110->gpu_pll_ss_divider = 1000; ++ dc110->ss_on_gpu_pll = false; ++ ++ dc_base->id = CLOCK_SOURCE_ID_DFS; ++/* Initially set max clocks state to nominal. This should be updated by ++ * via a pplib call to DAL IRI eventually calling a ++ * DisplayEngineClock_Dce110::StoreMaxClocksState(). This call will come in ++ * on PPLIB init. This is from DCE5x. in case HW wants to use mixed method.*/ ++ dc110->max_clks_state = CLOCKS_STATE_NOMINAL; ++ ++ dal_divider_range_construct( ++ ÷r_ranges[DIVIDER_RANGE_01], ++ DIVIDER_RANGE_01_START, ++ DIVIDER_RANGE_01_STEP_SIZE, ++ DIVIDER_RANGE_01_BASE_DIVIDER_ID, ++ DIVIDER_RANGE_02_BASE_DIVIDER_ID); ++ dal_divider_range_construct( ++ ÷r_ranges[DIVIDER_RANGE_02], ++ DIVIDER_RANGE_02_START, ++ DIVIDER_RANGE_02_STEP_SIZE, ++ DIVIDER_RANGE_02_BASE_DIVIDER_ID, ++ DIVIDER_RANGE_03_BASE_DIVIDER_ID); ++ dal_divider_range_construct( ++ ÷r_ranges[DIVIDER_RANGE_03], ++ DIVIDER_RANGE_03_START, ++ DIVIDER_RANGE_03_STEP_SIZE, ++ DIVIDER_RANGE_03_BASE_DIVIDER_ID, ++ DIVIDER_RANGE_MAX_DIVIDER_ID); ++ ++ { ++ uint32_t ss_info_num = ++ dal_adapter_service_get_ss_info_num( ++ as, ++ AS_SIGNAL_TYPE_GPU_PLL); ++ ++ if (ss_info_num) { ++ struct spread_spectrum_info info; ++ bool result; ++ ++ dc_service_memset(&info, 0, sizeof(info)); ++ ++ result = ++ dal_adapter_service_get_ss_info( ++ as, ++ AS_SIGNAL_TYPE_GPU_PLL, ++ 0, ++ &info); ++ ++ /* Based on VBIOS, VBIOS will keep entry for GPU PLL SS ++ * even if SS not enabled and in that case ++ * SSInfo.spreadSpectrumPercentage !=0 would be sign ++ * that SS is enabled ++ */ ++ if (result && info.spread_spectrum_percentage != 0) { ++ dc110->ss_on_gpu_pll = true; ++ dc110->gpu_pll_ss_divider = ++ info.spread_percentage_divider; ++ ++ if (info.type.CENTER_MODE == 0) { ++ /* Currently for DP Reference clock we ++ * need only SS percentage for ++ * downspread */ ++ dc110->gpu_pll_ss_percentage = ++ info.spread_spectrum_percentage; ++ } ++ } ++ ++ } ++ } ++ ++ return true; ++} ++ ++/***************************************************************************** ++ * public functions ++ *****************************************************************************/ ++ ++struct display_clock *dal_display_clock_dce110_create( ++ struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ struct display_clock_dce110 *dc110; ++ ++ dc110 = dc_service_alloc(ctx, sizeof(struct display_clock_dce110)); ++ ++ if (dc110 == NULL) ++ return NULL; ++ ++ if (dal_display_clock_dce110_construct(dc110, ctx, as)) ++ return &dc110->disp_clk_base; ++ ++ dc_service_free(ctx, dc110); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.h +new file mode 100644 +index 0000000..0cdc7b5 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/display_clock_dce110.h +@@ -0,0 +1,53 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_DISPLAY_CLOCK_DCE110_H__ ++#define __DAL_DISPLAY_CLOCK_DCE110_H__ ++ ++#include "gpu/display_clock.h" ++ ++struct display_clock_dce110 { ++ struct display_clock disp_clk_base; ++ /* Max display block clocks state*/ ++ enum clocks_state max_clks_state; ++ bool use_max_disp_clk; ++ uint32_t dentist_vco_freq_khz; ++ /* Cache the status of DFS-bypass feature*/ ++ bool dfs_bypass_enabled; ++ /* GPU PLL SS percentage (if down-spread enabled) */ ++ uint32_t gpu_pll_ss_percentage; ++ /* GPU PLL SS percentage Divider (100 or 1000) */ ++ uint32_t gpu_pll_ss_divider; ++ /* Flag for Enabled SS on GPU PLL */ ++ bool ss_on_gpu_pll; ++ /* Cache the display clock returned by VBIOS if DFS-bypass is enabled. ++ * This is basically "Crystal Frequency In KHz" (XTALIN) frequency */ ++ uint32_t dfs_bypass_disp_clk; ++ struct display_clock_state clock_state; ++}; ++ ++#define DCLCK110_FROM_BASE(dc_base) \ ++ container_of(dc_base, struct display_clock_dce110, disp_clk_base) ++ ++#endif /* __DAL_DISPLAY_CLOCK_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.c +new file mode 100644 +index 0000000..7fd5984 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.c +@@ -0,0 +1,383 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "include/logger_interface.h" ++#include "include/adapter_service_interface.h" ++#include "include/fixed32_32.h" ++ ++#include "ext_clock_source_dce110.h" ++ ++/** ++ * In this file ECS stands for External Clock Source. ++ */ ++ ++#define ECS110_FROM_BASE(clk_src_ptr)\ ++ container_of(\ ++ container_of((clk_src_ptr), struct ext_clock_source, base), \ ++ struct ext_clock_source_dce110, base) ++ ++#define ECS_WARNING(...) \ ++ dal_logger_write(ctx->logger, LOG_MAJOR_WARNING, \ ++ LOG_MINOR_COMPONENT_GPU, __VA_ARGS__) ++ ++#define ECS_ERROR(...) \ ++ dal_logger_write(ctx->logger, LOG_MAJOR_ERROR, \ ++ LOG_MINOR_COMPONENT_GPU, __VA_ARGS__) ++ ++/****************************************************************************** ++ * implementation functions ++ *****************************************************************************/ ++ ++static uint32_t controller_id_to_index( ++ struct clock_source *clk_src, ++ enum controller_id controller_id) ++{ ++ struct dc_context *ctx = clk_src->ctx; ++ uint32_t index = 0; ++ ++ switch (controller_id) { ++ case CONTROLLER_ID_D0: ++ index = 0; ++ break; ++ case CONTROLLER_ID_D1: ++ index = 1; ++ break; ++ case CONTROLLER_ID_D2: ++ index = 2; ++ break; ++ default: ++ ECS_ERROR("%s: invalid input controller_id = %d!\n", ++ __func__, controller_id); ++ break; ++ } ++ ++ return index; ++} ++ ++/* Adjust pixel rate by DTO programming (used for DisplayPort) */ ++static bool adjust_dto_pixel_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t requested_pix_clk_hz) ++{ ++ struct ext_clock_source_dce110 *ecs110 = ++ ECS110_FROM_BASE(clk_src); ++ struct dc_context *ctx = clk_src->ctx; ++ uint32_t index; ++ uint32_t dto_phase_reg; ++ uint32_t dto_modulo_reg; ++ uint32_t dto_phase_rnd; ++ uint32_t addr; ++ uint32_t value; ++ struct fixed32_32 dto_phase; ++ ++ if (NULL == pix_clk_params) { ++ ECS_WARNING("%s: invalid input!\n", __func__); ++ return false; ++ } ++ ++ index = controller_id_to_index(clk_src, pix_clk_params->controller_id); ++ ++ addr = ecs110->registers[index].dp_dtox_phase; ++ dto_phase_reg = dal_read_reg(ctx, addr); ++ ++ addr = ecs110->registers[index].dp_dtox_modulo; ++ dto_modulo_reg = dal_read_reg(ctx, addr); ++ ++ if (!dto_modulo_reg) { ++ ECS_WARNING("%s: current modulo is zero!\n", __func__); ++ return false; ++ } ++ ++ dto_phase = dal_fixed32_32_from_int(requested_pix_clk_hz); ++ ++ dto_phase = dal_fixed32_32_mul_int(dto_phase, dto_modulo_reg); ++ ++ dto_phase = dal_fixed32_32_div_int(dto_phase, ++ pix_clk_params->dp_ref_clk * 1000); ++ ++ dto_phase_rnd = dal_fixed32_32_round(dto_phase); ++ ++ /* Program DTO Phase */ ++ if (dto_phase_reg != dto_phase_rnd) { ++ /* If HW De-Spreading enabled on DP REF clock and if there will ++ * be case when Pixel rate > average DP Ref Clock, then need to ++ * disable de-spread for DP DTO (ATOMBIOS will program MODULO ++ * for average DP REF clock so no further SW adjustment ++ * needed) */ ++ if (pix_clk_params->de_spread_params.hw_dso_n_dp_ref_clk) { ++ ++ addr = ecs110->registers[index].crtcx_pixel_rate_cntl; ++ value = dal_read_reg(ctx, addr); ++ ++ if (requested_pix_clk_hz / 1000 > ++ pix_clk_params-> ++ de_spread_params.avg_dp_ref_clk_khz) { ++ ++ set_reg_field_value(value, 1, ++ CRTC0_PIXEL_RATE_CNTL, ++ DP_DTO0_DS_DISABLE); ++ } else { ++ set_reg_field_value(value, 0, ++ CRTC0_PIXEL_RATE_CNTL, ++ DP_DTO0_DS_DISABLE); ++ } ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ value = 0; ++ addr = ecs110->registers[index].dp_dtox_phase; ++ ++ set_reg_field_value(value, dto_phase_rnd, ++ DP_DTO0_PHASE, ++ DP_DTO0_PHASE); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ return true; ++} ++ ++/** ++ * Retrieve Pixel Rate (in Hz) from HW registers already programmed. ++ */ ++uint32_t retrieve_dp_pixel_rate_from_display_pll( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *params) ++{ ++ struct dc_context *ctx = clk_src->ctx; ++ ++ /* TODO: update when DAL2 implements this function. */ ++ DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_GPU, "%s\n", __func__); ++ return 0; ++} ++ ++static uint32_t retrieve_dto_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *params) ++{ ++ struct ext_clock_source_dce110 *ecs110 = ++ ECS110_FROM_BASE(clk_src); ++ struct dc_context *ctx = clk_src->ctx; ++ uint32_t index; ++ uint32_t dto_phase_reg; ++ uint32_t dto_modulo_reg; ++ uint32_t addr; ++ uint32_t value; ++ uint32_t pix_rate_hz; ++ struct fixed32_32 p_clk; ++ ++ if (params == NULL) ++ return 0; ++ ++ if (NULL == params) { ++ ECS_WARNING("%s: invalid input!\n", __func__); ++ return false; ++ } ++ ++ index = controller_id_to_index(clk_src, params->controller_id); ++ ++ addr = ecs110->registers[index].crtcx_pixel_rate_cntl; ++ value = dal_read_reg(ctx, addr); ++ ++ if (get_reg_field_value(value, CRTC0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE) ++ == 1) { ++ ++ addr = ecs110->registers[index].dp_dtox_phase; ++ dto_phase_reg = dal_read_reg(ctx, addr); ++ ++ addr = ecs110->registers[index].dp_dtox_modulo; ++ dto_modulo_reg = dal_read_reg(ctx, addr); ++ ++ if (!dto_modulo_reg) { ++ ECS_WARNING("%s: current modulo is zero!\n", __func__); ++ return 0; ++ } ++ ++ /* Calculate pixel clock from DTO Phase & Modulo*/ ++ p_clk = dal_fixed32_32_from_int(params->dp_ref_clk * 1000); ++ ++ p_clk = dal_fixed32_32_mul_int(p_clk, dto_phase_reg); ++ ++ p_clk = dal_fixed32_32_div_int(p_clk, dto_modulo_reg); ++ ++ pix_rate_hz = dal_fixed32_32_round(p_clk); ++ } else { ++ pix_rate_hz = retrieve_dp_pixel_rate_from_display_pll(clk_src, ++ params); ++ } ++ ++ return pix_rate_hz; ++} ++ ++/****************************************************************************** ++ * create/destroy functions ++ *****************************************************************************/ ++ ++static void destruct(struct ext_clock_source_dce110 *ecs110) ++{ ++ struct ext_clock_source *ext_cs = &ecs110->base; ++ struct clock_source *base = &ext_cs->base; ++ ++ if (NULL != base->dp_ss_params) { ++ dc_service_free(base->ctx, base->dp_ss_params); ++ base->dp_ss_params = NULL; ++ } ++ ++ dc_service_free(base->ctx, ecs110->registers); ++ ecs110->registers = NULL; ++} ++ ++ ++static void destroy(struct clock_source **clk_src) ++{ ++ struct ext_clock_source_dce110 *ecs110; ++ ++ ecs110 = ECS110_FROM_BASE(*clk_src); ++ ++ destruct(ecs110); ++ ++ dc_service_free((*clk_src)->ctx, ecs110); ++ ++ *clk_src = NULL; ++} ++ ++static const struct clock_source_impl funcs = { ++ .program_pix_clk = dal_ext_clock_source_program_pix_clk, ++ .adjust_pll_pixel_rate = dal_clock_source_base_adjust_pll_pixel_rate, ++ .adjust_dto_pixel_rate = adjust_dto_pixel_rate, ++ .retrieve_pll_pix_rate_hz = ++ dal_clock_source_base_retrieve_pll_pix_rate_hz, ++ .get_pix_clk_dividers = dal_ext_clock_source_get_pix_clk_dividers, ++ .destroy = destroy, ++ .retrieve_dto_pix_rate_hz = retrieve_dto_pix_rate_hz, ++ .power_down_pll = dal_ext_clock_source_power_down_pll ++}; ++ ++static bool construct( ++ struct ext_clock_source_dce110 *ecs110, ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ struct dc_context *ctx = clk_src_init_data->ctx; ++ struct ext_clock_source *ext_cs = &ecs110->base; ++ struct clock_source *base = &ext_cs->base; ++ uint32_t controllers_num; ++ struct registers *registers; ++ ++ /* None of the base construct() functions allocates memory. ++ * That means, in case of error, we don't have to free memory ++ * allocated by base. */ ++ if (!dal_ext_clock_source_construct(ext_cs, clk_src_init_data)) ++ return false; ++ ++ base->funcs = &funcs; ++ ++ base->is_gen_lock_capable = false; ++ base->dp_ss_params = NULL; ++ base->dp_ss_params_cnt = 0; ++ ++ ecs110->registers = NULL; ++ ++ if (base->clk_src_id != CLOCK_SOURCE_ID_EXTERNAL) { ++ ECS_ERROR("ECS110:Invalid ClockSourceId = %d!\n", ++ base->clk_src_id); ++ return false; ++ } ++ ++ controllers_num = dal_adapter_service_get_controllers_num( ++ base->adapter_service); ++ ++ if (controllers_num <= 0 || controllers_num > 3) { ++ ECS_ERROR("ECS110:Invalid number of controllers = %d!\n", ++ controllers_num); ++ return false; ++ } ++ ++ ecs110->registers = (struct registers *) ++ (dc_service_alloc(clk_src_init_data->ctx, sizeof(struct registers) * controllers_num)); ++ ++ if (ecs110->registers == NULL) { ++ ECS_ERROR("ECS110:Failed to allocate 'registers'!\n"); ++ return false; ++ } ++ ++ registers = ecs110->registers; ++ ++ /* Assign register address. No break between cases */ ++ switch (controllers_num) { ++ case 3: ++ registers[2].dp_dtox_phase = mmDP_DTO2_PHASE; ++ registers[2].dp_dtox_modulo = mmDP_DTO2_MODULO; ++ registers[2].crtcx_pixel_rate_cntl = mmCRTC2_PIXEL_RATE_CNTL; ++ /* fallthrough */ ++ case 2: ++ registers[1].dp_dtox_phase = mmDP_DTO1_PHASE; ++ registers[1].dp_dtox_modulo = mmDP_DTO1_MODULO; ++ registers[1].crtcx_pixel_rate_cntl = mmCRTC1_PIXEL_RATE_CNTL; ++ /* fallthrough */ ++ case 1: ++ registers[0].dp_dtox_phase = mmDP_DTO0_PHASE; ++ registers[0].dp_dtox_modulo = mmDP_DTO0_MODULO; ++ registers[0].crtcx_pixel_rate_cntl = mmCRTC0_PIXEL_RATE_CNTL; ++ break; ++ ++ default: ++ /* We can not get here because we checked number of ++ * controllers already. */ ++ break; ++ } ++ ++ dal_clock_source_get_ss_info_from_atombios( ++ base, ++ AS_SIGNAL_TYPE_DISPLAY_PORT, ++ &base->dp_ss_params, ++ &base->dp_ss_params_cnt); ++ ++ return true; ++} ++ ++ ++struct clock_source *dal_ext_clock_source_dce110_create( ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ struct ext_clock_source_dce110 *ecs110; ++ ++ ecs110 = dc_service_alloc(clk_src_init_data->ctx, sizeof(struct ext_clock_source_dce110)); ++ ++ if (ecs110 == NULL) ++ return NULL; ++ ++ if (!construct(ecs110, clk_src_init_data)) { ++ dc_service_free(clk_src_init_data->ctx, ecs110); ++ return NULL; ++ } ++ ++ return &ecs110->base.base; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.h +new file mode 100644 +index 0000000..4ea2ae2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/ext_clock_source_dce110.h +@@ -0,0 +1,38 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_EXT_CLOCK_SOURCE_DCE110__ ++#define __DAL_EXT_CLOCK_SOURCE_DCE110__ ++ ++#include "../ext_clock_source.h" ++ ++struct ext_clock_source_dce110 { ++ struct ext_clock_source base; ++ struct registers *registers; ++}; ++ ++struct clock_source *dal_ext_clock_source_dce110_create( ++ struct clock_source_init_data *clk_src_init_data); ++ ++#endif /*__DAL_EXT_CLOCK_SOURCE_DCE110__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.c +new file mode 100644 +index 0000000..d83eea3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.c +@@ -0,0 +1,718 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#include "include/logger_interface.h" ++#include "include/bios_parser_interface.h" ++#include "include/adapter_service_interface.h" ++#include "include/fixed32_32.h" ++#include "gpu/calc_pll_clock_source.h" ++#include "gpu/clock_source.h" ++#include "gpu/pll_clock_source.h" ++ ++#include "gpu/dce110/pll_clock_source_dce110.h" ++ ++enum fract_fb_divider_dec_points { ++ FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM = 6, ++ FRACT_FB_DIVIDER_DEC_POINTS_NO_DS_NUM = 1, ++}; ++ ++#define FROM_CLK_SRC(clk_src_ptr)\ ++ container_of(\ ++ container_of((clk_src_ptr), struct pll_clock_source, base), \ ++ struct pll_clock_source_dce110, base) ++ ++static bool calculate_ss( ++ struct pll_clock_source_dce110 *clk_src, ++ struct pll_settings *pll_settings, ++ const struct spread_spectrum_data *ss_data, ++ struct delta_sigma_data *ds_data) ++{ ++ struct fixed32_32 fb_div; ++ struct fixed32_32 ss_amount; ++ struct fixed32_32 ss_nslip_amount; ++ struct fixed32_32 ss_ds_frac_amount; ++ struct fixed32_32 ss_step_size; ++ struct fixed32_32 modulation_time; ++ ++ if (ds_data == NULL) ++ return false; ++ if (ss_data == NULL) ++ return false; ++ if (ss_data->percentage == 0) ++ return false; ++ if (pll_settings == NULL) ++ return false; ++ ++ ++ dc_service_memset(ds_data, 0, sizeof(struct delta_sigma_data)); ++ ++ ++ ++ /* compute SS_AMOUNT_FBDIV & SS_AMOUNT_NFRAC_SLIP & SS_AMOUNT_DSFRAC*/ ++ /* 6 decimal point support in fractional feedback divider */ ++ fb_div = dal_fixed32_32_from_fraction( ++ pll_settings->fract_feedback_divider, 1000000); ++ fb_div = dal_fixed32_32_add_int(fb_div, pll_settings->feedback_divider); ++ ++ ds_data->ds_frac_amount = 0; ++ /*spreadSpectrumPercentage is in the unit of .01%, ++ * so have to divided by 100 * 100*/ ++ ss_amount = dal_fixed32_32_mul( ++ fb_div, dal_fixed32_32_from_fraction(ss_data->percentage, ++ 100 * ss_data->percentage_divider)); ++ ds_data->feedback_amount = dal_fixed32_32_floor(ss_amount); ++ ++ ss_nslip_amount = dal_fixed32_32_sub(ss_amount, ++ dal_fixed32_32_from_int(ds_data->feedback_amount)); ++ ss_nslip_amount = dal_fixed32_32_mul_int(ss_nslip_amount, 10); ++ ds_data->nfrac_amount = dal_fixed32_32_floor(ss_nslip_amount); ++ ++ ss_ds_frac_amount = dal_fixed32_32_sub(ss_nslip_amount, ++ dal_fixed32_32_from_int(ds_data->nfrac_amount)); ++ ss_ds_frac_amount = dal_fixed32_32_mul_int(ss_ds_frac_amount, 65536); ++ ds_data->ds_frac_amount = dal_fixed32_32_floor(ss_ds_frac_amount); ++ ++ /* compute SS_STEP_SIZE_DSFRAC */ ++ modulation_time = dal_fixed32_32_from_fraction( ++ pll_settings->reference_freq * 1000, ++ pll_settings->reference_divider * ss_data->modulation_freq_hz); ++ ++ ++ if (ss_data->flags.CENTER_SPREAD) ++ modulation_time = dal_fixed32_32_div_int(modulation_time, 4); ++ else ++ modulation_time = dal_fixed32_32_div_int(modulation_time, 2); ++ ++ ss_step_size = dal_fixed32_32_div(ss_amount, modulation_time); ++ /* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/ ++ ss_step_size = dal_fixed32_32_mul_int(ss_step_size, 65536 * 10); ++ ds_data->ds_frac_size = dal_fixed32_32_floor(ss_step_size); ++ ++ return true; ++} ++ ++static bool disable_spread_spectrum(struct pll_clock_source_dce110 *clk_src) ++{ ++ enum bp_result result; ++ struct bp_spread_spectrum_parameters bp_ss_params = {0}; ++ struct clock_source *clock_source = NULL; ++ ++ clock_source = &clk_src->base.base; ++ bp_ss_params.pll_id = clock_source->clk_src_id; ++ ++ /*Call ASICControl to process ATOMBIOS Exec table*/ ++ result = dal_bios_parser_enable_spread_spectrum_on_ppll( ++ clock_source->bios_parser, ++ &bp_ss_params, ++ false); ++ ++ return result == BP_RESULT_OK; ++} ++ ++static bool enable_spread_spectrum( ++ struct pll_clock_source_dce110 *clk_src, ++ enum signal_type signal, struct pll_settings *pll_settings) ++{ ++ struct bp_spread_spectrum_parameters bp_params = {0}; ++ struct delta_sigma_data d_s_data; ++ struct clock_source *clock_source = NULL; ++ const struct spread_spectrum_data *ss_data = NULL; ++ ++ clock_source = &clk_src->base.base; ++ ss_data = dal_clock_source_get_ss_data_entry( ++ clock_source, ++ signal, ++ pll_settings->calculated_pix_clk); ++ ++/* Pixel clock PLL has been programmed to generate desired pixel clock, ++ * now enable SS on pixel clock */ ++/* TODO is it OK to return true not doing anything ??*/ ++ if (ss_data != NULL && pll_settings->ss_percentage != 0) { ++ if (calculate_ss(clk_src, pll_settings, ss_data, &d_s_data)) { ++ bp_params.ds.feedback_amount = ++ d_s_data.feedback_amount; ++ bp_params.ds.nfrac_amount = ++ d_s_data.nfrac_amount; ++ bp_params.ds.ds_frac_size = d_s_data.ds_frac_size; ++ bp_params.ds_frac_amount = ++ d_s_data.ds_frac_amount; ++ bp_params.flags.DS_TYPE = 1; ++ bp_params.pll_id = clock_source->clk_src_id; ++ bp_params.percentage = ss_data->percentage; ++ if (ss_data->flags.CENTER_SPREAD) ++ bp_params.flags.CENTER_SPREAD = 1; ++ if (ss_data->flags.EXTERNAL_SS) ++ bp_params.flags.EXTERNAL_SS = 1; ++ ++ if (BP_RESULT_OK != ++ dal_bios_parser_enable_spread_spectrum_on_ppll( ++ clock_source->bios_parser, ++ &bp_params, ++ true)) ++ return false; ++ } else ++ return false; ++ } ++ return true; ++} ++ ++static void program_pixel_clk_resync( ++ struct pll_clock_source_dce110 *clk_src, ++ enum signal_type signal_type, ++ enum dc_color_depth colordepth) ++{ ++ struct clock_source *clock_source = NULL; ++ uint32_t value = 0; ++ ++ clock_source = &clk_src->base.base; ++ ++ value = dal_read_reg( ++ clock_source->ctx, ++ clk_src->pixclkx_resync_cntl); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ PIXCLK1_RESYNC_CNTL, ++ DCCG_DEEP_COLOR_CNTL1); ++ ++ /* ++ 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) ++ 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) ++ 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) ++ 48 bit mode: TMDS clock = 2 x pixel clock (2:1) ++ */ ++ if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A) ++ return; ++ ++ switch (colordepth) { ++ case COLOR_DEPTH_888: ++ set_reg_field_value( ++ value, ++ 0, ++ PIXCLK1_RESYNC_CNTL, ++ DCCG_DEEP_COLOR_CNTL1); ++ break; ++ case COLOR_DEPTH_101010: ++ set_reg_field_value( ++ value, ++ 1, ++ PIXCLK1_RESYNC_CNTL, ++ DCCG_DEEP_COLOR_CNTL1); ++ break; ++ case COLOR_DEPTH_121212: ++ set_reg_field_value( ++ value, ++ 2, ++ PIXCLK1_RESYNC_CNTL, ++ DCCG_DEEP_COLOR_CNTL1); ++ break; ++ case COLOR_DEPTH_161616: ++ set_reg_field_value( ++ value, ++ 3, ++ PIXCLK1_RESYNC_CNTL, ++ DCCG_DEEP_COLOR_CNTL1); ++ break; ++ default: ++ break; ++ } ++ ++ dal_write_reg( ++ clock_source->ctx, ++ clk_src->pixclkx_resync_cntl, ++ value); ++} ++ ++static bool program_pix_clk( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct pll_clock_source_dce110 *pll_clk_src_dce110 = ++ FROM_CLK_SRC(clk_src); ++ struct bp_pixel_clock_parameters bp_pc_params = {0}; ++ ++ /* First disable SS ++ * ATOMBIOS will enable by default SS on PLL for DP, ++ * do not disable it here ++ */ ++ if (!dc_is_dp_signal(pix_clk_params->signal_type)) ++ disable_spread_spectrum(pll_clk_src_dce110); ++ ++ /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ ++ bp_pc_params.controller_id = pix_clk_params->controller_id; ++ bp_pc_params.pll_id = clk_src->clk_src_id; ++ bp_pc_params.target_pixel_clock = ++ pll_settings->actual_pix_clk; ++ bp_pc_params.reference_divider = pll_settings->reference_divider; ++ bp_pc_params.feedback_divider = pll_settings->feedback_divider; ++ bp_pc_params.fractional_feedback_divider = ++ pll_settings->fract_feedback_divider; ++ bp_pc_params.pixel_clock_post_divider = ++ pll_settings->pix_clk_post_divider; ++ bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; ++ bp_pc_params.signal_type = pix_clk_params->signal_type; ++ bp_pc_params.dvo_config = pix_clk_params->dvo_cfg; ++ bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = ++ pll_settings->use_external_clk; ++ ++ if (dal_bios_parser_set_pixel_clock(clk_src->bios_parser, ++ &bp_pc_params) != BP_RESULT_OK) ++ return false; ++ ++/* Enable SS ++ * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), ++ * based on HW display PLL team, SS control settings should be programmed ++ * during PLL Reset, but they do not have effect ++ * until SS_EN is asserted.*/ ++ if (pix_clk_params->flags.ENABLE_SS && !dc_is_dp_signal( ++ pix_clk_params->signal_type)) ++ if (!enable_spread_spectrum(pll_clk_src_dce110, ++ pix_clk_params->signal_type, ++ pll_settings)) ++ return false; ++ ++/* Resync deep color DTO */ ++ program_pixel_clk_resync(pll_clk_src_dce110, ++ pix_clk_params->signal_type, ++ pix_clk_params->color_depth); ++ ++ return true; ++} ++ ++static void ss_info_from_atombios_destroy( ++ struct pll_clock_source_dce110 *clk_src) ++{ ++ struct clock_source *cs = &clk_src->base.base; ++ ++ if (NULL != cs->ep_ss_params) { ++ dc_service_free(cs->ctx, cs->ep_ss_params); ++ cs->ep_ss_params = NULL; ++ } ++ ++ if (NULL != cs->dp_ss_params) { ++ dc_service_free(cs->ctx, cs->dp_ss_params); ++ cs->dp_ss_params = NULL; ++ } ++ ++ if (NULL != cs->hdmi_ss_params) { ++ dc_service_free(cs->ctx, cs->hdmi_ss_params); ++ cs->hdmi_ss_params = NULL; ++ } ++ ++ if (NULL != cs->dvi_ss_params) { ++ dc_service_free(cs->ctx, cs->dvi_ss_params); ++ cs->dvi_ss_params = NULL; ++ } ++} ++ ++static void destruct( ++ struct pll_clock_source_dce110 *pll_cs) ++{ ++ ss_info_from_atombios_destroy(pll_cs); ++ ++ if (NULL != pll_cs->registers) { ++ dc_service_free(pll_cs->base.base.ctx, pll_cs->registers); ++ pll_cs->registers = NULL; ++ } ++} ++ ++static void destroy(struct clock_source **clk_src) ++{ ++ struct pll_clock_source_dce110 *pll_clk_src; ++ ++ pll_clk_src = FROM_CLK_SRC(*clk_src); ++ ++ destruct(pll_clk_src); ++ dc_service_free((*clk_src)->ctx, pll_clk_src); ++ ++ *clk_src = NULL; ++} ++ ++/** ++ * Calculate PLL Dividers for given Clock Value. ++ * First will call VBIOS Adjust Exec table to check if requested Pixel clock ++ * will be Adjusted based on usage. ++ * Then it will calculate PLL Dividers for this Adjusted clock using preferred ++ * method (Maximum VCO frequency). ++ * ++ * \return ++ * Calculation error in units of 0.01% ++ */ ++static uint32_t get_pix_clk_dividers( ++ struct clock_source *cs, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct pll_clock_source_dce110 *pll_cs_110 = FROM_CLK_SRC(cs); ++ struct pll_clock_source *pll_base = &pll_cs_110->base; ++ uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; ++ uint32_t addr = 0; ++ uint32_t value = 0; ++ uint32_t field = 0; ++ ++ if (pix_clk_params == NULL || pll_settings == NULL ++ || pix_clk_params->requested_pix_clk == 0) { ++ dal_logger_write(cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Invalid parameters!!\n", __func__); ++ return pll_calc_error; ++ } ++ ++ dc_service_memset(pll_settings, 0, sizeof(*pll_settings)); ++ ++ /* Check if reference clock is external (not pcie/xtalin) ++ * HW Dce80 spec: ++ * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB ++ * 04 - HSYNCA, 05 - GENLK_CLK, 06 - PCIE_REFCLK, 07 - DVOCLK0 */ ++ addr = pll_cs_110->pxpll_cntl; ++ value = dal_read_reg(cs->ctx, addr); ++ field = get_reg_field_value(value, PLL_CNTL, PLL_REF_DIV_SRC); ++ pll_settings->use_external_clk = (field > 1); ++ ++ /* VBIOS by default enables DP SS (spread on IDCLK) for DCE 8.0 always ++ * (we do not care any more from SI for some older DP Sink which ++ * does not report SS support, no known issues) */ ++ if ((pix_clk_params->flags.ENABLE_SS) || ++ (dc_is_dp_signal(pix_clk_params->signal_type))) { ++ ++ const struct spread_spectrum_data *ss_data = ++ dal_clock_source_get_ss_data_entry( ++ cs, ++ pix_clk_params->signal_type, ++ pll_settings->adjusted_pix_clk); ++ ++ if (NULL != ss_data) ++ pll_settings->ss_percentage = ss_data->percentage; ++ } ++ ++ /* Check VBIOS AdjustPixelClock Exec table */ ++ if (!dal_pll_clock_source_adjust_pix_clk(pll_base, ++ pix_clk_params, pll_settings)) { ++ /* Should never happen, ASSERT and fill up values to be able ++ * to continue. */ ++ dal_logger_write(cs->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Failed to adjust pixel clock!!", __func__); ++ pll_settings->actual_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ pll_settings->adjusted_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ ++ if (dc_is_dp_signal(pix_clk_params->signal_type)) ++ pll_settings->adjusted_pix_clk = 100000; ++ } ++ ++ /* Calculate Dividers */ ++ if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) ++ /*Calculate Dividers by HDMI object, no SS case or SS case */ ++ pll_calc_error = ++ dal_clock_source_calculate_pixel_clock_pll_dividers( ++ &pll_cs_110->calc_pll_clock_source_hdmi, ++ pll_settings); ++ else ++ /*Calculate Dividers by default object, no SS case or SS case */ ++ pll_calc_error = ++ dal_clock_source_calculate_pixel_clock_pll_dividers( ++ &pll_cs_110->calc_pll_clock_source, ++ pll_settings); ++ ++ return pll_calc_error; ++} ++ ++static const struct clock_source_impl funcs = { ++ .program_pix_clk = program_pix_clk, ++ .adjust_pll_pixel_rate = NULL, ++ .adjust_dto_pixel_rate = NULL, ++ .retrieve_pll_pix_rate_hz = NULL, ++ .get_pix_clk_dividers = get_pix_clk_dividers, ++ .destroy = destroy, ++ .retrieve_dto_pix_rate_hz = NULL, ++ .power_down_pll = dal_pll_clock_source_power_down_pll, ++}; ++ ++static void ss_info_from_atombios_create( ++ struct pll_clock_source_dce110 *clk_src) ++{ ++ struct clock_source *base = &clk_src->base.base; ++ ++ dal_clock_source_get_ss_info_from_atombios( ++ base, ++ AS_SIGNAL_TYPE_DISPLAY_PORT, ++ &base->dp_ss_params, ++ &base->dp_ss_params_cnt); ++ dal_clock_source_get_ss_info_from_atombios( ++ base, ++ AS_SIGNAL_TYPE_LVDS, ++ &base->ep_ss_params, ++ &base->ep_ss_params_cnt); ++ dal_clock_source_get_ss_info_from_atombios( ++ base, ++ AS_SIGNAL_TYPE_HDMI, ++ &base->hdmi_ss_params, ++ &base->hdmi_ss_params_cnt); ++ dal_clock_source_get_ss_info_from_atombios( ++ base, ++ AS_SIGNAL_TYPE_DVI, ++ &base->dvi_ss_params, ++ &base->dvi_ss_params_cnt); ++} ++ ++ ++static bool construct( ++ struct pll_clock_source_dce110 *pll_cs_dce110, ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ uint32_t controllers_num = 1; ++ ++/* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ ++ struct calc_pll_clock_source_init_data calc_pll_cs_init_data = { ++ dal_adapter_service_get_bios_parser(clk_src_init_data->as), ++ 1, /* minPixelClockPLLPostDivider */ ++ PLL_POST_DIV__PLL_POST_DIV_PIXCLK_MASK, ++ /* maxPixelClockPLLPostDivider*/ ++ 1,/* minPLLRefDivider*/ ++ PLL_REF_DIV__PLL_REF_DIV_MASK,/* maxPLLRefDivider*/ ++ 0, ++/* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ ++ 0, ++/* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ ++ FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, ++/*numberOfFractFBDividerDecimalPoints*/ ++ FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, ++/*number of decimal point to round off for fractional feedback divider value*/ ++ clk_src_init_data->ctx ++ }; ++/*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ ++ struct calc_pll_clock_source_init_data calc_pll_cs_init_data_hdmi = { ++ dal_adapter_service_get_bios_parser(clk_src_init_data->as), ++ 1, /* minPixelClockPLLPostDivider */ ++ PLL_POST_DIV__PLL_POST_DIV_PIXCLK_MASK, ++ /* maxPixelClockPLLPostDivider*/ ++ 1,/* minPLLRefDivider*/ ++ PLL_REF_DIV__PLL_REF_DIV_MASK,/* maxPLLRefDivider*/ ++ 13500, ++ /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ ++ 27000, ++ /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ ++ FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, ++ /*numberOfFractFBDividerDecimalPoints*/ ++ FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, ++/*number of decimal point to round off for fractional feedback divider value*/ ++ clk_src_init_data->ctx ++ }; ++ ++ struct pll_clock_source *base = &pll_cs_dce110->base; ++ struct clock_source *superbase = &base->base; ++ ++ if (!dal_pll_clock_source_construct(base, clk_src_init_data)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ superbase->funcs = &funcs; ++ ++ superbase->is_clock_source_with_fixed_freq = false; ++ superbase->clk_sharing_lvl = CLOCK_SHARING_LEVEL_DISPLAY_PORT_SHAREABLE; ++ ++ pll_cs_dce110->registers = NULL; ++ ++/* PLL3 should not be used although it is available in online register spec */ ++ if ((superbase->clk_src_id != CLOCK_SOURCE_ID_PLL1) ++ && (superbase->clk_src_id != CLOCK_SOURCE_ID_PLL0)) { ++ ++ ++ ASSERT_CRITICAL(false); ++ goto failure; ++ } ++ ++/* From Driver side PLL0 is now used for non DP timing also, ++ * so it supports all signals except Wireless. ++ * Wireless signal type does not require a PLL clock source, ++ * so we will not waste a clock on it. ++*/ ++ superbase->output_signals &= ~SIGNAL_TYPE_WIRELESS; ++ ++ if (!dal_calc_pll_clock_source_max_vco_init( ++ &pll_cs_dce110->calc_pll_clock_source, ++ &calc_pll_cs_init_data)) { ++ ASSERT_CRITICAL(false); ++ goto failure; ++ } ++ ++ if (base->ref_freq_khz == 48000) { ++ calc_pll_cs_init_data_hdmi. ++ min_override_input_pxl_clk_pll_freq_khz = 24000; ++ calc_pll_cs_init_data_hdmi. ++ max_override_input_pxl_clk_pll_freq_khz = 48000; ++ } else if (base->ref_freq_khz == 100000) { ++ calc_pll_cs_init_data_hdmi. ++ min_override_input_pxl_clk_pll_freq_khz = 25000; ++ calc_pll_cs_init_data_hdmi. ++ max_override_input_pxl_clk_pll_freq_khz = 50000; ++ } ++ ++ if (!dal_calc_pll_clock_source_max_vco_init( ++ &pll_cs_dce110->calc_pll_clock_source_hdmi, ++ &calc_pll_cs_init_data_hdmi)) { ++ ASSERT_CRITICAL(false); ++ goto failure; ++ } ++ ++ switch (superbase->clk_src_id) { ++ case CLOCK_SOURCE_ID_PLL0: ++ pll_cs_dce110->pixclkx_resync_cntl = mmPIXCLK0_RESYNC_CNTL; ++ pll_cs_dce110->ppll_fb_div = mmBPHYC_PLL0_PLL_FB_DIV; ++ pll_cs_dce110->ppll_ref_div = mmBPHYC_PLL0_PLL_REF_DIV; ++ pll_cs_dce110->ppll_post_div = mmBPHYC_PLL0_PLL_POST_DIV; ++ pll_cs_dce110->pxpll_ds_cntl = mmBPHYC_PLL0_PLL_DS_CNTL; ++ pll_cs_dce110->pxpll_ss_cntl = mmBPHYC_PLL0_PLL_SS_CNTL; ++ pll_cs_dce110->pxpll_ss_dsfrac = ++ mmBPHYC_PLL0_PLL_SS_AMOUNT_DSFRAC; ++ pll_cs_dce110->pxpll_cntl = mmBPHYC_PLL0_PLL_CNTL; ++ break; ++ case CLOCK_SOURCE_ID_PLL1: ++ pll_cs_dce110->pixclkx_resync_cntl = mmPIXCLK1_RESYNC_CNTL; ++ pll_cs_dce110->ppll_fb_div = mmBPHYC_PLL1_PLL_FB_DIV; ++ pll_cs_dce110->ppll_ref_div = mmBPHYC_PLL1_PLL_REF_DIV; ++ pll_cs_dce110->ppll_post_div = mmBPHYC_PLL1_PLL_POST_DIV; ++ pll_cs_dce110->pxpll_ds_cntl = mmBPHYC_PLL1_PLL_DS_CNTL; ++ pll_cs_dce110->pxpll_ss_cntl = mmBPHYC_PLL1_PLL_SS_CNTL; ++ pll_cs_dce110->pxpll_ss_dsfrac = ++ mmBPHYC_PLL1_PLL_SS_AMOUNT_DSFRAC; ++ pll_cs_dce110->pxpll_cntl = mmBPHYC_PLL1_PLL_CNTL; ++ break; ++ case CLOCK_SOURCE_ID_PLL2: ++ /* PLL2 is not supported */ ++ default: ++ break; ++ } ++ ++ controllers_num = dal_adapter_service_get_controllers_num( ++ superbase->adapter_service); ++ ++ pll_cs_dce110->registers = dc_service_alloc( ++ clk_src_init_data->ctx, ++ sizeof(struct registers) * controllers_num); ++ ++ if (pll_cs_dce110->registers == NULL) { ++ ASSERT_CRITICAL(false); ++ goto failure; ++ } ++ ++ /* Assign register address. No break between cases */ ++ switch (controllers_num) { ++ case 6: ++ pll_cs_dce110->registers[5].dp_dtox_phase = ++ mmDP_DTO5_PHASE; ++ pll_cs_dce110->registers[5].dp_dtox_modulo = ++ mmDP_DTO5_MODULO; ++ pll_cs_dce110->registers[5].crtcx_pixel_rate_cntl = ++ mmCRTC5_PIXEL_RATE_CNTL; ++ /* fall through*/ ++ ++ case 5: ++ pll_cs_dce110->registers[4].dp_dtox_phase = ++ mmDP_DTO4_PHASE; ++ pll_cs_dce110->registers[4].dp_dtox_modulo = ++ mmDP_DTO4_MODULO; ++ pll_cs_dce110->registers[4].crtcx_pixel_rate_cntl = ++ mmCRTC4_PIXEL_RATE_CNTL; ++ /* fall through*/ ++ ++ case 4: ++ pll_cs_dce110->registers[3].dp_dtox_phase = ++ mmDP_DTO3_PHASE; ++ pll_cs_dce110->registers[3].dp_dtox_modulo = ++ mmDP_DTO3_MODULO; ++ pll_cs_dce110->registers[3].crtcx_pixel_rate_cntl = ++ mmCRTC3_PIXEL_RATE_CNTL; ++ /* fall through*/ ++ ++ case 3: ++ pll_cs_dce110->registers[2].dp_dtox_phase = ++ mmDP_DTO2_PHASE; ++ pll_cs_dce110->registers[2].dp_dtox_modulo = ++ mmDP_DTO2_MODULO; ++ pll_cs_dce110->registers[2].crtcx_pixel_rate_cntl = ++ mmCRTC2_PIXEL_RATE_CNTL; ++ /* fall through*/ ++ ++ case 2: ++ pll_cs_dce110->registers[1].dp_dtox_phase = ++ mmDP_DTO1_PHASE; ++ pll_cs_dce110->registers[1].dp_dtox_modulo = ++ mmDP_DTO1_MODULO; ++ pll_cs_dce110->registers[1].crtcx_pixel_rate_cntl = ++ mmCRTC1_PIXEL_RATE_CNTL; ++ /* fall through*/ ++ ++ case 1: ++ pll_cs_dce110->registers[0].dp_dtox_phase = ++ mmDP_DTO0_PHASE; ++ pll_cs_dce110->registers[0].dp_dtox_modulo = ++ mmDP_DTO0_MODULO; ++ pll_cs_dce110->registers[0].crtcx_pixel_rate_cntl = ++ mmCRTC0_PIXEL_RATE_CNTL; ++ ++ break; ++ ++ default: ++ ASSERT_CRITICAL(false); ++ goto failure; ++ } ++ ++ ss_info_from_atombios_create(pll_cs_dce110); ++ ++ return true; ++ ++failure: ++ destruct(pll_cs_dce110); ++ ++ return false; ++} ++ ++struct clock_source *dal_pll_clock_source_dce110_create( ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ struct pll_clock_source_dce110 *clk_src = ++ dc_service_alloc(clk_src_init_data->ctx, sizeof(struct pll_clock_source_dce110)); ++ ++ if (clk_src == NULL) ++ return NULL; ++ ++ if (!construct(clk_src, clk_src_init_data)) { ++ dc_service_free(clk_src_init_data->ctx, clk_src); ++ return NULL; ++ } ++ return &(clk_src->base.base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.h +new file mode 100644 +index 0000000..166b29a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/pll_clock_source_dce110.h +@@ -0,0 +1,55 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_PLL_CLOCK_SOURCE_DCE110_H__ ++#define __DAL_PLL_CLOCK_SOURCE_DCE110_H__ ++ ++#include "../pll_clock_source.h" ++#include "../calc_pll_clock_source.h" ++ ++struct pll_clock_source_dce110 { ++ struct pll_clock_source base; ++ ++ struct calc_pll_clock_source calc_pll_clock_source; ++/* object for normal circumstances, SS = 0 or SS >= 0.2% (LVDS or DP) ++ * or even for SS =~0.02 (DVI) */ ++ ++ struct calc_pll_clock_source calc_pll_clock_source_hdmi; ++/* object for HDMI no SS or SS <= 0.06% */ ++ ++ struct registers *registers; ++ ++ uint32_t pixclkx_resync_cntl; ++ uint32_t ppll_fb_div; ++ uint32_t ppll_ref_div; ++ uint32_t ppll_post_div; ++ uint32_t pxpll_ds_cntl; ++ uint32_t pxpll_ss_cntl; ++ uint32_t pxpll_ss_dsfrac; ++ uint32_t pxpll_cntl; ++}; ++ ++struct clock_source *dal_pll_clock_source_dce110_create( ++ struct clock_source_init_data *clk_src_init_data); ++ ++#endif /*__DAL_PLL_CLOCK_SOURCE_DCE110__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.c +new file mode 100644 +index 0000000..ce59228 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.c +@@ -0,0 +1,193 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dal_services.h" ++#include "vce_clock_source_dce110.h" ++#include "include/clock_source_types.h" ++#include "include/bios_parser_interface.h" ++#include "include/logger_interface.h" ++ ++struct vce_clock_source_dce110 { ++ struct clock_source base; ++ uint32_t ref_freq_khz; ++}; ++ ++static uint32_t get_pix_clk_dividers( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct vce_clock_source_dce110 *vce_clk_src_dce110 = ++ container_of( ++ clk_src, ++ struct vce_clock_source_dce110, ++ base); ++ if (pix_clk_params == NULL || ++ pll_settings == NULL || ++ pix_clk_params->requested_pix_clk == 0) { ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Invalid parameters!!", __func__); ++ return MAX_PLL_CALC_ERROR; ++ } ++ ++ dc_service_memset(pll_settings, 0, sizeof(struct pll_settings)); ++ pll_settings->reference_freq = vce_clk_src_dce110->ref_freq_khz; ++ pll_settings->actual_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ pll_settings->adjusted_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ pll_settings->calculated_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ ++ return 0; ++} ++static bool program_pix_clk(struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct bp_pixel_clock_parameters bp_pix_clk_params = { 0 }; ++ ++ if (pll_settings->actual_pix_clk == 0) ++ return false; ++ /* this is SimNow for Nutmeg*/ ++ ++ bp_pix_clk_params.controller_id = pix_clk_params->controller_id; ++ bp_pix_clk_params.pll_id = clk_src->clk_src_id; ++ bp_pix_clk_params.target_pixel_clock = pll_settings->actual_pix_clk; ++ bp_pix_clk_params.encoder_object_id = pix_clk_params->encoder_object_id; ++ bp_pix_clk_params.signal_type = pix_clk_params->signal_type; ++ ++ if (dal_bios_parser_set_pixel_clock(clk_src->bios_parser, ++ &bp_pix_clk_params) == BP_RESULT_OK) ++ return true; ++ ++ return false; ++} ++ ++static bool power_down_pll(struct clock_source *clk_src, ++ enum controller_id controller_id) ++{ ++ return true; ++} ++ ++static void destruct( ++ struct vce_clock_source_dce110 *vce_clk_src) ++{ ++ ++} ++ ++static void destroy( ++ struct clock_source **clk_src) ++{ ++ struct vce_clock_source_dce110 *vce_clk_src; ++ ++ vce_clk_src = ++ container_of(*clk_src, struct vce_clock_source_dce110, base); ++ ++ destruct(vce_clk_src); ++ dc_service_free((*clk_src)->ctx, vce_clk_src); ++ ++ *clk_src = NULL; ++} ++ ++static const struct clock_source_impl funcs = { ++ .program_pix_clk = program_pix_clk, ++ .adjust_pll_pixel_rate = dal_clock_source_base_adjust_pll_pixel_rate, ++ .adjust_dto_pixel_rate = dal_clock_source_base_adjust_dto_pix_rate, ++ .retrieve_pll_pix_rate_hz = ++ dal_clock_source_base_retrieve_pll_pix_rate_hz, ++ .get_pix_clk_dividers = get_pix_clk_dividers, ++ .destroy = destroy, ++ .retrieve_dto_pix_rate_hz = ++ dal_clock_source_base_retrieve_dto_pix_rate_hz, ++ .power_down_pll = power_down_pll, ++}; ++ ++static bool construct( ++ struct vce_clock_source_dce110 *vce_clk_src, ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ struct firmware_info fw_info = { { 0 } }; ++ ++ if (!dal_clock_source_construct( ++ &vce_clk_src->base, clk_src_init_data)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if (vce_clk_src->base.clk_src_id != CLOCK_SOURCE_ID_VCE) { ++ dal_logger_write(clk_src_init_data->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "Invalid ClockSourceId = %d!\n", ++ vce_clk_src->base.clk_src_id); ++ ASSERT_CRITICAL(false); ++ dal_logger_write(clk_src_init_data->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_GPU, ++ "Failed to create DCE110VceClockSource.\n"); ++ return false; ++ } ++ ++ vce_clk_src->base.funcs = &funcs; ++ vce_clk_src->base.clk_sharing_lvl = CLOCK_SHARING_LEVEL_NOT_SHAREABLE; ++ vce_clk_src->base.is_clock_source_with_fixed_freq = false; ++ ++ ++ /*VCE clock source only supports SignalType_Wireless*/ ++ vce_clk_src->base.output_signals |= SIGNAL_TYPE_WIRELESS; ++ ++ /*Get Reference frequency, Input frequency range into PLL ++ * and Output frequency range of the PLL ++ * from ATOMBIOS Data table */ ++ if (dal_bios_parser_get_firmware_info( ++ vce_clk_src->base.bios_parser, ++ &fw_info) != BP_RESULT_OK) ++ return false; ++ ++ vce_clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; ++ ++ return true; ++} ++ ++ ++struct clock_source *dal_vce_clock_source_dce110_create( ++ struct clock_source_init_data *clk_src_init_data) ++ ++{ ++ struct vce_clock_source_dce110 *clk_src; ++ ++ clk_src = dc_service_alloc(clk_src_init_data->ctx, sizeof(struct vce_clock_source_dce110)); ++ ++ if (clk_src == NULL) ++ return NULL; ++ ++ if (!construct(clk_src, clk_src_init_data)) { ++ dc_service_free(clk_src_init_data->ctx, clk_src); ++ return NULL; ++ } ++ ++ return &clk_src->base; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.h +new file mode 100644 +index 0000000..227b169 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce110/vce_clock_source_dce110.h +@@ -0,0 +1,32 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_VCE_CLOCK_SOURCE_DCE110__ ++#define __DAL_VCE_CLOCK_SOURCE_DCE110__ ++ ++#include "../clock_source.h" ++ ++struct clock_source *dal_vce_clock_source_dce110_create( ++ struct clock_source_init_data *clk_src_init_data); ++ ++#endif /*__DAL_VCE_CLOCK_SOURCE_DCE110__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/display_clock.c b/drivers/gpu/drm/amd/dal/dc/gpu/display_clock.c +new file mode 100644 +index 0000000..a11aa84 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/display_clock.c +@@ -0,0 +1,204 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "display_clock.h" ++#include "adapter_service_interface.h" ++ ++void dal_display_clock_base_set_dp_ref_clock_source( ++ struct display_clock *disp_clk, ++ enum clock_source_id clk_src) ++{/*must be implemented in derived*/ ++ ++} ++ ++void dal_display_clock_base_set_clock_state(struct display_clock *disp_clk, ++ struct display_clock_state clk_state) ++{ ++ /*Implemented only in DCE81*/ ++} ++struct display_clock_state dal_display_clock_base_get_clock_state( ++ struct display_clock *disp_clk) ++{ ++ /*Implemented only in DCE81*/ ++ struct display_clock_state state = {0}; ++ return state; ++} ++uint32_t dal_display_clock_base_get_dfs_bypass_threshold( ++ struct display_clock *disp_clk) ++{ ++ /*Implemented only in DCE81*/ ++ return 0; ++} ++ ++bool dal_display_clock_construct_base( ++ struct display_clock *base, ++ struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ base->ctx = ctx; ++ base->id = CLOCK_SOURCE_ID_DCPLL; ++ base->min_display_clk_threshold_khz = 0; ++ base->as = as; ++ ++/* Initially set current min clocks state to invalid since we ++ * cannot make any assumption about PPLIB's initial state. This will be updated ++ * by HWSS via SetMinClocksState() on first mode set prior to programming ++ * state dependent clocks.*/ ++ base->cur_min_clks_state = CLOCKS_STATE_INVALID; ++ ++ return true; ++} ++ ++void dal_display_clock_destroy(struct display_clock **disp_clk) ++{ ++ if (!disp_clk || !*disp_clk) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ (*disp_clk)->funcs->destroy(disp_clk); ++ ++ *disp_clk = NULL; ++} ++ ++bool dal_display_clock_validate( ++ struct display_clock *disp_clk, ++ struct min_clock_params *params) ++{ ++ return disp_clk->funcs->validate(disp_clk, params); ++} ++ ++uint32_t dal_display_clock_calculate_min_clock( ++ struct display_clock *disp_clk, ++ uint32_t path_num, ++ struct min_clock_params *params) ++{ ++ return disp_clk->funcs->calculate_min_clock(disp_clk, path_num, params); ++} ++ ++uint32_t dal_display_clock_get_validation_clock(struct display_clock *disp_clk) ++{ ++ return disp_clk->funcs->get_validation_clock(disp_clk); ++} ++ ++void dal_display_clock_set_clock( ++ struct display_clock *disp_clk, ++ uint32_t requested_clock_khz) ++{ ++ disp_clk->funcs->set_clock(disp_clk, requested_clock_khz); ++} ++ ++uint32_t dal_display_clock_get_clock(struct display_clock *disp_clk) ++{ ++ return disp_clk->funcs->get_clock(disp_clk); ++} ++ ++enum clocks_state dal_display_clock_get_min_clocks_state( ++ struct display_clock *disp_clk) ++{ ++ return disp_clk->funcs->get_min_clocks_state(disp_clk); ++} ++ ++enum clocks_state dal_display_clock_get_required_clocks_state( ++ struct display_clock *disp_clk, ++ struct state_dependent_clocks *req_clocks) ++{ ++ return disp_clk->funcs->get_required_clocks_state(disp_clk, req_clocks); ++} ++ ++bool dal_display_clock_set_min_clocks_state( ++ struct display_clock *disp_clk, ++ enum clocks_state clocks_state) ++{ ++ return disp_clk->funcs->set_min_clocks_state(disp_clk, clocks_state); ++} ++ ++uint32_t dal_display_clock_get_dp_ref_clk_frequency( ++ struct display_clock *disp_clk) ++{ ++ return disp_clk->funcs->get_dp_ref_clk_frequency(disp_clk); ++} ++ ++/*the second parameter of "switchreferenceclock" is ++ * a dummy argument for all pre dce 6.0 versions*/ ++ ++void dal_display_clock_switch_reference_clock( ++ struct display_clock *disp_clk, ++ bool use_external_ref_clk, ++ uint32_t requested_clk_khz) ++{ ++ /* TODO: requires Asic Control*/ ++ /* ++ struct ac_pixel_clk_params params; ++ struct asic_control *ac = ++ dal_adapter_service_get_asic_control(disp_clk->as); ++ dc_service_memset(¶ms, 0, sizeof(struct ac_pixel_clk_params)); ++ ++ params.tgt_pixel_clk_khz = requested_clk_khz; ++ params.flags.SET_EXTERNAL_REF_DIV_SRC = use_external_ref_clk; ++ params.pll_id = disp_clk->id; ++ dal_asic_control_program_display_engine_pll(ac, ¶ms); ++ */ ++} ++ ++void dal_display_clock_set_dp_ref_clock_source( ++ struct display_clock *disp_clk, ++ enum clock_source_id clk_src) ++{ ++ disp_clk->funcs->set_dp_ref_clock_source(disp_clk, clk_src); ++} ++ ++void dal_display_clock_store_max_clocks_state( ++ struct display_clock *disp_clk, ++ enum clocks_state max_clocks_state) ++{ ++ disp_clk->funcs->store_max_clocks_state(disp_clk, max_clocks_state); ++} ++ ++void dal_display_clock_set_clock_state( ++ struct display_clock *disp_clk, ++ struct display_clock_state clk_state) ++{ ++ disp_clk->funcs->set_clock_state(disp_clk, clk_state); ++} ++ ++struct display_clock_state dal_display_clock_get_clock_state( ++ struct display_clock *disp_clk) ++{ ++ return disp_clk->funcs->get_clock_state(disp_clk); ++} ++ ++uint32_t dal_display_clock_get_dfs_bypass_threshold( ++ struct display_clock *disp_clk) ++{ ++ return disp_clk->funcs->get_dfs_bypass_threshold(disp_clk); ++} ++ ++void dal_display_clock_invalid_clock_state( ++ struct display_clock *disp_clk) ++{ ++ disp_clk->cur_min_clks_state = CLOCKS_STATE_INVALID; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/display_clock.h b/drivers/gpu/drm/amd/dal/dc/gpu/display_clock.h +new file mode 100644 +index 0000000..845393b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/display_clock.h +@@ -0,0 +1,82 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DISPLAY_CLOCK_H__ ++#define __DAL_DISPLAY_CLOCK_H__ ++ ++#include "include/display_clock_interface.h" ++ ++struct display_clock_funcs { ++ void (*destroy)(struct display_clock **to_destroy); ++ bool (*validate)(struct display_clock *disp_clk, ++ struct min_clock_params *params); ++ uint32_t (*calculate_min_clock)(struct display_clock *disp_clk, ++ uint32_t path_num, struct min_clock_params *params); ++ uint32_t (*get_validation_clock)(struct display_clock *disp_clk); ++ void (*set_clock)(struct display_clock *disp_clk, ++ uint32_t requested_clock_khz); ++ uint32_t (*get_clock)(struct display_clock *disp_clk); ++ enum clocks_state (*get_min_clocks_state)( ++ struct display_clock *disp_clk); ++ enum clocks_state (*get_required_clocks_state)( ++ struct display_clock *disp_clk, ++ struct state_dependent_clocks *req_clocks); ++ bool (*set_min_clocks_state)(struct display_clock *disp_clk, ++ enum clocks_state clocks_state); ++ uint32_t (*get_dp_ref_clk_frequency)(struct display_clock *disp_clk); ++ void (*set_dp_ref_clock_source)(struct display_clock *disp_clk, ++ enum clock_source_id clk_src); ++ void (*store_max_clocks_state)(struct display_clock *disp_clk, ++ enum clocks_state max_clocks_state); ++ void (*set_clock_state)(struct display_clock *disp_clk, ++ struct display_clock_state clk_state); ++ struct display_clock_state (*get_clock_state)( ++ struct display_clock *disp_clk); ++ uint32_t (*get_dfs_bypass_threshold)(struct display_clock *disp_clk); ++}; ++ ++struct display_clock { ++ struct dc_context *ctx; ++ const struct display_clock_funcs *funcs; ++ uint32_t min_display_clk_threshold_khz; ++ enum clock_source_id id; ++ struct adapter_service *as; ++ ++ enum clocks_state cur_min_clks_state; ++}; ++void dal_display_clock_base_set_dp_ref_clock_source( ++ struct display_clock *disp_clk, ++ enum clock_source_id clk_src); ++struct display_clock_state dal_display_clock_base_get_clock_state( ++ struct display_clock *disp_clk); ++uint32_t dal_display_clock_base_get_dfs_bypass_threshold( ++ struct display_clock *disp_clk); ++void dal_display_clock_base_set_clock_state(struct display_clock *disp_clk, ++ struct display_clock_state clk_state); ++bool dal_display_clock_construct_base( ++ struct display_clock *base, ++ struct dc_context *ctx, ++ struct adapter_service *as); ++#endif /* __DAL_DISPLAY_CLOCK_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/divider_range.c b/drivers/gpu/drm/amd/dal/dc/gpu/divider_range.c +new file mode 100644 +index 0000000..3b04447 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/divider_range.c +@@ -0,0 +1,127 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#include "dal_services.h" ++#include "divider_range.h" ++ ++bool dal_divider_range_construct( ++ struct divider_range *div_range, ++ uint32_t range_start, ++ uint32_t range_step, ++ uint32_t did_min, ++ uint32_t did_max) ++{ ++ div_range->div_range_start = range_start; ++ div_range->div_range_step = range_step; ++ div_range->did_min = did_min; ++ div_range->did_max = did_max; ++ ++ if (div_range->div_range_step == 0) { ++ div_range->div_range_step = 1; ++ /*div_range_step cannot be zero*/ ++ BREAK_TO_DEBUGGER(); ++ } ++ /* Calculate this based on the other inputs.*/ ++ /* See DividerRange.h for explanation of */ ++ /* the relationship between divider id (DID) and a divider.*/ ++ /* Number of Divider IDs = (Maximum Divider ID - Minimum Divider ID)*/ ++ /* Maximum divider identified in this range = ++ * (Number of Divider IDs)*Step size between dividers ++ * + The start of this range.*/ ++ div_range->div_range_end = (did_max - did_min) * range_step ++ + range_start; ++ return true; ++} ++ ++static uint32_t dal_divider_range_calc_divider( ++ struct divider_range *div_range, ++ uint32_t did) ++{ ++ /* Is this DID within our range?*/ ++ if ((did < div_range->did_min) || (did >= div_range->did_max)) ++ return INVALID_DIVIDER; ++ ++ return ((did - div_range->did_min) * div_range->div_range_step) ++ + div_range->div_range_start; ++ ++} ++ ++static uint32_t dal_divider_range_calc_did( ++ struct divider_range *div_range, ++ uint32_t div) ++{ ++ uint32_t did; ++ /* Check before dividing.*/ ++ if (div_range->div_range_step == 0) { ++ div_range->div_range_step = 1; ++ /*div_range_step cannot be zero*/ ++ BREAK_TO_DEBUGGER(); ++ } ++ /* Is this divider within our range?*/ ++ if ((div < div_range->div_range_start) ++ || (div >= div_range->div_range_end)) ++ return INVALID_DID; ++/* did = (divider - range_start + (range_step-1)) / range_step) + did_min*/ ++ did = div - div_range->div_range_start; ++ did += div_range->div_range_step - 1; ++ did /= div_range->div_range_step; ++ did += div_range->did_min; ++ return did; ++} ++ ++uint32_t dal_divider_range_get_divider( ++ struct divider_range *div_range, ++ uint32_t ranges_num, ++ uint32_t did) ++{ ++ uint32_t div = INVALID_DIVIDER; ++ uint32_t i; ++ ++ for (i = 0; i < ranges_num; i++) { ++ /* Calculate divider with given divider ID*/ ++ div = dal_divider_range_calc_divider(&div_range[i], did); ++ /* Found a valid return divider*/ ++ if (div != INVALID_DIVIDER) ++ break; ++ } ++ return div; ++} ++uint32_t dal_divider_range_get_did( ++ struct divider_range *div_range, ++ uint32_t ranges_num, ++ uint32_t divider) ++{ ++ uint32_t did = INVALID_DID; ++ uint32_t i; ++ ++ for (i = 0; i < ranges_num; i++) { ++ /* CalcDid returns InvalidDid if a divider ID isn't found*/ ++ did = dal_divider_range_calc_did(&div_range[i], divider); ++ /* Found a valid return did*/ ++ if (did != INVALID_DID) ++ break; ++ } ++ return did; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/divider_range.h b/drivers/gpu/drm/amd/dal/dc/gpu/divider_range.h +new file mode 100644 +index 0000000..2ec1034 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/divider_range.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DIVIDER_RANGE_H__ ++#define __DAL_DIVIDER_RANGE_H__ ++ ++enum divider_error_types { ++ INVALID_DID = 0, ++ INVALID_DIVIDER = 1 ++}; ++ ++struct divider_range { ++ uint32_t div_range_start; ++ /* The end of this range of dividers.*/ ++ uint32_t div_range_end; ++ /* The distance between each divider in this range.*/ ++ uint32_t div_range_step; ++ /* The divider id for the lowest divider.*/ ++ uint32_t did_min; ++ /* The divider id for the highest divider.*/ ++ uint32_t did_max; ++}; ++ ++bool dal_divider_range_construct( ++ struct divider_range *div_range, ++ uint32_t range_start, ++ uint32_t range_step, ++ uint32_t did_min, ++ uint32_t did_max); ++ ++uint32_t dal_divider_range_get_divider( ++ struct divider_range *div_range, ++ uint32_t ranges_num, ++ uint32_t did); ++uint32_t dal_divider_range_get_did( ++ struct divider_range *div_range, ++ uint32_t ranges_num, ++ uint32_t divider); ++ ++ ++#endif /* __DAL_DIVIDER_RANGE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.c b/drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.c +new file mode 100644 +index 0000000..ac27cd7 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.c +@@ -0,0 +1,119 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/clock_source_types.h" ++#include "include/bios_parser_interface.h" ++#include "include/logger_interface.h" ++#include "ext_clock_source.h" ++ ++uint32_t dal_ext_clock_source_get_pix_clk_dividers( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct ext_clock_source *ext_clk_src = container_of( ++ clk_src, ++ struct ext_clock_source, ++ base); ++ ++ if (pix_clk_params == NULL || ++ pll_settings == NULL || ++ pix_clk_params->requested_pix_clk == 0) { ++ dal_logger_write(clk_src->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "%s: Invalid parameters!!", __func__); ++ return MAX_PLL_CALC_ERROR; ++ } ++ ++ dc_service_memset(pll_settings, 0, sizeof(struct pll_settings)); ++ pll_settings->adjusted_pix_clk = ext_clk_src->ext_clk_freq_khz; ++ pll_settings->calculated_pix_clk = ext_clk_src->ext_clk_freq_khz; ++ pll_settings->actual_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ return 0; ++} ++ ++bool dal_ext_clock_source_program_pix_clk( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct bp_pixel_clock_parameters bp_pix_clk_params = {0}; ++ ++ bp_pix_clk_params.controller_id = pix_clk_params->controller_id; ++ bp_pix_clk_params.pll_id = clk_src->clk_src_id; ++ bp_pix_clk_params.target_pixel_clock = ++ pix_clk_params->requested_pix_clk; ++ bp_pix_clk_params.encoder_object_id = pix_clk_params->encoder_object_id; ++ bp_pix_clk_params.signal_type = pix_clk_params->signal_type; ++ bp_pix_clk_params.dvo_config = pix_clk_params->dvo_cfg; ++ ++ ++ if (dal_bios_parser_set_pixel_clock( ++ clk_src->bios_parser, ++ &bp_pix_clk_params) == BP_RESULT_OK) ++ return true; ++ return false; ++ ++} ++ ++bool dal_ext_clock_source_power_down_pll(struct clock_source *clk_src, ++ enum controller_id controller_id) ++{ ++ return true; ++} ++ ++bool dal_ext_clock_source_construct( ++ struct ext_clock_source *ext_clk_src, ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ struct firmware_info fw_info = { { 0 } }; ++ ++ if (!dal_clock_source_construct( ++ &ext_clk_src->base, clk_src_init_data)) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ ext_clk_src->base.clk_sharing_lvl = ++ CLOCK_SHARING_LEVEL_DISPLAY_PORT_SHAREABLE; ++ ext_clk_src->base.is_clock_source_with_fixed_freq = true; ++ /* ExtClock has fixed frequency, ++ * so it supports only DisplayPort signals.*/ ++ ext_clk_src->base.output_signals = ++ SIGNAL_TYPE_DISPLAY_PORT | ++ SIGNAL_TYPE_DISPLAY_PORT_MST | ++ SIGNAL_TYPE_EDP; ++ ++ /*Get External clock frequency from ATOMBIOS Data table */ ++ if (dal_bios_parser_get_firmware_info( ++ ext_clk_src->base.bios_parser, ++ &fw_info) != BP_RESULT_OK) ++ return false; ++ ext_clk_src->ext_clk_freq_khz = fw_info. ++ external_clock_source_frequency_for_dp; ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.h b/drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.h +new file mode 100644 +index 0000000..bef1dc4 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/ext_clock_source.h +@@ -0,0 +1,47 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_EXT_CLOCK_SOURCE_H__ ++#define __DAL_EXT_CLOCK_SOURCE_H__ ++ ++#include "clock_source.h" ++ ++struct ext_clock_source { ++ struct clock_source base; ++ uint32_t ext_clk_freq_khz; ++}; ++ ++bool dal_ext_clock_source_construct( ++ struct ext_clock_source *ext_cs, ++ struct clock_source_init_data *clk_src_init_data); ++bool dal_ext_clock_source_power_down_pll(struct clock_source *clk_src, ++ enum controller_id controller_id); ++uint32_t dal_ext_clock_source_get_pix_clk_dividers( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++bool dal_ext_clock_source_program_pix_clk( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++#endif /*__DAL_EXT_CLOCK_SOURCE_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.c b/drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.c +new file mode 100644 +index 0000000..8bb0304 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.c +@@ -0,0 +1,141 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/bios_parser_interface.h" ++#include "pll_clock_source.h" ++ ++bool dal_pll_clock_source_power_down_pll( ++ struct clock_source *clk_src, ++ enum controller_id controller_id) ++{ ++ ++ enum bp_result bp_result; ++ struct bp_pixel_clock_parameters bp_pixel_clock_params = {0}; ++ ++ /* If Pixel Clock is 0 it means Power Down Pll*/ ++ bp_pixel_clock_params.controller_id = controller_id; ++ bp_pixel_clock_params.pll_id = clk_src->clk_src_id; ++ bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1; ++ ++ /*Call ASICControl to process ATOMBIOS Exec table*/ ++ bp_result = dal_bios_parser_set_pixel_clock( ++ clk_src->bios_parser, ++ &bp_pixel_clock_params); ++ ++ return bp_result == BP_RESULT_OK; ++} ++ ++bool dal_pll_clock_source_adjust_pix_clk( ++ struct pll_clock_source *pll_clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ uint32_t actual_pix_clk_khz = 0; ++ uint32_t requested_clk_khz = 0; ++ struct bp_adjust_pixel_clock_parameters bp_adjust_pixel_clock_params = { ++ 0 }; ++ enum bp_result bp_result; ++ ++ switch (pix_clk_params->signal_type) { ++ case SIGNAL_TYPE_HDMI_TYPE_A: { ++ requested_clk_khz = pix_clk_params->requested_pix_clk; ++ ++ switch (pix_clk_params->color_depth) { ++ case COLOR_DEPTH_101010: ++ requested_clk_khz = (requested_clk_khz * 5) >> 2; ++ break; /* x1.25*/ ++ case COLOR_DEPTH_121212: ++ requested_clk_khz = (requested_clk_khz * 6) >> 2; ++ break; /* x1.5*/ ++ case COLOR_DEPTH_161616: ++ requested_clk_khz = requested_clk_khz * 2; ++ break; /* x2.0*/ ++ default: ++ break; ++ } ++ ++ actual_pix_clk_khz = requested_clk_khz; ++ } ++ break; ++ ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ case SIGNAL_TYPE_EDP: ++ requested_clk_khz = pix_clk_params->requested_sym_clk; ++ actual_pix_clk_khz = pix_clk_params->requested_pix_clk; ++ break; ++ ++ default: ++ requested_clk_khz = pix_clk_params->requested_pix_clk; ++ actual_pix_clk_khz = pix_clk_params->requested_pix_clk; ++ break; ++ } ++ ++ bp_adjust_pixel_clock_params.pixel_clock = requested_clk_khz; ++ bp_adjust_pixel_clock_params. ++ encoder_object_id = pix_clk_params->encoder_object_id; ++ bp_adjust_pixel_clock_params.signal_type = pix_clk_params->signal_type; ++ bp_adjust_pixel_clock_params.dvo_config = pix_clk_params->dvo_cfg; ++ bp_adjust_pixel_clock_params. ++ display_pll_config = pix_clk_params->disp_pll_cfg; ++ bp_adjust_pixel_clock_params. ++ ss_enable = pix_clk_params->flags.ENABLE_SS; ++ bp_result = dal_bios_parser_adjust_pixel_clock( ++ pll_clk_src->base.bios_parser, ++ &bp_adjust_pixel_clock_params); ++ if (bp_result == BP_RESULT_OK) { ++ pll_settings->actual_pix_clk = actual_pix_clk_khz; ++ pll_settings->adjusted_pix_clk = ++ bp_adjust_pixel_clock_params.adjusted_pixel_clock; ++ pll_settings->reference_divider = ++ bp_adjust_pixel_clock_params.reference_divider; ++ pll_settings->pix_clk_post_divider = ++ bp_adjust_pixel_clock_params.pixel_clock_post_divider; ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++bool dal_pll_clock_source_construct( ++ struct pll_clock_source *pll_clk_src, ++ struct clock_source_init_data *clk_src_init_data) ++{ ++ struct firmware_info fw_info = { { 0 } }; ++ ++ if (!dal_clock_source_construct( ++ &pll_clk_src->base, ++ clk_src_init_data)) ++ return false; ++ ++ if (dal_bios_parser_get_firmware_info( ++ pll_clk_src->base.bios_parser, ++ &fw_info) != BP_RESULT_OK) ++ return false; ++ pll_clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.h b/drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.h +new file mode 100644 +index 0000000..8339e1f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/pll_clock_source.h +@@ -0,0 +1,52 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_PLL_CLOCK_SOURCE_H__ ++#define __DAL_PLL_CLOCK_SOURCE_H__ ++ ++#include "gpu/clock_source.h" ++ ++struct pll_clock_source { ++ struct clock_source base; ++ uint32_t ref_freq_khz; ++}; ++ ++struct delta_sigma_data { ++ uint32_t feedback_amount; ++ uint32_t nfrac_amount; ++ uint32_t ds_frac_size; ++ uint32_t ds_frac_amount; ++}; ++ ++bool dal_pll_clock_source_construct( ++ struct pll_clock_source *pll_clk_src, ++ struct clock_source_init_data *clk_src_init_data); ++ ++bool dal_pll_clock_source_adjust_pix_clk( ++ struct pll_clock_source *pll_clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++bool dal_pll_clock_source_power_down_pll( ++ struct clock_source *clk_src, ++ enum controller_id controller_id); ++#endif /*__DAL_PLL_CLOCK_SOURCE_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile b/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile +new file mode 100644 +index 0000000..15902a8 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile +@@ -0,0 +1,23 @@ ++# ++# Makefile for the 'i2c' sub-component of DAL. ++# It provides the control and status of HW i2c engine of the adapter. ++ ++I2CAUX = aux_engine.o engine_base.o i2caux.o i2c_engine.o \ ++ i2c_generic_hw_engine.o i2c_hw_engine.o i2c_sw_engine.o ++ ++AMD_DAL_I2CAUX = $(addprefix $(AMDDALPATH)/dc/i2caux/,$(I2CAUX)) ++ ++AMD_DAL_FILES += $(AMD_DAL_I2CAUX) ++ ++ ++############################################################################### ++# DCE 11x family ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++I2CAUX_DCE110 = i2caux_dce110.o i2c_sw_engine_dce110.o i2c_hw_engine_dce110.o \ ++ aux_engine_dce110.o ++ ++AMD_DAL_I2CAUX_DCE110 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce110/,$(I2CAUX_DCE110)) ++ ++AMD_DAL_FILES += $(AMD_DAL_I2CAUX_DCE110) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.c b/drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.c +new file mode 100644 +index 0000000..824ceec +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.c +@@ -0,0 +1,568 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "aux_engine.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++#include "include/link_service_types.h" ++ ++/* ++ * This unit ++ */ ++ ++enum { ++ AUX_INVALID_REPLY_RETRY_COUNTER = 1, ++ AUX_TIMED_OUT_RETRY_COUNTER = 2, ++ AUX_DEFER_RETRY_COUNTER = 6 ++}; ++ ++#define FROM_ENGINE(ptr) \ ++ container_of((ptr), struct aux_engine, base) ++ ++enum i2caux_engine_type dal_aux_engine_get_engine_type( ++ const struct engine *engine) ++{ ++ return I2CAUX_ENGINE_TYPE_AUX; ++} ++ ++bool dal_aux_engine_acquire( ++ struct engine *engine, ++ struct ddc *ddc) ++{ ++ struct aux_engine *aux_engine = FROM_ENGINE(engine); ++ ++ enum gpio_result result; ++ ++ result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, ++ GPIO_DDC_CONFIG_TYPE_MODE_AUX); ++ ++ if (result != GPIO_RESULT_OK) ++ return false; ++ ++ if (!aux_engine->funcs->acquire_engine(aux_engine)) { ++ dal_ddc_close(ddc); ++ return false; ++ } ++ ++ engine->ddc = ddc; ++ ++ return true; ++} ++ ++struct read_command_context { ++ uint8_t *buffer; ++ uint8_t current_read_length; ++ uint32_t offset; ++ enum i2caux_transaction_status status; ++ ++ struct aux_request_transaction_data request; ++ struct aux_reply_transaction_data reply; ++ ++ uint8_t returned_byte; ++ ++ uint32_t timed_out_retry_aux; ++ uint32_t invalid_reply_retry_aux; ++ uint32_t defer_retry_aux; ++ uint32_t defer_retry_i2c; ++ uint32_t invalid_reply_retry_aux_on_ack; ++ ++ bool transaction_complete; ++ bool operation_succeeded; ++}; ++ ++static void process_read_reply( ++ struct aux_engine *engine, ++ struct read_command_context *ctx) ++{ ++ engine->funcs->process_channel_reply(engine, &ctx->reply); ++ ++ switch (ctx->reply.status) { ++ case AUX_TRANSACTION_REPLY_AUX_ACK: ++ ctx->defer_retry_aux = 0; ++ if (ctx->returned_byte > ctx->current_read_length) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else if (ctx->returned_byte < ctx->current_read_length) { ++ ctx->current_read_length -= ctx->returned_byte; ++ ++ ctx->offset += ctx->returned_byte; ++ ++ ++ctx->invalid_reply_retry_aux_on_ack; ++ ++ if (ctx->invalid_reply_retry_aux_on_ack > ++ AUX_INVALID_REPLY_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } ++ } else { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ ctx->transaction_complete = true; ++ ctx->operation_succeeded = true; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_NACK: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ ctx->operation_succeeded = false; ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_DEFER: ++ ++ctx->defer_retry_aux; ++ ++ if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_I2C_DEFER: ++ ctx->defer_retry_aux = 0; ++ ++ ++ctx->defer_retry_i2c; ++ ++ if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++ ++static void process_read_request( ++ struct aux_engine *engine, ++ struct read_command_context *ctx) ++{ ++ enum aux_channel_operation_result operation_result; ++ ++ engine->funcs->submit_channel_request(engine, &ctx->request); ++ ++ operation_result = engine->funcs->get_channel_status( ++ engine, &ctx->returned_byte); ++ ++ switch (operation_result) { ++ case AUX_CHANNEL_OPERATION_SUCCEEDED: ++ if (ctx->returned_byte > ctx->current_read_length) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else { ++ ctx->timed_out_retry_aux = 0; ++ ctx->invalid_reply_retry_aux = 0; ++ ++ ctx->reply.length = ctx->returned_byte; ++ ctx->reply.data = ctx->buffer; ++ ++ process_read_reply(engine, ctx); ++ } ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: ++ ++ctx->invalid_reply_retry_aux; ++ ++ if (ctx->invalid_reply_retry_aux > ++ AUX_INVALID_REPLY_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else ++ dc_service_delay_in_microseconds(engine->base.ctx, 400); ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ ++ctx->timed_out_retry_aux; ++ ++ if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } else { ++ /* DP 1.2a, table 2-58: ++ * "S3: AUX Request CMD PENDING: ++ * retry 3 times, with 400usec wait on each" ++ * The HW timeout is set to 550usec, ++ * so we should not wait here */ ++ } ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++ ++static bool read_command( ++ struct aux_engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction) ++{ ++ struct read_command_context ctx; ++ ++ ctx.buffer = request->payload.data; ++ ctx.current_read_length = request->payload.length; ++ ctx.offset = 0; ++ ctx.timed_out_retry_aux = 0; ++ ctx.invalid_reply_retry_aux = 0; ++ ctx.defer_retry_aux = 0; ++ ctx.defer_retry_i2c = 0; ++ ctx.invalid_reply_retry_aux_on_ack = 0; ++ ctx.transaction_complete = false; ++ ctx.operation_succeeded = true; ++ ++ if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_DP; ++ ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ; ++ ctx.request.address = request->payload.address; ++ } else if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_I2C; ++ ctx.request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_READ; ++ ctx.request.address = request->payload.address >> 1; ++ } else { ++ /* in DAL2, there was no return in such case */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ ctx.request.delay = 0; ++ ++ do { ++ dc_service_memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length); ++ ++ ctx.request.data = ctx.buffer + ctx.offset; ++ ctx.request.length = ctx.current_read_length; ++ ++ process_read_request(engine, &ctx); ++ ++ request->status = ctx.status; ++ ++ if (ctx.operation_succeeded && !ctx.transaction_complete) ++ if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) ++ dc_service_sleep_in_milliseconds(engine->base.ctx, engine->delay); ++ } while (ctx.operation_succeeded && !ctx.transaction_complete); ++ ++ return ctx.operation_succeeded; ++} ++ ++struct write_command_context { ++ bool mot; ++ ++ uint8_t *buffer; ++ uint8_t current_write_length; ++ enum i2caux_transaction_status status; ++ ++ struct aux_request_transaction_data request; ++ struct aux_reply_transaction_data reply; ++ ++ uint8_t returned_byte; ++ ++ uint32_t timed_out_retry_aux; ++ uint32_t invalid_reply_retry_aux; ++ uint32_t defer_retry_aux; ++ uint32_t defer_retry_i2c; ++ uint32_t max_defer_retry; ++ uint32_t ack_m_retry; ++ ++ uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE]; ++ ++ bool transaction_complete; ++ bool operation_succeeded; ++}; ++ ++static void process_write_reply( ++ struct aux_engine *engine, ++ struct write_command_context *ctx) ++{ ++ engine->funcs->process_channel_reply(engine, &ctx->reply); ++ ++ switch (ctx->reply.status) { ++ case AUX_TRANSACTION_REPLY_AUX_ACK: ++ ctx->operation_succeeded = true; ++ ++ if (ctx->returned_byte) { ++ ctx->request.action = ctx->mot ? ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; ++ ++ ctx->current_write_length = 0; ++ ++ ++ctx->ack_m_retry; ++ ++ if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } else ++ dc_service_delay_in_microseconds(engine->base.ctx, 300); ++ } else { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ ctx->defer_retry_aux = 0; ++ ctx->ack_m_retry = 0; ++ ctx->transaction_complete = true; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_NACK: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ ctx->operation_succeeded = false; ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_DEFER: ++ ++ctx->defer_retry_aux; ++ ++ if (ctx->defer_retry_aux > ctx->max_defer_retry) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_I2C_DEFER: ++ ctx->defer_retry_aux = 0; ++ ctx->current_write_length = 0; ++ ++ ctx->request.action = ctx->mot ? ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; ++ ++ ++ctx->defer_retry_i2c; ++ ++ if (ctx->defer_retry_i2c > ctx->max_defer_retry) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++ ++static void process_write_request( ++ struct aux_engine *engine, ++ struct write_command_context *ctx) ++{ ++ enum aux_channel_operation_result operation_result; ++ ++ engine->funcs->submit_channel_request(engine, &ctx->request); ++ ++ operation_result = engine->funcs->get_channel_status( ++ engine, &ctx->returned_byte); ++ ++ switch (operation_result) { ++ case AUX_CHANNEL_OPERATION_SUCCEEDED: ++ ctx->timed_out_retry_aux = 0; ++ ctx->invalid_reply_retry_aux = 0; ++ ++ ctx->reply.length = ctx->returned_byte; ++ ctx->reply.data = ctx->reply_data; ++ ++ process_write_reply(engine, ctx); ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: ++ ++ctx->invalid_reply_retry_aux; ++ ++ if (ctx->invalid_reply_retry_aux > ++ AUX_INVALID_REPLY_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else ++ dc_service_delay_in_microseconds(engine->base.ctx, 400); ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ ++ctx->timed_out_retry_aux; ++ ++ if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } else { ++ /* DP 1.2a, table 2-58: ++ * "S3: AUX Request CMD PENDING: ++ * retry 3 times, with 400usec wait on each" ++ * The HW timeout is set to 550usec, ++ * so we should not wait here */ ++ } ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++ ++static bool write_command( ++ struct aux_engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction) ++{ ++ struct write_command_context ctx; ++ ++ ctx.mot = middle_of_transaction; ++ ctx.buffer = request->payload.data; ++ ctx.current_write_length = request->payload.length; ++ ctx.timed_out_retry_aux = 0; ++ ctx.invalid_reply_retry_aux = 0; ++ ctx.defer_retry_aux = 0; ++ ctx.defer_retry_i2c = 0; ++ ctx.ack_m_retry = 0; ++ ctx.transaction_complete = false; ++ ctx.operation_succeeded = true; ++ ++ if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_DP; ++ ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE; ++ ctx.request.address = request->payload.address; ++ } else if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_I2C; ++ ctx.request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE; ++ ctx.request.address = request->payload.address >> 1; ++ } else { ++ /* in DAL2, there was no return in such case */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ ctx.request.delay = 0; ++ ++ ctx.max_defer_retry = ++ (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ? ++ engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER; ++ ++ do { ++ ctx.request.data = ctx.buffer; ++ ctx.request.length = ctx.current_write_length; ++ ++ process_write_request(engine, &ctx); ++ ++ request->status = ctx.status; ++ ++ if (ctx.operation_succeeded && !ctx.transaction_complete) ++ if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) ++ dc_service_sleep_in_milliseconds(engine->base.ctx, engine->delay); ++ } while (ctx.operation_succeeded && !ctx.transaction_complete); ++ ++ return ctx.operation_succeeded; ++} ++ ++static bool end_of_transaction_command( ++ struct aux_engine *engine, ++ struct i2caux_transaction_request *request) ++{ ++ struct i2caux_transaction_request dummy_request; ++ uint8_t dummy_data; ++ ++ /* [tcheng] We only need to send the stop (read with MOT = 0) ++ * for I2C-over-Aux, not native AUX */ ++ ++ if (request->payload.address_space != ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) ++ return false; ++ ++ dummy_request.operation = request->operation; ++ dummy_request.payload.address_space = request->payload.address_space; ++ dummy_request.payload.address = request->payload.address; ++ ++ /* ++ * Add a dummy byte due to some receiver quirk ++ * where one byte is sent along with MOT = 0. ++ * Ideally this should be 0. ++ */ ++ ++ dummy_request.payload.length = 0; ++ dummy_request.payload.data = &dummy_data; ++ ++ if (request->operation == I2CAUX_TRANSACTION_READ) ++ return read_command(engine, &dummy_request, false); ++ else ++ return write_command(engine, &dummy_request, false); ++ ++ /* according Syed, it does not need now DoDummyMOT */ ++} ++ ++bool dal_aux_engine_submit_request( ++ struct engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction) ++{ ++ struct aux_engine *aux_engine = FROM_ENGINE(engine); ++ ++ bool result; ++ bool mot_used = true; ++ ++ switch (request->operation) { ++ case I2CAUX_TRANSACTION_READ: ++ result = read_command(aux_engine, request, mot_used); ++ break; ++ case I2CAUX_TRANSACTION_WRITE: ++ result = write_command(aux_engine, request, mot_used); ++ break; ++ default: ++ result = false; ++ } ++ ++ /* [tcheng] ++ * need to send stop for the last transaction to free up the AUX ++ * if the above command fails, this would be the last transaction */ ++ ++ if (!middle_of_transaction || !result) ++ end_of_transaction_command(aux_engine, request); ++ ++ /* mask AUX interrupt */ ++ ++ return result; ++} ++ ++bool dal_aux_engine_construct( ++ struct aux_engine *engine, ++ struct dc_context *ctx) ++{ ++ if (!dal_i2caux_construct_engine(&engine->base, ctx)) ++ return false; ++ engine->delay = 0; ++ engine->max_defer_write_retry = 0; ++ return true; ++} ++ ++void dal_aux_engine_destruct( ++ struct aux_engine *engine) ++{ ++ dal_i2caux_destruct_engine(&engine->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.h b/drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.h +new file mode 100644 +index 0000000..474f5e9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/aux_engine.h +@@ -0,0 +1,119 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_AUX_ENGINE_H__ ++#define __DAL_AUX_ENGINE_H__ ++ ++enum aux_transaction_type { ++ AUX_TRANSACTION_TYPE_DP, ++ AUX_TRANSACTION_TYPE_I2C ++}; ++ ++struct aux_request_transaction_data { ++ enum aux_transaction_type type; ++ enum i2caux_transaction_action action; ++ /* 20-bit AUX channel transaction address */ ++ uint32_t address; ++ /* delay, in 100-microsecond units */ ++ uint8_t delay; ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++enum aux_transaction_reply { ++ AUX_TRANSACTION_REPLY_AUX_ACK = 0x00, ++ AUX_TRANSACTION_REPLY_AUX_NACK = 0x01, ++ AUX_TRANSACTION_REPLY_AUX_DEFER = 0x02, ++ ++ AUX_TRANSACTION_REPLY_I2C_ACK = 0x00, ++ AUX_TRANSACTION_REPLY_I2C_NACK = 0x10, ++ AUX_TRANSACTION_REPLY_I2C_DEFER = 0x20, ++ ++ AUX_TRANSACTION_REPLY_INVALID = 0xFF ++}; ++ ++struct aux_reply_transaction_data { ++ enum aux_transaction_reply status; ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++enum aux_channel_operation_result { ++ AUX_CHANNEL_OPERATION_SUCCEEDED, ++ AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN, ++ AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY, ++ AUX_CHANNEL_OPERATION_FAILED_TIMEOUT ++}; ++ ++struct aux_engine; ++ ++struct aux_engine_funcs { ++ void (*destroy)( ++ struct aux_engine **ptr); ++ bool (*acquire_engine)( ++ struct aux_engine *engine); ++ void (*configure)( ++ struct aux_engine *engine, ++ union aux_config cfg); ++ bool (*start_gtc_sync)( ++ struct aux_engine *engine); ++ void (*stop_gtc_sync)( ++ struct aux_engine *engine); ++ void (*submit_channel_request)( ++ struct aux_engine *engine, ++ struct aux_request_transaction_data *request); ++ void (*process_channel_reply)( ++ struct aux_engine *engine, ++ struct aux_reply_transaction_data *reply); ++ enum aux_channel_operation_result (*get_channel_status)( ++ struct aux_engine *engine, ++ uint8_t *returned_bytes); ++}; ++ ++struct aux_engine { ++ struct engine base; ++ const struct aux_engine_funcs *funcs; ++ /* following values are expressed in milliseconds */ ++ uint32_t delay; ++ uint32_t max_defer_write_retry; ++}; ++ ++bool dal_aux_engine_construct( ++ struct aux_engine *engine, ++ struct dc_context *ctx); ++ ++void dal_aux_engine_destruct( ++ struct aux_engine *engine); ++bool dal_aux_engine_submit_request( ++ struct engine *ptr, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction); ++bool dal_aux_engine_acquire( ++ struct engine *ptr, ++ struct ddc *ddc); ++enum i2caux_engine_type dal_aux_engine_get_engine_type( ++ const struct engine *engine); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.c +new file mode 100644 +index 0000000..1b40a78 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.c +@@ -0,0 +1,789 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "../engine.h" ++#include "../aux_engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "aux_engine_dce110.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++/* ++ * This unit ++ */ ++ ++/* ++ * @brief ++ * Cast 'struct aux_engine *' ++ * to 'struct aux_engine_dce110 *' ++ */ ++#define FROM_AUX_ENGINE(ptr) \ ++ container_of((ptr), struct aux_engine_dce110, base) ++ ++/* ++ * @brief ++ * Cast 'struct engine *' ++ * to 'struct aux_engine_dce110 *' ++ */ ++#define FROM_ENGINE(ptr) \ ++ FROM_AUX_ENGINE(container_of((ptr), struct aux_engine, base)) ++ ++static void release_engine( ++ struct engine *engine) ++{ ++ struct aux_engine_dce110 *aux_engine = FROM_ENGINE(engine); ++ ++ const uint32_t addr = aux_engine->addr.aux_arb_control; ++ ++ uint32_t value = dal_read_reg(engine->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_ARB_CONTROL, ++ AUX_SW_DONE_USING_AUX_REG); ++ ++ dal_write_reg(engine->ctx, addr, value); ++} ++ ++static void destruct( ++ struct aux_engine_dce110 *engine); ++ ++static void destroy( ++ struct aux_engine **aux_engine) ++{ ++ struct aux_engine_dce110 *engine = FROM_AUX_ENGINE(*aux_engine); ++ ++ destruct(engine); ++ ++ dc_service_free((*aux_engine)->base.ctx, engine); ++ ++ *aux_engine = NULL; ++} ++ ++#define SW_CAN_ACCESS_AUX 1 ++ ++static bool acquire_engine( ++ struct aux_engine *engine) ++{ ++ struct aux_engine_dce110 *aux_engine = FROM_AUX_ENGINE(engine); ++ uint32_t value; ++ uint32_t field; ++ ++ /* enable AUX before request SW to access AUX */ ++ { ++ const uint32_t addr = aux_engine->addr.aux_control; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ field = get_reg_field_value( ++ value, ++ AUX_CONTROL, ++ AUX_EN); ++ ++ if (field == 0) { ++ uint8_t counter = 0; ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_CONTROL, ++ AUX_EN); ++ ++ /*DP_AUX block as part of the enable sequence*/ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_CONTROL, ++ AUX_RESET); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ /*poll HW to make sure reset it done*/ ++ do { ++ dc_service_delay_in_microseconds(engine->base.ctx, 1); ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ field = get_reg_field_value( ++ value, ++ AUX_CONTROL, ++ AUX_RESET_DONE); ++ ++ counter++; ++ ++ } while ((field == 0) && (counter < 11)); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUX_CONTROL, ++ AUX_RESET); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ counter = 0; ++ ++ do { ++ dc_service_delay_in_microseconds(engine->base.ctx, 1); ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ field = get_reg_field_value( ++ value, ++ AUX_CONTROL, ++ AUX_RESET_DONE); ++ ++ counter++; ++ ++ } while ((field == 1) && (counter < 11)); ++ } /*if (field)*/ ++ } ++ ++ /* request SW to access AUX */ ++ { ++ const uint32_t addr = aux_engine->addr.aux_arb_control; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_ARB_CONTROL, ++ AUX_SW_USE_AUX_REG_REQ); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ field = get_reg_field_value( ++ value, ++ AUX_ARB_CONTROL, ++ AUX_REG_RW_CNTL_STATUS); ++ ++ return field == SW_CAN_ACCESS_AUX; ++ } ++} ++ ++static void configure( ++ struct aux_engine *engine, ++ union aux_config cfg) ++{ ++ struct aux_engine_dce110 *aux_engine = FROM_AUX_ENGINE(engine); ++ ++ const uint32_t addr = aux_engine->addr.aux_control; ++ ++ uint32_t value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ (0 != cfg.bits.ALLOW_AUX_WHEN_HPD_LOW), ++ AUX_CONTROL, ++ AUX_IGNORE_HPD_DISCON); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++} ++ ++static bool start_gtc_sync( ++ struct aux_engine *engine) ++{ ++ /*TODO*/ ++ return false; ++} ++ ++static void stop_gtc_sync( ++ struct aux_engine *engine) ++{ ++ /*TODO*/ ++} ++ ++#define COMPOSE_AUX_SW_DATA_16_20(command, address) \ ++ ((command) | ((0xF0000 & (address)) >> 16)) ++ ++#define COMPOSE_AUX_SW_DATA_8_15(address) \ ++ ((0xFF00 & (address)) >> 8) ++ ++#define COMPOSE_AUX_SW_DATA_0_7(address) \ ++ (0xFF & (address)) ++ ++static void submit_channel_request( ++ struct aux_engine *engine, ++ struct aux_request_transaction_data *request) ++{ ++ struct aux_engine_dce110 *aux_engine = FROM_AUX_ENGINE(engine); ++ uint32_t value; ++ uint32_t length; ++ ++ bool is_write = ++ ((request->type == AUX_TRANSACTION_TYPE_DP) && ++ (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || ++ ((request->type == AUX_TRANSACTION_TYPE_I2C) && ++ ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || ++ (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); ++ ++ /* clear_aux_error */ ++ { ++ const uint32_t addr = mmAUXN_IMPCAL; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUXN_IMPCAL, ++ AUXN_CALOUT_ERROR_AK); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUXN_IMPCAL, ++ AUXN_CALOUT_ERROR_AK); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ { ++ const uint32_t addr = mmAUXP_IMPCAL; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUXP_IMPCAL, ++ AUXP_CALOUT_ERROR_AK); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUXP_IMPCAL, ++ AUXP_CALOUT_ERROR_AK); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ ++ /* force_default_calibrate */ ++ { ++ const uint32_t addr = mmAUXN_IMPCAL; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUXN_IMPCAL, ++ AUXN_IMPCAL_ENABLE); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUXN_IMPCAL, ++ AUXN_IMPCAL_OVERRIDE_ENABLE); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ { ++ const uint32_t addr = mmAUXP_IMPCAL; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUXP_IMPCAL, ++ AUXP_IMPCAL_OVERRIDE_ENABLE); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUXP_IMPCAL, ++ AUXP_IMPCAL_OVERRIDE_ENABLE); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ ++ /* set the delay and the number of bytes to write */ ++ { ++ const uint32_t addr = aux_engine->addr.aux_sw_control; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ request->delay, ++ AUX_SW_CONTROL, ++ AUX_SW_START_DELAY); ++ ++ /* The length include ++ * the 4 bit header and the 20 bit address ++ * (that is 3 byte). ++ * If the requested length is non zero this means ++ * an addition byte specifying the length is required. */ ++ ++ length = request->length ? 4 : 3; ++ if (is_write) ++ length += request->length; ++ ++ set_reg_field_value( ++ value, ++ length, ++ AUX_SW_CONTROL, ++ AUX_SW_WR_BYTES); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ ++ /* program action and address and payload data (if 'is_write') */ ++ { ++ const uint32_t addr = aux_engine->addr.aux_sw_data; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUX_SW_DATA, ++ AUX_SW_INDEX); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUX_SW_DATA, ++ AUX_SW_DATA_RW); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_SW_DATA, ++ AUX_SW_AUTOINCREMENT_DISABLE); ++ ++ set_reg_field_value( ++ value, ++ COMPOSE_AUX_SW_DATA_16_20( ++ request->action, request->address), ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUX_SW_DATA, ++ AUX_SW_AUTOINCREMENT_DISABLE); ++ ++ set_reg_field_value( ++ value, ++ COMPOSE_AUX_SW_DATA_8_15(request->address), ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ COMPOSE_AUX_SW_DATA_0_7(request->address), ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ if (request->length) { ++ set_reg_field_value( ++ value, ++ request->length - 1, ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ ++ if (is_write) { ++ /* Load the HW buffer with the Data to be sent. ++ * This is relevant for write operation. ++ * For read, the data recived data will be ++ * processed in process_channel_reply(). */ ++ uint32_t i = 0; ++ ++ while (i < request->length) { ++ ++ set_reg_field_value( ++ value, ++ request->data[i], ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ dal_write_reg( ++ engine->base.ctx, addr, value); ++ ++ ++i; ++ } ++ } ++ } ++ ++ { ++ const uint32_t addr = aux_engine->addr.aux_interrupt_control; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_INTERRUPT_CONTROL, ++ AUX_SW_DONE_ACK); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++ ++ { ++ const uint32_t addr = aux_engine->addr.aux_sw_control; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_SW_CONTROL, ++ AUX_SW_GO); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ } ++} ++ ++static void process_channel_reply( ++ struct aux_engine *engine, ++ struct aux_reply_transaction_data *reply) ++{ ++ struct aux_engine_dce110 *aux_engine = FROM_AUX_ENGINE(engine); ++ ++ /* Need to do a read to get the number of bytes to process ++ * Alternatively, this information can be passed - ++ * but that causes coupling which isn't good either. */ ++ ++ uint32_t bytes_replied; ++ uint32_t value; ++ ++ { ++ const uint32_t addr = aux_engine->addr.aux_sw_status; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ bytes_replied = get_reg_field_value( ++ value, ++ AUX_SW_STATUS, ++ AUX_SW_REPLY_BYTE_COUNT); ++ } ++ ++ if (bytes_replied) { ++ uint32_t reply_result; ++ ++ const uint32_t addr = aux_engine->addr.aux_sw_data; ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUX_SW_DATA, ++ AUX_SW_INDEX); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_SW_DATA, ++ AUX_SW_AUTOINCREMENT_DISABLE); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_SW_DATA, ++ AUX_SW_DATA_RW); ++ ++ dal_write_reg(engine->base.ctx, addr, value); ++ ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ reply_result = get_reg_field_value( ++ value, ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ reply_result = reply_result >> 4; ++ ++ switch (reply_result) { ++ case 0: /* ACK */ { ++ uint32_t i = 0; ++ ++ /* first byte was already used ++ * to get the command status */ ++ --bytes_replied; ++ ++ while (i < bytes_replied) { ++ value = dal_read_reg( ++ engine->base.ctx, addr); ++ ++ reply->data[i] = get_reg_field_value( ++ value, ++ AUX_SW_DATA, ++ AUX_SW_DATA); ++ ++ ++i; ++ } ++ ++ reply->status = AUX_TRANSACTION_REPLY_AUX_ACK; ++ } ++ break; ++ case 1: /* NACK */ ++ reply->status = AUX_TRANSACTION_REPLY_AUX_NACK; ++ break; ++ case 2: /* DEFER */ ++ reply->status = AUX_TRANSACTION_REPLY_AUX_DEFER; ++ break; ++ case 4: /* AUX ACK / I2C NACK */ ++ reply->status = AUX_TRANSACTION_REPLY_I2C_NACK; ++ break; ++ case 8: /* AUX ACK / I2C DEFER */ ++ reply->status = AUX_TRANSACTION_REPLY_I2C_DEFER; ++ break; ++ default: ++ reply->status = AUX_TRANSACTION_REPLY_INVALID; ++ } ++ } else { ++ /* Need to handle an error case... ++ * hopefully, upper layer function won't call this function ++ * if the number of bytes in the reply was 0 ++ * because there was surely an error that was asserted ++ * that should have been handled ++ * for hot plug case, this could happens*/ ++ if (!(value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) ++ ASSERT_CRITICAL(false); ++ } ++} ++ ++static enum aux_channel_operation_result get_channel_status( ++ struct aux_engine *engine, ++ uint8_t *returned_bytes) ++{ ++ struct aux_engine_dce110 *aux_engine = FROM_AUX_ENGINE(engine); ++ ++ const uint32_t addr = aux_engine->addr.aux_sw_status; ++ ++ uint32_t value; ++ uint32_t aux_sw_done; ++ ++ if (returned_bytes == NULL) { ++ /*caller pass NULL pointer*/ ++ ASSERT_CRITICAL(false); ++ return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; ++ } ++ *returned_bytes = 0; ++ ++ /* poll to make sure that SW_DONE is asserted */ ++ { ++ uint32_t time_elapsed = 0; ++ ++ do { ++ value = dal_read_reg(engine->base.ctx, addr); ++ ++ aux_sw_done = get_reg_field_value( ++ value, ++ AUX_SW_STATUS, ++ AUX_SW_DONE); ++ ++ if (aux_sw_done) ++ break; ++ ++ dc_service_delay_in_microseconds(engine->base.ctx, 10); ++ ++ time_elapsed += 10; ++ } while (time_elapsed < aux_engine->timeout_period); ++ ++ ++ } ++ ++ /* Note that the following bits are set in 'status.bits' ++ * during CTS 4.2.1.2: ++ * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, ++ * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. ++ * ++ * AUX_SW_RX_MIN_COUNT_VIOL is an internal, ++ * HW debugging bit and should be ignored. */ ++ if (aux_sw_done) { ++ if (get_reg_field_value( ++ value, ++ AUX_SW_STATUS, ++ AUX_SW_RX_TIMEOUT_STATE) || ++ get_reg_field_value( ++ value, ++ AUX_SW_STATUS, ++ AUX_SW_RX_TIMEOUT)) ++ return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; ++ else if (get_reg_field_value( ++ value, ++ AUX_SW_STATUS, ++ AUX_SW_RX_INVALID_STOP)) ++ return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; ++ ++ *returned_bytes = get_reg_field_value( ++ value, ++ AUX_SW_STATUS, ++ AUX_SW_REPLY_BYTE_COUNT); ++ if (*returned_bytes == 0) ++ return ++ AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; ++ else { ++ *returned_bytes -= 1; ++ return AUX_CHANNEL_OPERATION_SUCCEEDED; ++ } ++ } else { ++ /*time_elapsed >= aux_engine->timeout_period */ ++ if (!(value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) ++ ASSERT_CRITICAL(false); ++ return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; ++ } ++} ++ ++static const int32_t aux_channel_offset[] = { ++ mmDP_AUX0_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, ++ mmDP_AUX1_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, ++ mmDP_AUX2_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, ++ mmDP_AUX3_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, ++ mmDP_AUX4_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, ++ mmDP_AUX5_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL ++}; ++ ++static const struct aux_engine_funcs aux_engine_funcs = { ++ .destroy = destroy, ++ .acquire_engine = acquire_engine, ++ .configure = configure, ++ .start_gtc_sync = start_gtc_sync, ++ .stop_gtc_sync = stop_gtc_sync, ++ .submit_channel_request = submit_channel_request, ++ .process_channel_reply = process_channel_reply, ++ .get_channel_status = get_channel_status, ++}; ++ ++static const struct engine_funcs engine_funcs = { ++ .release_engine = release_engine, ++ .submit_request = dal_aux_engine_submit_request, ++ .keep_power_up_count = dal_i2caux_keep_power_up_count, ++ .get_engine_type = dal_aux_engine_get_engine_type, ++ .acquire = dal_aux_engine_acquire, ++}; ++ ++static bool construct( ++ struct aux_engine_dce110 *engine, ++ const struct aux_engine_dce110_init_data *aux_init_data) ++{ ++ int32_t offset; ++ ++ if (aux_init_data->engine_id >= ++ sizeof(aux_channel_offset) / sizeof(int32_t)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ if (!dal_aux_engine_construct( ++ &engine->base, aux_init_data->ctx)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ engine->base.base.funcs = &engine_funcs; ++ engine->base.funcs = &aux_engine_funcs; ++ offset = aux_channel_offset[aux_init_data->engine_id]; ++ engine->addr.aux_control = mmAUX_CONTROL + offset; ++ engine->addr.aux_arb_control = mmAUX_ARB_CONTROL + offset; ++ engine->addr.aux_sw_data = mmAUX_SW_DATA + offset; ++ engine->addr.aux_sw_control = mmAUX_SW_CONTROL + offset; ++ engine->addr.aux_interrupt_control = mmAUX_INTERRUPT_CONTROL + offset; ++ engine->addr.aux_sw_status = mmAUX_SW_STATUS + offset; ++ engine->addr.aux_gtc_sync_control = mmAUX_GTC_SYNC_CONTROL + offset; ++ engine->addr.aux_gtc_sync_status = mmAUX_GTC_SYNC_STATUS + offset; ++ engine->addr.aux_gtc_sync_controller_status = ++ mmAUX_GTC_SYNC_CONTROLLER_STATUS + offset; ++ ++ engine->timeout_period = aux_init_data->timeout_period; ++ ++ return true; ++} ++ ++static void destruct( ++ struct aux_engine_dce110 *engine) ++{ ++ dal_aux_engine_destruct(&engine->base); ++} ++ ++struct aux_engine *dal_aux_engine_dce110_create( ++ const struct aux_engine_dce110_init_data *aux_init_data) ++{ ++ struct aux_engine_dce110 *engine; ++ ++ if (!aux_init_data) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ engine = dc_service_alloc(aux_init_data->ctx, sizeof(*engine)); ++ ++ if (!engine) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(engine, aux_init_data)) ++ return &engine->base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(aux_init_data->ctx, engine); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.h +new file mode 100644 +index 0000000..ec6899e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/aux_engine_dce110.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_AUX_ENGINE_DCE110_H__ ++#define __DAL_AUX_ENGINE_DCE110_H__ ++ ++#include "../aux_engine.h" ++ ++struct aux_engine_dce110 { ++ struct aux_engine base; ++ struct { ++ uint32_t aux_control; ++ uint32_t aux_arb_control; ++ uint32_t aux_sw_data; ++ uint32_t aux_sw_control; ++ uint32_t aux_interrupt_control; ++ uint32_t aux_sw_status; ++ uint32_t aux_gtc_sync_control; ++ uint32_t aux_gtc_sync_status; ++ uint32_t aux_gtc_sync_controller_status; ++ } addr; ++ uint32_t timeout_period; ++}; ++ ++struct aux_engine_dce110_init_data { ++ uint32_t engine_id; ++ uint32_t timeout_period; ++ struct dc_context *ctx; ++}; ++ ++struct aux_engine *dal_aux_engine_dce110_create( ++ const struct aux_engine_dce110_init_data *aux_init_data); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_generic_hw_engine_dce110.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_generic_hw_engine_dce110.h +new file mode 100644 +index 0000000..e6b6a97 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_generic_hw_engine_dce110.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.c +new file mode 100644 +index 0000000..17e89ce +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.c +@@ -0,0 +1,954 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++#include "include/logger_interface.h" ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "../engine.h" ++#include "../i2c_engine.h" ++#include "../i2c_hw_engine.h" ++#include "../i2c_generic_hw_engine.h" ++/* ++ * Header of this unit ++ */ ++ ++#include "i2c_hw_engine_dce110.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++/* ++ * This unit ++ */ ++ ++enum dc_i2c_status { ++ DC_I2C_STATUS__DC_I2C_STATUS_IDLE, ++ DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, ++ DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW ++}; ++ ++enum dc_i2c_arbitration { ++ DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, ++ DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH ++}; ++ ++enum { ++ /* No timeout in HW ++ * (timeout implemented in SW by querying status) */ ++ I2C_SETUP_TIME_LIMIT = 255, ++ I2C_HW_BUFFER_SIZE = 144 ++}; ++ ++/* ++ * @brief ++ * Cast pointer to 'struct i2c_hw_engine *' ++ * to pointer 'struct i2c_hw_engine_dce110 *' ++ */ ++#define FROM_I2C_HW_ENGINE(ptr) \ ++ container_of((ptr), struct i2c_hw_engine_dce110, base) ++/* ++ * @brief ++ * Cast pointer to 'struct i2c_engine *' ++ * to pointer to 'struct i2c_hw_engine_dce110 *' ++ */ ++#define FROM_I2C_ENGINE(ptr) \ ++ FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) ++ ++/* ++ * @brief ++ * Cast pointer to 'struct engine *' ++ * to 'pointer to struct i2c_hw_engine_dce110 *' ++ */ ++#define FROM_ENGINE(ptr) \ ++ FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) ++ ++ ++static void disable_i2c_hw_engine( ++ struct i2c_hw_engine_dce110 *engine) ++{ ++ const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; ++ uint32_t value = 0; ++ ++ struct dc_context *ctx = NULL; ++ ++ ctx = engine->base.base.base.ctx; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_ENABLE); ++ ++ dal_write_reg(ctx, addr, value); ++} ++ ++static void release_engine( ++ struct engine *engine) ++{ ++ struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); ++ ++ struct i2c_engine *base = NULL; ++ bool safe_to_reset; ++ uint32_t value = 0; ++ ++ base = &hw_engine->base.base; ++ ++ /* Restore original HW engine speed */ ++ ++ base->funcs->set_speed(base, hw_engine->base.original_speed); ++ ++ /* Release I2C */ ++ { ++ value = dal_read_reg(engine->ctx, mmDC_I2C_ARBITRATION); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_ARBITRATION, ++ DC_I2C_SW_DONE_USING_I2C_REG); ++ ++ dal_write_reg(engine->ctx, mmDC_I2C_ARBITRATION, value); ++ } ++ ++ /* Reset HW engine */ ++ { ++ uint32_t i2c_sw_status = 0; ++ ++ value = dal_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); ++ ++ i2c_sw_status = get_reg_field_value( ++ value, ++ DC_I2C_SW_STATUS, ++ DC_I2C_SW_STATUS); ++ /* if used by SW, safe to reset */ ++ safe_to_reset = (i2c_sw_status == 1); ++ } ++ { ++ value = dal_read_reg(engine->ctx, mmDC_I2C_CONTROL); ++ ++ if (safe_to_reset) ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_CONTROL, ++ DC_I2C_SOFT_RESET); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_CONTROL, ++ DC_I2C_SW_STATUS_RESET); ++ ++ dal_write_reg(engine->ctx, mmDC_I2C_CONTROL, value); ++ } ++ ++ /* HW I2c engine - clock gating feature */ ++ if (!hw_engine->engine_keep_power_up_count) ++ disable_i2c_hw_engine(hw_engine); ++} ++ ++static void keep_power_up_count( ++ struct engine *engine, ++ bool keep_power_up) ++{ ++ struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); ++ ++ if (keep_power_up) ++ ++hw_engine->engine_keep_power_up_count; ++ else { ++ --hw_engine->engine_keep_power_up_count; ++ ++ if (!hw_engine->engine_keep_power_up_count) ++ disable_i2c_hw_engine(hw_engine); ++ } ++} ++ ++static bool setup_engine( ++ struct i2c_engine *i2c_engine) ++{ ++ uint32_t value = 0; ++ struct i2c_hw_engine_dce110 *engine = FROM_I2C_ENGINE(i2c_engine); ++ ++ /* Program pin select */ ++ { ++ const uint32_t addr = mmDC_I2C_CONTROL; ++ ++ value = dal_read_reg(i2c_engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_GO); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_SOFT_RESET); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_SEND_RESET); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_SW_STATUS_RESET); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_TRANSACTION_COUNT); ++ ++ set_reg_field_value( ++ value, ++ engine->engine_id, ++ DC_I2C_CONTROL, ++ DC_I2C_DDC_SELECT); ++ ++ ++ dal_write_reg(i2c_engine->base.ctx, addr, value); ++ } ++ ++ /* Program time limit */ ++ { ++ const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; ++ ++ value = dal_read_reg(i2c_engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ I2C_SETUP_TIME_LIMIT, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_TIME_LIMIT); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_ENABLE); ++ ++ dal_write_reg(i2c_engine->base.ctx, addr, value); ++ } ++ ++ /* Program HW priority ++ * set to High - interrupt software I2C at any time ++ * Enable restart of SW I2C that was interrupted by HW ++ * disable queuing of software while I2C is in use by HW */ ++ { ++ value = dal_read_reg(i2c_engine->base.ctx, ++ mmDC_I2C_ARBITRATION); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_ARBITRATION, ++ DC_I2C_NO_QUEUED_SW_GO); ++ ++ set_reg_field_value( ++ value, ++ DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, ++ DC_I2C_ARBITRATION, ++ DC_I2C_SW_PRIORITY); ++ ++ dal_write_reg(i2c_engine->base.ctx, ++ mmDC_I2C_ARBITRATION, value); ++ } ++ ++ return true; ++} ++ ++static uint32_t get_speed( ++ const struct i2c_engine *i2c_engine) ++{ ++ const struct i2c_hw_engine_dce110 *engine = FROM_I2C_ENGINE(i2c_engine); ++ ++ const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED; ++ ++ uint32_t pre_scale = 0; ++ ++ uint32_t value = dal_read_reg(i2c_engine->base.ctx, addr); ++ ++ pre_scale = get_reg_field_value( ++ value, ++ DC_I2C_DDC1_SPEED, ++ DC_I2C_DDC1_PRESCALE); ++ ++ /* [anaumov] it seems following is unnecessary */ ++ /*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/ ++ ++ return pre_scale ? ++ engine->reference_frequency / pre_scale : ++ engine->base.default_speed; ++} ++ ++static void set_speed( ++ struct i2c_engine *i2c_engine, ++ uint32_t speed) ++{ ++ struct i2c_hw_engine_dce110 *engine = FROM_I2C_ENGINE(i2c_engine); ++ ++ if (speed) { ++ const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED; ++ ++ uint32_t value = dal_read_reg(i2c_engine->base.ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ engine->reference_frequency / speed, ++ DC_I2C_DDC1_SPEED, ++ DC_I2C_DDC1_PRESCALE); ++ ++ set_reg_field_value( ++ value, ++ 2, ++ DC_I2C_DDC1_SPEED, ++ DC_I2C_DDC1_THRESHOLD); ++ ++ /*DCE11, HW add 100Khz support for I2c*/ ++ if (speed > 50) { ++ set_reg_field_value( ++ value, ++ 2, ++ DC_I2C_DDC1_SPEED, ++ DC_I2C_DDC1_START_STOP_TIMING_CNTL); ++ } else { ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_DDC1_SPEED, ++ DC_I2C_DDC1_START_STOP_TIMING_CNTL); ++ } ++ ++ dal_write_reg(i2c_engine->base.ctx, addr, value); ++ } ++} ++ ++static inline void reset_hw_engine(struct engine *engine) ++{ ++ uint32_t value = dal_read_reg(engine->ctx, mmDC_I2C_CONTROL); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_CONTROL, ++ DC_I2C_SOFT_RESET); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_CONTROL, ++ DC_I2C_SW_STATUS_RESET); ++ ++ dal_write_reg(engine->ctx, mmDC_I2C_CONTROL, value); ++} ++ ++static bool is_hw_busy(struct engine *engine) ++{ ++ uint32_t i2c_sw_status = 0; ++ ++ uint32_t value = dal_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); ++ ++ i2c_sw_status = get_reg_field_value( ++ value, ++ DC_I2C_SW_STATUS, ++ DC_I2C_SW_STATUS); ++ ++ if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) ++ return false; ++ ++ reset_hw_engine(engine); ++ ++ value = dal_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); ++ ++ i2c_sw_status = get_reg_field_value( ++ value, ++ DC_I2C_SW_STATUS, ++ DC_I2C_SW_STATUS); ++ ++ return i2c_sw_status != DC_I2C_STATUS__DC_I2C_STATUS_IDLE; ++} ++ ++/* ++ * @brief ++ * DC_GPIO_DDC MM register offsets ++ */ ++static const uint32_t transaction_addr[] = { ++ mmDC_I2C_TRANSACTION0, ++ mmDC_I2C_TRANSACTION1, ++ mmDC_I2C_TRANSACTION2, ++ mmDC_I2C_TRANSACTION3 ++}; ++ ++static bool process_transaction( ++ struct i2c_hw_engine_dce110 *engine, ++ struct i2c_request_transaction_data *request) ++{ ++ uint8_t length = request->length; ++ uint8_t *buffer = request->data; ++ ++ bool last_transaction = false; ++ uint32_t value = 0; ++ ++ struct dc_context *ctx = NULL; ++ ++ ctx = engine->base.base.base.ctx; ++ ++ { ++ const uint32_t addr = ++ transaction_addr[engine->transaction_count]; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_TRANSACTION0, ++ DC_I2C_STOP_ON_NACK0); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_TRANSACTION0, ++ DC_I2C_START0); ++ ++ ++ if ((engine->transaction_count == 3) || ++ (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || ++ (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_TRANSACTION0, ++ DC_I2C_STOP0); ++ ++ last_transaction = true; ++ } else ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_TRANSACTION0, ++ DC_I2C_STOP0); ++ ++ set_reg_field_value( ++ value, ++ (0 != (request->action & ++ I2CAUX_TRANSACTION_ACTION_I2C_READ)), ++ DC_I2C_TRANSACTION0, ++ DC_I2C_RW0); ++ ++ set_reg_field_value( ++ value, ++ length, ++ DC_I2C_TRANSACTION0, ++ DC_I2C_COUNT0); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* Write the I2C address and I2C data ++ * into the hardware circular buffer, one byte per entry. ++ * As an example, the 7-bit I2C slave address for CRT monitor ++ * for reading DDC/EDID information is 0b1010001. ++ * For an I2C send operation, the LSB must be programmed to 0; ++ * for I2C receive operation, the LSB must be programmed to 1. */ ++ ++ { ++ value = 0; ++ ++ set_reg_field_value( ++ value, ++ false, ++ DC_I2C_DATA, ++ DC_I2C_DATA_RW); ++ ++ set_reg_field_value( ++ value, ++ request->address, ++ DC_I2C_DATA, ++ DC_I2C_DATA); ++ ++ if (engine->transaction_count == 0) { ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DATA, ++ DC_I2C_INDEX); ++ ++ /*enable index write*/ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_DATA, ++ DC_I2C_INDEX_WRITE); ++ ++ engine->buffer_used_write = 0; ++ } ++ ++ dal_write_reg(ctx, mmDC_I2C_DATA, value); ++ ++ engine->buffer_used_write++; ++ ++ if (!(request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DATA, ++ DC_I2C_INDEX_WRITE); ++ ++ while (length) { ++ ++ set_reg_field_value( ++ value, ++ *buffer++, ++ DC_I2C_DATA, ++ DC_I2C_DATA); ++ ++ dal_write_reg(ctx, mmDC_I2C_DATA, value); ++ ++ engine->buffer_used_write++; ++ --length; ++ } ++ } ++ } ++ ++ ++engine->transaction_count; ++ engine->buffer_used_bytes += length + 1; ++ ++ return last_transaction; ++} ++ ++static void execute_transaction( ++ struct i2c_hw_engine_dce110 *engine) ++{ ++ uint32_t value = 0; ++ struct dc_context *ctx = NULL; ++ ++ ctx = engine->base.base.base.ctx; ++ ++ { ++ const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_DATA_DRIVE_EN); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_CLK_DRIVE_EN); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_DATA_DRIVE_SEL); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_INTRA_TRANSACTION_DELAY); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_DDC1_SETUP, ++ DC_I2C_DDC1_INTRA_BYTE_DELAY); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ { ++ const uint32_t addr = mmDC_I2C_CONTROL; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_SOFT_RESET); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_SW_STATUS_RESET); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_SEND_RESET); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ DC_I2C_CONTROL, ++ DC_I2C_GO); ++ ++ set_reg_field_value( ++ value, ++ engine->transaction_count - 1, ++ DC_I2C_CONTROL, ++ DC_I2C_TRANSACTION_COUNT); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* start I2C transfer */ ++ { ++ const uint32_t addr = mmDC_I2C_CONTROL; ++ ++ value = dal_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_CONTROL, ++ DC_I2C_GO); ++ ++ dal_write_reg(ctx, addr, value); ++ } ++ ++ /* all transactions were executed and HW buffer became empty ++ * (even though it actually happens when status becomes DONE) */ ++ engine->transaction_count = 0; ++ engine->buffer_used_bytes = 0; ++} ++ ++static void submit_channel_request( ++ struct i2c_engine *engine, ++ struct i2c_request_transaction_data *request) ++{ ++ request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; ++ ++ if (!process_transaction(FROM_I2C_ENGINE(engine), request)) ++ return; ++ ++ if (is_hw_busy(&engine->base)) { ++ request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; ++ return; ++ } ++ ++ execute_transaction(FROM_I2C_ENGINE(engine)); ++} ++ ++static void process_channel_reply( ++ struct i2c_engine *engine, ++ struct i2c_reply_transaction_data *reply) ++{ ++ uint8_t length = reply->length; ++ uint8_t *buffer = reply->data; ++ ++ struct i2c_hw_engine_dce110 *i2c_hw_engine_dce110 = ++ FROM_I2C_ENGINE(engine); ++ ++ uint32_t value = 0; ++ ++ /*set index*/ ++ set_reg_field_value( ++ value, ++ i2c_hw_engine_dce110->buffer_used_write, ++ DC_I2C_DATA, ++ DC_I2C_INDEX); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_DATA, ++ DC_I2C_DATA_RW); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_I2C_DATA, ++ DC_I2C_INDEX_WRITE); ++ ++ dal_write_reg(engine->base.ctx, mmDC_I2C_DATA, value); ++ ++ while (length) { ++ /* after reading the status, ++ * if the I2C operation executed successfully ++ * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller ++ * should read data bytes from I2C circular data buffer */ ++ ++ value = dal_read_reg(engine->base.ctx, mmDC_I2C_DATA); ++ ++ *buffer++ = get_reg_field_value( ++ value, ++ DC_I2C_DATA, ++ DC_I2C_DATA); ++ ++ --length; ++ } ++} ++ ++static enum i2c_channel_operation_result get_channel_status( ++ struct i2c_engine *engine, ++ uint8_t *returned_bytes) ++{ ++ uint32_t i2c_sw_status = 0; ++ uint32_t value = dal_read_reg(engine->base.ctx, mmDC_I2C_SW_STATUS); ++ ++ i2c_sw_status = get_reg_field_value( ++ value, ++ DC_I2C_SW_STATUS, ++ DC_I2C_SW_STATUS); ++ ++ if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) ++ return I2C_CHANNEL_OPERATION_ENGINE_BUSY; ++ else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_STOPPED_ON_NACK_MASK) ++ return I2C_CHANNEL_OPERATION_NO_RESPONSE; ++ else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_TIMEOUT_MASK) ++ return I2C_CHANNEL_OPERATION_TIMEOUT; ++ else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_ABORTED_MASK) ++ return I2C_CHANNEL_OPERATION_FAILED; ++ else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_DONE_MASK) ++ return I2C_CHANNEL_OPERATION_SUCCEEDED; ++ ++ /* in DAL2, I2C_RESULT_OK was returned */ ++ return I2C_CHANNEL_OPERATION_NOT_STARTED; ++} ++ ++static uint8_t get_hw_buffer_available_size( ++ const struct i2c_hw_engine *engine) ++{ ++ return I2C_HW_BUFFER_SIZE - ++ FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes; ++} ++ ++static uint32_t get_transaction_timeout( ++ const struct i2c_hw_engine *engine, ++ uint32_t length) ++{ ++ uint32_t speed = engine->base.funcs->get_speed(&engine->base); ++ ++ uint32_t period_timeout; ++ uint32_t num_of_clock_stretches; ++ ++ if (!speed) ++ return 0; ++ ++ period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; ++ ++ num_of_clock_stretches = 1 + (length << 3) + 1; ++ num_of_clock_stretches += ++ (FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes << 3) + ++ (FROM_I2C_HW_ENGINE(engine)->transaction_count << 1); ++ ++ return period_timeout * num_of_clock_stretches; ++} ++ ++static void destroy( ++ struct i2c_engine **i2c_engine) ++{ ++ struct i2c_hw_engine_dce110 *engine_dce110 = ++ FROM_I2C_ENGINE(*i2c_engine); ++ ++ dal_i2c_hw_engine_destruct(&engine_dce110->base); ++ ++ dc_service_free((*i2c_engine)->base.ctx, engine_dce110); ++ ++ *i2c_engine = NULL; ++} ++/* ++ * @brief ++ * DC_I2C_DDC1_SETUP MM register offsets ++ * ++ * @note ++ * The indices of this offset array are DDC engine IDs ++ */ ++static const int32_t ddc_setup_offset[] = { ++ ++ mmDC_I2C_DDC1_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 1 */ ++ mmDC_I2C_DDC2_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 2 */ ++ mmDC_I2C_DDC3_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 3 */ ++ mmDC_I2C_DDC4_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 4 */ ++ mmDC_I2C_DDC5_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 5 */ ++ mmDC_I2C_DDC6_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 6 */ ++ mmDC_I2C_DDCVGA_SETUP - mmDC_I2C_DDC1_SETUP /* DDC Engine 7 */ ++}; ++ ++/* ++ * @brief ++ * DC_I2C_DDC1_SPEED MM register offsets ++ * ++ * @note ++ * The indices of this offset array are DDC engine IDs ++ */ ++static const int32_t ddc_speed_offset[] = { ++ mmDC_I2C_DDC1_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 1 */ ++ mmDC_I2C_DDC2_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 2 */ ++ mmDC_I2C_DDC3_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 3 */ ++ mmDC_I2C_DDC4_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 4 */ ++ mmDC_I2C_DDC5_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 5 */ ++ mmDC_I2C_DDC6_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 6 */ ++ mmDC_I2C_DDCVGA_SPEED - mmDC_I2C_DDC1_SPEED /* DDC Engine 7 */ ++}; ++ ++static const struct i2c_engine_funcs i2c_engine_funcs = { ++ .destroy = destroy, ++ .get_speed = get_speed, ++ .set_speed = set_speed, ++ .setup_engine = setup_engine, ++ .submit_channel_request = submit_channel_request, ++ .process_channel_reply = process_channel_reply, ++ .get_channel_status = get_channel_status, ++ .acquire_engine = dal_i2c_hw_engine_acquire_engine, ++}; ++ ++static const struct engine_funcs engine_funcs = { ++ .release_engine = release_engine, ++ .keep_power_up_count = keep_power_up_count, ++ .get_engine_type = dal_i2c_hw_engine_get_engine_type, ++ .acquire = dal_i2c_engine_acquire, ++ .submit_request = dal_i2c_hw_engine_submit_request, ++}; ++ ++static const struct i2c_hw_engine_funcs i2c_hw_engine_funcs = { ++ .get_hw_buffer_available_size = get_hw_buffer_available_size, ++ .get_transaction_timeout = get_transaction_timeout, ++ .wait_on_operation_result = dal_i2c_hw_engine_wait_on_operation_result, ++}; ++ ++static bool construct( ++ struct i2c_hw_engine_dce110 *engine_dce110, ++ const struct i2c_hw_engine_dce110_create_arg *arg) ++{ ++ uint32_t xtal_ref_div = 0; ++ uint32_t value = 0; ++ ++ /*ddc_setup_offset of dce80 and dce110 have the same register name ++ * but different offset. Do not need different array*/ ++ if (arg->engine_id >= sizeof(ddc_setup_offset) / sizeof(int32_t)) ++ return false; ++ if (arg->engine_id >= sizeof(ddc_speed_offset) / sizeof(int32_t)) ++ return false; ++ if (!arg->reference_frequency) ++ return false; ++ ++ if (!dal_i2c_hw_engine_construct(&engine_dce110->base, arg->ctx)) ++ return false; ++ ++ engine_dce110->base.base.base.funcs = &engine_funcs; ++ engine_dce110->base.base.funcs = &i2c_engine_funcs; ++ engine_dce110->base.funcs = &i2c_hw_engine_funcs; ++ engine_dce110->base.default_speed = arg->default_speed; ++ ++ engine_dce110->engine_id = arg->engine_id; ++ ++ engine_dce110->buffer_used_bytes = 0; ++ engine_dce110->transaction_count = 0; ++ engine_dce110->engine_keep_power_up_count = 1; ++ ++ /*values which are not included by arg*/ ++ engine_dce110->addr.DC_I2C_DDCX_SETUP = ++ mmDC_I2C_DDC1_SETUP + ddc_setup_offset[arg->engine_id]; ++ engine_dce110->addr.DC_I2C_DDCX_SPEED = ++ mmDC_I2C_DDC1_SPEED + ddc_speed_offset[arg->engine_id]; ++ ++ ++ value = dal_read_reg( ++ engine_dce110->base.base.base.ctx, ++ mmMICROSECOND_TIME_BASE_DIV); ++ ++ xtal_ref_div = get_reg_field_value( ++ value, ++ MICROSECOND_TIME_BASE_DIV, ++ XTAL_REF_DIV); ++ ++ if (xtal_ref_div == 0) { ++ dal_logger_write( ++ engine_dce110->base.base.base.ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_I2C_AUX, ++ "Invalid base timer divider\n", ++ __func__); ++ xtal_ref_div = 2; ++ } ++ ++ /*Calculating Reference Clock by divding original frequency by ++ * XTAL_REF_DIV. ++ * At upper level, uint32_t reference_frequency = ++ * dal_i2caux_get_reference_clock(as) >> 1 ++ * which already divided by 2. So we need x2 to get original ++ * reference clock from ppll_info ++ */ ++ engine_dce110->reference_frequency = ++ (arg->reference_frequency * 2) / xtal_ref_div; ++ ++ ++ return true; ++} ++ ++struct i2c_engine *dal_i2c_hw_engine_dce110_create( ++ const struct i2c_hw_engine_dce110_create_arg *arg) ++{ ++ struct i2c_hw_engine_dce110 *engine_dce10; ++ ++ if (!arg) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ engine_dce10 = dc_service_alloc(arg->ctx, sizeof(struct i2c_hw_engine_dce110)); ++ ++ if (!engine_dce10) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(engine_dce10, arg)) ++ return &engine_dce10->base.base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(arg->ctx, engine_dce10); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.h +new file mode 100644 +index 0000000..fc2ae36 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_hw_engine_dce110.h +@@ -0,0 +1,58 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_HW_ENGINE_DCE110_H__ ++#define __DAL_I2C_HW_ENGINE_DCE110_H__ ++ ++struct i2c_hw_engine_dce110 { ++ struct i2c_hw_engine base; ++ struct { ++ uint32_t DC_I2C_DDCX_SETUP; ++ uint32_t DC_I2C_DDCX_SPEED; ++ } addr; ++ uint32_t engine_id; ++ /* expressed in kilohertz */ ++ uint32_t reference_frequency; ++ /* number of bytes currently used in HW buffer */ ++ uint32_t buffer_used_bytes; ++ /* number of bytes used for write transaction in HW buffer ++ * - this will be used as the index to read from*/ ++ uint32_t buffer_used_write; ++ /* number of pending transactions (before GO) */ ++ uint32_t transaction_count; ++ uint32_t engine_keep_power_up_count; ++}; ++ ++struct i2c_hw_engine_dce110_create_arg { ++ uint32_t engine_id; ++ uint32_t reference_frequency; ++ uint32_t default_speed; ++ struct dc_context *ctx; ++}; ++ ++struct i2c_engine *dal_i2c_hw_engine_dce110_create( ++ const struct i2c_hw_engine_dce110_create_arg *arg); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.c +new file mode 100644 +index 0000000..c415a4e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.c +@@ -0,0 +1,172 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "../engine.h" ++#include "../i2c_engine.h" ++#include "../i2c_sw_engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "i2c_sw_engine_dce110.h" ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++/* ++ * @brief ++ * Cast 'struct i2c_sw_engine *' ++ * to 'struct i2c_sw_engine_dce110 *' ++ */ ++#define FROM_I2C_SW_ENGINE(ptr) \ ++ container_of((ptr), struct i2c_sw_engine_dce110, base) ++/* ++ * @brief ++ * Cast 'struct i2c_engine *' ++ * to 'struct i2c_sw_engine_dce80 *' ++ */ ++#define FROM_I2C_ENGINE(ptr) \ ++ FROM_I2C_SW_ENGINE(container_of((ptr), struct i2c_sw_engine, base)) ++ ++/* ++ * @brief ++ * Cast 'struct engine *' ++ * to 'struct i2c_sw_engine_dce80 *' ++ */ ++#define FROM_ENGINE(ptr) \ ++ FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) ++ ++static void release_engine( ++ struct engine *engine) ++{ ++} ++ ++static void destruct( ++ struct i2c_sw_engine_dce110 *engine) ++{ ++ dal_i2c_sw_engine_destruct(&engine->base); ++} ++ ++static void destroy( ++ struct i2c_engine **engine) ++{ ++ struct i2c_sw_engine_dce110 *sw_engine = FROM_I2C_ENGINE(*engine); ++ ++ destruct(sw_engine); ++ ++ dc_service_free((*engine)->base.ctx, sw_engine); ++ ++ *engine = NULL; ++} ++ ++static bool acquire_engine( ++ struct i2c_engine *engine, ++ struct ddc *ddc_handle) ++{ ++ return dal_i2caux_i2c_sw_engine_acquire_engine(engine, ddc_handle); ++} ++ ++static const struct i2c_engine_funcs i2c_engine_funcs = { ++ .acquire_engine = acquire_engine, ++ .destroy = destroy, ++ .get_speed = dal_i2c_sw_engine_get_speed, ++ .set_speed = dal_i2c_sw_engine_set_speed, ++ .setup_engine = dal_i2c_engine_setup_i2c_engine, ++ .submit_channel_request = dal_i2c_sw_engine_submit_channel_request, ++ .process_channel_reply = dal_i2c_engine_process_channel_reply, ++ .get_channel_status = dal_i2c_sw_engine_get_channel_status, ++}; ++ ++static const struct engine_funcs engine_funcs = { ++ .release_engine = release_engine, ++ .get_engine_type = dal_i2c_sw_engine_get_engine_type, ++ .acquire = dal_i2c_engine_acquire, ++ .submit_request = dal_i2c_sw_engine_submit_request, ++ .keep_power_up_count = dal_i2caux_keep_power_up_count, ++}; ++ ++static bool construct( ++ struct i2c_sw_engine_dce110 *engine_dce110, ++ const struct i2c_sw_engine_dce110_create_arg *arg_dce110) ++{ ++ struct i2c_sw_engine_create_arg arg_base; ++ ++ arg_base.ctx = arg_dce110->ctx; ++ arg_base.default_speed = arg_dce110->default_speed; ++ ++ if (!dal_i2c_sw_engine_construct( ++ &engine_dce110->base, &arg_base)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ /*struct engine struct engine_funcs*/ ++ engine_dce110->base.base.base.funcs = &engine_funcs; ++ /*struct i2c_engine struct i2c_engine_funcs*/ ++ engine_dce110->base.base.funcs = &i2c_engine_funcs; ++ engine_dce110->base.default_speed = arg_dce110->default_speed; ++ engine_dce110->engine_id = arg_dce110->engine_id; ++ ++ return true; ++} ++ ++struct i2c_engine *dal_i2c_sw_engine_dce110_create( ++ const struct i2c_sw_engine_dce110_create_arg *arg) ++{ ++ struct i2c_sw_engine_dce110 *engine_dce110; ++ ++ if (!arg) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ engine_dce110 = dc_service_alloc(arg->ctx, sizeof(struct i2c_sw_engine_dce110)); ++ ++ if (!engine_dce110) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(engine_dce110, arg)) ++ return &engine_dce110->base.base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(arg->ctx, engine_dce110); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.h +new file mode 100644 +index 0000000..c48c61f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2c_sw_engine_dce110.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_SW_ENGINE_DCE110_H__ ++#define __DAL_I2C_SW_ENGINE_DCE110_H__ ++ ++struct i2c_sw_engine_dce110 { ++ struct i2c_sw_engine base; ++ uint32_t engine_id; ++}; ++ ++struct i2c_sw_engine_dce110_create_arg { ++ uint32_t engine_id; ++ uint32_t default_speed; ++ struct dc_context *ctx; ++}; ++ ++struct i2c_engine *dal_i2c_sw_engine_dce110_create( ++ const struct i2c_sw_engine_dce110_create_arg *arg); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.c +new file mode 100644 +index 0000000..71d1a6c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.c +@@ -0,0 +1,260 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "../i2caux.h" ++#include "../engine.h" ++#include "../i2c_engine.h" ++#include "../i2c_sw_engine.h" ++#include "../i2c_hw_engine.h" ++ ++/* ++ * Header of this unit ++ */ ++#include "i2caux_dce110.h" ++#include "i2c_sw_engine_dce110.h" ++#include "i2c_hw_engine_dce110.h" ++#include "aux_engine_dce110.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++/*cast pointer to struct i2caux TO pointer to struct i2caux_dce110*/ ++#define FROM_I2C_AUX(ptr) \ ++ container_of((ptr), struct i2caux_dce110, base) ++ ++static void destruct( ++ struct i2caux_dce110 *i2caux_dce110) ++{ ++ dal_i2caux_destruct(&i2caux_dce110->base); ++} ++ ++static void destroy( ++ struct i2caux **i2c_engine) ++{ ++ struct i2caux_dce110 *i2caux_dce110 = FROM_I2C_AUX(*i2c_engine); ++ ++ destruct(i2caux_dce110); ++ ++ dc_service_free((*i2c_engine)->ctx, i2caux_dce110); ++ ++ *i2c_engine = NULL; ++} ++ ++static struct i2c_engine *acquire_i2c_hw_engine( ++ struct i2caux *i2caux, ++ struct ddc *ddc) ++{ ++ struct i2caux_dce110 *i2caux_dce110 = FROM_I2C_AUX(i2caux); ++ ++ struct i2c_engine *engine = NULL; ++ /* generic hw engine is not used for EDID read ++ * It may be needed for external i2c device, like thermal chip, ++ * TODO will be implemented when needed. ++ * check dce80 bool non_generic for generic hw engine; ++ */ ++ ++ if (!ddc) ++ return NULL; ++ ++ if (dal_ddc_is_hw_supported(ddc)) { ++ enum gpio_ddc_line line = dal_ddc_get_line(ddc); ++ ++ if (line < GPIO_DDC_LINE_COUNT) ++ engine = i2caux->i2c_hw_engines[line]; ++ } ++ ++ if (!engine) ++ return NULL; ++ ++ if (!i2caux_dce110->i2c_hw_buffer_in_use && ++ engine->base.funcs->acquire(&engine->base, ddc)) { ++ i2caux_dce110->i2c_hw_buffer_in_use = true; ++ return engine; ++ } ++ ++ return NULL; ++} ++ ++static void release_engine( ++ struct i2caux *i2caux, ++ struct engine *engine) ++{ ++ struct i2caux_dce110 *i2caux_dce110 = FROM_I2C_AUX(i2caux); ++ ++ if (engine->funcs->get_engine_type(engine) == ++ I2CAUX_ENGINE_TYPE_I2C_DDC_HW) ++ i2caux_dce110->i2c_hw_buffer_in_use = false; ++ ++ dal_i2caux_release_engine(i2caux, engine); ++} ++ ++static const enum gpio_ddc_line hw_ddc_lines[] = { ++ GPIO_DDC_LINE_DDC1, ++ GPIO_DDC_LINE_DDC2, ++ GPIO_DDC_LINE_DDC3, ++}; ++ ++static const enum gpio_ddc_line hw_aux_lines[] = { ++ GPIO_DDC_LINE_DDC1, ++ GPIO_DDC_LINE_DDC2, ++ GPIO_DDC_LINE_DDC3, ++}; ++ ++/* function table */ ++static const struct i2caux_funcs i2caux_funcs = { ++ .destroy = destroy, ++ .acquire_i2c_hw_engine = acquire_i2c_hw_engine, ++ .release_engine = release_engine, ++ .acquire_i2c_sw_engine = dal_i2caux_acquire_i2c_sw_engine, ++ .acquire_aux_engine = dal_i2caux_acquire_aux_engine, ++}; ++ ++static bool construct( ++ struct i2caux_dce110 *i2caux_dce110, ++ struct adapter_service *as, ++ struct dc_context *ctx) ++{ ++ uint32_t i = 0; ++ uint32_t reference_frequency = 0; ++ bool use_i2c_sw_engine = false; ++ struct i2caux *base = NULL; ++ /*TODO: For CZ bring up, if dal_i2caux_get_reference_clock ++ * does not return 48KHz, we need hard coded for 48Khz. ++ * Some BIOS setting incorrect cause this ++ * For production, we always get value from BIOS*/ ++ reference_frequency = ++ dal_i2caux_get_reference_clock(as) >> 1; ++ ++ use_i2c_sw_engine = dal_adapter_service_is_feature_supported( ++ FEATURE_RESTORE_USAGE_I2C_SW_ENGINE); ++ ++ base = &i2caux_dce110->base; ++ ++ if (!dal_i2caux_construct(base, as, ctx)) { ++ ASSERT_CRITICAL(false); ++ return false; ++ } ++ ++ i2caux_dce110->base.funcs = &i2caux_funcs; ++ i2caux_dce110->i2c_hw_buffer_in_use = false; ++ /* Create I2C engines (DDC lines per connector) ++ * different I2C/AUX usage cases, DDC, Generic GPIO, AUX. ++ */ ++ do { ++ enum gpio_ddc_line line_id = hw_ddc_lines[i]; ++ ++ struct i2c_hw_engine_dce110_create_arg hw_arg_dce110; ++ ++ if (use_i2c_sw_engine) { ++ struct i2c_sw_engine_dce110_create_arg sw_arg; ++ ++ sw_arg.engine_id = i; ++ sw_arg.default_speed = base->default_i2c_sw_speed; ++ sw_arg.ctx = ctx; ++ base->i2c_sw_engines[line_id] = ++ dal_i2c_sw_engine_dce110_create(&sw_arg); ++ } ++ ++ hw_arg_dce110.engine_id = i; ++ hw_arg_dce110.reference_frequency = reference_frequency; ++ hw_arg_dce110.default_speed = base->default_i2c_hw_speed; ++ hw_arg_dce110.ctx = ctx; ++ ++ base->i2c_hw_engines[line_id] = ++ dal_i2c_hw_engine_dce110_create(&hw_arg_dce110); ++ ++ ++i; ++ } while (i < ARRAY_SIZE(hw_ddc_lines)); ++ ++ /* Create AUX engines for all lines which has assisted HW AUX ++ * 'i' (loop counter) used as DDC/AUX engine_id */ ++ ++ i = 0; ++ ++ do { ++ enum gpio_ddc_line line_id = hw_aux_lines[i]; ++ ++ struct aux_engine_dce110_init_data aux_init_data; ++ ++ aux_init_data.engine_id = i; ++ aux_init_data.timeout_period = base->aux_timeout_period; ++ aux_init_data.ctx = ctx; ++ ++ base->aux_engines[line_id] = ++ dal_aux_engine_dce110_create(&aux_init_data); ++ ++ ++i; ++ } while (i < ARRAY_SIZE(hw_aux_lines)); ++ ++ /*TODO Generic I2C SW and HW*/ ++ ++ return true; ++} ++ ++/* ++ * dal_i2caux_dce110_create ++ * ++ * @brief ++ * public interface to allocate memory for DCE11 I2CAUX ++ * ++ * @param ++ * struct adapter_service *as - [in] ++ * struct dc_context *ctx - [in] ++ * ++ * @return ++ * pointer to the base struct of DCE11 I2CAUX ++ */ ++struct i2caux *dal_i2caux_dce110_create( ++ struct adapter_service *as, ++ struct dc_context *ctx) ++{ ++ struct i2caux_dce110 *i2caux_dce110 = ++ dc_service_alloc(ctx, sizeof(struct i2caux_dce110)); ++ ++ if (!i2caux_dce110) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(i2caux_dce110, as, ctx)) ++ return &i2caux_dce110->base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dc_service_free(ctx, i2caux_dce110); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.h +new file mode 100644 +index 0000000..1a7ba1b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce110/i2caux_dce110.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_AUX_DCE110_H__ ++#define __DAL_I2C_AUX_DCE110_H__ ++ ++struct i2caux_dce110 { ++ struct i2caux base; ++ /* indicate the I2C HW circular buffer is in use */ ++ bool i2c_hw_buffer_in_use; ++}; ++ ++struct i2caux *dal_i2caux_dce110_create( ++ struct adapter_service *as, ++ struct dc_context *ctx); ++ ++#endif /* __DAL_I2C_AUX_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/engine.h b/drivers/gpu/drm/amd/dal/dc/i2caux/engine.h +new file mode 100644 +index 0000000..d3635f8 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/engine.h +@@ -0,0 +1,129 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ENGINE_H__ ++#define __DAL_ENGINE_H__ ++ ++enum i2caux_transaction_operation { ++ I2CAUX_TRANSACTION_READ, ++ I2CAUX_TRANSACTION_WRITE ++}; ++ ++enum i2caux_transaction_address_space { ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C = 1, ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD ++}; ++ ++struct i2caux_transaction_payload { ++ enum i2caux_transaction_address_space address_space; ++ uint32_t address; ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++enum i2caux_transaction_status { ++ I2CAUX_TRANSACTION_STATUS_UNKNOWN = (-1L), ++ I2CAUX_TRANSACTION_STATUS_SUCCEEDED, ++ I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY, ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT, ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR, ++ I2CAUX_TRANSACTION_STATUS_FAILED_NACK, ++ I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE, ++ I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION, ++ I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION, ++ I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW ++}; ++ ++struct i2caux_transaction_request { ++ enum i2caux_transaction_operation operation; ++ struct i2caux_transaction_payload payload; ++ enum i2caux_transaction_status status; ++}; ++ ++enum i2caux_engine_type { ++ I2CAUX_ENGINE_TYPE_UNKNOWN = (-1L), ++ I2CAUX_ENGINE_TYPE_AUX, ++ I2CAUX_ENGINE_TYPE_I2C_DDC_HW, ++ I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW, ++ I2CAUX_ENGINE_TYPE_I2C_SW ++}; ++ ++enum i2c_default_speed { ++ I2CAUX_DEFAULT_I2C_HW_SPEED = 50, ++ I2CAUX_DEFAULT_I2C_SW_SPEED = 50 ++}; ++ ++enum i2caux_transaction_action { ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE = 0x00, ++ I2CAUX_TRANSACTION_ACTION_I2C_READ = 0x10, ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST = 0x20, ++ ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT = 0x40, ++ I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT = 0x50, ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT = 0x60, ++ ++ I2CAUX_TRANSACTION_ACTION_DP_WRITE = 0x80, ++ I2CAUX_TRANSACTION_ACTION_DP_READ = 0x90 ++}; ++ ++struct engine; ++ ++struct engine_funcs { ++ enum i2caux_engine_type (*get_engine_type)( ++ const struct engine *engine); ++ bool (*acquire)( ++ struct engine *engine, ++ struct ddc *ddc); ++ bool (*submit_request)( ++ struct engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction); ++ /* [anaumov] Actually, following method is meaningful ++ * only in I2C HW engines */ ++ void (*keep_power_up_count)( ++ struct engine *engine, ++ bool keep_power_up); ++ void (*release_engine)( ++ struct engine *engine); ++}; ++ ++struct engine { ++ const struct engine_funcs *funcs; ++ struct ddc *ddc; ++ struct dc_context *ctx; ++}; ++ ++bool dal_i2caux_construct_engine( ++ struct engine *engine, ++ struct dc_context *ctx); ++ ++void dal_i2caux_destruct_engine( ++ struct engine *engine); ++ ++void dal_i2caux_keep_power_up_count( ++ struct engine *engine, ++ bool keep_power_up); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/engine_base.c b/drivers/gpu/drm/amd/dal/dc/i2caux/engine_base.c +new file mode 100644 +index 0000000..2f87a65 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/engine_base.c +@@ -0,0 +1,68 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "engine.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++void dal_i2caux_keep_power_up_count( ++ struct engine *engine, ++ bool keep_power_up) ++{ ++ ++} ++ ++bool dal_i2caux_construct_engine( ++ struct engine *engine, ++ struct dc_context *ctx) ++{ ++ engine->ddc = NULL; ++ engine->ctx = ctx; ++ return true; ++} ++ ++void dal_i2caux_destruct_engine( ++ struct engine *engine) ++{ ++ /* nothing to do */ ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.c +new file mode 100644 +index 0000000..78c7d61 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.c +@@ -0,0 +1,122 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "i2c_engine.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++#define FROM_ENGINE(ptr) \ ++ container_of((ptr), struct i2c_engine, base) ++ ++bool dal_i2c_engine_acquire( ++ struct engine *engine, ++ struct ddc *ddc_handle) ++{ ++ struct i2c_engine *i2c_engine = FROM_ENGINE(engine); ++ ++ uint32_t counter = 0; ++ bool result; ++ ++ do { ++ result = i2c_engine->funcs->acquire_engine( ++ i2c_engine, ddc_handle); ++ ++ if (result) ++ break; ++ ++ /* i2c_engine is busy by VBios, lets wait and retry */ ++ ++ dc_service_delay_in_microseconds(engine->ctx, 10); ++ ++ ++counter; ++ } while (counter < 2); ++ ++ if (result) { ++ if (!i2c_engine->funcs->setup_engine(i2c_engine)) { ++ engine->funcs->release_engine(engine); ++ result = false; ++ } ++ } ++ ++ return result; ++} ++ ++bool dal_i2c_engine_setup_i2c_engine( ++ struct i2c_engine *engine) ++{ ++ /* Derivative classes do not have to override this */ ++ ++ return true; ++} ++ ++void dal_i2c_engine_submit_channel_request( ++ struct i2c_engine *engine, ++ struct i2c_request_transaction_data *request) ++{ ++ ++} ++ ++void dal_i2c_engine_process_channel_reply( ++ struct i2c_engine *engine, ++ struct i2c_reply_transaction_data *reply) ++{ ++ ++} ++ ++bool dal_i2c_engine_construct( ++ struct i2c_engine *engine, ++ struct dc_context *ctx) ++{ ++ if (!dal_i2caux_construct_engine(&engine->base, ctx)) ++ return false; ++ ++ engine->timeout_delay = 0; ++ return true; ++} ++ ++void dal_i2c_engine_destruct( ++ struct i2c_engine *engine) ++{ ++ dal_i2caux_destruct_engine(&engine->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.h b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.h +new file mode 100644 +index 0000000..20299fd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_engine.h +@@ -0,0 +1,113 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_ENGINE_H__ ++#define __DAL_I2C_ENGINE_H__ ++ ++enum i2c_channel_operation_result { ++ I2C_CHANNEL_OPERATION_SUCCEEDED, ++ I2C_CHANNEL_OPERATION_FAILED, ++ I2C_CHANNEL_OPERATION_NOT_GRANTED, ++ I2C_CHANNEL_OPERATION_IS_BUSY, ++ I2C_CHANNEL_OPERATION_NO_HANDLE_PROVIDED, ++ I2C_CHANNEL_OPERATION_CHANNEL_IN_USE, ++ I2C_CHANNEL_OPERATION_CHANNEL_CLIENT_MAX_ALLOWED, ++ I2C_CHANNEL_OPERATION_ENGINE_BUSY, ++ I2C_CHANNEL_OPERATION_TIMEOUT, ++ I2C_CHANNEL_OPERATION_NO_RESPONSE, ++ I2C_CHANNEL_OPERATION_HW_REQUEST_I2C_BUS, ++ I2C_CHANNEL_OPERATION_WRONG_PARAMETER, ++ I2C_CHANNEL_OPERATION_OUT_NB_OF_RETRIES, ++ I2C_CHANNEL_OPERATION_NOT_STARTED ++}; ++ ++struct i2c_request_transaction_data { ++ enum i2caux_transaction_action action; ++ enum i2c_channel_operation_result status; ++ uint8_t address; ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++struct i2c_reply_transaction_data { ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++struct i2c_engine; ++ ++struct i2c_engine_funcs { ++ void (*destroy)( ++ struct i2c_engine **ptr); ++ uint32_t (*get_speed)( ++ const struct i2c_engine *engine); ++ void (*set_speed)( ++ struct i2c_engine *engine, ++ uint32_t speed); ++ bool (*acquire_engine)( ++ struct i2c_engine *engine, ++ struct ddc *ddc); ++ bool (*setup_engine)( ++ struct i2c_engine *engine); ++ void (*submit_channel_request)( ++ struct i2c_engine *engine, ++ struct i2c_request_transaction_data *request); ++ void (*process_channel_reply)( ++ struct i2c_engine *engine, ++ struct i2c_reply_transaction_data *reply); ++ enum i2c_channel_operation_result (*get_channel_status)( ++ struct i2c_engine *engine, ++ uint8_t *returned_bytes); ++}; ++ ++struct i2c_engine { ++ struct engine base; ++ const struct i2c_engine_funcs *funcs; ++ uint32_t timeout_delay; ++}; ++ ++bool dal_i2c_engine_construct( ++ struct i2c_engine *engine, ++ struct dc_context *ctx); ++ ++void dal_i2c_engine_destruct( ++ struct i2c_engine *engine); ++ ++bool dal_i2c_engine_setup_i2c_engine( ++ struct i2c_engine *engine); ++ ++void dal_i2c_engine_submit_channel_request( ++ struct i2c_engine *engine, ++ struct i2c_request_transaction_data *request); ++ ++void dal_i2c_engine_process_channel_reply( ++ struct i2c_engine *engine, ++ struct i2c_reply_transaction_data *reply); ++ ++bool dal_i2c_engine_acquire( ++ struct engine *ptr, ++ struct ddc *ddc_handle); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.c +new file mode 100644 +index 0000000..d91e259 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.c +@@ -0,0 +1,287 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "engine.h" ++#include "i2c_engine.h" ++#include "i2c_hw_engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "i2c_generic_hw_engine.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++/* ++ * @brief ++ * Cast 'struct i2c_hw_engine *' ++ * to 'struct i2c_generic_hw_engine *' ++ */ ++#define FROM_I2C_HW_ENGINE(ptr) \ ++ container_of((ptr), struct i2c_generic_hw_engine, base) ++ ++/* ++ * @brief ++ * Cast 'struct i2c_engine *' ++ * to 'struct i2c_generic_hw_engine *' ++ */ ++#define FROM_I2C_ENGINE(ptr) \ ++ FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) ++ ++/* ++ * @brief ++ * Cast 'struct engine *' ++ * to 'struct i2c_generic_hw_engine *' ++ */ ++#define FROM_ENGINE(ptr) \ ++ FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) ++ ++enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type( ++ const struct engine *engine) ++{ ++ return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW; ++} ++ ++/* ++ * @brief ++ * Single transaction handling. ++ * Since transaction may be bigger than HW buffer size, ++ * it divides transaction to sub-transactions ++ * and uses batch transaction feature of the engine. ++ */ ++bool dal_i2c_generic_hw_engine_submit_request( ++ struct engine *engine, ++ struct i2caux_transaction_request *i2caux_request, ++ bool middle_of_transaction) ++{ ++ struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine); ++ ++ struct i2c_hw_engine *base = &hw_engine->base; ++ ++ uint8_t max_payload_size = ++ base->funcs->get_hw_buffer_available_size(base); ++ ++ bool initial_stop_bit = !middle_of_transaction; ++ ++ struct i2c_generic_transaction_attributes attributes; ++ ++ enum i2c_channel_operation_result operation_result = ++ I2C_CHANNEL_OPERATION_FAILED; ++ ++ bool result = false; ++ ++ /* setup transaction initial properties */ ++ ++ uint8_t address = i2caux_request->payload.address; ++ uint8_t *current_payload = i2caux_request->payload.data; ++ uint8_t remaining_payload_size = i2caux_request->payload.length; ++ ++ bool first_iteration = true; ++ ++ if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) ++ attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ; ++ else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) ++ attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE; ++ else { ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; ++ return false; ++ } ++ ++ /* Do batch transaction. ++ * Divide read/write data into payloads which fit HW buffer size. ++ * 1. Single transaction: ++ * start_bit = 1, stop_bit depends on session state, ack_on_read = 0; ++ * 2. Start of batch transaction: ++ * start_bit = 1, stop_bit = 0, ack_on_read = 1; ++ * 3. Middle of batch transaction: ++ * start_bit = 0, stop_bit = 0, ack_on_read = 1; ++ * 4. End of batch transaction: ++ * start_bit = 0, stop_bit depends on session state, ack_on_read = 0. ++ * Session stop bit is set if 'middle_of_transaction' = 0. */ ++ ++ while (remaining_payload_size) { ++ uint8_t current_transaction_size; ++ uint8_t current_payload_size; ++ ++ bool last_iteration; ++ bool stop_bit; ++ ++ /* Calculate current transaction size and payload size. ++ * Transaction size = total number of bytes in transaction, ++ * including slave's address; ++ * Payload size = number of data bytes in transaction. */ ++ ++ if (first_iteration) { ++ /* In the first sub-transaction we send slave's address ++ * thus we need to reserve one byte for it */ ++ current_transaction_size = ++ (remaining_payload_size > max_payload_size - 1) ? ++ max_payload_size : ++ remaining_payload_size + 1; ++ ++ current_payload_size = current_transaction_size - 1; ++ } else { ++ /* Second and further sub-transactions will have ++ * entire buffer reserved for data */ ++ current_transaction_size = ++ (remaining_payload_size > max_payload_size) ? ++ max_payload_size : ++ remaining_payload_size; ++ ++ current_payload_size = current_transaction_size; ++ } ++ ++ last_iteration = ++ (remaining_payload_size == current_payload_size); ++ ++ stop_bit = last_iteration ? initial_stop_bit : false; ++ ++ /* write slave device address */ ++ ++ if (first_iteration) ++ hw_engine->funcs->write_address(hw_engine, address); ++ ++ /* write current portion of data, if requested */ ++ ++ if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) ++ hw_engine->funcs->write_data( ++ hw_engine, ++ current_payload, ++ current_payload_size); ++ ++ /* execute transaction */ ++ ++ attributes.start_bit = first_iteration; ++ attributes.stop_bit = stop_bit; ++ attributes.last_read = last_iteration; ++ attributes.transaction_size = current_transaction_size; ++ ++ hw_engine->funcs->execute_transaction(hw_engine, &attributes); ++ ++ /* wait until transaction is processed; if it fails - quit */ ++ ++ operation_result = base->funcs->wait_on_operation_result( ++ base, ++ base->funcs->get_transaction_timeout( ++ base, current_transaction_size), ++ I2C_CHANNEL_OPERATION_ENGINE_BUSY); ++ ++ if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED) ++ break; ++ ++ /* read current portion of data, if requested */ ++ ++ /* the read offset should be 1 for first sub-transaction, ++ * and 0 for any next one */ ++ ++ if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) ++ hw_engine->funcs->read_data(hw_engine, current_payload, ++ current_payload_size, first_iteration ? 1 : 0); ++ ++ /* update loop variables */ ++ ++ first_iteration = false; ++ current_payload += current_payload_size; ++ remaining_payload_size -= current_payload_size; ++ } ++ ++ /* update transaction status */ ++ ++ switch (operation_result) { ++ case I2C_CHANNEL_OPERATION_SUCCEEDED: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ result = true; ++ break; ++ case I2C_CHANNEL_OPERATION_NO_RESPONSE: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ break; ++ case I2C_CHANNEL_OPERATION_TIMEOUT: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ break; ++ case I2C_CHANNEL_OPERATION_FAILED: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; ++ break; ++ default: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; ++ } ++ ++ return result; ++} ++ ++/* ++ * @brief ++ * Returns number of microseconds to wait until timeout to be considered ++ */ ++uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout( ++ const struct i2c_hw_engine *engine, ++ uint32_t length) ++{ ++ const struct i2c_engine *base = &engine->base; ++ ++ uint32_t speed = base->funcs->get_speed(base); ++ ++ if (!speed) ++ return 0; ++ ++ /* total timeout = period_timeout * (start + data bits count + stop) */ ++ ++ return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) * ++ (1 + (length << 3) + 1); ++} ++ ++bool dal_i2c_generic_hw_engine_construct( ++ struct i2c_generic_hw_engine *engine, ++ struct dc_context *ctx) ++{ ++ if (!dal_i2c_hw_engine_construct(&engine->base, ctx)) ++ return false; ++ return true; ++} ++ ++void dal_i2c_generic_hw_engine_destruct( ++ struct i2c_generic_hw_engine *engine) ++{ ++ dal_i2c_hw_engine_destruct(&engine->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.h b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.h +new file mode 100644 +index 0000000..52f2aa2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_generic_hw_engine.h +@@ -0,0 +1,77 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_GENERIC_HW_ENGINE_H__ ++#define __DAL_I2C_GENERIC_HW_ENGINE_H__ ++ ++struct i2c_generic_transaction_attributes { ++ enum i2caux_transaction_action action; ++ uint8_t transaction_size; ++ bool start_bit; ++ bool stop_bit; ++ bool last_read; ++}; ++ ++struct i2c_generic_hw_engine; ++ ++struct i2c_generic_hw_engine_funcs { ++ void (*write_address)( ++ struct i2c_generic_hw_engine *engine, ++ uint8_t address); ++ void (*write_data)( ++ struct i2c_generic_hw_engine *engine, ++ const uint8_t *buffer, ++ uint8_t length); ++ void (*read_data)( ++ struct i2c_generic_hw_engine *engine, ++ uint8_t *buffer, ++ uint8_t length, ++ uint32_t offset); ++ void (*execute_transaction)( ++ struct i2c_generic_hw_engine *engine, ++ struct i2c_generic_transaction_attributes *attributes); ++}; ++ ++struct i2c_generic_hw_engine { ++ struct i2c_hw_engine base; ++ const struct i2c_generic_hw_engine_funcs *funcs; ++}; ++ ++bool dal_i2c_generic_hw_engine_construct( ++ struct i2c_generic_hw_engine *engine, ++ struct dc_context *ctx); ++ ++void dal_i2c_generic_hw_engine_destruct( ++ struct i2c_generic_hw_engine *engine); ++enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type( ++ const struct engine *engine); ++bool dal_i2c_generic_hw_engine_submit_request( ++ struct engine *ptr, ++ struct i2caux_transaction_request *i2caux_request, ++ bool middle_of_transaction); ++uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout( ++ const struct i2c_hw_engine *engine, ++ uint32_t length); ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.c +new file mode 100644 +index 0000000..77f2b84 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.c +@@ -0,0 +1,247 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "engine.h" ++#include "i2c_engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "i2c_hw_engine.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++/* ++ * @brief ++ * Cast 'struct i2c_engine *' ++ * to 'struct i2c_hw_engine *' ++ */ ++#define FROM_I2C_ENGINE(ptr) \ ++ container_of((ptr), struct i2c_hw_engine, base) ++ ++/* ++ * @brief ++ * Cast 'struct engine *' ++ * to 'struct i2c_hw_engine *' ++ */ ++#define FROM_ENGINE(ptr) \ ++ FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) ++ ++enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type( ++ const struct engine *engine) ++{ ++ return I2CAUX_ENGINE_TYPE_I2C_DDC_HW; ++} ++ ++bool dal_i2c_hw_engine_submit_request( ++ struct engine *engine, ++ struct i2caux_transaction_request *i2caux_request, ++ bool middle_of_transaction) ++{ ++ struct i2c_hw_engine *hw_engine = FROM_ENGINE(engine); ++ ++ struct i2c_request_transaction_data request; ++ ++ uint32_t transaction_timeout; ++ ++ enum i2c_channel_operation_result operation_result; ++ ++ bool result = false; ++ ++ /* We need following: ++ * transaction length will not exceed ++ * the number of free bytes in HW buffer (minus one for address)*/ ++ ++ if (i2caux_request->payload.length >= ++ hw_engine->funcs->get_hw_buffer_available_size(hw_engine)) { ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW; ++ return false; ++ } ++ ++ if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) ++ request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_READ; ++ else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) ++ request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE; ++ else { ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; ++ /* [anaumov] in DAL2, there was no "return false" */ ++ return false; ++ } ++ ++ request.address = (uint8_t)i2caux_request->payload.address; ++ request.length = i2caux_request->payload.length; ++ request.data = i2caux_request->payload.data; ++ ++ /* obtain timeout value before submitting request */ ++ ++ transaction_timeout = hw_engine->funcs->get_transaction_timeout( ++ hw_engine, i2caux_request->payload.length + 1); ++ ++ hw_engine->base.funcs->submit_channel_request( ++ &hw_engine->base, &request); ++ ++ if ((request.status == I2C_CHANNEL_OPERATION_FAILED) || ++ (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) { ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY; ++ return false; ++ } ++ ++ /* wait until transaction proceed */ ++ ++ operation_result = hw_engine->funcs->wait_on_operation_result( ++ hw_engine, ++ transaction_timeout, ++ I2C_CHANNEL_OPERATION_ENGINE_BUSY); ++ ++ /* update transaction status */ ++ ++ switch (operation_result) { ++ case I2C_CHANNEL_OPERATION_SUCCEEDED: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ result = true; ++ break; ++ case I2C_CHANNEL_OPERATION_NO_RESPONSE: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ break; ++ case I2C_CHANNEL_OPERATION_TIMEOUT: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ break; ++ case I2C_CHANNEL_OPERATION_FAILED: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; ++ break; ++ default: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; ++ } ++ ++ if (result && (i2caux_request->operation == I2CAUX_TRANSACTION_READ)) { ++ struct i2c_reply_transaction_data reply; ++ ++ reply.data = i2caux_request->payload.data; ++ reply.length = i2caux_request->payload.length; ++ ++ hw_engine->base.funcs-> ++ process_channel_reply(&hw_engine->base, &reply); ++ } ++ ++ return result; ++} ++ ++bool dal_i2c_hw_engine_acquire_engine( ++ struct i2c_engine *engine, ++ struct ddc *ddc) ++{ ++ enum gpio_result result; ++ uint32_t current_speed; ++ ++ result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, ++ GPIO_DDC_CONFIG_TYPE_MODE_I2C); ++ ++ if (result != GPIO_RESULT_OK) ++ return false; ++ ++ engine->base.ddc = ddc; ++ ++ current_speed = engine->funcs->get_speed(engine); ++ ++ if (current_speed) ++ FROM_I2C_ENGINE(engine)->original_speed = current_speed; ++ ++ return true; ++} ++/* ++ * @brief ++ * Queries in a loop for current engine status ++ * until retrieved status matches 'expected_result', or timeout occurs. ++ * Timeout given in microseconds ++ * and the status query frequency is also one per microsecond. ++ */ ++enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result( ++ struct i2c_hw_engine *engine, ++ uint32_t timeout, ++ enum i2c_channel_operation_result expected_result) ++{ ++ enum i2c_channel_operation_result result; ++ uint32_t i = 0; ++ ++ if (!timeout) ++ return I2C_CHANNEL_OPERATION_SUCCEEDED; ++ ++ do { ++ result = engine->base.funcs->get_channel_status( ++ &engine->base, NULL); ++ ++ if (result != expected_result) ++ break; ++ ++ dc_service_delay_in_microseconds(engine->base.base.ctx, 1); ++ ++ ++i; ++ } while (i < timeout); ++ ++ return result; ++} ++ ++bool dal_i2c_hw_engine_construct( ++ struct i2c_hw_engine *engine, ++ struct dc_context *ctx) ++{ ++ if (!dal_i2c_engine_construct(&engine->base, ctx)) ++ return false; ++ engine->original_speed = I2CAUX_DEFAULT_I2C_HW_SPEED; ++ engine->default_speed = I2CAUX_DEFAULT_I2C_HW_SPEED; ++ return true; ++} ++ ++void dal_i2c_hw_engine_destruct( ++ struct i2c_hw_engine *engine) ++{ ++ dal_i2c_engine_destruct(&engine->base); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.h b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.h +new file mode 100644 +index 0000000..5afbd70 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_hw_engine.h +@@ -0,0 +1,80 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_HW_ENGINE_H__ ++#define __DAL_I2C_HW_ENGINE_H__ ++ ++enum { ++ TRANSACTION_TIMEOUT_IN_I2C_CLOCKS = 32 ++}; ++ ++struct i2c_hw_engine; ++ ++struct i2c_hw_engine_funcs { ++ uint8_t (*get_hw_buffer_available_size)( ++ const struct i2c_hw_engine *engine); ++ enum i2c_channel_operation_result (*wait_on_operation_result)( ++ struct i2c_hw_engine *engine, ++ uint32_t timeout, ++ enum i2c_channel_operation_result expected_result); ++ uint32_t (*get_transaction_timeout)( ++ const struct i2c_hw_engine *engine, ++ uint32_t length); ++}; ++ ++struct i2c_hw_engine { ++ struct i2c_engine base; ++ const struct i2c_hw_engine_funcs *funcs; ++ ++ /* Values below are in kilohertz */ ++ uint32_t original_speed; ++ uint32_t default_speed; ++}; ++ ++bool dal_i2c_hw_engine_construct( ++ struct i2c_hw_engine *engine, ++ struct dc_context *ctx); ++ ++void dal_i2c_hw_engine_destruct( ++ struct i2c_hw_engine *engine); ++ ++enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result( ++ struct i2c_hw_engine *engine, ++ uint32_t timeout, ++ enum i2c_channel_operation_result expected_result); ++ ++bool dal_i2c_hw_engine_acquire_engine( ++ struct i2c_engine *engine, ++ struct ddc *ddc); ++ ++bool dal_i2c_hw_engine_submit_request( ++ struct engine *ptr, ++ struct i2caux_transaction_request *i2caux_request, ++ bool middle_of_transaction); ++ ++enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type( ++ const struct engine *engine); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.c +new file mode 100644 +index 0000000..c253917 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.c +@@ -0,0 +1,615 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++#include "engine.h" ++#include "i2c_engine.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "i2c_sw_engine.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++/* ++ * This unit ++ */ ++ ++#define SCL false ++#define SDA true ++ ++static inline bool read_bit_from_ddc( ++ struct ddc *ddc, ++ bool data_nor_clock) ++{ ++ uint32_t value = 0; ++ ++ if (data_nor_clock) ++ dal_ddc_get_data(ddc, &value); ++ else ++ dal_ddc_get_clock(ddc, &value); ++ ++ return (value != 0); ++} ++ ++static inline void write_bit_to_ddc( ++ struct ddc *ddc, ++ bool data_nor_clock, ++ bool bit) ++{ ++ uint32_t value = bit ? 1 : 0; ++ ++ if (data_nor_clock) ++ dal_ddc_set_data(ddc, value); ++ else ++ dal_ddc_set_clock(ddc, value); ++} ++ ++static bool wait_for_scl_high( ++ struct dc_context *ctx, ++ struct ddc *ddc, ++ uint16_t clock_delay_div_4) ++{ ++ uint32_t scl_retry = 0; ++ uint32_t scl_retry_max = I2C_SW_TIMEOUT_DELAY / clock_delay_div_4; ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ /* 3 milliseconds delay ++ * to wake up some displays from "low power" state. ++ */ ++ ++ do { ++ if (read_bit_from_ddc(ddc, SCL)) ++ return true; ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ ++scl_retry; ++ } while (scl_retry <= scl_retry_max); ++ ++ return false; ++} ++ ++static bool start_sync( ++ struct dc_context *ctx, ++ struct ddc *ddc_handle, ++ uint16_t clock_delay_div_4) ++{ ++ uint32_t retry = 0; ++ ++ /* The I2C communications start signal is: ++ * the SDA going low from high, while the SCL is high. */ ++ ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ do { ++ write_bit_to_ddc(ddc_handle, SDA, true); ++ ++ if (!read_bit_from_ddc(ddc_handle, SDA)) { ++ ++retry; ++ continue; ++ } ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) ++ break; ++ ++ write_bit_to_ddc(ddc_handle, SDA, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SCL, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ return true; ++ } while (retry <= I2C_SW_RETRIES); ++ ++ return false; ++} ++ ++static bool stop_sync( ++ struct dc_context *ctx, ++ struct ddc *ddc_handle, ++ uint16_t clock_delay_div_4) ++{ ++ uint32_t retry = 0; ++ ++ /* The I2C communications stop signal is: ++ * the SDA going high from low, while the SCL is high. */ ++ ++ write_bit_to_ddc(ddc_handle, SCL, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SDA, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) ++ return false; ++ ++ write_bit_to_ddc(ddc_handle, SDA, true); ++ ++ do { ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ if (read_bit_from_ddc(ddc_handle, SDA)) ++ return true; ++ ++ ++retry; ++ } while (retry <= 2); ++ ++ return false; ++} ++ ++static bool write_byte( ++ struct dc_context *ctx, ++ struct ddc *ddc_handle, ++ uint16_t clock_delay_div_4, ++ uint8_t byte) ++{ ++ int32_t shift = 7; ++ bool ack; ++ ++ /* bits are transmitted serially, starting from MSB */ ++ ++ do { ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SDA, (byte >> shift) & 1); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) ++ return false; ++ ++ write_bit_to_ddc(ddc_handle, SCL, false); ++ ++ --shift; ++ } while (shift >= 0); ++ ++ /* The display sends ACK by preventing the SDA from going high ++ * after the SCL pulse we use to send our last data bit. ++ * If the SDA goes high after that bit, it's a NACK */ ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SDA, true); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) ++ return false; ++ ++ /* read ACK bit */ ++ ++ ack = !read_bit_from_ddc(ddc_handle, SDA); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4 << 1); ++ ++ write_bit_to_ddc(ddc_handle, SCL, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4 << 1); ++ ++ return ack; ++} ++ ++static bool read_byte( ++ struct dc_context *ctx, ++ struct ddc *ddc_handle, ++ uint16_t clock_delay_div_4, ++ uint8_t *byte, ++ bool more) ++{ ++ int32_t shift = 7; ++ ++ uint8_t data = 0; ++ ++ /* The data bits are read from MSB to LSB; ++ * bit is read while SCL is high */ ++ ++ do { ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) ++ return false; ++ ++ if (read_bit_from_ddc(ddc_handle, SDA)) ++ data |= (1 << shift); ++ ++ write_bit_to_ddc(ddc_handle, SCL, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4 << 1); ++ ++ --shift; ++ } while (shift >= 0); ++ ++ /* read only whole byte */ ++ ++ *byte = data; ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ /* send the acknowledge bit: ++ * SDA low means ACK, SDA high means NACK */ ++ ++ write_bit_to_ddc(ddc_handle, SDA, !more); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SCL, true); ++ ++ if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) ++ return false; ++ ++ write_bit_to_ddc(ddc_handle, SCL, false); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ write_bit_to_ddc(ddc_handle, SDA, true); ++ ++ dc_service_delay_in_microseconds(ctx, clock_delay_div_4); ++ ++ return true; ++} ++ ++static bool i2c_write( ++ struct dc_context *ctx, ++ struct ddc *ddc_handle, ++ uint16_t clock_delay_div_4, ++ uint8_t address, ++ uint8_t length, ++ const uint8_t *data) ++{ ++ uint32_t i = 0; ++ ++ if (!write_byte(ctx, ddc_handle, clock_delay_div_4, address)) ++ return false; ++ ++ while (i < length) { ++ if (!write_byte(ctx, ddc_handle, clock_delay_div_4, data[i])) ++ return false; ++ ++i; ++ } ++ ++ return true; ++} ++ ++static bool i2c_read( ++ struct dc_context *ctx, ++ struct ddc *ddc_handle, ++ uint16_t clock_delay_div_4, ++ uint8_t address, ++ uint8_t length, ++ uint8_t *data) ++{ ++ uint32_t i = 0; ++ ++ if (!write_byte(ctx, ddc_handle, clock_delay_div_4, address)) ++ return false; ++ ++ while (i < length) { ++ if (!read_byte(ctx, ddc_handle, clock_delay_div_4, data + i, ++ i < length - 1)) ++ return false; ++ ++i; ++ } ++ ++ return true; ++} ++ ++/* ++ * @brief ++ * Cast 'struct i2c_engine *' ++ * to 'struct i2c_sw_engine *' ++ */ ++#define FROM_I2C_ENGINE(ptr) \ ++ container_of((ptr), struct i2c_sw_engine, base) ++ ++/* ++ * @brief ++ * Cast 'struct engine *' ++ * to 'struct i2c_sw_engine *' ++ */ ++#define FROM_ENGINE(ptr) \ ++ FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) ++ ++enum i2caux_engine_type dal_i2c_sw_engine_get_engine_type( ++ const struct engine *engine) ++{ ++ return I2CAUX_ENGINE_TYPE_I2C_SW; ++} ++ ++bool dal_i2c_sw_engine_submit_request( ++ struct engine *engine, ++ struct i2caux_transaction_request *i2caux_request, ++ bool middle_of_transaction) ++{ ++ struct i2c_sw_engine *sw_engine = FROM_ENGINE(engine); ++ ++ struct i2c_engine *base = &sw_engine->base; ++ ++ struct i2c_request_transaction_data request; ++ bool operation_succeeded = false; ++ ++ if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) ++ request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_READ; ++ else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) ++ request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE; ++ else { ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; ++ /* in DAL2, there was no "return false" */ ++ return false; ++ } ++ ++ request.address = (uint8_t)i2caux_request->payload.address; ++ request.length = i2caux_request->payload.length; ++ request.data = i2caux_request->payload.data; ++ ++ base->funcs->submit_channel_request(base, &request); ++ ++ if ((request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY) || ++ (request.status == I2C_CHANNEL_OPERATION_FAILED)) ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY; ++ else { ++ enum i2c_channel_operation_result operation_result; ++ ++ do { ++ operation_result = ++ base->funcs->get_channel_status(base, NULL); ++ ++ switch (operation_result) { ++ case I2C_CHANNEL_OPERATION_SUCCEEDED: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ operation_succeeded = true; ++ break; ++ case I2C_CHANNEL_OPERATION_NO_RESPONSE: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ break; ++ case I2C_CHANNEL_OPERATION_TIMEOUT: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ break; ++ case I2C_CHANNEL_OPERATION_FAILED: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; ++ break; ++ default: ++ i2caux_request->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; ++ break; ++ } ++ } while (operation_result == I2C_CHANNEL_OPERATION_ENGINE_BUSY); ++ } ++ ++ return operation_succeeded; ++} ++ ++uint32_t dal_i2c_sw_engine_get_speed( ++ const struct i2c_engine *engine) ++{ ++ return FROM_I2C_ENGINE(engine)->speed; ++} ++ ++void dal_i2c_sw_engine_set_speed( ++ struct i2c_engine *engine, ++ uint32_t speed) ++{ ++ struct i2c_sw_engine *sw_engine = FROM_I2C_ENGINE(engine); ++ ++ ASSERT(speed); ++ ++ sw_engine->speed = speed ? speed : I2CAUX_DEFAULT_I2C_SW_SPEED; ++ ++ sw_engine->clock_delay = 1000 / sw_engine->speed; ++ ++ if (sw_engine->clock_delay < 12) ++ sw_engine->clock_delay = 12; ++} ++ ++bool dal_i2caux_i2c_sw_engine_acquire_engine( ++ struct i2c_engine *engine, ++ struct ddc *ddc) ++{ ++ enum gpio_result result; ++ ++ result = dal_ddc_open(ddc, GPIO_MODE_FAST_OUTPUT, ++ GPIO_DDC_CONFIG_TYPE_MODE_I2C); ++ ++ if (result != GPIO_RESULT_OK) ++ return false; ++ ++ engine->base.ddc = ddc; ++ ++ return true; ++} ++ ++void dal_i2c_sw_engine_submit_channel_request( ++ struct i2c_engine *engine, ++ struct i2c_request_transaction_data *req) ++{ ++ struct i2c_sw_engine *sw_engine = FROM_I2C_ENGINE(engine); ++ ++ struct ddc *ddc = engine->base.ddc; ++ uint16_t clock_delay_div_4 = sw_engine->clock_delay >> 2; ++ ++ /* send sync (start / repeated start) */ ++ ++ bool result = start_sync(engine->base.ctx, ddc, clock_delay_div_4); ++ ++ /* process payload */ ++ ++ if (result) { ++ switch (req->action) { ++ case I2CAUX_TRANSACTION_ACTION_I2C_WRITE: ++ case I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT: ++ result = i2c_write(engine->base.ctx, ddc, clock_delay_div_4, ++ req->address, req->length, req->data); ++ break; ++ case I2CAUX_TRANSACTION_ACTION_I2C_READ: ++ case I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT: ++ result = i2c_read(engine->base.ctx, ddc, clock_delay_div_4, ++ req->address, req->length, req->data); ++ break; ++ default: ++ result = false; ++ break; ++ } ++ } ++ ++ /* send stop if not 'mot' or operation failed */ ++ ++ if (!result || ++ (req->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || ++ (req->action == I2CAUX_TRANSACTION_ACTION_I2C_READ)) ++ if (!stop_sync(engine->base.ctx, ddc, clock_delay_div_4)) ++ result = false; ++ ++ req->status = result ? ++ I2C_CHANNEL_OPERATION_SUCCEEDED : ++ I2C_CHANNEL_OPERATION_FAILED; ++} ++ ++enum i2c_channel_operation_result dal_i2c_sw_engine_get_channel_status( ++ struct i2c_engine *engine, ++ uint8_t *returned_bytes) ++{ ++ return dal_ddc_check_line_aborted(engine->base.ddc) ? ++ I2C_CHANNEL_OPERATION_FAILED : ++ I2C_CHANNEL_OPERATION_SUCCEEDED; ++} ++ ++void dal_i2c_sw_engine_destruct( ++ struct i2c_sw_engine *engine) ++{ ++ dal_i2c_engine_destruct(&engine->base); ++} ++ ++static void destroy( ++ struct i2c_engine **ptr) ++{ ++ dal_i2c_sw_engine_destruct(FROM_I2C_ENGINE(*ptr)); ++ ++ dc_service_free((*ptr)->base.ctx, *ptr); ++ *ptr = NULL; ++} ++ ++static const struct i2c_engine_funcs i2c_engine_funcs = { ++ .acquire_engine = dal_i2caux_i2c_sw_engine_acquire_engine, ++ .destroy = destroy, ++ .get_speed = dal_i2c_sw_engine_get_speed, ++ .set_speed = dal_i2c_sw_engine_set_speed, ++ .setup_engine = dal_i2c_engine_setup_i2c_engine, ++ .submit_channel_request = dal_i2c_sw_engine_submit_channel_request, ++ .process_channel_reply = dal_i2c_engine_process_channel_reply, ++ .get_channel_status = dal_i2c_sw_engine_get_channel_status, ++}; ++ ++static void release_engine( ++ struct engine *engine) ++{ ++ ++} ++ ++static const struct engine_funcs engine_funcs = { ++ .release_engine = release_engine, ++ .get_engine_type = dal_i2c_sw_engine_get_engine_type, ++ .acquire = dal_i2c_engine_acquire, ++ .submit_request = dal_i2c_sw_engine_submit_request, ++ .keep_power_up_count = dal_i2caux_keep_power_up_count, ++}; ++ ++bool dal_i2c_sw_engine_construct( ++ struct i2c_sw_engine *engine, ++ const struct i2c_sw_engine_create_arg *arg) ++{ ++ if (!dal_i2c_engine_construct(&engine->base, arg->ctx)) ++ return false; ++ ++ dal_i2c_sw_engine_set_speed(&engine->base, arg->default_speed); ++ engine->base.funcs = &i2c_engine_funcs; ++ engine->base.base.funcs = &engine_funcs; ++ return true; ++} ++ ++ ++ ++struct i2c_engine *dal_i2c_sw_engine_create( ++ const struct i2c_sw_engine_create_arg *arg) ++{ ++ struct i2c_sw_engine *engine; ++ ++ if (!arg) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ engine = dc_service_alloc(arg->ctx, sizeof(struct i2c_sw_engine)); ++ ++ if (!engine) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ if (dal_i2c_sw_engine_construct(engine, arg)) ++ return &engine->base; ++ ++ BREAK_TO_DEBUGGER(); ++ ++ dc_service_free(arg->ctx, engine); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.h b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.h +new file mode 100644 +index 0000000..e0cb4c3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2c_sw_engine.h +@@ -0,0 +1,81 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_SW_ENGINE_H__ ++#define __DAL_I2C_SW_ENGINE_H__ ++ ++enum { ++ I2C_SW_RETRIES = 10, ++ I2C_SW_SCL_READ_RETRIES = 128, ++ /* following value is in microseconds */ ++ I2C_SW_TIMEOUT_DELAY = 3000 ++}; ++ ++struct i2c_sw_engine; ++ ++struct i2c_sw_engine { ++ struct i2c_engine base; ++ uint32_t clock_delay; ++ /* Values below are in KHz */ ++ uint32_t speed; ++ uint32_t default_speed; ++}; ++ ++struct i2c_sw_engine_create_arg { ++ uint32_t default_speed; ++ struct dc_context *ctx; ++}; ++ ++bool dal_i2c_sw_engine_construct( ++ struct i2c_sw_engine *engine, ++ const struct i2c_sw_engine_create_arg *arg); ++ ++bool dal_i2caux_i2c_sw_engine_acquire_engine( ++ struct i2c_engine *engine, ++ struct ddc *ddc_handle); ++ ++void dal_i2c_sw_engine_destruct( ++ struct i2c_sw_engine *engine); ++ ++struct i2c_engine *dal_i2c_sw_engine_create( ++ const struct i2c_sw_engine_create_arg *arg); ++enum i2caux_engine_type dal_i2c_sw_engine_get_engine_type( ++ const struct engine *engine); ++bool dal_i2c_sw_engine_submit_request( ++ struct engine *ptr, ++ struct i2caux_transaction_request *i2caux_request, ++ bool middle_of_transaction); ++uint32_t dal_i2c_sw_engine_get_speed( ++ const struct i2c_engine *engine); ++void dal_i2c_sw_engine_set_speed( ++ struct i2c_engine *ptr, ++ uint32_t speed); ++void dal_i2c_sw_engine_submit_channel_request( ++ struct i2c_engine *ptr, ++ struct i2c_request_transaction_data *req); ++enum i2c_channel_operation_result dal_i2c_sw_engine_get_channel_status( ++ struct i2c_engine *engine, ++ uint8_t *returned_bytes); ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c +new file mode 100644 +index 0000000..50262a4 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c +@@ -0,0 +1,519 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++/* ++ * Pre-requisites: headers required by header of this unit ++ */ ++ ++#include "include/i2caux_interface.h" ++ ++/* ++ * Header of this unit ++ */ ++ ++#include "i2caux.h" ++ ++/* ++ * Post-requisites: headers required by this unit ++ */ ++ ++#include "engine.h" ++#include "i2c_engine.h" ++#include "aux_engine.h" ++ ++/* ++ * This unit ++ */ ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/i2caux_dce110.h" ++#endif ++ ++/* ++ * @brief ++ * Plain API, available publicly ++ */ ++ ++struct i2caux *dal_i2caux_create( ++ struct adapter_service *as, ++ struct dc_context *ctx) ++{ ++ enum dce_version dce_version; ++ ++ if (!as) { ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++ ++ dce_version = dal_adapter_service_get_dce_version(as); ++ ++ switch (dce_version) { ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ return dal_i2caux_dce110_create(as, ctx); ++#endif ++ default: ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++ } ++} ++ ++bool dal_i2caux_submit_i2c_command( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ struct i2c_command *cmd) ++{ ++ struct i2c_engine *engine; ++ uint8_t index_of_payload = 0; ++ bool result; ++ ++ if (!ddc) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!cmd) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ switch (cmd->engine) { ++ case I2C_COMMAND_ENGINE_SW: ++ /* try to acquire SW engine first, ++ * acquire HW engine if SW engine not available */ ++ engine = i2caux->funcs->acquire_i2c_sw_engine(i2caux, ddc); ++ ++ if (!engine) ++ engine = i2caux->funcs->acquire_i2c_hw_engine( ++ i2caux, ddc); ++ break; ++ case I2C_COMMAND_ENGINE_HW: ++ case I2C_COMMAND_ENGINE_DEFAULT: ++ default: ++ /* try to acquire HW engine first, ++ * acquire SW engine if HW engine not available */ ++ engine = i2caux->funcs->acquire_i2c_hw_engine(i2caux, ddc); ++ ++ if (!engine) ++ engine = i2caux->funcs->acquire_i2c_sw_engine( ++ i2caux, ddc); ++ } ++ ++ if (!engine) ++ return false; ++ ++ engine->funcs->set_speed(engine, cmd->speed); ++ ++ result = true; ++ ++ while (index_of_payload < cmd->number_of_payloads) { ++ bool mot = (index_of_payload != cmd->number_of_payloads - 1); ++ ++ struct i2c_payload *payload = cmd->payloads + index_of_payload; ++ ++ struct i2caux_transaction_request request = { 0 }; ++ ++ request.operation = payload->write ? ++ I2CAUX_TRANSACTION_WRITE : ++ I2CAUX_TRANSACTION_READ; ++ ++ request.payload.address_space = ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C; ++ request.payload.address = (payload->address << 1) | ++ !payload->write; ++ request.payload.length = payload->length; ++ request.payload.data = payload->data; ++ ++ if (!engine->base.funcs->submit_request( ++ &engine->base, &request, mot)) { ++ result = false; ++ break; ++ } ++ ++ ++index_of_payload; ++ } ++ ++ i2caux->funcs->release_engine(i2caux, &engine->base); ++ ++ return result; ++} ++ ++bool dal_i2caux_submit_aux_command( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ struct aux_command *cmd) ++{ ++ struct aux_engine *engine; ++ uint8_t index_of_payload = 0; ++ bool result; ++ ++ if (!ddc) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!cmd) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ engine = i2caux->funcs->acquire_aux_engine(i2caux, ddc); ++ ++ if (!engine) ++ return false; ++ ++ engine->delay = cmd->defer_delay; ++ engine->max_defer_write_retry = cmd->max_defer_write_retry; ++ ++ result = true; ++ ++ while (index_of_payload < cmd->number_of_payloads) { ++ bool mot = (index_of_payload != cmd->number_of_payloads - 1); ++ ++ struct aux_payload *payload = cmd->payloads + index_of_payload; ++ ++ struct i2caux_transaction_request request = { 0 }; ++ ++ request.operation = payload->write ? ++ I2CAUX_TRANSACTION_WRITE : ++ I2CAUX_TRANSACTION_READ; ++ ++ if (payload->i2c_over_aux) { ++ request.payload.address_space = ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C; ++ ++ request.payload.address = (payload->address << 1) | ++ !payload->write; ++ } else { ++ request.payload.address_space = ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD; ++ ++ request.payload.address = payload->address; ++ } ++ ++ request.payload.length = payload->length; ++ request.payload.data = payload->data; ++ ++ if (!engine->base.funcs->submit_request( ++ &engine->base, &request, mot)) { ++ result = false; ++ break; ++ } ++ ++ ++index_of_payload; ++ } ++ ++ i2caux->funcs->release_engine(i2caux, &engine->base); ++ ++ return result; ++} ++ ++static bool get_hw_supported_ddc_line( ++ struct ddc *ddc, ++ enum gpio_ddc_line *line) ++{ ++ enum gpio_ddc_line line_found; ++ ++ if (!ddc) { ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ if (!dal_ddc_is_hw_supported(ddc)) ++ return false; ++ ++ line_found = dal_ddc_get_line(ddc); ++ ++ if (line_found >= GPIO_DDC_LINE_COUNT) ++ return false; ++ ++ *line = line_found; ++ ++ return true; ++} ++ ++void dal_i2caux_keep_engine_power_up( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ bool keep_power_up) ++{ ++ enum gpio_ddc_line line; ++ struct i2c_engine *engine; ++ ++ if (!get_hw_supported_ddc_line(ddc, &line)) ++ return; ++ ++ engine = i2caux->i2c_hw_engines[line]; ++ ++ engine->base.funcs->keep_power_up_count(&engine->base, keep_power_up); ++} ++ ++bool dal_i2caux_start_gtc_sync( ++ struct i2caux *i2caux, ++ struct ddc *ddc) ++{ ++ enum gpio_ddc_line line; ++ ++ struct aux_engine *engine; ++ ++ bool result; ++ ++ if (!get_hw_supported_ddc_line(ddc, &line)) ++ return false; ++ ++ engine = i2caux->aux_engines[line]; ++ ++ if (!engine) ++ return false; ++ ++ if (!engine->base.funcs->acquire(&engine->base, ddc)) ++ return false; ++ ++ result = engine->funcs->start_gtc_sync(engine); ++ ++ i2caux->funcs->release_engine(i2caux, &engine->base); ++ ++ return result; ++} ++ ++bool dal_i2caux_stop_gtc_sync( ++ struct i2caux *i2caux, ++ struct ddc *ddc) ++{ ++ enum gpio_ddc_line line; ++ ++ struct aux_engine *engine; ++ ++ if (!get_hw_supported_ddc_line(ddc, &line)) ++ return false; ++ ++ engine = i2caux->aux_engines[line]; ++ ++ if (!engine) ++ return false; ++ ++ if (!engine->base.funcs->acquire(&engine->base, ddc)) ++ return false; ++ ++ engine->funcs->stop_gtc_sync(engine); ++ ++ i2caux->funcs->release_engine(i2caux, &engine->base); ++ ++ return true; ++} ++ ++void dal_i2caux_configure_aux( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ union aux_config cfg) ++{ ++ struct aux_engine *engine = ++ i2caux->funcs->acquire_aux_engine(i2caux, ddc); ++ ++ if (!engine) ++ return; ++ ++ engine->funcs->configure(engine, cfg); ++ ++ i2caux->funcs->release_engine(i2caux, &engine->base); ++} ++ ++void dal_i2caux_destroy( ++ struct i2caux **i2caux) ++{ ++ if (!i2caux || !*i2caux) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ (*i2caux)->funcs->destroy(i2caux); ++ ++ *i2caux = NULL; ++} ++ ++/* ++ * @brief ++ * An utility function used by 'struct i2caux' and its descendants ++ */ ++ ++uint32_t dal_i2caux_get_reference_clock( ++ struct adapter_service *as) ++{ ++ struct firmware_info info = { { 0 } }; ++ ++ if (!dal_adapter_service_get_firmware_info(as, &info)) ++ return 0; ++ ++ return info.pll_info.crystal_frequency; ++} ++ ++/* ++ * @brief ++ * i2caux ++ */ ++ ++enum { ++ /* following are expressed in KHz */ ++ DEFAULT_I2C_SW_SPEED = 50, ++ DEFAULT_I2C_HW_SPEED = 50, ++ ++ /* This is the timeout as defined in DP 1.2a, ++ * 2.3.4 "Detailed uPacket TX AUX CH State Description". */ ++ AUX_TIMEOUT_PERIOD = 400, ++ ++ /* Ideally, the SW timeout should be just above 550usec ++ * which is programmed in HW. ++ * But the SW timeout of 600usec is not reliable, ++ * because on some systems, delay_in_microseconds() ++ * returns faster than it should. ++ * EPR #379763: by trial-and-error on different systems, ++ * 700usec is the minimum reliable SW timeout for polling ++ * the AUX_SW_STATUS.AUX_SW_DONE bit. ++ * This timeout expires *only* when there is ++ * AUX Error or AUX Timeout conditions - not during normal operation. ++ * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set ++ * at most within ~240usec. That means, ++ * increasing this timeout will not affect normal operation, ++ * and we'll timeout after ++ * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 1600usec. ++ * This timeout is especially important for ++ * resume from S3 and CTS. */ ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 4 ++}; ++ ++struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine( ++ struct i2caux *i2caux, ++ struct ddc *ddc) ++{ ++ enum gpio_ddc_line line; ++ struct i2c_engine *engine = NULL; ++ ++ if (get_hw_supported_ddc_line(ddc, &line)) ++ engine = i2caux->i2c_sw_engines[line]; ++ ++ if (!engine) ++ engine = i2caux->i2c_generic_sw_engine; ++ ++ if (!engine) ++ return NULL; ++ ++ if (!engine->base.funcs->acquire(&engine->base, ddc)) ++ return NULL; ++ ++ return engine; ++} ++ ++struct aux_engine *dal_i2caux_acquire_aux_engine( ++ struct i2caux *i2caux, ++ struct ddc *ddc) ++{ ++ enum gpio_ddc_line line; ++ struct aux_engine *engine; ++ ++ if (!get_hw_supported_ddc_line(ddc, &line)) ++ return NULL; ++ ++ engine = i2caux->aux_engines[line]; ++ ++ if (!engine) ++ return NULL; ++ ++ if (!engine->base.funcs->acquire(&engine->base, ddc)) ++ return NULL; ++ ++ return engine; ++} ++ ++void dal_i2caux_release_engine( ++ struct i2caux *i2caux, ++ struct engine *engine) ++{ ++ engine->funcs->release_engine(engine); ++ ++ dal_ddc_close(engine->ddc); ++ ++ engine->ddc = NULL; ++} ++ ++bool dal_i2caux_construct( ++ struct i2caux *i2caux, ++ struct adapter_service *as, ++ struct dc_context *ctx) ++{ ++ uint32_t i = 0; ++ ++ i2caux->ctx = ctx; ++ do { ++ i2caux->i2c_sw_engines[i] = NULL; ++ i2caux->i2c_hw_engines[i] = NULL; ++ i2caux->aux_engines[i] = NULL; ++ ++ ++i; ++ } while (i < GPIO_DDC_LINE_COUNT); ++ ++ i2caux->i2c_generic_sw_engine = NULL; ++ i2caux->i2c_generic_hw_engine = NULL; ++ ++ i2caux->aux_timeout_period = ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD; ++ ++ i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED; ++ i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED; ++ ++ return true; ++} ++ ++void dal_i2caux_destruct( ++ struct i2caux *i2caux) ++{ ++ uint32_t i = 0; ++ ++ if (i2caux->i2c_generic_hw_engine) ++ i2caux->i2c_generic_hw_engine->funcs->destroy( ++ &i2caux->i2c_generic_hw_engine); ++ ++ if (i2caux->i2c_generic_sw_engine) ++ i2caux->i2c_generic_sw_engine->funcs->destroy( ++ &i2caux->i2c_generic_sw_engine); ++ ++ do { ++ if (i2caux->aux_engines[i]) ++ i2caux->aux_engines[i]->funcs->destroy( ++ &i2caux->aux_engines[i]); ++ ++ if (i2caux->i2c_hw_engines[i]) ++ i2caux->i2c_hw_engines[i]->funcs->destroy( ++ &i2caux->i2c_hw_engines[i]); ++ ++ if (i2caux->i2c_sw_engines[i]) ++ i2caux->i2c_sw_engines[i]->funcs->destroy( ++ &i2caux->i2c_sw_engines[i]); ++ ++ ++i; ++ } while (i < GPIO_DDC_LINE_COUNT); ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.h b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.h +new file mode 100644 +index 0000000..76f5b63 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.h +@@ -0,0 +1,123 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2C_AUX_H__ ++#define __DAL_I2C_AUX_H__ ++ ++uint32_t dal_i2caux_get_reference_clock( ++ struct adapter_service *as); ++ ++struct i2caux; ++ ++struct engine; ++ ++struct i2caux_funcs { ++ void (*destroy)(struct i2caux **ptr); ++ struct i2c_engine * (*acquire_i2c_sw_engine)( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ struct i2c_engine * (*acquire_i2c_hw_engine)( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ struct aux_engine * (*acquire_aux_engine)( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ void (*release_engine)( ++ struct i2caux *i2caux, ++ struct engine *engine); ++}; ++ ++struct i2c_engine; ++struct aux_engine; ++ ++struct i2caux { ++ struct dc_context *ctx; ++ const struct i2caux_funcs *funcs; ++ /* On ASIC we have certain amount of lines with HW DDC engine ++ * (4, 6, or maybe more in the future). ++ * For every such line, we create separate HW DDC engine ++ * (since we have these engines in HW) and separate SW DDC engine ++ * (to allow concurrent use of few lines). ++ * In similar way we have AUX engines. */ ++ ++ /* I2C SW engines, per DDC line. ++ * Only lines with HW DDC support will be initialized */ ++ struct i2c_engine *i2c_sw_engines[GPIO_DDC_LINE_COUNT]; ++ ++ /* I2C HW engines, per DDC line. ++ * Only lines with HW DDC support will be initialized */ ++ struct i2c_engine *i2c_hw_engines[GPIO_DDC_LINE_COUNT]; ++ ++ /* AUX engines, per DDC line. ++ * Only lines with HW AUX support will be initialized */ ++ struct aux_engine *aux_engines[GPIO_DDC_LINE_COUNT]; ++ ++ /* For all other lines, we can use ++ * single instance of generic I2C HW engine ++ * (since in HW, there is single instance of it) ++ * or single instance of generic I2C SW engine. ++ * AUX is not supported for other lines. */ ++ ++ /* General-purpose I2C SW engine. ++ * Can be assigned dynamically to any line per transaction */ ++ struct i2c_engine *i2c_generic_sw_engine; ++ ++ /* General-purpose I2C generic HW engine. ++ * Can be assigned dynamically to almost any line per transaction */ ++ struct i2c_engine *i2c_generic_hw_engine; ++ ++ /* [anaumov] in DAL2, there is a Mutex */ ++ ++ uint32_t aux_timeout_period; ++ ++ /* expressed in KHz */ ++ uint32_t default_i2c_sw_speed; ++ uint32_t default_i2c_hw_speed; ++}; ++ ++bool dal_i2caux_construct( ++ struct i2caux *i2caux, ++ struct adapter_service *as, ++ struct dc_context *ctx); ++ ++void dal_i2caux_release_engine( ++ struct i2caux *i2caux, ++ struct engine *engine); ++ ++void dal_i2caux_destruct( ++ struct i2caux *i2caux); ++ ++void dal_i2caux_destroy( ++ struct i2caux **ptr); ++ ++struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ ++struct aux_engine *dal_i2caux_acquire_aux_engine( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h b/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h +new file mode 100644 +index 0000000..f7315c6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h +@@ -0,0 +1,463 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++/** ++ * Bandwidth and Watermark calculations interface. ++ * (Refer to "DCE11_mode_support.xlsm" from Perforce.) ++ */ ++#ifndef __BANDWIDTH_CALCS_H__ ++#define __BANDWIDTH_CALCS_H__ ++ ++#include "bw_fixed.h" ++/******************************************************************************* ++ * There are three types of input into Calculations: ++ * 1. per-DCE static values - these are "hardcoded" properties of the DCEIP ++ * 2. board-level values - these are generally coming from VBIOS parser ++ * 3. mode/configuration values - depending Mode, Scaling number of Displays etc. ++ ******************************************************************************/ ++ ++enum bw_stereo_mode { ++ mono, ++ side_by_side, ++ top_bottom ++}; ++ ++enum bw_ul_mode { ++ ul_none, ++ ul_only, ++ ul_blend ++}; ++ ++enum bw_tiling_mode { ++ linear, ++ tiled ++}; ++ ++enum bw_panning_and_bezel_adj { ++ none, ++ any_lines ++}; ++ ++enum bw_underlay_surface_type { ++ yuv_420, ++ yuv_422 ++}; ++ ++struct bw_calcs_input_dceip { ++ struct bw_fixed dmif_request_buffer_size; ++ struct bw_fixed de_tiling_buffer; ++ struct bw_fixed dcfclk_request_generation; ++ struct bw_fixed lines_interleaved_into_lb; ++ struct bw_fixed chunk_width; ++ struct bw_fixed number_of_graphics_pipes; ++ struct bw_fixed number_of_underlay_pipes; ++ bool display_write_back_supported; ++ bool argb_compression_support; ++ struct bw_fixed underlay_vscaler_efficiency6_bit_per_component; ++ struct bw_fixed underlay_vscaler_efficiency8_bit_per_component; ++ struct bw_fixed underlay_vscaler_efficiency10_bit_per_component; ++ struct bw_fixed underlay_vscaler_efficiency12_bit_per_component; ++ struct bw_fixed graphics_vscaler_efficiency6_bit_per_component; ++ struct bw_fixed graphics_vscaler_efficiency8_bit_per_component; ++ struct bw_fixed graphics_vscaler_efficiency10_bit_per_component; ++ struct bw_fixed graphics_vscaler_efficiency12_bit_per_component; ++ struct bw_fixed alpha_vscaler_efficiency; ++ struct bw_fixed max_dmif_buffer_allocated; ++ struct bw_fixed graphics_dmif_size; ++ struct bw_fixed underlay_luma_dmif_size; ++ struct bw_fixed underlay_chroma_dmif_size; ++ bool pre_downscaler_enabled; ++ bool underlay_downscale_prefetch_enabled; ++ struct bw_fixed lb_write_pixels_per_dispclk; ++ struct bw_fixed lb_size_per_component444; ++ bool graphics_lb_nodownscaling_multi_line_prefetching; ++ struct bw_fixed stutter_and_dram_clock_state_change_gated_before_cursor; ++ struct bw_fixed underlay420_luma_lb_size_per_component; ++ struct bw_fixed underlay420_chroma_lb_size_per_component; ++ struct bw_fixed underlay422_lb_size_per_component; ++ struct bw_fixed cursor_chunk_width; ++ struct bw_fixed cursor_dcp_buffer_lines; ++ struct bw_fixed cursor_memory_interface_buffer_pixels; ++ struct bw_fixed underlay_maximum_width_efficient_for_tiling; ++ struct bw_fixed underlay_maximum_height_efficient_for_tiling; ++ struct bw_fixed peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display; ++ struct bw_fixed peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation; ++ struct bw_fixed minimum_outstanding_pte_request_limit; ++ struct bw_fixed maximum_total_outstanding_pte_requests_allowed_by_saw; ++ bool limit_excessive_outstanding_dmif_requests; ++ struct bw_fixed linear_mode_line_request_alternation_slice; ++ struct bw_fixed scatter_gather_lines_of_pte_prefetching_in_linear_mode; ++ struct bw_fixed display_write_back420_luma_mcifwr_buffer_size; ++ struct bw_fixed display_write_back420_chroma_mcifwr_buffer_size; ++ struct bw_fixed request_efficiency; ++ struct bw_fixed dispclk_per_request; ++ struct bw_fixed dispclk_ramping_factor; ++ struct bw_fixed display_pipe_throughput_factor; ++ struct bw_fixed scatter_gather_pte_request_rows_in_tiling_mode; ++ struct bw_fixed mcifwr_all_surfaces_burst_time; /* 0 todo: this is a bug*/ ++}; ++ ++struct bw_calcs_input_vbios { ++ struct bw_fixed dram_channel_width_in_bits; ++ struct bw_fixed number_of_dram_channels; ++ struct bw_fixed number_of_dram_banks; ++ struct bw_fixed high_yclk; ++ struct bw_fixed high_dram_bandwidth_per_channel; ++ struct bw_fixed low_yclk; ++ struct bw_fixed low_dram_bandwidth_per_channel; ++ struct bw_fixed low_sclk; ++ struct bw_fixed mid_sclk; ++ struct bw_fixed high_sclk; ++ struct bw_fixed low_voltage_max_dispclk; ++ struct bw_fixed mid_voltage_max_dispclk; ++ struct bw_fixed high_voltage_max_dispclk; ++ struct bw_fixed data_return_bus_width; ++ struct bw_fixed trc; ++ struct bw_fixed dmifmc_urgent_latency; ++ struct bw_fixed stutter_self_refresh_exit_latency; ++ struct bw_fixed nbp_state_change_latency; ++ struct bw_fixed mcifwrmc_urgent_latency; ++ bool scatter_gather_enable; ++ struct bw_fixed down_spread_percentage; ++ struct bw_fixed cursor_width; ++ struct bw_fixed average_compression_rate; ++ struct bw_fixed number_of_request_slots_gmc_reserves_for_dmif_per_channel; ++ struct bw_fixed blackout_duration; ++ struct bw_fixed maximum_blackout_recovery_time; ++}; ++ ++struct bw_calcs_input_mode_data_internal { ++ /* data for all displays */ ++ uint32_t number_of_displays; ++ struct bw_fixed graphics_rotation_angle; ++ struct bw_fixed underlay_rotation_angle; ++ bool display_synchronization_enabled; ++ enum bw_underlay_surface_type underlay_surface_type; ++ enum bw_panning_and_bezel_adj panning_and_bezel_adjustment; ++ enum bw_tiling_mode graphics_tiling_mode; ++ bool graphics_interlace_mode; ++ struct bw_fixed graphics_bytes_per_pixel; ++ struct bw_fixed graphics_htaps; ++ struct bw_fixed graphics_vtaps; ++ struct bw_fixed graphics_lb_bpc; ++ struct bw_fixed underlay_lb_bpc; ++ enum bw_tiling_mode underlay_tiling_mode; ++ struct bw_fixed underlay_htaps; ++ struct bw_fixed underlay_vtaps; ++ struct bw_fixed underlay_src_width; ++ struct bw_fixed underlay_src_height; ++ struct bw_fixed underlay_pitch_in_pixels; ++ enum bw_stereo_mode underlay_stereo_mode; ++ bool d0_fbc_enable; ++ bool d0_lpt_enable; ++ struct bw_fixed d0_htotal; ++ struct bw_fixed d0_pixel_rate; ++ struct bw_fixed d0_graphics_src_width; ++ struct bw_fixed d0_graphics_src_height; ++ struct bw_fixed d0_graphics_scale_ratio; ++ enum bw_stereo_mode d0_graphics_stereo_mode; ++ enum bw_ul_mode d0_underlay_mode; ++ struct bw_fixed d0_underlay_scale_ratio; ++ struct bw_fixed d1_htotal; ++ struct bw_fixed d1_pixel_rate; ++ struct bw_fixed d1_graphics_src_width; ++ struct bw_fixed d1_graphics_src_height; ++ struct bw_fixed d1_graphics_scale_ratio; ++ enum bw_stereo_mode d1_graphics_stereo_mode; ++ bool d1_display_write_back_dwb_enable; ++ enum bw_ul_mode d1_underlay_mode; ++ struct bw_fixed d1_underlay_scale_ratio; ++ struct bw_fixed d2_htotal; ++ struct bw_fixed d2_pixel_rate; ++ struct bw_fixed d2_graphics_src_width; ++ struct bw_fixed d2_graphics_src_height; ++ struct bw_fixed d2_graphics_scale_ratio; ++ enum bw_stereo_mode d2_graphics_stereo_mode; ++}; ++ ++struct bw_calcs_input_single_display { ++ uint32_t graphics_rotation_angle; ++ uint32_t underlay_rotation_angle; ++ enum bw_underlay_surface_type underlay_surface_type; ++ enum bw_panning_and_bezel_adj panning_and_bezel_adjustment; ++ uint32_t graphics_bytes_per_pixel; ++ bool graphics_interlace_mode; ++ enum bw_tiling_mode graphics_tiling_mode; ++ uint32_t graphics_h_taps; ++ uint32_t graphics_v_taps; ++ uint32_t graphics_lb_bpc; ++ uint32_t underlay_lb_bpc; ++ enum bw_tiling_mode underlay_tiling_mode; ++ uint32_t underlay_h_taps; ++ uint32_t underlay_v_taps; ++ uint32_t underlay_src_width; ++ uint32_t underlay_src_height; ++ uint32_t underlay_pitch_in_pixels; ++ enum bw_stereo_mode underlay_stereo_mode; ++ bool fbc_enable; ++ bool lpt_enable; ++ uint32_t h_total; ++ struct bw_fixed pixel_rate; ++ uint32_t graphics_src_width; ++ uint32_t graphics_src_height; ++ struct bw_fixed graphics_scale_ratio; ++ enum bw_stereo_mode graphics_stereo_mode; ++ enum bw_ul_mode underlay_mode; ++}; ++ ++#define BW_CALCS_MAX_NUM_DISPLAYS 3 ++ ++struct bw_calcs_input_mode_data { ++ /* data for all displays */ ++ uint8_t number_of_displays; ++ bool display_synchronization_enabled; ++ ++ struct bw_calcs_input_single_display ++ displays_data[BW_CALCS_MAX_NUM_DISPLAYS]; ++}; ++ ++/******************************************************************************* ++ * Output data structure(s). ++ ******************************************************************************/ ++#define maximum_number_of_surfaces 12 ++struct bw_results_internal { ++ bool cpup_state_change_enable; ++ bool cpuc_state_change_enable; ++ bool nbp_state_change_enable; ++ bool stutter_mode_enable; ++ struct bw_fixed number_of_underlay_surfaces; ++ struct bw_fixed src_width_after_surface_type; ++ struct bw_fixed src_height_after_surface_type; ++ struct bw_fixed hsr_after_surface_type; ++ struct bw_fixed vsr_after_surface_type; ++ struct bw_fixed src_width_after_rotation; ++ struct bw_fixed src_height_after_rotation; ++ struct bw_fixed hsr_after_rotation; ++ struct bw_fixed vsr_after_rotation; ++ struct bw_fixed source_height_pixels; ++ struct bw_fixed hsr_after_stereo; ++ struct bw_fixed vsr_after_stereo; ++ struct bw_fixed source_width_in_lb; ++ struct bw_fixed lb_line_pitch; ++ struct bw_fixed underlay_maximum_source_efficient_for_tiling; ++ struct bw_fixed num_lines_at_frame_start; ++ struct bw_fixed min_dmif_size_in_time; ++ struct bw_fixed min_mcifwr_size_in_time; ++ struct bw_fixed total_requests_for_dmif_size; ++ struct bw_fixed peak_pte_request_to_eviction_ratio_limiting; ++ struct bw_fixed useful_pte_per_pte_request; ++ struct bw_fixed scatter_gather_pte_request_rows; ++ struct bw_fixed scatter_gather_row_height; ++ struct bw_fixed scatter_gather_pte_requests_in_vblank; ++ struct bw_fixed inefficient_linear_pitch_in_bytes; ++ struct bw_fixed inefficient_underlay_pitch_in_pixels; ++ struct bw_fixed minimum_underlay_pitch_padding_recommended_for_efficiency; ++ struct bw_fixed cursor_total_data; ++ struct bw_fixed cursor_total_request_groups; ++ struct bw_fixed scatter_gather_total_pte_requests; ++ struct bw_fixed scatter_gather_total_pte_request_groups; ++ struct bw_fixed tile_width_in_pixels; ++ struct bw_fixed dmif_total_number_of_data_request_page_close_open; ++ struct bw_fixed mcifwr_total_number_of_data_request_page_close_open; ++ struct bw_fixed bytes_per_page_close_open; ++ struct bw_fixed mcifwr_total_page_close_open_time; ++ struct bw_fixed total_requests_for_adjusted_dmif_size; ++ struct bw_fixed total_dmifmc_urgent_trips; ++ struct bw_fixed total_dmifmc_urgent_latency; ++ struct bw_fixed total_display_reads_required_data; ++ struct bw_fixed total_display_reads_required_dram_access_data; ++ struct bw_fixed total_display_writes_required_data; ++ struct bw_fixed total_display_writes_required_dram_access_data; ++ struct bw_fixed display_reads_required_data; ++ struct bw_fixed display_reads_required_dram_access_data; ++ struct bw_fixed dmif_total_page_close_open_time; ++ struct bw_fixed min_cursor_memory_interface_buffer_size_in_time; ++ struct bw_fixed min_read_buffer_size_in_time; ++ struct bw_fixed display_reads_time_for_data_transfer; ++ struct bw_fixed display_writes_time_for_data_transfer; ++ struct bw_fixed dmif_required_dram_bandwidth; ++ struct bw_fixed mcifwr_required_dram_bandwidth; ++ struct bw_fixed required_dmifmc_urgent_latency_for_page_close_open; ++ struct bw_fixed required_mcifmcwr_urgent_latency; ++ struct bw_fixed required_dram_bandwidth_gbyte_per_second; ++ struct bw_fixed dram_bandwidth; ++ struct bw_fixed dmif_required_sclk; ++ struct bw_fixed mcifwr_required_sclk; ++ struct bw_fixed required_sclk; ++ struct bw_fixed downspread_factor; ++ struct bw_fixed v_scaler_efficiency; ++ struct bw_fixed scaler_limits_factor; ++ struct bw_fixed display_pipe_pixel_throughput; ++ struct bw_fixed total_dispclk_required_with_ramping; ++ struct bw_fixed total_dispclk_required_without_ramping; ++ struct bw_fixed total_read_request_bandwidth; ++ struct bw_fixed total_write_request_bandwidth; ++ struct bw_fixed dispclk_required_for_total_read_request_bandwidth; ++ struct bw_fixed total_dispclk_required_with_ramping_with_request_bandwidth; ++ struct bw_fixed total_dispclk_required_without_ramping_with_request_bandwidth; ++ struct bw_fixed dispclk; ++ struct bw_fixed blackout_recovery_time; ++ struct bw_fixed min_pixels_per_data_fifo_entry; ++ struct bw_fixed sclk_deep_sleep; ++ struct bw_fixed chunk_request_time; ++ struct bw_fixed cursor_request_time; ++ struct bw_fixed line_source_pixels_transfer_time; ++ struct bw_fixed dmifdram_access_efficiency; ++ struct bw_fixed mcifwrdram_access_efficiency; ++ struct bw_fixed total_average_bandwidth_no_compression; ++ struct bw_fixed total_average_bandwidth; ++ struct bw_fixed total_stutter_cycle_duration; ++ struct bw_fixed stutter_burst_time; ++ struct bw_fixed time_in_self_refresh; ++ struct bw_fixed stutter_efficiency; ++ struct bw_fixed worst_number_of_trips_to_memory; ++ struct bw_fixed immediate_flip_time; ++ struct bw_fixed latency_for_non_dmif_clients; ++ struct bw_fixed latency_for_non_mcifwr_clients; ++ struct bw_fixed dmifmc_urgent_latency_supported_in_high_sclk_and_yclk; ++ struct bw_fixed nbp_state_dram_speed_change_margin; ++ struct bw_fixed display_reads_time_for_data_transfer_and_urgent_latency; ++ bool use_alpha[maximum_number_of_surfaces]; ++ bool orthogonal_rotation[maximum_number_of_surfaces]; ++ bool enable[maximum_number_of_surfaces]; ++ bool access_one_channel_only[maximum_number_of_surfaces]; ++ bool scatter_gather_enable_for_pipe[maximum_number_of_surfaces]; ++ bool interlace_mode[maximum_number_of_surfaces]; ++ struct bw_fixed bytes_per_pixel[maximum_number_of_surfaces]; ++ struct bw_fixed h_total[maximum_number_of_surfaces]; ++ struct bw_fixed pixel_rate[maximum_number_of_surfaces]; ++ struct bw_fixed src_width[maximum_number_of_surfaces]; ++ struct bw_fixed pitch_in_pixels[maximum_number_of_surfaces]; ++ struct bw_fixed pitch_in_pixels_after_surface_type[maximum_number_of_surfaces]; ++ struct bw_fixed src_height[maximum_number_of_surfaces]; ++ struct bw_fixed scale_ratio[maximum_number_of_surfaces]; ++ struct bw_fixed h_taps[maximum_number_of_surfaces]; ++ struct bw_fixed v_taps[maximum_number_of_surfaces]; ++ struct bw_fixed rotation_angle[maximum_number_of_surfaces]; ++ struct bw_fixed lb_bpc[maximum_number_of_surfaces]; ++ struct bw_fixed compression_rate[maximum_number_of_surfaces]; ++ struct bw_fixed hsr[maximum_number_of_surfaces]; ++ struct bw_fixed vsr[maximum_number_of_surfaces]; ++ struct bw_fixed source_width_rounded_up_to_chunks[maximum_number_of_surfaces]; ++ struct bw_fixed source_width_pixels[maximum_number_of_surfaces]; ++ struct bw_fixed source_height_rounded_up_to_chunks[maximum_number_of_surfaces]; ++ struct bw_fixed display_bandwidth[maximum_number_of_surfaces]; ++ struct bw_fixed request_bandwidth[maximum_number_of_surfaces]; ++ struct bw_fixed bytes_per_request[maximum_number_of_surfaces]; ++ struct bw_fixed useful_bytes_per_request[maximum_number_of_surfaces]; ++ struct bw_fixed lines_interleaved_in_mem_access[maximum_number_of_surfaces]; ++ struct bw_fixed latency_hiding_lines[maximum_number_of_surfaces]; ++ struct bw_fixed lb_partitions[maximum_number_of_surfaces]; ++ struct bw_fixed lb_partitions_max[maximum_number_of_surfaces]; ++ struct bw_fixed dispclk_required_with_ramping[maximum_number_of_surfaces]; ++ struct bw_fixed dispclk_required_without_ramping[maximum_number_of_surfaces]; ++ struct bw_fixed data_buffer_size[maximum_number_of_surfaces]; ++ struct bw_fixed outstanding_chunk_request_limit[maximum_number_of_surfaces]; ++ struct bw_fixed urgent_watermark[maximum_number_of_surfaces]; ++ struct bw_fixed stutter_exit_watermark[maximum_number_of_surfaces]; ++ struct bw_fixed nbp_state_change_watermark[maximum_number_of_surfaces]; ++ struct bw_fixed v_filter_init[maximum_number_of_surfaces]; ++ struct bw_fixed stutter_cycle_duration[maximum_number_of_surfaces]; ++ struct bw_fixed average_bandwidth[maximum_number_of_surfaces]; ++ struct bw_fixed average_bandwidth_no_compression[maximum_number_of_surfaces]; ++ struct bw_fixed scatter_gather_pte_request_limit[maximum_number_of_surfaces]; ++ struct bw_fixed lb_size_per_component[maximum_number_of_surfaces]; ++ struct bw_fixed memory_chunk_size_in_bytes[maximum_number_of_surfaces]; ++ struct bw_fixed pipe_chunk_size_in_bytes[maximum_number_of_surfaces]; ++ struct bw_fixed number_of_trips_to_memory_for_getting_apte_row[maximum_number_of_surfaces]; ++ struct bw_fixed adjusted_data_buffer_size[maximum_number_of_surfaces]; ++ struct bw_fixed adjusted_data_buffer_size_in_memory[maximum_number_of_surfaces]; ++ struct bw_fixed pixels_per_data_fifo_entry[maximum_number_of_surfaces]; ++ struct bw_fixed scatter_gather_pte_requests_in_row[maximum_number_of_surfaces]; ++ struct bw_fixed pte_request_per_chunk[maximum_number_of_surfaces]; ++ struct bw_fixed scatter_gather_page_width[maximum_number_of_surfaces]; ++ struct bw_fixed scatter_gather_page_height[maximum_number_of_surfaces]; ++ struct bw_fixed lb_lines_in_per_line_out_in_beginning_of_frame[maximum_number_of_surfaces]; ++ struct bw_fixed lb_lines_in_per_line_out_in_middle_of_frame[maximum_number_of_surfaces]; ++ struct bw_fixed cursor_width_pixels[maximum_number_of_surfaces]; ++ struct bw_fixed line_buffer_prefetch[maximum_number_of_surfaces]; ++ struct bw_fixed minimum_latency_hiding[maximum_number_of_surfaces]; ++ struct bw_fixed maximum_latency_hiding[maximum_number_of_surfaces]; ++ struct bw_fixed minimum_latency_hiding_with_cursor[maximum_number_of_surfaces]; ++ struct bw_fixed maximum_latency_hiding_with_cursor[maximum_number_of_surfaces]; ++ struct bw_fixed src_pixels_for_first_output_pixel[maximum_number_of_surfaces]; ++ struct bw_fixed src_pixels_for_last_output_pixel[maximum_number_of_surfaces]; ++ struct bw_fixed src_data_for_first_output_pixel[maximum_number_of_surfaces]; ++ struct bw_fixed src_data_for_last_output_pixel[maximum_number_of_surfaces]; ++ struct bw_fixed active_time[maximum_number_of_surfaces]; ++ struct bw_fixed horizontal_blank_and_chunk_granularity_factor[maximum_number_of_surfaces]; ++ struct bw_fixed cursor_latency_hiding[maximum_number_of_surfaces]; ++ struct bw_fixed dmif_burst_time[3][3]; ++ struct bw_fixed mcifwr_burst_time[3][3]; ++ struct bw_fixed line_source_transfer_time[maximum_number_of_surfaces][3][3]; ++ struct bw_fixed dram_speed_change_margin[3][3]; ++ struct bw_fixed dispclk_required_for_dram_speed_change[3][3]; ++ struct bw_fixed blackout_duration_margin[3][3]; ++ struct bw_fixed dispclk_required_for_blackout_duration[3][3]; ++ struct bw_fixed dispclk_required_for_blackout_recovery[3][3]; ++ struct bw_fixed dmif_required_sclk_for_urgent_latency[6]; ++}; ++ ++struct bw_watermarks { ++ uint32_t a_mark; ++ uint32_t b_mark; ++}; ++ ++struct bw_calcs_output { ++ bool cpuc_state_change_enable; ++ bool cpup_state_change_enable; ++ bool stutter_mode_enable; ++ bool nbp_state_change_enable; ++ struct bw_watermarks urgent_watermark[4]; ++ struct bw_watermarks stutter_exit_watermark[4]; ++ struct bw_watermarks nbp_state_change_watermark[4]; ++ uint32_t required_sclk; ++ uint32_t dispclk; ++}; ++ ++ ++/** ++ * Initialize structures with data which will NOT change at runtime. ++ */ ++void bw_calcs_init( ++ struct bw_calcs_input_dceip *bw_dceip, ++ struct bw_calcs_input_vbios *bw_vbios); ++ ++/** ++ * Return: ++ * true - Display(s) configuration supported. ++ * In this case 'calcs_output' contains data for HW programming ++ * false - Display(s) configuration not supported (not enough bandwidth). ++ */ ++bool bw_calcs( ++ struct dc_context *ctx, ++ const struct bw_calcs_input_dceip *dceip, ++ const struct bw_calcs_input_vbios *vbios, ++ const struct bw_calcs_input_mode_data *mode_data, ++ struct bw_calcs_output *calcs_output); ++ ++ ++#endif /* __BANDWIDTH_CALCS_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/bw_fixed.h b/drivers/gpu/drm/amd/dal/dc/inc/bw_fixed.h +new file mode 100644 +index 0000000..f9e267b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/bw_fixed.h +@@ -0,0 +1,60 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef BW_FIXED_H_ ++#define BW_FIXED_H_ ++ ++struct bw_fixed { ++ signed long long value; ++}; ++ ++struct bw_fixed bw_min3(struct bw_fixed v1, struct bw_fixed v2, struct bw_fixed v3); ++ ++struct bw_fixed bw_max3(struct bw_fixed v1, struct bw_fixed v2, struct bw_fixed v3); ++ ++struct bw_fixed int_to_fixed(long long value); ++ ++struct bw_fixed frc_to_fixed(long long num, long long denum); ++ ++struct bw_fixed fixed31_32_to_bw_fixed(long long raw); ++ ++struct bw_fixed add(const struct bw_fixed arg1, const struct bw_fixed arg2); ++struct bw_fixed sub(const struct bw_fixed arg1, const struct bw_fixed arg2); ++struct bw_fixed mul(const struct bw_fixed arg1, const struct bw_fixed arg2); ++struct bw_fixed bw_div(const struct bw_fixed arg1, const struct bw_fixed arg2); ++ ++struct bw_fixed bw_min(const struct bw_fixed arg1, const struct bw_fixed arg2); ++struct bw_fixed bw_max(const struct bw_fixed arg1, const struct bw_fixed arg2); ++struct bw_fixed bw_floor(const struct bw_fixed arg, const struct bw_fixed significance); ++struct bw_fixed bw_ceil(const struct bw_fixed arg, const struct bw_fixed significance); ++ ++bool equ(const struct bw_fixed arg1, const struct bw_fixed arg2); ++bool neq(const struct bw_fixed arg1, const struct bw_fixed arg2); ++bool leq(const struct bw_fixed arg1, const struct bw_fixed arg2); ++bool geq(const struct bw_fixed arg1, const struct bw_fixed arg2); ++bool ltn(const struct bw_fixed arg1, const struct bw_fixed arg2); ++bool gtn(const struct bw_fixed arg1, const struct bw_fixed arg2); ++ ++#endif //BW_FIXED_H_ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/compressor.h b/drivers/gpu/drm/amd/dal/dc/inc/compressor.h +new file mode 100644 +index 0000000..4992ffd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/compressor.h +@@ -0,0 +1,140 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_COMPRESSOR_H__ ++#define __DAL_COMPRESSOR_H__ ++ ++#include "include/grph_object_id.h" ++ ++enum fbc_compress_ratio { ++ FBC_COMPRESS_RATIO_INVALID = 0, ++ FBC_COMPRESS_RATIO_1TO1 = 1, ++ FBC_COMPRESS_RATIO_2TO1 = 2, ++ FBC_COMPRESS_RATIO_4TO1 = 4, ++ FBC_COMPRESS_RATIO_8TO1 = 8, ++}; ++ ++union fbc_physical_address { ++ struct { ++ uint32_t low_part; ++ int32_t high_part; ++ } addr; ++}; ++ ++struct compr_addr_and_pitch_params { ++ uint32_t inst; ++ uint32_t source_view_width; ++ uint32_t source_view_height; ++}; ++ ++struct fbc_lpt_config { ++ uint32_t mem_channels_num; ++ uint32_t banks_num; ++ uint32_t chan_interleave_size; ++ uint32_t row_size; ++}; ++ ++struct fbc_input_info { ++ bool dynamic_fbc_buffer_alloc; ++ uint32_t source_view_width; ++ uint32_t source_view_height; ++ uint32_t active_targets_num; ++ struct fbc_lpt_config lpt_config; ++}; ++ ++struct fbc_requested_compressed_size { ++ uint32_t preferred_size; ++ uint32_t preferred_size_alignment; ++ uint32_t min_size; ++ uint32_t min_size_alignment; ++ union { ++ struct { ++ /*Above preferred_size must be allocated in FB pool */ ++ uint32_t PREFERRED_MUST_BE_FRAME_BUFFER_POOL:1; ++ /*Above min_size must be allocated in FB pool */ ++ uint32_t MIN_MUST_BE_FRAME_BUFFER_POOL:1; ++ } flags; ++ uint32_t bits; ++ }; ++}; ++ ++struct fbc_compressed_surface_info { ++ union fbc_physical_address compressed_surface_address; ++ uint32_t allocated_size; ++ union { ++ struct { ++ uint32_t FB_POOL:1; /*Allocated in FB Pool */ ++ uint32_t DYNAMIC_ALLOC:1; /*Dynamic allocation */ ++ } allocation_flags; ++ uint32_t bits; ++ }; ++}; ++ ++enum fbc_hw_max_resolution_supported { ++ FBC_MAX_X = 3840, ++ FBC_MAX_Y = 2400 ++}; ++ ++struct fbc_max_resolution_supported { ++ uint32_t source_view_width; ++ uint32_t source_view_height; ++}; ++ ++struct compressor { ++ struct dc_context *ctx; ++ uint32_t attached_inst; ++ bool is_enabled; ++ ++ union { ++ uint32_t raw; ++ struct { ++ uint32_t FBC_SUPPORT:1; ++ uint32_t FB_POOL:1; ++ uint32_t DYNAMIC_ALLOC:1; ++ uint32_t LPT_SUPPORT:1; ++ uint32_t LPT_MC_CONFIG:1; ++ uint32_t DUMMY_BACKEND:1; ++ uint32_t CLK_GATING_DISABLED:1; ++ ++ } bits; ++ } options; ++ ++ union fbc_physical_address compr_surface_address; ++ ++ uint32_t embedded_panel_h_size; ++ uint32_t embedded_panel_v_size; ++ uint32_t memory_bus_width; ++ uint32_t banks_num; ++ uint32_t raw_size; ++ uint32_t channel_interleave_size; ++ uint32_t dram_channels_num; ++ ++ uint32_t allocated_size; ++ uint32_t preferred_requested_size; ++ uint32_t lpt_channels_num; ++ enum fbc_compress_ratio min_compress_ratio; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/core_dc.h b/drivers/gpu/drm/amd/dal/dc/inc/core_dc.h +new file mode 100644 +index 0000000..dc246e8 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/core_dc.h +@@ -0,0 +1,39 @@ ++/* ++ * core_dc.h ++ * ++ * Created on: Nov 13, 2015 ++ * Author: yonsun ++ */ ++ ++#ifndef __CORE_DC_H__ ++#define __CORE_DC_H__ ++ ++#include "core_types.h" ++#include "hw_sequencer.h" ++ ++ ++struct dc { ++ struct dc_context *ctx; ++ ++ /** link-related data - begin **/ ++ uint8_t link_count; ++ struct core_link **links; ++ /** link-related data - end **/ ++ ++ /* TODO: determine max number of targets*/ ++ struct validate_context current_context; ++ struct resource_pool res_pool; ++ ++ /*Power State*/ ++ enum dc_video_power_state previous_power_state; ++ enum dc_video_power_state current_power_state; ++ ++ /* Inputs into BW and WM calculations. */ ++ struct bw_calcs_input_dceip bw_dceip; ++ struct bw_calcs_input_vbios bw_vbios; ++ ++ /* HW functions */ ++ struct hw_sequencer_funcs hwss; ++}; ++ ++#endif /* __CORE_DC_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/core_status.h b/drivers/gpu/drm/amd/dal/dc/inc/core_status.h +new file mode 100644 +index 0000000..9682cf8 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/core_status.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _CORE_STATUS_H_ ++#define _CORE_STATUS_H_ ++ ++enum dc_status { ++ DC_OK = 1, ++ ++ DC_NO_CONTROLLER_RESOURCE, ++ DC_NO_STREAM_ENG_RESOURCE, ++ DC_NO_STREAM_AUDIO_RESOURCE, ++ DC_NO_CLOCK_SOURCE_RESOURCE, ++ DC_FAIL_CONTROLLER_VALIDATE, ++ DC_FAIL_ENC_VALIDATE, ++ DC_FAIL_ATTACH_SURFACES, ++ DC_NO_DP_LINK_BANDWIDTH, ++ DC_EXCEED_DONGLE_MAX_CLK, ++ DC_FAIL_BANDWIDTH_VALIDATE, /* BW and Watermark validation */ ++ ++ DC_ERROR_UNEXPECTED = -1 ++}; ++ ++#endif /* _CORE_STATUS_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/core_types.h b/drivers/gpu/drm/amd/dal/dc/inc/core_types.h +new file mode 100644 +index 0000000..22ab6cb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/core_types.h +@@ -0,0 +1,308 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _CORE_TYPES_H_ ++#define _CORE_TYPES_H_ ++ ++#include "dc.h" ++#include "bandwidth_calcs.h" ++#include "ddc_service_types.h" ++ ++struct core_stream; ++/********* core_target *************/ ++ ++#define CONST_DC_TARGET_TO_CORE(dc_target) \ ++ container_of(dc_target, const struct core_target, public) ++#define DC_TARGET_TO_CORE(dc_target) \ ++ container_of(dc_target, struct core_target, public) ++ ++#define MAX_PIPES 6 ++#define MAX_STREAMS 6 ++#define MAX_CLOCK_SOURCES 4 ++ ++struct core_target { ++ struct dc_target public; ++ struct dc_target_status status; ++ ++ struct core_stream *streams[MAX_STREAMS]; ++ uint8_t stream_count; ++ struct dc_context *ctx; ++}; ++ ++/********* core_surface **********/ ++#define DC_SURFACE_TO_CORE(dc_surface) \ ++ container_of(dc_surface, struct core_surface, public) ++ ++struct core_surface { ++ struct dc_surface public; ++ struct dc_surface_status status; ++ struct dc_context *ctx; ++}; ++ ++void enable_surface_flip_reporting(struct dc_surface *dc_surface, ++ uint32_t controller_id); ++ ++/********* core_stream ************/ ++#include "grph_object_id.h" ++#include "encoder_interface.h" ++#include "clock_source_interface.h" ++#include "audio_interface.h" ++ ++#define DC_STREAM_TO_CORE(dc_stream) container_of( \ ++ dc_stream, struct core_stream, public) ++ ++#define PIXEL_CLOCK 27030 ++ ++struct core_stream { ++ struct dc_stream public; ++ ++ /* field internal to DC */ ++ const struct core_sink *sink; ++ ++ struct clock_source *clock_source; ++ ++ struct mem_input *mi; ++ struct input_pixel_processor *ipp; ++ struct transform *xfm; ++ struct output_pixel_processor *opp; ++ struct timing_generator *tg; ++ struct stream_encoder *stream_enc; ++ struct display_clock *dis_clk; ++ ++ struct overscan_info overscan; ++ struct scaling_ratios ratios; ++ struct rect viewport; ++ struct scaling_taps taps; ++ enum pixel_format format; ++ ++ uint8_t controller_idx; ++ ++ struct audio *audio; ++ ++ enum signal_type signal; ++ ++ /* TODO: move these members into appropriate places (work in progress)*/ ++ /* timing validation (HDMI only) */ ++ uint32_t max_tmds_clk_from_edid_in_mhz; ++ /* maximum supported deep color depth for HDMI */ ++ enum dc_color_depth max_hdmi_deep_color; ++ /* maximum supported pixel clock for HDMI */ ++ uint32_t max_hdmi_pixel_clock; ++ /* end of TODO */ ++ ++ /*TODO: AUTO merge if possible*/ ++ struct pixel_clk_params pix_clk_params; ++ struct pll_settings pll_settings; ++ ++ /*fmt*/ ++ /*TODO: AUTO new codepath in apply_context to hw to ++ * generate these bw unrelated/no fail params*/ ++ struct bit_depth_reduction_params fmt_bit_depth; ++ struct clamping_and_pixel_encoding_params clamping; ++ struct hw_info_frame info_frame; ++ struct encoder_info_frame encoder_info_frame; ++ ++ struct audio_output audio_output; ++ struct dc_context *ctx; ++}; ++ ++ ++/************ core_sink *****************/ ++ ++#define DC_SINK_TO_CORE(dc_sink) \ ++ container_of(dc_sink, struct core_sink, public) ++ ++struct core_sink { ++ /** The public, read-only (for DM) area of sink. **/ ++ struct dc_sink public; ++ /** End-of-public area. **/ ++ ++ /** The 'protected' area - read/write access, for use only inside DC **/ ++ /* not used for now */ ++ struct core_link *link; ++ struct dc_context *ctx; ++ uint32_t dongle_max_pix_clk; ++ bool converter_disable_audio; ++}; ++ ++/************ link *****************/ ++#define DC_LINK_TO_CORE(dc_link) container_of(dc_link, struct core_link, public) ++ ++struct link_init_data { ++ const struct dc *dc; ++ struct dc_context *ctx; /* TODO: remove 'dal' when DC is complete. */ ++ uint32_t connector_index; /* this will be mapped to the HPD pins */ ++ uint32_t link_index; /* this is mapped to DAL display_index ++ TODO: remove it when DC is complete. */ ++ struct adapter_service *adapter_srv; ++}; ++ ++struct link_caps { ++ /* support for Spread Spectrum(SS) */ ++ bool ss_supported; ++ /* DP link settings (laneCount, linkRate, Spread) */ ++ uint32_t lane_count; ++ uint32_t rate; ++ uint32_t spread; ++ enum dpcd_revision dpcd_revision; ++}; ++ ++struct dpcd_caps { ++ union dpcd_rev dpcd_rev; ++ union max_lane_count max_ln_count; ++ ++ /* dongle type (DP converter, CV smart dongle) */ ++ enum display_dongle_type dongle_type; ++ /* Dongle's downstream count. */ ++ union sink_count sink_count; ++ /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, ++ indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ ++ bool is_dp_hdmi_s3d_converter; ++ ++ bool allow_invalid_MSA_timing_param; ++ bool panel_mode_edp; ++ uint32_t sink_dev_id; ++ uint32_t branch_dev_id; ++ int8_t branch_dev_name[6]; ++}; ++ ++union dp_wa { ++ struct { ++ /* keep DP receiver powered up on DisplayOutput */ ++ uint32_t KEEP_RECEIVER_POWERED:1; ++ ++ /* TODO: may add other member in.*/ ++ } bits; ++ uint32_t raw; ++}; ++ ++struct core_link { ++ struct dc_link public; ++ const struct dc *dc; ++ ++ struct dc_context *ctx; /* TODO: AUTO remove 'dal' when DC is complete*/ ++ ++ uint8_t connector_index; /* this will be mapped to the HPD pins */ ++ uint8_t link_index; /* this is mapped to DAL display_index ++ TODO: #flip remove it as soon as possible. */ ++ ++ struct adapter_service *adapter_srv; ++ struct connector *connector; ++ struct link_encoder *link_enc; ++ struct ddc_service *ddc; ++ struct graphics_object_id link_id; ++ /* caps is the same as reported_link_cap. link_traing use ++ * reported_link_cap. Will clean up. TODO */ ++ struct link_settings reported_link_cap; ++ struct link_settings verified_link_cap; ++ struct link_settings max_link_setting; ++ struct link_settings cur_link_settings; ++ struct lane_settings ln_setting; ++ struct dpcd_caps dpcd_caps; ++ unsigned int dpcd_sink_count; ++ ++ enum edp_revision edp_revision; ++ union dp_wa dp_wa; ++}; ++ ++#define DC_LINK_TO_LINK(dc_link) container_of(dc_link, struct core_link, public) ++ ++struct core_link *link_create(const struct link_init_data *init_params); ++void link_destroy(struct core_link **link); ++enum dc_status core_link_enable(struct core_stream *stream); ++ ++enum dc_status core_link_disable(struct core_stream *stream); ++ ++enum dc_status dc_link_validate_mode_timing( ++ const struct core_sink *sink, ++ struct core_link *link, ++ const struct dc_crtc_timing *timing); ++ ++void core_link_resume(struct core_link *link); ++ ++/********** DAL Core*********************/ ++#include "display_clock_interface.h" ++ ++struct resource_pool { ++ struct scaler_filter * scaler_filter; ++ ++ struct mem_input *mis[MAX_PIPES]; ++ struct input_pixel_processor *ipps[MAX_PIPES]; ++ struct transform *transforms[MAX_PIPES]; ++ struct output_pixel_processor *opps[MAX_PIPES]; ++ struct timing_generator *timing_generators[MAX_STREAMS]; ++ struct stream_encoder *stream_enc[MAX_STREAMS]; ++ ++ uint8_t controller_count; ++ uint8_t stream_enc_count; ++ ++ union supported_stream_engines stream_engines; ++ ++ struct clock_source *clock_sources[MAX_CLOCK_SOURCES]; ++ uint8_t clk_src_count; ++ ++ struct audio *audios[MAX_STREAMS]; ++ uint8_t audio_count; ++ ++ struct display_clock *display_clock; ++ struct adapter_service *adapter_srv; ++ struct irq_service *irqs; ++}; ++ ++struct controller_ctx { ++ struct core_surface *surface; ++ struct core_stream *stream; ++ struct flags { ++ bool unchanged; ++ bool timing_changed; ++ } flags; ++}; ++ ++struct resource_context { ++ struct resource_pool pool; ++ struct controller_ctx controller_ctx[MAX_PIPES]; ++ union supported_stream_engines used_stream_engines; ++ bool is_stream_enc_acquired[MAX_STREAMS]; ++ bool is_audio_acquired[MAX_STREAMS]; ++ uint8_t clock_source_ref_count[MAX_CLOCK_SOURCES]; ++ }; ++ ++struct target_flags { ++ bool unchanged; ++}; ++struct validate_context { ++ struct core_target *targets[MAX_PIPES]; ++ struct target_flags target_flags[MAX_PIPES]; ++ uint8_t target_count; ++ ++ struct resource_context res_ctx; ++ ++ struct bw_calcs_input_mode_data bw_mode_data; ++ /* The output from BW and WM calculations. */ ++ struct bw_calcs_output bw_results; ++}; ++ ++ ++#endif /* _CORE_TYPES_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/dc_link_dp.h b/drivers/gpu/drm/amd/dal/dc/inc/dc_link_dp.h +new file mode 100644 +index 0000000..e3e4778 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/dc_link_dp.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_LINK_DP_H__ ++#define __DC_LINK_DP_H__ ++ ++bool dp_hbr_verify_link_cap( ++ struct core_link *link, ++ struct link_settings *known_limit_link_setting); ++ ++bool dp_validate_mode_timing( ++ struct core_link *link, ++ const struct dc_crtc_timing *timing); ++ ++void decide_link_settings( ++ struct core_stream *stream, ++ struct link_settings *link_setting); ++ ++bool perform_link_training( ++ struct core_link *link, ++ const struct link_settings *link_setting, ++ bool skip_video_pattern); ++ ++/*dp mst functions*/ ++bool is_mst_supported(struct core_link *link); ++ ++void detect_dp_sink_caps(struct core_link *link); ++ ++#endif /* __DC_LINK_DP_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/hw_sequencer.h b/drivers/gpu/drm/amd/dal/dc/inc/hw_sequencer.h +new file mode 100644 +index 0000000..2c5738f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/hw_sequencer.h +@@ -0,0 +1,170 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_HW_SEQUENCER_H__ ++#define __DC_HW_SEQUENCER_H__ ++#include "core_types.h" ++ ++struct hw_sequencer_funcs { ++ ++ enum dc_status (*apply_ctx_to_hw)( ++ const struct dc *dc, ++ struct validate_context *context); ++ ++ void (*reset_hw_ctx)(struct dc *dc, ++ struct validate_context *context, ++ uint8_t target_count); ++ ++ bool (*set_plane_config)( ++ struct core_surface *surface, ++ struct core_target *target); ++ ++ bool (*update_plane_address)( ++ const struct core_surface *surface, ++ struct core_target *target); ++ ++ bool (*enable_memory_requests)(struct timing_generator *tg); ++ ++ bool (*disable_memory_requests)(struct timing_generator *tg); ++ ++ bool (*transform_power_up)(struct transform *xfm); ++ ++ bool (*cursor_set_attributes)( ++ struct input_pixel_processor *ipp, ++ const struct dc_cursor_attributes *attributes); ++ ++ bool (*cursor_set_position)( ++ struct input_pixel_processor *ipp, ++ const struct dc_cursor_position *position); ++ ++ bool (*set_gamma_ramp)( ++ struct input_pixel_processor *ipp, ++ struct output_pixel_processor *opp, ++ const struct gamma_ramp *ramp, ++ const struct gamma_parameters *params); ++ ++ void (*power_down)(struct validate_context *context); ++ ++ void (*enable_accelerated_mode)(struct validate_context *context); ++ ++ void (*get_crtc_positions)( ++ struct timing_generator *tg, ++ int32_t *h_position, ++ int32_t *v_position); ++ ++ uint32_t (*get_vblank_counter)(struct timing_generator *tg); ++ ++ void (*enable_timing_synchronization)( ++ struct dc_context *dc_ctx, ++ uint32_t timing_generator_num, ++ struct timing_generator *tgs[]); ++ ++ void (*disable_vga)(struct timing_generator *tg); ++ ++ ++ ++ /* link encoder sequences */ ++ struct link_encoder *(*encoder_create)(const struct encoder_init_data *init); ++ ++ void (*encoder_destroy)(struct link_encoder **enc); ++ ++ enum encoder_result (*encoder_power_up)( ++ struct link_encoder *enc); ++ ++ enum encoder_result (*encoder_enable_output)( ++ struct link_encoder *enc, ++ const struct link_settings *link_settings, ++ enum engine_id engine, ++ enum clock_source_id clock_source, ++ enum signal_type signal, ++ enum dc_color_depth color_depth, ++ uint32_t pixel_clock); ++ ++ enum encoder_result (*encoder_disable_output)( ++ struct link_encoder *enc, ++ enum signal_type signal); ++ ++ void (*encoder_set_dp_phy_pattern)( ++ struct link_encoder *enc, ++ const struct encoder_set_dp_phy_pattern_param *param); ++ ++ enum encoder_result (*encoder_dp_set_lane_settings)( ++ struct link_encoder *enc, ++ const struct link_training_settings *link_settings); ++ ++ /* backlight control */ ++ void (*encoder_set_lcd_backlight_level)(struct link_encoder *enc, ++ uint32_t level); ++ ++ ++ /* power management */ ++ void (*clock_gating_power_up)( ++ struct dc_context *ctx, ++ bool enable); ++ ++ void (*enable_display_pipe_clock_gating)( ++ struct dc_context *ctx, ++ bool clock_gating); ++ ++ bool (*enable_display_power_gating)( ++ struct dc_context *ctx, ++ uint8_t controller_id, ++ struct bios_parser *bp, ++ enum pipe_gating_control power_gating); ++ ++ void (*set_afmt_memory_power_state)( ++ const struct dc_context *ctx, ++ enum engine_id id, ++ bool enable); ++ ++ /* resource management and validation*/ ++ bool (*construct_resource_pool)( ++ struct adapter_service *adapter_serv, ++ struct dc *dc, ++ struct resource_pool *pool); ++ ++ void (*destruct_resource_pool)(struct resource_pool *pool); ++ ++ enum dc_status (*validate_with_context)( ++ const struct dc *dc, ++ const struct dc_validation_set set[], ++ uint8_t set_count, ++ struct validate_context *context); ++ ++ enum dc_status (*validate_bandwidth)( ++ const struct dc *dc, ++ struct validate_context *context); ++ void (*program_bw)( ++ struct dc *dc, ++ struct validate_context *context); ++ ++}; ++ ++bool dc_construct_hw_sequencer( ++ struct adapter_service *adapter_serv, ++ struct dc *dc); ++ ++ ++#endif /* __DC_HW_SEQUENCER_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/ipp.h b/drivers/gpu/drm/amd/dal/dc/inc/ipp.h +new file mode 100644 +index 0000000..602b4cb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/ipp.h +@@ -0,0 +1,66 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IPP_H__ ++#define __DAL_IPP_H__ ++ ++#include "include/plane_types.h" ++#include "include/grph_object_id.h" ++#include "include/grph_csc_types.h" ++#include "include/video_csc_types.h" ++#include "include/hw_sequencer_types.h" ++ ++ ++#define MAXTRIX_COEFFICIENTS_NUMBER 12 ++#define MAXTRIX_COEFFICIENTS_WRAP_NUMBER (MAXTRIX_COEFFICIENTS_NUMBER + 4) ++#define MAX_OVL_MATRIX_COUNT 12 ++ ++/* IPP RELATED */ ++struct input_pixel_processor { ++ struct dc_context *ctx; ++ uint32_t inst; ++}; ++ ++enum wide_gamut_degamma_mode { ++ /* 00 - BITS1:0 Bypass */ ++ WIDE_GAMUT_DEGAMMA_MODE_GRAPHICS_BYPASS, ++ /* 0x1 - PWL gamma ROM A */ ++ WIDE_GAMUT_DEGAMMA_MODE_GRAPHICS_PWL_ROM_A, ++ /* 0x2 - PWL gamma ROM B */ ++ WIDE_GAMUT_DEGAMMA_MODE_GRAPHICS_PWL_ROM_B, ++ /* 00 - BITS5:4 Bypass */ ++ WIDE_GAMUT_DEGAMMA_MODE_OVL_BYPASS, ++ /* 0x1 - PWL gamma ROM A */ ++ WIDE_GAMUT_DEGAMMA_MODE_OVL_PWL_ROM_A, ++ /* 0x2 - PWL gamma ROM B */ ++ WIDE_GAMUT_DEGAMMA_MODE_OVL_PWL_ROM_B, ++}; ++ ++struct dcp_video_matrix { ++ enum ovl_color_space color_space; ++ int32_t value[MAXTRIX_COEFFICIENTS_NUMBER]; ++}; ++ ++#endif /* __DAL_IPP_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/link_hwss.h b/drivers/gpu/drm/amd/dal/dc/inc/link_hwss.h +new file mode 100644 +index 0000000..7110357 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/link_hwss.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_LINK_HWSS_H__ ++#define __DC_LINK_HWSS_H__ ++ ++#include "inc/core_status.h" ++ ++enum dc_status core_link_read_dpcd( ++ struct core_link* link, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size); ++ ++enum dc_status core_link_write_dpcd( ++ struct core_link* link, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t size); ++ ++enum dc_status dp_enable_link_phy( ++ struct core_link *link, ++ enum signal_type signal, ++ enum engine_id engine, ++ const struct link_settings *link_settings); ++ ++void dp_receiver_power_ctrl(struct core_link *link, bool on); ++ ++void dp_disable_link_phy(struct core_link *link, enum signal_type signal); ++ ++bool dp_set_hw_training_pattern( ++ struct core_link *link, ++ enum hw_dp_training_pattern pattern); ++ ++bool dp_set_hw_lane_settings( ++ struct core_link *link, ++ const struct link_training_settings *link_settings); ++ ++void dp_set_hw_test_pattern( ++ struct core_link *link, ++ enum dp_test_pattern test_pattern); ++ ++enum dp_panel_mode dp_get_panel_mode(struct core_link *link); ++ ++#endif /* __DC_LINK_HWSS_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/mem_input.h b/drivers/gpu/drm/amd/dal/dc/inc/mem_input.h +new file mode 100644 +index 0000000..458e7b5 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/mem_input.h +@@ -0,0 +1,55 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_MEM_INPUT_H__ ++#define __DAL_MEM_INPUT_H__ ++ ++#include "include/plane_types.h" ++#include "include/grph_object_id.h" ++#include "dc.h" ++ ++struct mem_input { ++ struct dc_context *ctx; ++ uint32_t inst; ++}; ++ ++enum stutter_mode_type { ++ STUTTER_MODE_LEGACY = 0X00000001, ++ STUTTER_MODE_ENHANCED = 0X00000002, ++ STUTTER_MODE_FID_NBP_STATE = 0X00000004, ++ STUTTER_MODE_WATERMARK_NBP_STATE = 0X00000008, ++ STUTTER_MODE_SINGLE_DISPLAY_MODEL = 0X00000010, ++ STUTTER_MODE_MIXED_DISPLAY_MODEL = 0X00000020, ++ STUTTER_MODE_DUAL_DMIF_BUFFER = 0X00000040, ++ STUTTER_MODE_NO_DMIF_BUFFER_ALLOCATION = 0X00000080, ++ STUTTER_MODE_NO_ADVANCED_REQUEST = 0X00000100, ++ STUTTER_MODE_NO_LB_RESET = 0X00000200, ++ STUTTER_MODE_DISABLED = 0X00000400, ++ STUTTER_MODE_AGGRESSIVE_MARKS = 0X00000800, ++ STUTTER_MODE_URGENCY = 0X00001000, ++ STUTTER_MODE_QUAD_DMIF_BUFFER = 0X00002000, ++ STUTTER_MODE_NOT_USED = 0X00008000 ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/opp.h b/drivers/gpu/drm/amd/dal/dc/inc/opp.h +new file mode 100644 +index 0000000..3293e3b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/opp.h +@@ -0,0 +1,206 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_OPP_H__ ++#define __DAL_OPP_H__ ++ ++#include "dc_temp.h" ++#include "grph_object_id.h" ++#include "grph_csc_types.h" ++ ++struct fixed31_32; ++ ++/* TODO: Need cleanup */ ++ ++enum wide_gamut_regamma_mode { ++ /* 0x0 - BITS2:0 Bypass */ ++ WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS, ++ /* 0x1 - Fixed curve sRGB 2.4 */ ++ WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24, ++ /* 0x2 - Fixed curve xvYCC 2.22 */ ++ WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22, ++ /* 0x3 - Programmable control A */ ++ WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A, ++ /* 0x4 - Programmable control B */ ++ WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B, ++ /* 0x0 - BITS6:4 Bypass */ ++ WIDE_GAMUT_REGAMMA_MODE_OVL_BYPASS, ++ /* 0x1 - Fixed curve sRGB 2.4 */ ++ WIDE_GAMUT_REGAMMA_MODE_OVL_SRGB24, ++ /* 0x2 - Fixed curve xvYCC 2.22 */ ++ WIDE_GAMUT_REGAMMA_MODE_OVL_XYYCC22, ++ /* 0x3 - Programmable control A */ ++ WIDE_GAMUT_REGAMMA_MODE_OVL_MATRIX_A, ++ /* 0x4 - Programmable control B */ ++ WIDE_GAMUT_REGAMMA_MODE_OVL_MATRIX_B ++}; ++ ++struct pwl_result_data { ++ struct fixed31_32 red; ++ struct fixed31_32 green; ++ struct fixed31_32 blue; ++ ++ struct fixed31_32 delta_red; ++ struct fixed31_32 delta_green; ++ struct fixed31_32 delta_blue; ++ ++ uint32_t red_reg; ++ uint32_t green_reg; ++ uint32_t blue_reg; ++ ++ uint32_t delta_red_reg; ++ uint32_t delta_green_reg; ++ uint32_t delta_blue_reg; ++}; ++ ++struct gamma_pixel { ++ struct fixed31_32 r; ++ struct fixed31_32 g; ++ struct fixed31_32 b; ++}; ++ ++struct gamma_curve { ++ uint32_t offset; ++ uint32_t segments_num; ++}; ++ ++struct curve_points { ++ struct fixed31_32 x; ++ struct fixed31_32 y; ++ struct fixed31_32 offset; ++ struct fixed31_32 slope; ++ ++ uint32_t custom_float_x; ++ uint32_t custom_float_y; ++ uint32_t custom_float_offset; ++ uint32_t custom_float_slope; ++}; ++ ++enum channel_name { ++ CHANNEL_NAME_RED, ++ CHANNEL_NAME_GREEN, ++ CHANNEL_NAME_BLUE ++}; ++ ++struct custom_float_format { ++ uint32_t mantissa_bits; ++ uint32_t exponenta_bits; ++ bool sign; ++}; ++ ++struct custom_float_value { ++ uint32_t mantissa; ++ uint32_t exponenta; ++ uint32_t value; ++ bool negative; ++}; ++ ++struct hw_x_point { ++ uint32_t custom_float_x; ++ uint32_t custom_float_x_adjusted; ++ struct fixed31_32 x; ++ struct fixed31_32 adjusted_x; ++ struct fixed31_32 regamma_y_red; ++ struct fixed31_32 regamma_y_green; ++ struct fixed31_32 regamma_y_blue; ++ ++}; ++ ++struct pwl_float_data_ex { ++ struct fixed31_32 r; ++ struct fixed31_32 g; ++ struct fixed31_32 b; ++ struct fixed31_32 delta_r; ++ struct fixed31_32 delta_g; ++ struct fixed31_32 delta_b; ++}; ++ ++enum hw_point_position { ++ /* hw point sits between left and right sw points */ ++ HW_POINT_POSITION_MIDDLE, ++ /* hw point lays left from left (smaller) sw point */ ++ HW_POINT_POSITION_LEFT, ++ /* hw point lays stays from right (bigger) sw point */ ++ HW_POINT_POSITION_RIGHT ++}; ++ ++struct gamma_point { ++ int32_t left_index; ++ int32_t right_index; ++ enum hw_point_position pos; ++ struct fixed31_32 coeff; ++}; ++ ++struct pixel_gamma_point { ++ struct gamma_point r; ++ struct gamma_point g; ++ struct gamma_point b; ++}; ++ ++struct gamma_coefficients { ++ struct fixed31_32 a0[3]; ++ struct fixed31_32 a1[3]; ++ struct fixed31_32 a2[3]; ++ struct fixed31_32 a3[3]; ++ struct fixed31_32 user_gamma[3]; ++ struct fixed31_32 user_contrast; ++ struct fixed31_32 user_brightness; ++}; ++ ++struct csc_adjustments { ++ struct fixed31_32 contrast; ++ struct fixed31_32 saturation; ++ struct fixed31_32 brightness; ++ struct fixed31_32 hue; ++}; ++ ++struct pwl_float_data { ++ struct fixed31_32 r; ++ struct fixed31_32 g; ++ struct fixed31_32 b; ++}; ++ ++ ++/* TODO: Use when we redefine the OPP interface */ ++enum opp_regamma { ++ OPP_REGAMMA_BYPASS = 0, ++ OPP_REGAMMA_SRGB, ++ OPP_REGAMMA_3_6, ++ OPP_REGAMMA_PQ, ++ OPP_REGAMMA_PQ_INTERIM, ++}; ++ ++struct output_pixel_processor { ++ struct dc_context *ctx; ++ uint32_t inst; ++}; ++ ++enum fmt_stereo_action { ++ FMT_STEREO_ACTION_ENABLE = 0, ++ FMT_STEREO_ACTION_DISABLE, ++ FMT_STEREO_ACTION_UPDATE_POLARITY ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/resource.h b/drivers/gpu/drm/amd/dal/dc/inc/resource.h +new file mode 100644 +index 0000000..0e0ba47 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/resource.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ */ ++ ++#ifndef DRIVERS_GPU_DRM_AMD_DAL_DEV_DC_INC_RESOURCE_H_ ++#define DRIVERS_GPU_DRM_AMD_DAL_DEV_DC_INC_RESOURCE_H_ ++ ++#include "core_types.h" ++#include "core_status.h" ++#include "core_dc.h" ++ ++void build_scaling_params( ++ const struct dc_surface *surface, ++ struct core_stream *stream); ++ ++void build_scaling_params_for_context( ++ const struct dc *dc, ++ struct validate_context *context); ++ ++void unreference_clock_source( ++ struct resource_context *res_ctx, ++ struct clock_source *clock_source); ++ ++void reference_clock_source( ++ struct resource_context *res_ctx, ++ struct clock_source *clock_source); ++ ++bool is_same_timing( ++ const struct dc_crtc_timing *timing1, ++ const struct dc_crtc_timing *timing2); ++ ++struct clock_source *find_used_clk_src_for_sharing( ++ struct validate_context *context, ++ struct core_stream *stream); ++ ++bool logical_attach_surfaces_to_target( ++ struct dc_surface *surfaces[], ++ uint8_t surface_count, ++ struct dc_target *dc_target); ++ ++#endif /* DRIVERS_GPU_DRM_AMD_DAL_DEV_DC_INC_RESOURCE_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/transform.h b/drivers/gpu/drm/amd/dal/dc/inc/transform.h +new file mode 100644 +index 0000000..8e111ce +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/inc/transform.h +@@ -0,0 +1,81 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_TRANSFORM_H__ ++#define __DAL_TRANSFORM_H__ ++ ++#include "include/scaler_types.h" ++#include "calcs/scaler_filter.h" ++#include "grph_object_id.h" ++ ++enum scaling_type { ++ SCALING_TYPE_NO_SCALING = 0, ++ SCALING_TYPE_UPSCALING, ++ SCALING_TYPE_DOWNSCALING ++}; ++ ++struct transform { ++ struct dc_context *ctx; ++ uint32_t inst; ++ struct scaler_filter *filter; ++}; ++ ++struct scaler_taps_and_ratio { ++ uint32_t h_tap; ++ uint32_t v_tap; ++ uint32_t lo_ratio; ++ uint32_t hi_ratio; ++}; ++ ++struct scaler_taps { ++ uint32_t h_tap; ++ uint32_t v_tap; ++}; ++ ++struct sclv_ratios_inits { ++ uint32_t chroma_enable; ++ uint32_t h_int_scale_ratio_luma; ++ uint32_t h_int_scale_ratio_chroma; ++ uint32_t v_int_scale_ratio_luma; ++ uint32_t v_int_scale_ratio_chroma; ++ struct init_int_and_frac h_init_luma; ++ struct init_int_and_frac h_init_chroma; ++ struct init_int_and_frac v_init_luma; ++ struct init_int_and_frac v_init_chroma; ++ struct init_int_and_frac h_init_lumabottom; ++ struct init_int_and_frac h_init_chromabottom; ++ struct init_int_and_frac v_init_lumabottom; ++ struct init_int_and_frac v_init_chromabottom; ++}; ++ ++enum lb_pixel_depth { ++ /* do not change the values because it is used as bit vector */ ++ LB_PIXEL_DEPTH_18BPP = 1, ++ LB_PIXEL_DEPTH_24BPP = 2, ++ LB_PIXEL_DEPTH_30BPP = 4, ++ LB_PIXEL_DEPTH_36BPP = 8 ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/irq/Makefile b/drivers/gpu/drm/amd/dal/dc/irq/Makefile +new file mode 100644 +index 0000000..f1c5faf +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/irq/Makefile +@@ -0,0 +1,21 @@ ++# ++# Makefile for the 'audio' sub-component of DAL. ++# It provides the control and status of HW adapter resources, ++# that are global for the ASIC and sharable between pipes. ++ ++IRQ = irq_service.o ++ ++AMD_DAL_IRQ = $(addprefix $(AMDDALPATH)/dc/irq/,$(IRQ)) ++ ++AMD_DAL_FILES += $(AMD_DAL_IRQ) ++ ++############################################################################### ++# DCE 11x ++############################################################################### ++ifdef CONFIG_DRM_AMD_DAL_DCE11_0 ++IRQ_DCE11 = irq_service_dce110.o ++ ++AMD_DAL_IRQ_DCE11 = $(addprefix $(AMDDALPATH)/dc/irq/dce110/,$(IRQ_DCE11)) ++ ++AMD_DAL_FILES += $(AMD_DAL_IRQ_DCE11) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.c b/drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.c +new file mode 100644 +index 0000000..2a4f14c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.c +@@ -0,0 +1,389 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/logger_interface.h" ++ ++#include "irq_service_dce110.h" ++ ++#include "dce/dce_11_0_d.h" ++#include "dce/dce_11_0_sh_mask.h" ++#include "ivsrcid/ivsrcid_vislands30.h" ++ ++static bool hpd_ack( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info) ++{ ++ uint32_t addr = info->status_reg; ++ uint32_t value = dal_read_reg(irq_service->ctx, addr); ++ uint32_t current_status = ++ get_reg_field_value( ++ value, ++ DC_HPD_INT_STATUS, ++ DC_HPD_SENSE_DELAYED); ++ ++ dal_irq_service_ack_generic(irq_service, info); ++ ++ value = dal_read_reg(irq_service->ctx, info->enable_reg); ++ ++ set_reg_field_value( ++ value, ++ current_status ? 0 : 1, ++ DC_HPD_INT_CONTROL, ++ DC_HPD_INT_POLARITY); ++ ++ dal_write_reg(irq_service->ctx, info->enable_reg, value); ++ ++ return true; ++} ++ ++static const struct irq_source_info_funcs hpd_irq_info_funcs = { ++ .set = NULL, ++ .ack = hpd_ack ++}; ++ ++static const struct irq_source_info_funcs hpd_rx_irq_info_funcs = { ++ .set = NULL, ++ .ack = NULL ++}; ++ ++static const struct irq_source_info_funcs pflip_irq_info_funcs = { ++ .set = NULL, ++ .ack = NULL ++}; ++ ++static const struct irq_source_info_funcs vblank_irq_info_funcs = { ++ .set = NULL, ++ .ack = NULL ++}; ++ ++#define hpd_int_entry(reg_num)\ ++ [DC_IRQ_SOURCE_HPD1 + reg_num] = {\ ++ .enable_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ ++ .enable_mask = DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK,\ ++ .enable_value = {\ ++ DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK,\ ++ ~DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK\ ++ },\ ++ .ack_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ ++ .ack_mask = DC_HPD_INT_CONTROL__DC_HPD_INT_ACK_MASK,\ ++ .ack_value = DC_HPD_INT_CONTROL__DC_HPD_INT_ACK_MASK,\ ++ .status_reg = mmHPD ## reg_num ## _DC_HPD_INT_STATUS,\ ++ .funcs = &hpd_irq_info_funcs\ ++ } ++ ++#define hpd_rx_int_entry(reg_num)\ ++ [DC_IRQ_SOURCE_HPD1RX + reg_num] = {\ ++ .enable_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ ++ .enable_mask = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK,\ ++ .enable_value = {\ ++ DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK,\ ++ ~DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK },\ ++ .ack_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ ++ .ack_mask = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_ACK_MASK,\ ++ .ack_value = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_ACK_MASK,\ ++ .status_reg = mmHPD ## reg_num ## _DC_HPD_INT_STATUS,\ ++ .funcs = &hpd_rx_irq_info_funcs\ ++ } ++#define pflip_int_entry(reg_num)\ ++ [DC_IRQ_SOURCE_PFLIP1 + reg_num] = {\ ++ .enable_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_CONTROL,\ ++ .enable_mask =\ ++ GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ ++ .enable_value = {\ ++ GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ ++ ~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ ++ .ack_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_STATUS,\ ++ .ack_mask = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ ++ .ack_value = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ ++ .status_reg = mmDCP ## reg_num ##_GRPH_INTERRUPT_STATUS,\ ++ .funcs = &pflip_irq_info_funcs\ ++ } ++ ++#define vsync_int_entry(reg_num) \ ++ [DC_IRQ_SOURCE_CRTC ## reg_num ## VSYNC] = dummy_irq_entry() ++ ++#define vupdate_int_entry(reg_num)\ ++ [DC_IRQ_SOURCE_VUPDATE1 + reg_num] = {\ ++ .enable_reg = mmCRTC ## reg_num ## _CRTC_INTERRUPT_CONTROL,\ ++ .enable_mask =\ ++ CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ ++ .enable_value = {\ ++ CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ ++ ~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ ++ .ack_reg = mmCRTC ## reg_num ## _CRTC_V_UPDATE_INT_STATUS,\ ++ .ack_mask =\ ++ CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ ++ .ack_value =\ ++ CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ ++ .funcs = &vblank_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 azalia_int_entry(reg_num) \ ++ [DC_IRQ_SOURCE_AZALIA ## 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 bool dummy_set( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info, ++ bool enable) ++{ ++ dal_logger_write( ++ irq_service->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_IRQ_SERVICE, ++ "%s: called for non-implemented irq source\n", ++ __func__); ++ return false; ++} ++ ++static bool dummy_ack( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info) ++{ ++ dal_logger_write( ++ irq_service->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_IRQ_SERVICE, ++ "%s: called for non-implemented irq source\n", ++ __func__); ++ return false; ++} ++ ++static const struct irq_source_info_funcs dummy_irq_info_funcs = { ++ .set = dummy_set, ++ .ack = dummy_ack ++}; ++ ++static const struct irq_source_info ++irq_source_info_dce110[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_int_entry(4), ++ hpd_int_entry(5), ++ hpd_rx_int_entry(0), ++ hpd_rx_int_entry(1), ++ hpd_rx_int_entry(2), ++ hpd_rx_int_entry(3), ++ hpd_rx_int_entry(4), ++ hpd_rx_int_entry(5), ++ i2c_int_entry(1), ++ i2c_int_entry(2), ++ i2c_int_entry(3), ++ i2c_int_entry(4), ++ i2c_int_entry(5), ++ i2c_int_entry(6), ++ azalia_int_entry(0), ++ azalia_int_entry(1), ++ azalia_int_entry(2), ++ azalia_int_entry(3), ++ azalia_int_entry(4), ++ azalia_int_entry(5), ++ 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), ++ vsync_int_entry(1), ++ vsync_int_entry(2), ++ vsync_int_entry(3), ++ vsync_int_entry(3), ++ vsync_int_entry(4), ++ vsync_int_entry(5), ++ [DC_IRQ_SOURCE_TIMER] = dummy_irq_entry(), ++ pflip_int_entry(0), ++ pflip_int_entry(1), ++ pflip_int_entry(2), ++ pflip_int_entry(3), ++ pflip_int_entry(4), ++ pflip_int_entry(5), ++ [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(), ++ vupdate_int_entry(0), ++ vupdate_int_entry(1), ++ vupdate_int_entry(2), ++ vupdate_int_entry(3), ++ vupdate_int_entry(4), ++ vupdate_int_entry(5), ++}; ++ ++static enum dc_irq_source to_dal_irq_source( ++ struct irq_service *irq_service, ++ uint32_t src_id, ++ uint32_t ext_id) ++{ ++ switch (src_id) { ++ case VISLANDS30_IV_SRCID_D1_V_UPDATE_INT: ++ return DC_IRQ_SOURCE_VUPDATE1; ++ case VISLANDS30_IV_SRCID_D2_V_UPDATE_INT: ++ return DC_IRQ_SOURCE_VUPDATE2; ++ case VISLANDS30_IV_SRCID_D3_V_UPDATE_INT: ++ return DC_IRQ_SOURCE_VUPDATE3; ++ case VISLANDS30_IV_SRCID_D4_V_UPDATE_INT: ++ return DC_IRQ_SOURCE_VUPDATE4; ++ case VISLANDS30_IV_SRCID_D5_V_UPDATE_INT: ++ return DC_IRQ_SOURCE_VUPDATE5; ++ case VISLANDS30_IV_SRCID_D6_V_UPDATE_INT: ++ return DC_IRQ_SOURCE_VUPDATE6; ++ case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP: ++ return DC_IRQ_SOURCE_PFLIP1; ++ case VISLANDS30_IV_SRCID_D2_GRPH_PFLIP: ++ return DC_IRQ_SOURCE_PFLIP2; ++ case VISLANDS30_IV_SRCID_D3_GRPH_PFLIP: ++ return DC_IRQ_SOURCE_PFLIP3; ++ case VISLANDS30_IV_SRCID_D4_GRPH_PFLIP: ++ return DC_IRQ_SOURCE_PFLIP4; ++ case VISLANDS30_IV_SRCID_D5_GRPH_PFLIP: ++ return DC_IRQ_SOURCE_PFLIP5; ++ case VISLANDS30_IV_SRCID_D6_GRPH_PFLIP: ++ return DC_IRQ_SOURCE_PFLIP6; ++ ++ case VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A: ++ /* generic src_id for all HPD and HPDRX interrupts */ ++ switch (ext_id) { ++ case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_A: ++ return DC_IRQ_SOURCE_HPD1; ++ case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_B: ++ return DC_IRQ_SOURCE_HPD2; ++ case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_C: ++ return DC_IRQ_SOURCE_HPD3; ++ case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_D: ++ return DC_IRQ_SOURCE_HPD4; ++ case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_E: ++ return DC_IRQ_SOURCE_HPD5; ++ case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_F: ++ return DC_IRQ_SOURCE_HPD6; ++ case VISLANDS30_IV_EXTID_HPD_RX_A: ++ return DC_IRQ_SOURCE_HPD1RX; ++ case VISLANDS30_IV_EXTID_HPD_RX_B: ++ return DC_IRQ_SOURCE_HPD2RX; ++ case VISLANDS30_IV_EXTID_HPD_RX_C: ++ return DC_IRQ_SOURCE_HPD3RX; ++ case VISLANDS30_IV_EXTID_HPD_RX_D: ++ return DC_IRQ_SOURCE_HPD4RX; ++ case VISLANDS30_IV_EXTID_HPD_RX_E: ++ return DC_IRQ_SOURCE_HPD5RX; ++ case VISLANDS30_IV_EXTID_HPD_RX_F: ++ return DC_IRQ_SOURCE_HPD6RX; ++ default: ++ return DC_IRQ_SOURCE_INVALID; ++ } ++ break; ++ ++ default: ++ return DC_IRQ_SOURCE_INVALID; ++ } ++} ++ ++static const struct irq_service_funcs irq_service_funcs_dce110 = { ++ .to_dal_irq_source = to_dal_irq_source ++}; ++ ++bool construct( ++ struct irq_service *irq_service, ++ struct irq_service_init_data *init_data) ++{ ++ if (!dal_irq_service_construct(irq_service, init_data)) ++ return false; ++ ++ irq_service->info = irq_source_info_dce110; ++ irq_service->funcs = &irq_service_funcs_dce110; ++ ++ return true; ++} ++ ++struct irq_service *dal_irq_service_dce110_create( ++ struct irq_service_init_data *init_data) ++{ ++ struct irq_service *irq_service = dc_service_alloc(init_data->ctx, sizeof(*irq_service)); ++ ++ if (!irq_service) ++ return NULL; ++ ++ if (construct(irq_service, init_data)) ++ return irq_service; ++ ++ dc_service_free(init_data->ctx, irq_service); ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.h b/drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.h +new file mode 100644 +index 0000000..d6c28e9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/irq/dce110/irq_service_dce110.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IRQ_SERVICE_DCE110_H__ ++#define __DAL_IRQ_SERVICE_DCE110_H__ ++ ++#include "../irq_service.h" ++ ++struct irq_service *dal_irq_service_dce110_create( ++ struct irq_service_init_data *init_data); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c +new file mode 100644 +index 0000000..0c7429c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c +@@ -0,0 +1,173 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dal_services.h" ++ ++#include "include/irq_service_interface.h" ++#include "include/logger_interface.h" ++ ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++#include "dce110/irq_service_dce110.h" ++#endif ++ ++#include "irq_service.h" ++ ++bool dal_irq_service_construct( ++ struct irq_service *irq_service, ++ struct irq_service_init_data *init_data) ++{ ++ if (!init_data || !init_data->ctx) ++ return false; ++ ++ irq_service->ctx = init_data->ctx; ++ return true; ++} ++ ++struct irq_service *dal_irq_service_create( ++ enum dce_version version, ++ struct irq_service_init_data *init_data) ++{ ++ switch (version) { ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++ return dal_irq_service_dce110_create(init_data); ++#endif ++ default: ++ return NULL; ++ } ++} ++ ++void dal_irq_service_destroy(struct irq_service **irq_service) ++{ ++ if (!irq_service || !*irq_service) { ++ BREAK_TO_DEBUGGER(); ++ return; ++ } ++ ++ dc_service_free((*irq_service)->ctx, *irq_service); ++ ++ *irq_service = NULL; ++} ++ ++const struct irq_source_info *find_irq_source_info( ++ struct irq_service *irq_service, ++ enum dc_irq_source source) ++{ ++ if (source > DAL_IRQ_SOURCES_NUMBER || source < DC_IRQ_SOURCE_INVALID) ++ return NULL; ++ ++ return &irq_service->info[source]; ++} ++ ++void dal_irq_service_set_generic( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info, ++ bool enable) ++{ ++ uint32_t addr = info->enable_reg; ++ uint32_t value = dal_read_reg(irq_service->ctx, addr); ++ ++ value = (value & ~info->enable_mask) | ++ (info->enable_value[enable ? 0 : 1] & info->enable_mask); ++ dal_write_reg(irq_service->ctx, addr, value); ++} ++ ++bool dal_irq_service_set( ++ struct irq_service *irq_service, ++ enum dc_irq_source source, ++ bool enable) ++{ ++ const struct irq_source_info *info = ++ find_irq_source_info(irq_service, source); ++ ++ if (!info) { ++ dal_logger_write( ++ irq_service->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_IRQ_SERVICE, ++ "%s: cannot find irq info table entry for %d\n", ++ __func__, ++ source); ++ return false; ++ } ++ ++ dal_irq_service_ack(irq_service, source); ++ ++ if (info->funcs->set) ++ return info->funcs->set(irq_service, info, enable); ++ ++ dal_irq_service_set_generic(irq_service, info, enable); ++ ++ return true; ++} ++ ++void dal_irq_service_ack_generic( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info) ++{ ++ uint32_t addr = info->ack_reg; ++ uint32_t value = dal_read_reg(irq_service->ctx, addr); ++ ++ value = (value & ~info->ack_mask) | ++ (info->ack_value & info->ack_mask); ++ dal_write_reg(irq_service->ctx, addr, value); ++} ++ ++bool dal_irq_service_ack( ++ struct irq_service *irq_service, ++ enum dc_irq_source source) ++{ ++ const struct irq_source_info *info = ++ find_irq_source_info(irq_service, source); ++ ++ if (!info) { ++ dal_logger_write( ++ irq_service->ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_IRQ_SERVICE, ++ "%s: cannot find irq info table entry for %d\n", ++ __func__, ++ source); ++ return false; ++ } ++ ++ if (info->funcs->ack) ++ return info->funcs->ack(irq_service, info); ++ ++ dal_irq_service_ack_generic(irq_service, info); ++ ++ return true; ++} ++ ++enum dc_irq_source dal_irq_service_to_irq_source( ++ struct irq_service *irq_service, ++ uint32_t src_id, ++ uint32_t ext_id) ++{ ++ return irq_service->funcs->to_dal_irq_source( ++ irq_service, ++ src_id, ++ ext_id); ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/irq/irq_service.h b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.h +new file mode 100644 +index 0000000..a2a2d69 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.h +@@ -0,0 +1,85 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IRQ_SERVICE_H__ ++#define __DAL_IRQ_SERVICE_H__ ++ ++#include "include/irq_service_interface.h" ++ ++#include "irq_types.h" ++ ++struct irq_service; ++struct irq_source_info; ++ ++struct irq_source_info_funcs { ++ bool (*set)( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info, ++ bool enable); ++ bool (*ack)( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info); ++}; ++ ++struct irq_source_info { ++ uint32_t src_id; ++ uint32_t ext_id; ++ uint32_t enable_reg; ++ uint32_t enable_mask; ++ uint32_t enable_value[2]; ++ uint32_t ack_reg; ++ uint32_t ack_mask; ++ uint32_t ack_value; ++ uint32_t status_reg; ++ const struct irq_source_info_funcs *funcs; ++}; ++ ++struct irq_service_funcs { ++ enum dc_irq_source (*to_dal_irq_source)( ++ struct irq_service *irq_service, ++ uint32_t src_id, ++ uint32_t ext_id); ++}; ++ ++struct irq_service { ++ struct dc_context *ctx; ++ const struct irq_source_info *info; ++ const struct irq_service_funcs *funcs; ++}; ++ ++bool dal_irq_service_construct( ++ struct irq_service *irq_service, ++ struct irq_service_init_data *init_data); ++ ++void dal_irq_service_ack_generic( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info); ++ ++void dal_irq_service_set_generic( ++ struct irq_service *irq_service, ++ const struct irq_source_info *info, ++ bool enable); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/irq_types.h b/drivers/gpu/drm/amd/dal/dc/irq_types.h +new file mode 100644 +index 0000000..051a1f6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/irq_types.h +@@ -0,0 +1,199 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IRQ_TYPES_H__ ++#define __DAL_IRQ_TYPES_H__ ++ ++struct dc_context; ++ ++typedef void (*interrupt_handler)(void *); ++ ++typedef void *irq_handler_idx; ++#define DAL_INVALID_IRQ_HANDLER_IDX NULL ++ ++ ++/* The order of the IRQ sources is important and MUST match the one's ++of base driver */ ++enum dc_irq_source { ++ /* Use as mask to specify invalid irq source */ ++ DC_IRQ_SOURCE_INVALID = 0, ++ ++ DC_IRQ_SOURCE_HPD1, ++ DC_IRQ_SOURCE_HPD2, ++ DC_IRQ_SOURCE_HPD3, ++ DC_IRQ_SOURCE_HPD4, ++ DC_IRQ_SOURCE_HPD5, ++ DC_IRQ_SOURCE_HPD6, ++ ++ DC_IRQ_SOURCE_HPD1RX, ++ DC_IRQ_SOURCE_HPD2RX, ++ DC_IRQ_SOURCE_HPD3RX, ++ DC_IRQ_SOURCE_HPD4RX, ++ DC_IRQ_SOURCE_HPD5RX, ++ DC_IRQ_SOURCE_HPD6RX, ++ ++ DC_IRQ_SOURCE_I2C_DDC1, ++ DC_IRQ_SOURCE_I2C_DDC2, ++ DC_IRQ_SOURCE_I2C_DDC3, ++ DC_IRQ_SOURCE_I2C_DDC4, ++ DC_IRQ_SOURCE_I2C_DDC5, ++ DC_IRQ_SOURCE_I2C_DDC6, ++ ++ DC_IRQ_SOURCE_AZALIA0, ++ DC_IRQ_SOURCE_AZALIA1, ++ DC_IRQ_SOURCE_AZALIA2, ++ DC_IRQ_SOURCE_AZALIA3, ++ DC_IRQ_SOURCE_AZALIA4, ++ DC_IRQ_SOURCE_AZALIA5, ++ ++ DC_IRQ_SOURCE_DPSINK1, ++ DC_IRQ_SOURCE_DPSINK2, ++ DC_IRQ_SOURCE_DPSINK3, ++ DC_IRQ_SOURCE_DPSINK4, ++ DC_IRQ_SOURCE_DPSINK5, ++ DC_IRQ_SOURCE_DPSINK6, ++ ++ DC_IRQ_SOURCE_CRTC1VSYNC, ++ DC_IRQ_SOURCE_CRTC2VSYNC, ++ DC_IRQ_SOURCE_CRTC3VSYNC, ++ DC_IRQ_SOURCE_CRTC4VSYNC, ++ DC_IRQ_SOURCE_CRTC5VSYNC, ++ DC_IRQ_SOURCE_CRTC6VSYNC, ++ DC_IRQ_SOURCE_TIMER, ++ ++ DC_IRQ_SOURCE_PFLIP_FIRST, ++ DC_IRQ_SOURCE_PFLIP1 = DC_IRQ_SOURCE_PFLIP_FIRST, ++ DC_IRQ_SOURCE_PFLIP2, ++ DC_IRQ_SOURCE_PFLIP3, ++ DC_IRQ_SOURCE_PFLIP4, ++ DC_IRQ_SOURCE_PFLIP5, ++ DC_IRQ_SOURCE_PFLIP6, ++ DC_IRQ_SOURCE_PFLIP_UNDERLAY0, ++ DC_IRQ_SOURCE_PFLIP_LAST = DC_IRQ_SOURCE_PFLIP_UNDERLAY0, ++ ++ DC_IRQ_SOURCE_GPIOPAD0, ++ DC_IRQ_SOURCE_GPIOPAD1, ++ DC_IRQ_SOURCE_GPIOPAD2, ++ DC_IRQ_SOURCE_GPIOPAD3, ++ DC_IRQ_SOURCE_GPIOPAD4, ++ DC_IRQ_SOURCE_GPIOPAD5, ++ DC_IRQ_SOURCE_GPIOPAD6, ++ DC_IRQ_SOURCE_GPIOPAD7, ++ DC_IRQ_SOURCE_GPIOPAD8, ++ DC_IRQ_SOURCE_GPIOPAD9, ++ DC_IRQ_SOURCE_GPIOPAD10, ++ DC_IRQ_SOURCE_GPIOPAD11, ++ DC_IRQ_SOURCE_GPIOPAD12, ++ DC_IRQ_SOURCE_GPIOPAD13, ++ DC_IRQ_SOURCE_GPIOPAD14, ++ DC_IRQ_SOURCE_GPIOPAD15, ++ DC_IRQ_SOURCE_GPIOPAD16, ++ DC_IRQ_SOURCE_GPIOPAD17, ++ DC_IRQ_SOURCE_GPIOPAD18, ++ DC_IRQ_SOURCE_GPIOPAD19, ++ DC_IRQ_SOURCE_GPIOPAD20, ++ DC_IRQ_SOURCE_GPIOPAD21, ++ DC_IRQ_SOURCE_GPIOPAD22, ++ DC_IRQ_SOURCE_GPIOPAD23, ++ DC_IRQ_SOURCE_GPIOPAD24, ++ DC_IRQ_SOURCE_GPIOPAD25, ++ DC_IRQ_SOURCE_GPIOPAD26, ++ DC_IRQ_SOURCE_GPIOPAD27, ++ DC_IRQ_SOURCE_GPIOPAD28, ++ DC_IRQ_SOURCE_GPIOPAD29, ++ DC_IRQ_SOURCE_GPIOPAD30, ++ ++ DC_IRQ_SOURCE_DC1UNDERFLOW, ++ DC_IRQ_SOURCE_DC2UNDERFLOW, ++ DC_IRQ_SOURCE_DC3UNDERFLOW, ++ DC_IRQ_SOURCE_DC4UNDERFLOW, ++ DC_IRQ_SOURCE_DC5UNDERFLOW, ++ DC_IRQ_SOURCE_DC6UNDERFLOW, ++ ++ DC_IRQ_SOURCE_DMCU_SCP, ++ DC_IRQ_SOURCE_VBIOS_SW, ++ ++ DC_IRQ_SOURCE_VUPDATE1, ++ DC_IRQ_SOURCE_VUPDATE2, ++ DC_IRQ_SOURCE_VUPDATE3, ++ DC_IRQ_SOURCE_VUPDATE4, ++ DC_IRQ_SOURCE_VUPDATE5, ++ DC_IRQ_SOURCE_VUPDATE6, ++ ++ DAL_IRQ_SOURCES_NUMBER ++}; ++ ++enum irq_type ++{ ++ IRQ_TYPE_PFLIP = DC_IRQ_SOURCE_PFLIP1, ++ IRQ_TYPE_VUPDATE = DC_IRQ_SOURCE_VUPDATE1, ++}; ++ ++#define DAL_VALID_IRQ_SRC_NUM(src) \ ++ ((src) <= DAL_IRQ_SOURCES_NUMBER && (src) > DC_IRQ_SOURCE_INVALID) ++ ++/* Number of Page Flip IRQ Sources. */ ++#define DAL_PFLIP_IRQ_SRC_NUM \ ++ (DC_IRQ_SOURCE_PFLIP_LAST - DC_IRQ_SOURCE_PFLIP_FIRST + 1) ++ ++/* the number of contexts may be expanded in the future based on needs */ ++enum dc_interrupt_context { ++ INTERRUPT_LOW_IRQ_CONTEXT = 0, ++ INTERRUPT_HIGH_IRQ_CONTEXT, ++ INTERRUPT_CONTEXT_NUMBER ++}; ++ ++enum dc_interrupt_porlarity { ++ INTERRUPT_POLARITY_DEFAULT = 0, ++ INTERRUPT_POLARITY_LOW = INTERRUPT_POLARITY_DEFAULT, ++ INTERRUPT_POLARITY_HIGH, ++ INTERRUPT_POLARITY_BOTH ++}; ++ ++#define DC_DECODE_INTERRUPT_POLARITY(int_polarity) \ ++ (int_polarity == INTERRUPT_POLARITY_LOW) ? "Low" : \ ++ (int_polarity == INTERRUPT_POLARITY_HIGH) ? "High" : \ ++ (int_polarity == INTERRUPT_POLARITY_BOTH) ? "Both" : "Invalid" ++ ++struct dc_timer_interrupt_params { ++ uint64_t micro_sec_interval; ++ enum dc_interrupt_context int_context; ++}; ++ ++struct dc_interrupt_params { ++ /* The polarity *change* which will trigger an interrupt. ++ * If 'requested_polarity == INTERRUPT_POLARITY_BOTH', then ++ * 'current_polarity' must be initialised. */ ++ enum dc_interrupt_porlarity requested_polarity; ++ /* If 'requested_polarity == INTERRUPT_POLARITY_BOTH', ++ * 'current_polarity' should contain the current state, which means ++ * the interrupt will be triggered when state changes from what is, ++ * in 'current_polarity'. */ ++ enum dc_interrupt_porlarity current_polarity; ++ enum dc_irq_source irq_source; ++ enum dc_interrupt_context int_context; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/adapter_service_interface.h b/drivers/gpu/drm/amd/dal/include/adapter_service_interface.h +new file mode 100644 +index 0000000..aa503a8 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/adapter_service_interface.h +@@ -0,0 +1,628 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ADAPTER_SERVICE_INTERFACE_H__ ++#define __DAL_ADAPTER_SERVICE_INTERFACE_H__ ++ ++#include "grph_object_ctrl_defs.h" ++#include "gpio_interface.h" ++#include "ddc_interface.h" ++#include "irq_interface.h" ++#include "bios_parser_interface.h" ++#include "adapter_service_types.h" ++#include "dal_types.h" ++#include "asic_capability_types.h" ++ ++/* forward declaration */ ++struct bios_parser; ++struct i2caux; ++struct adapter_service; ++ ++ ++/* ++ * enum adapter_feature_id ++ * ++ * Definition of all adapter features ++ * ++ * The enumeration defines the IDs of all the adapter features. The enum ++ * organizes all the features into several feature sets. The range of feature ++ * set N is from ((N-1)*32+1) to (N*32). Because there may be three value-type ++ * feature, boolean-type, unsigned char-type and unsinged int-type, the number ++ * of features should be 32, 4 and 1 in the feature set accordingly. ++ * ++ * In a boolean-type feature set N, the enumeration value of the feature should ++ * be ((N-1)*32+1), ((N-1)*32+2), ..., (N*32). ++ * ++ * In an unsigned char-type feature set N, the enumeration value of the ++ * feature should be ((N-1)*32+1), ((N-1)*32+8), ((N-1)*32+16) and (N*32). ++ * ++ * In an unsigned int-type feature set N, the enumeration value of the feature ++ * should be ((N-1)*32+1) ++ */ ++enum adapter_feature_id { ++ FEATURE_UNKNOWN = 0, ++ ++ /* Boolean set, up to 32 entries */ ++ FEATURE_ENABLE_HW_EDID_POLLING = 1, ++ FEATURE_SET_01_START = FEATURE_ENABLE_HW_EDID_POLLING, ++ FEATURE_DP_SINK_DETECT_POLL_DATA_PIN, ++ FEATURE_UNDERFLOW_INTERRUPT, ++ FEATURE_ALLOW_WATERMARK_ADJUSTMENT, ++ FEATURE_LIGHT_SLEEP, ++ FEATURE_DCP_DITHER_FRAME_RANDOM_ENABLE, ++ FEATURE_DCP_DITHER_RGB_RANDOM_ENABLE, ++ FEATURE_DCP_DITHER_HIGH_PASS_RANDOM_ENABLE, ++ FEATURE_DETECT_REQUIRE_HPD_HIGH, ++ FEATURE_LINE_BUFFER_ENHANCED_PIXEL_DEPTH, /* 10th */ ++ FEATURE_MAXIMIZE_URGENCY_WATERMARKS, ++ FEATURE_MAXIMIZE_STUTTER_MARKS, ++ FEATURE_MAXIMIZE_NBP_MARKS, ++ FEATURE_RESTORE_USAGE_I2C_SW_ENGINE, ++ FEATURE_USE_MAX_DISPLAY_CLK, ++ FEATURE_ALLOW_EDP_RESOURCE_SHARING, ++ FEATURE_SUPPORT_DP_YUV, ++ FEATURE_SUPPORT_DP_Y_ONLY, ++ FEATURE_DISABLE_DP_GTC_SYNC, ++ FEATURE_NO_HPD_LOW_POLLING_VCC_OFF, /* 20th */ ++ FEATURE_ENABLE_DFS_BYPASS, ++ FEATURE_LB_HIGH_RESOLUTION, ++ FEATURE_DP_DISPLAY_FORCE_SS_ENABLE, ++ FEATURE_REPORT_CE_MODE_ONLY, ++ FEATURE_ALLOW_OPTIMIZED_MODE_AS_DEFAULT, ++ FEATURE_DDC_READ_FORCE_REPEATED_START, ++ FEATURE_FORCE_TIMING_RESYNC, ++ FEATURE_TMDS_DISABLE_DITHERING, ++ FEATURE_HDMI_DISABLE_DITHERING, ++ FEATURE_DP_DISABLE_DITHERING, /* 30th */ ++ FEATURE_EMBEDDED_DISABLE_DITHERING, ++ FEATURE_DISABLE_AZ_CLOCK_GATING, /* 32th. This set is full */ ++ FEATURE_SET_01_END = FEATURE_SET_01_START + 31, ++ ++ /* Boolean set, up to 32 entries */ ++ FEATURE_WIRELESS_ENABLE = FEATURE_SET_01_END + 1, ++ FEATURE_SET_02_START = FEATURE_WIRELESS_ENABLE, ++ FEATURE_WIRELESS_FULL_TIMING_ADJUSTMENT, ++ FEATURE_WIRELESS_LIMIT_720P, ++ FEATURE_WIRELESS_ENABLE_COMPRESSED_AUDIO, ++ FEATURE_WIRELESS_INCLUDE_UNVERIFIED_TIMINGS, ++ FEATURE_MODIFY_TIMINGS_FOR_WIRELESS, ++ FEATURE_ALLOW_SELF_REFRESH, ++ FEATURE_ALLOW_DYNAMIC_PIXEL_ENCODING_CHANGE, ++ FEATURE_ALLOW_HSYNC_VSYNC_ADJUSTMENT, ++ FEATURE_FORCE_PSR, /* 10th */ ++ FEATURE_PREFER_3D_TIMING, ++ FEATURE_VARI_BRIGHT_ENABLE, ++ FEATURE_PSR_ENABLE, ++ FEATURE_EDID_STRESS_READ, ++ FEATURE_DP_FRAME_PACK_STEREO3D, ++ FEATURE_ALLOW_HDMI_WITHOUT_AUDIO, ++ FEATURE_RESTORE_USAGE_I2C_SW_ENGING, ++ FEATURE_ABM_2_0, ++ FEATURE_SUPPORT_MIRABILIS, ++ FEATURE_LOAD_DMCU_FIRMWARE, /* 20th */ ++ FEATURE_ENABLE_GPU_SCALING, ++ FEATURE_DONGLE_SINK_COUNT_CHECK, ++ FEATURE_INSTANT_UP_SCALE_DOWN_SCALE, ++ FEATURE_TILED_DISPLAY, ++ FEATURE_CHANGE_I2C_SPEED_CONTROL, ++ FEATURE_REPORT_SINGLE_SELECTED_TIMING, ++ FEATURE_ALLOW_HDMI_HIGH_CLK_DP_DONGLE, ++ FEATURE_SUPPORT_EXTERNAL_PANEL_DRR, ++ FEATURE_SUPPORT_SMOOTH_BRIGHTNESS, ++ FEATURE_ALLOW_DIRECT_MEMORY_ACCESS_TRIG, /* 30th */ ++ FEATURE_POWER_GATING_LB_PORTION, /* 31nd. One more left. */ ++ FEATURE_SET_02_END = FEATURE_SET_02_START + 31, ++ ++ /* UInt set, 1 entry: DCP Bit Depth Reduction Mode */ ++ FEATURE_DCP_BIT_DEPTH_REDUCTION_MODE = FEATURE_SET_02_END + 1, ++ FEATURE_SET_03_START = FEATURE_DCP_BIT_DEPTH_REDUCTION_MODE, ++ FEATURE_SET_03_END = FEATURE_SET_03_START + 31, ++ ++ /* UInt set, 1 entry: DCP Dither Mode */ ++ FEATURE_DCP_DITHER_MODE = FEATURE_SET_03_END + 1, ++ FEATURE_SET_04_START = FEATURE_DCP_DITHER_MODE, ++ FEATURE_SET_04_END = FEATURE_SET_04_START + 31, ++ ++ /* UInt set, 1 entry: DCP Programming WA(workaround) */ ++ FEATURE_DCP_PROGRAMMING_WA = FEATURE_SET_04_END + 1, ++ FEATURE_SET_06_START = FEATURE_DCP_PROGRAMMING_WA, ++ FEATURE_SET_06_END = FEATURE_SET_06_START + 31, ++ ++ /* UInt set, 1 entry: Maximum co-functional non-DP displays */ ++ FEATURE_MAX_COFUNC_NON_DP_DISPLAYS = FEATURE_SET_06_END + 1, ++ FEATURE_SET_07_START = FEATURE_MAX_COFUNC_NON_DP_DISPLAYS, ++ FEATURE_SET_07_END = FEATURE_SET_07_START + 31, ++ ++ /* UInt set, 1 entry: Number of supported HDMI connection */ ++ FEATURE_SUPPORTED_HDMI_CONNECTION_NUM = FEATURE_SET_07_END + 1, ++ FEATURE_SET_08_START = FEATURE_SUPPORTED_HDMI_CONNECTION_NUM, ++ FEATURE_SET_08_END = FEATURE_SET_08_START + 31, ++ ++ /* UInt set, 1 entry: Maximum number of controllers */ ++ FEATURE_MAX_CONTROLLER_NUM = FEATURE_SET_08_END + 1, ++ FEATURE_SET_09_START = FEATURE_MAX_CONTROLLER_NUM, ++ FEATURE_SET_09_END = FEATURE_SET_09_START + 31, ++ ++ /* UInt set, 1 entry: Type of DRR support */ ++ FEATURE_DRR_SUPPORT = FEATURE_SET_09_END + 1, ++ FEATURE_SET_10_START = FEATURE_DRR_SUPPORT, ++ FEATURE_SET_10_END = FEATURE_SET_10_START + 31, ++ ++ /* UInt set, 1 entry: Stutter mode support */ ++ FEATURE_STUTTER_MODE = FEATURE_SET_10_END + 1, ++ FEATURE_SET_11_START = FEATURE_STUTTER_MODE, ++ FEATURE_SET_11_END = FEATURE_SET_11_START + 31, ++ ++ /* UInt set, 1 entry: Measure PSR setup time */ ++ FEATURE_PSR_SETUP_TIME_TEST = FEATURE_SET_11_END + 1, ++ FEATURE_SET_12_START = FEATURE_PSR_SETUP_TIME_TEST, ++ FEATURE_SET_12_END = FEATURE_SET_12_START + 31, ++ ++ /* Boolean set, up to 32 entries */ ++ FEATURE_POWER_GATING_PIPE_IN_TILE = FEATURE_SET_12_END + 1, ++ FEATURE_SET_13_START = FEATURE_POWER_GATING_PIPE_IN_TILE, ++ FEATURE_USE_PPLIB, ++ FEATURE_DISABLE_LPT_SUPPORT, ++ FEATURE_DUMMY_FBC_BACKEND, ++ FEATURE_DISABLE_FBC_COMP_CLK_GATE, ++ FEATURE_DPMS_AUDIO_ENDPOINT_CONTROL, ++ FEATURE_PIXEL_PERFECT_OUTPUT, ++ FEATURE_8BPP_SUPPORTED, ++ FEATURE_SET_13_END = FEATURE_SET_13_START + 31, ++ ++ /* UInt set, 1 entry: Display preferred view ++ * 0: no preferred view ++ * 1: native and preferred timing of embedded display will have high ++ * priority, so other displays will support it always ++ */ ++ FEATURE_DISPLAY_PREFERRED_VIEW = FEATURE_SET_13_END + 1, ++ FEATURE_SET_15_START = FEATURE_DISPLAY_PREFERRED_VIEW, ++ FEATURE_SET_15_END = FEATURE_SET_15_START + 31, ++ ++ /* UInt set, 1 entry: DAL optimization */ ++ FEATURE_OPTIMIZATION = FEATURE_SET_15_END + 1, ++ FEATURE_SET_16_START = FEATURE_OPTIMIZATION, ++ FEATURE_SET_16_END = FEATURE_SET_16_START + 31, ++ ++ /* UInt set, 1 entry: Performance measurement */ ++ FEATURE_PERF_MEASURE = FEATURE_SET_16_END + 1, ++ FEATURE_SET_17_START = FEATURE_PERF_MEASURE, ++ FEATURE_SET_17_END = FEATURE_SET_17_START + 31, ++ ++ /* UInt set, 1 entry: Minimum backlight value [0-255] */ ++ FEATURE_MIN_BACKLIGHT_LEVEL = FEATURE_SET_17_END + 1, ++ FEATURE_SET_18_START = FEATURE_MIN_BACKLIGHT_LEVEL, ++ FEATURE_SET_18_END = FEATURE_SET_18_START + 31, ++ ++ /* UInt set, 1 entry: Maximum backlight value [0-255] */ ++ FEATURE_MAX_BACKLIGHT_LEVEL = FEATURE_SET_18_END + 1, ++ FEATURE_SET_19_START = FEATURE_MAX_BACKLIGHT_LEVEL, ++ FEATURE_SET_19_END = FEATURE_SET_19_START + 31, ++ ++ /* UInt set, 1 entry: AMB setting ++ * ++ * Each byte will control the ABM configuration to use for a specific ++ * ABM level. ++ * ++ * HW team provided 12 different ABM min/max reduction pairs to choose ++ * between for each ABM level. ++ * ++ * ABM level Byte Setting ++ * 1 0 Default = 0 (setting 3), can be override to 1-12 ++ * 2 1 Default = 0 (setting 7), can be override to 1-12 ++ * 3 2 Default = 0 (setting 8), can be override to 1-12 ++ * 4 3 Default = 0 (setting 10), can be override to 1-12 ++ * ++ * For example, ++ * FEATURE_PREFERRED_ABM_CONFIG_SET = 0x0C060500, this represents: ++ * ABM level 1 use default setting (setting 3) ++ * ABM level 2 uses setting 5 ++ * ABM level 3 uses setting 6 ++ * ABM level 4 uses setting 12 ++ * Internal use only! ++ */ ++ FEATURE_PREFERRED_ABM_CONFIG_SET = FEATURE_SET_19_END + 1, ++ FEATURE_SET_20_START = FEATURE_PREFERRED_ABM_CONFIG_SET, ++ FEATURE_SET_20_END = FEATURE_SET_20_START + 31, ++ ++ /* UInt set, 1 entry: Change SW I2C speed */ ++ FEATURE_CHANGE_SW_I2C_SPEED = FEATURE_SET_20_END + 1, ++ FEATURE_SET_21_START = FEATURE_CHANGE_SW_I2C_SPEED, ++ FEATURE_SET_21_END = FEATURE_SET_21_START + 31, ++ ++ /* UInt set, 1 entry: Change HW I2C speed */ ++ FEATURE_CHANGE_HW_I2C_SPEED = FEATURE_SET_21_END + 1, ++ FEATURE_SET_22_START = FEATURE_CHANGE_HW_I2C_SPEED, ++ FEATURE_SET_22_END = FEATURE_SET_22_START + 31, ++ ++ /* UInt set, 1 entry: ++ * When PSR issue occurs, it is sometimes hard to debug since the ++ * failure occurs immediately at boot. Use this setting to skip or ++ * postpone PSR functionality and re-enable through DSAT. */ ++ FEATURE_DEFAULT_PSR_LEVEL = FEATURE_SET_22_END + 1, ++ FEATURE_SET_23_START = FEATURE_DEFAULT_PSR_LEVEL, ++ FEATURE_SET_23_END = FEATURE_SET_23_START + 31, ++ ++ /* UInt set, 1 entry: Allowed pixel clock range for LVDS */ ++ FEATURE_LVDS_SAFE_PIXEL_CLOCK_RANGE = FEATURE_SET_23_END + 1, ++ FEATURE_SET_24_START = FEATURE_LVDS_SAFE_PIXEL_CLOCK_RANGE, ++ FEATURE_SET_24_END = FEATURE_SET_24_START + 31, ++ ++ /* UInt set, 1 entry: Max number of clock sources */ ++ FEATURE_MAX_CLOCK_SOURCE_NUM = FEATURE_SET_24_END + 1, ++ FEATURE_SET_25_START = FEATURE_MAX_CLOCK_SOURCE_NUM, ++ FEATURE_SET_25_END = FEATURE_SET_25_START + 31, ++ ++ /* UInt set, 1 entry: Select the ABM configuration to use. ++ * ++ * This feature set is used to allow packaging option to be defined ++ * to allow OEM to select between the default ABM configuration or ++ * alternative predefined configurations that may be more aggressive. ++ * ++ * Note that this regkey is meant for external use to select the ++ * configuration OEM wants. Whereas the other PREFERRED_ABM_CONFIG_SET ++ * key is only used for internal use and allows full reconfiguration. ++ */ ++ FEATURE_ABM_CONFIG = FEATURE_SET_25_END + 1, ++ FEATURE_SET_26_START = FEATURE_ABM_CONFIG, ++ FEATURE_SET_26_END = FEATURE_SET_26_START + 31, ++ ++ /* UInt set, 1 entry: Select the default speed in which smooth ++ * brightness feature should converge towards target backlight level. ++ * ++ * For example, a setting of 500 means it takes 500ms to transition ++ * from current backlight level to the new requested backlight level. ++ */ ++ FEATURE_SMOOTH_BRTN_ADJ_TIME_IN_MS = FEATURE_SET_26_END + 1, ++ FEATURE_SET_27_START = FEATURE_SMOOTH_BRTN_ADJ_TIME_IN_MS, ++ FEATURE_SET_27_END = FEATURE_SET_27_START + 31, ++ ++ /* Set 28: UInt set, 1 entry: Allow runtime parameter to force specific ++ * Static Screen Event triggers for test purposes. */ ++ FEATURE_FORCE_STATIC_SCREEN_EVENT_TRIGGERS = FEATURE_SET_27_END + 1, ++ FEATURE_SET_28_START = FEATURE_FORCE_STATIC_SCREEN_EVENT_TRIGGERS, ++ FEATURE_SET_28_END = FEATURE_SET_28_START + 31, ++ ++ FEATURE_MAXIMUM ++}; ++ ++/* Adapter Service type of DRR support*/ ++enum as_drr_support { ++ AS_DRR_SUPPORT_DISABLED = 0x0, ++ AS_DRR_SUPPORT_ENABLED = 0x1, ++ AS_DRR_SUPPORT_MIN_FORCED_FPS = 0xA ++}; ++ ++/* Adapter service initialize data structure*/ ++struct as_init_data { ++ struct hw_asic_id hw_init_data; ++ struct bp_init_data bp_init_data; ++ struct dc_context *ctx; ++ struct bdf_info bdf_info; ++ const struct dal_override_parameters *display_param; ++}; ++ ++/* Create adapter service */ ++struct adapter_service *dal_adapter_service_create( ++ struct as_init_data *init_data); ++ ++/* Destroy adapter service and objects it contains */ ++void dal_adapter_service_destroy( ++ struct adapter_service **as); ++ ++/* Get the DCE version of current ASIC */ ++enum dce_version dal_adapter_service_get_dce_version( ++ const struct adapter_service *as); ++ ++/* Get firmware information from BIOS */ ++bool dal_adapter_service_get_firmware_info( ++ struct adapter_service *as, ++ struct firmware_info *info); ++ ++ ++/* functions to get a total number of objects of specific type */ ++uint8_t dal_adapter_service_get_connectors_num( ++ struct adapter_service *as); ++ ++/* Get number of controllers */ ++uint8_t dal_adapter_service_get_controllers_num( ++ struct adapter_service *as); ++ ++/* Get number of clock sources */ ++uint8_t dal_adapter_service_get_clock_sources_num( ++ struct adapter_service *as); ++ ++/* Get number of controllers */ ++uint8_t dal_adapter_service_get_func_controllers_num( ++ struct adapter_service *as); ++ ++/* Get number of stream engines */ ++uint8_t dal_adapter_service_get_stream_engines_num( ++ struct adapter_service *as); ++ ++/* functions to get object id based on object index */ ++struct graphics_object_id dal_adapter_service_get_connector_obj_id( ++ struct adapter_service *as, ++ uint8_t connector_index); ++ ++/* Get number of spread spectrum entries from BIOS */ ++uint32_t dal_adapter_service_get_ss_info_num( ++ struct adapter_service *as, ++ enum as_signal_type signal); ++ ++/* Get spread spectrum info from BIOS */ ++bool dal_adapter_service_get_ss_info( ++ struct adapter_service *as, ++ enum as_signal_type signal, ++ uint32_t idx, ++ struct spread_spectrum_info *info); ++ ++/* Check if DFS bypass is enabled */ ++bool dal_adapter_service_is_dfs_bypass_enabled(struct adapter_service *as); ++ ++/* Get memory controller latency */ ++uint32_t dal_adapter_service_get_mc_latency( ++ struct adapter_service *as); ++ ++/* Get the video RAM bit width set on the ASIC */ ++uint32_t dal_adapter_service_get_asic_vram_bit_width( ++ struct adapter_service *as); ++ ++/* Get the bug flags set on this ASIC */ ++struct asic_bugs dal_adapter_service_get_asic_bugs( ++ struct adapter_service *as); ++ ++/* Get efficiency of DRAM */ ++uint32_t dal_adapter_service_get_dram_bandwidth_efficiency( ++ struct adapter_service *as); ++ ++/* Get multiplier for the memory type */ ++uint32_t dal_adapter_service_get_memory_type_multiplier( ++ struct adapter_service *as); ++ ++/* Get parameters for bandwidth tuning */ ++bool dal_adapter_service_get_bandwidth_tuning_params( ++ struct adapter_service *as, ++ union bandwidth_tuning_params *params); ++ ++/* Get integrated information on BIOS */ ++bool dal_adapter_service_get_integrated_info( ++ struct adapter_service *as, ++ struct integrated_info *info); ++ ++/* Return if a given feature is supported by the ASIC */ ++bool dal_adapter_service_is_feature_supported( ++ enum adapter_feature_id feature_id); ++ ++/* Get the cached value of a given feature */ ++bool dal_adapter_service_get_feature_value( ++ const enum adapter_feature_id feature_id, ++ void *data, ++ uint32_t size); ++ ++/* Get a copy of ASIC feature flags */ ++struct asic_feature_flags dal_adapter_service_get_feature_flags( ++ struct adapter_service *as); ++ ++/* Obtain DDC */ ++struct ddc *dal_adapter_service_obtain_ddc( ++ struct adapter_service *as, ++ struct graphics_object_id id); ++ ++/* Release DDC */ ++void dal_adapter_service_release_ddc( ++ struct adapter_service *as, ++ struct ddc *ddc); ++ ++/* Obtain HPD interrupt request */ ++struct irq *dal_adapter_service_obtain_hpd_irq( ++ struct adapter_service *as, ++ struct graphics_object_id id); ++ ++/* Release interrupt request */ ++void dal_adapter_service_release_irq( ++ struct adapter_service *as, ++ struct irq *irq); ++ ++/* Obtain GPIO */ ++struct gpio *dal_adapter_service_obtain_gpio( ++ struct adapter_service *as, ++ enum gpio_id id, ++ uint32_t en); ++ ++/* Obtain GPIO for stereo3D*/ ++struct gpio *dal_adapter_service_obtain_stereo_gpio(struct adapter_service *as); ++ ++/* Release GPIO */ ++void dal_adapter_service_release_gpio( ++ struct adapter_service *as, ++ struct gpio *gpio); ++ ++/* Get SW I2C speed */ ++uint32_t dal_adapter_service_get_sw_i2c_speed(struct adapter_service *as); ++ ++/* Get HW I2C speed */ ++uint32_t dal_adapter_service_get_hw_i2c_speed(struct adapter_service *as); ++ ++/* Get line buffer size */ ++uint32_t dal_adapter_service_get_line_buffer_size(struct adapter_service *as); ++ ++/* Get information on audio support */ ++union audio_support dal_adapter_service_get_audio_support( ++ struct adapter_service *as); ++ ++/* Get I2C information from BIOS */ ++bool dal_adapter_service_get_i2c_info( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ struct graphics_object_i2c_info *i2c_info); ++ ++/* Get bios parser handler */ ++struct bios_parser *dal_adapter_service_get_bios_parser( ++ struct adapter_service *as); ++ ++/* Get i2c aux handler */ ++struct i2caux *dal_adapter_service_get_i2caux( ++ struct adapter_service *as); ++ ++struct dal_asic_runtime_flags dal_adapter_service_get_asic_runtime_flags( ++ struct adapter_service *as); ++ ++bool dal_adapter_service_initialize_hw_data( ++ struct adapter_service *as); ++ ++struct graphics_object_id dal_adapter_service_enum_fake_path_resource( ++ struct adapter_service *as, ++ uint32_t index); ++ ++struct graphics_object_id dal_adapter_service_enum_stereo_sync_object( ++ struct adapter_service *as, ++ uint32_t index); ++ ++struct graphics_object_id dal_adapter_service_enum_sync_output_object( ++ struct adapter_service *as, ++ uint32_t index); ++ ++struct graphics_object_id dal_adapter_service_enum_audio_object( ++ struct adapter_service *as, ++ uint32_t index); ++ ++void dal_adapter_service_update_audio_connectivity( ++ struct adapter_service *as, ++ uint32_t number_of_audio_capable_display_path); ++ ++bool dal_adapter_service_has_embedded_display_connector( ++ struct adapter_service *as); ++ ++bool dal_adapter_service_get_embedded_panel_info( ++ struct adapter_service *as, ++ struct embedded_panel_info *info); ++ ++bool dal_adapter_service_enum_embedded_panel_patch_mode( ++ struct adapter_service *as, ++ uint32_t index, ++ struct embedded_panel_patch_mode *mode); ++ ++bool dal_adapter_service_get_faked_edid_len( ++ struct adapter_service *as, ++ uint32_t *len); ++ ++bool dal_adapter_service_get_faked_edid_buf( ++ struct adapter_service *as, ++ uint8_t *buf, ++ uint32_t len); ++ ++uint32_t dal_adapter_service_get_max_cofunc_non_dp_displays(void); ++ ++uint32_t dal_adapter_service_get_single_selected_timing_signals(void); ++ ++bool dal_adapter_service_get_device_tag( ++ struct adapter_service *as, ++ struct graphics_object_id connector_object_id, ++ uint32_t device_tag_index, ++ struct connector_device_tag_info *info); ++ ++bool dal_adapter_service_is_device_id_supported( ++ struct adapter_service *as, ++ struct device_id id); ++ ++bool dal_adapter_service_is_meet_underscan_req(struct adapter_service *as); ++ ++bool dal_adapter_service_underscan_for_hdmi_only(struct adapter_service *as); ++ ++uint32_t dal_adapter_service_get_src_num( ++ struct adapter_service *as, ++ struct graphics_object_id id); ++ ++struct graphics_object_id dal_adapter_service_get_src_obj( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ uint32_t index); ++ ++/* Is this Fusion ASIC */ ++bool dal_adapter_service_is_fusion(struct adapter_service *as); ++ ++/* Is this ASIC support dynamic DFSbypass switch */ ++bool dal_adapter_service_is_dfsbyass_dynamic(struct adapter_service *as); ++ ++/* Reports whether driver settings allow requested optimization */ ++bool dal_adapter_service_should_optimize( ++ struct adapter_service *as, enum optimization_feature feature); ++ ++/* Determine if driver is in accelerated mode */ ++bool dal_adapter_service_is_in_accelerated_mode(struct adapter_service *as); ++ ++struct ddc *dal_adapter_service_obtain_ddc_from_i2c_info( ++ struct adapter_service *as, ++ struct graphics_object_i2c_info *info); ++ ++struct bdf_info dal_adapter_service_get_adapter_info( ++ struct adapter_service *as); ++ ++ ++/* Determine if this ASIC needs to wait on PLL lock bit */ ++bool dal_adapter_service_should_psr_skip_wait_for_pll_lock( ++ struct adapter_service *as); ++ ++#define SIZEOF_BACKLIGHT_LUT 101 ++#define ABSOLUTE_BACKLIGHT_MAX 255 ++#define DEFAULT_MIN_BACKLIGHT 12 ++#define DEFAULT_MAX_BACKLIGHT 255 ++#define BACKLIGHT_CURVE_COEFFB 100 ++#define BACKLIGHT_CURVE_COEFFA_FACTOR 10000 ++#define BACKLIGHT_CURVE_COEFFB_FACTOR 100 ++ ++struct panel_backlight_levels { ++ uint32_t ac_level_percentage; ++ uint32_t dc_level_percentage; ++}; ++ ++bool dal_adapter_service_is_lid_open(struct adapter_service *as); ++ ++bool dal_adapter_service_get_panel_backlight_default_levels( ++ struct adapter_service *as, ++ struct panel_backlight_levels *levels); ++ ++bool dal_adapter_service_get_panel_backlight_boundaries( ++ struct adapter_service *as, ++ struct panel_backlight_boundaries *boundaries); ++ ++uint32_t dal_adapter_service_get_view_port_pixel_granularity( ++ struct adapter_service *as); ++ ++uint32_t dal_adapter_service_get_num_of_path_per_dp_mst_connector( ++ struct adapter_service *as); ++ ++uint32_t dal_adapter_service_get_num_of_underlays( ++ struct adapter_service *as); ++ ++bool dal_adapter_service_get_encoder_cap_info( ++ struct adapter_service *as, ++ struct graphics_object_id id, ++ struct graphics_object_encoder_cap_info *info); ++ ++bool dal_adapter_service_is_mc_tuning_req(struct adapter_service *as); ++ ++#endif /* __DAL_ADAPTER_SERVICE_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/adapter_service_types.h b/drivers/gpu/drm/amd/dal/include/adapter_service_types.h +new file mode 100644 +index 0000000..fb47ef3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/adapter_service_types.h +@@ -0,0 +1,70 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ADAPTER_SERVICE_TYPES_H__ ++#define __DAL_ADAPTER_SERVICE_TYPES_H__ ++ ++enum as_signal_type { ++ AS_SIGNAL_TYPE_NONE = 0L, /* no signal */ ++ AS_SIGNAL_TYPE_DVI, ++ AS_SIGNAL_TYPE_HDMI, ++ AS_SIGNAL_TYPE_LVDS, ++ AS_SIGNAL_TYPE_DISPLAY_PORT, ++ AS_SIGNAL_TYPE_GPU_PLL, ++ AS_SIGNAL_TYPE_UNKNOWN ++}; ++ ++/* ++ * Struct used for algorithm of Bandwidth tuning parameters ++ * the sequence of the fields is binded with runtime parameter. ++ */ ++union bandwidth_tuning_params { ++ struct bandwidth_tuning_params_struct { ++ uint32_t read_delay_stutter_off_usec; ++ uint32_t ignore_hblank_time;/*bool*/ ++ uint32_t extra_reordering_latency_usec; ++ uint32_t extra_mc_latency_usec; ++ uint32_t data_return_bandwidth_eff;/*in %*/ ++ uint32_t dmif_request_bandwidth_eff;/*in %*/ ++ uint32_t sclock_latency_multiplier;/*in unit of 0.01*/ ++ uint32_t mclock_latency_multiplier;/*in unit of 0.01*/ ++ uint32_t fix_latency_multiplier;/*in unit of 0.01*/ ++ /*in unit represent in watermark*/ ++ uint32_t use_urgency_watermark_offset; ++ } tuning_info; ++ uint32_t arr_info[sizeof(struct bandwidth_tuning_params_struct) ++ / sizeof(uint32_t)]; ++}; ++ ++union audio_support { ++ struct { ++ uint32_t DP_AUDIO:1; ++ uint32_t HDMI_AUDIO_ON_DONGLE:1; ++ uint32_t HDMI_AUDIO_NATIVE:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/adjustment_interface.h b/drivers/gpu/drm/amd/dal/include/adjustment_interface.h +new file mode 100644 +index 0000000..64a9f9f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/adjustment_interface.h +@@ -0,0 +1,230 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ADJUSTMENT_INTERFACE_H__ ++#define __DAL_ADJUSTMENT_INTERFACE_H__ ++ ++#include "include/display_service_types.h" ++#include "include/adjustment_types.h" ++#include "include/overlay_types.h" ++#include "include/display_path_interface.h" ++ ++struct ds_underscan_desc; ++struct adj_container; ++struct info_frame; ++struct ds_dispatch; ++struct hw_adjustment_set; ++struct path_mode; ++struct hw_path_mode; ++ ++enum build_path_set_reason; ++ ++bool dal_ds_dispatch_is_adjustment_supported( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id adjust_id); ++ ++enum ds_return dal_ds_dispatch_get_type( ++ struct ds_dispatch *adj, ++ enum adjustment_id adjust_id, ++ enum adjustment_data_type *type); ++ ++enum ds_return dal_ds_dispatch_get_property( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ enum adjustment_id adjust_id, ++ union adjustment_property *property); ++ ++enum ds_return dal_ds_dispatch_set_adjustment( ++ struct ds_dispatch *ds, ++ const uint32_t display_index, ++ enum adjustment_id adjust_id, ++ int32_t value); ++ ++enum ds_return dal_ds_dispatch_get_adjustment_current_value( ++ struct ds_dispatch *ds, ++ struct adj_container *container, ++ struct adjustment_info *info, ++ enum adjustment_id id, ++ bool fall_back_to_default); ++ ++enum ds_return dal_ds_dispatch_get_adjustment_value( ++ struct ds_dispatch *ds, ++ struct display_path *disp_path, ++ enum adjustment_id adj_id, ++ bool fall_back_to_default, ++ int32_t *value); ++ ++const struct raw_gamma_ramp *dal_ds_dispatch_get_current_gamma( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id adjust_id); ++ ++const struct raw_gamma_ramp *dal_ds_dispatch_get_default_gamma( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id adjust_id); ++ ++enum ds_return dal_ds_dispatch_set_current_gamma( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id adjust_id, ++ const struct raw_gamma_ramp *gamma); ++ ++enum ds_return dal_ds_dispatch_set_gamma( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id adjust_id, ++ const struct raw_gamma_ramp *gamma); ++ ++bool dal_ds_dispatch_get_underscan_info( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ struct ds_underscan_info *info); ++ ++bool dal_ds_dispatch_get_underscan_mode( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ struct ds_underscan_desc *desc); ++ ++bool dal_ds_dispatch_set_underscan_mode( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ struct ds_underscan_desc *desc); ++ ++bool dal_ds_dispatch_setup_overlay( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ struct overlay_data *data); ++ ++struct adj_container *dal_ds_dispatch_get_adj_container_for_path( ++ const struct ds_dispatch *ds, ++ uint32_t display_index); ++ ++void dal_ds_dispatch_set_applicable_adj( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ const struct adj_container *applicable); ++ ++enum ds_return dal_ds_dispatch_set_color_gamut( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ const struct ds_set_gamut_data *data); ++ ++enum ds_return dal_ds_dispatch_get_color_gamut( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ const struct ds_gamut_reference_data *ref, ++ struct ds_get_gamut_data *data); ++ ++enum ds_return dal_ds_dispatch_get_color_gamut_info( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ const struct ds_gamut_reference_data *ref, ++ struct ds_gamut_info *data); ++ ++enum ds_return dal_ds_dispatch_get_regamma_lut( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ struct ds_regamma_lut *data); ++ ++enum ds_return dal_ds_dispatch_set_regamma_lut( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ struct ds_regamma_lut *data); ++ ++enum ds_return dal_ds_dispatch_set_info_packets( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ const struct info_frame *info_frames); ++ ++enum ds_return dal_ds_dispatch_get_info_packets( ++ struct ds_dispatch *adj, ++ uint32_t display_index, ++ struct info_frame *info_frames); ++ ++bool dal_ds_dispatch_initialize_adjustment(struct ds_dispatch *ds); ++ ++void dal_ds_dispatch_cleanup_adjustment(struct ds_dispatch *ds); ++ ++bool dal_ds_dispatch_build_post_set_mode_adj( ++ struct ds_dispatch *ds, ++ const struct path_mode *mode, ++ struct display_path *display_path, ++ struct hw_adjustment_set *set); ++ ++bool dal_ds_dispatch_build_color_control_adj( ++ struct ds_dispatch *ds, ++ const struct path_mode *mode, ++ struct display_path *display_path, ++ struct hw_adjustment_set *set); ++ ++bool dal_ds_dispatch_build_include_adj( ++ struct ds_dispatch *ds, ++ const struct path_mode *mode, ++ struct display_path *display_path, ++ struct hw_path_mode *hw_mode, ++ struct hw_adjustment_set *set); ++ ++bool dal_ds_dispatch_apply_scaling( ++ struct ds_dispatch *ds, ++ const struct path_mode *mode, ++ struct adj_container *adj_container, ++ enum build_path_set_reason reason, ++ struct hw_path_mode *hw_mode); ++ ++void dal_ds_dispatch_update_adj_container_for_path_with_mode_info( ++ struct ds_dispatch *ds, ++ struct display_path *display_path, ++ const struct path_mode *path_mode); ++ ++enum ds_return dal_ds_dispatch_get_adjustment_info( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id adjust_id, ++ struct adjustment_info *adj_info); ++ ++bool dal_ds_dispatch_include_adjustment( ++ struct ds_dispatch *ds, ++ struct display_path *disp_path, ++ struct ds_adj_id_value adj, ++ struct hw_adjustment_set *set); ++ ++enum ds_return dal_ds_dispatch_set_gamma_adjustment( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum adjustment_id ad_id, ++ const struct raw_gamma_ramp *gamma); ++ ++void dal_ds_dispatch_update_adj_container_for_path_with_color_space( ++ struct ds_dispatch *ds, ++ uint32_t display_index, ++ enum ds_color_space color_space); ++ ++void dal_ds_dispatch_setup_default_regamma( ++ struct ds_dispatch *ds, ++ struct ds_regamma_lut *regamma); ++ ++#endif /* __DAL_ADJUSTMENT_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/adjustment_types.h b/drivers/gpu/drm/amd/dal/include/adjustment_types.h +new file mode 100644 +index 0000000..f6c0d61 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/adjustment_types.h +@@ -0,0 +1,420 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ADJUSTMENT_TYPES_H__ ++#define __DAL_ADJUSTMENT_TYPES_H__ ++ ++#include "dal_services.h" ++ ++/* make sure to update this when updating adj_global_info_array */ ++#define CURRENT_ADJUSTMENT_NUM 12 ++#define MAX_ADJUSTMENT_NUM (ADJ_ID_END - ADJ_ID_BEGIN) ++#define REGAMMA_VALUE 256 ++#define REGAMMA_RANGE (REGAMMA_VALUE*3) ++#define ADJUST_DIVIDER 100 ++#define GAMUT_DIVIDER 10000 ++ ++ ++enum adjustment_id { ++ ++ /*this useful type when i need to indicate unknown adjustment and code ++ look if not the specific type*/ ++ ADJ_ID_INVALID, ++ ++ ADJ_ID_CONTRAST, ++ ADJ_ID_BRIGHTNESS, ++ ADJ_ID_HUE, ++ ADJ_ID_SATURATION, ++ ADJ_ID_GAMMA_RAMP, ++ ADJ_ID_GAMMA_RAMP_REGAMMA_UPDATE, ++ ADJ_ID_TEMPERATURE, ++ ADJ_ID_NOMINAL_RANGE_RGB_LIMITED, ++ ++ ADJ_ID_LP_FILTER_DEFLICKER, ++ ADJ_ID_HP_FILTER_DEFLICKER, ++ ADJ_ID_SHARPNESS_GAIN, /*0 - 10*/ ++ ++ ADJ_ID_REDUCED_BLANKING, ++ ADJ_ID_COHERENT, ++ ADJ_ID_MULTIMEDIA_PASS_THROUGH, ++ ++ ADJ_ID_VERTICAL_POSITION, ++ ADJ_ID_HORIZONTA_LPOSITION, ++ ADJ_ID_VERTICAL_SIZE, ++ ADJ_ID_HORIZONTAL_SIZE, ++ ADJ_ID_VERTICAL_SYNC, ++ ADJ_ID_HORIZONTAL_SYNC, ++ ADJ_ID_OVERSCAN, ++ ADJ_ID_COMPOSITE_SYNC, ++ ++ ADJ_ID_BIT_DEPTH_REDUCTION,/*CWDDEDI_DISPLAY_ADJINFOTYPE_BITVECTOR*/ ++ ADJ_ID_UNDERSCAN,/*CWDDEDI_DISPLAY_ADJINFOTYPE_RANGE*/ ++ ADJ_ID_UNDERSCAN_TYPE,/*CWDDEDI_DISPLAY_ADJINFOTYPE_RANGE*/ ++ ADJ_ID_TEMPERATURE_SOURCE,/*CWDDEDI_DISPLAY_ADJINFOTYPE_BITVECTOR*/ ++ ++ ADJ_ID_OVERLAY_BRIGHTNESS, ++ ADJ_ID_OVERLAY_CONTRAST, ++ ADJ_ID_OVERLAY_SATURATION, ++ ADJ_ID_OVERLAY_HUE, ++ ADJ_ID_OVERLAY_GAMMA, ++ ADJ_ID_OVERLAY_ALPHA, ++ ADJ_ID_OVERLAY_ALPHA_PER_PIX, ++ ADJ_ID_OVERLAY_INV_GAMMA, ++ ADJ_ID_OVERLAY_TEMPERATURE,/*done ,but code is commented*/ ++ ADJ_ID_OVERLAY_NOMINAL_RANGE_RGB_LIMITED, ++ ++ ++ ADJ_ID_UNDERSCAN_TV_INTERNAL,/*internal usage only for HDMI*/ ++ /*custom TV modes*/ ++ ADJ_ID_DRIVER_REQUESTED_GAMMA,/*used to get current gamma*/ ++ ADJ_ID_GAMUT_SOURCE_GRPH,/*logical adjustment visible for DS and CDB*/ ++ ADJ_ID_GAMUT_SOURCE_OVL,/*logical adjustment visible for DS and CDB*/ ++ ADJ_ID_GAMUT_DESTINATION,/*logical adjustment visible for DS and CDB*/ ++ ADJ_ID_REGAMMA,/*logical adjustment visible for DS and CDB*/ ++ ADJ_ID_ITC_ENABLE,/*ITC flag enable by default*/ ++ ADJ_ID_CNC_CONTENT,/*display image content*/ ++ /*internal adjustment, in order to provide backward compatibility ++ gamut with color temperature*/ ++ ++ /* Backlight Adjustment Group*/ ++ ADJ_ID_BACKLIGHT, ++ ADJ_ID_BACKLIGHT_OPTIMIZATION, ++ ++ /* flag the first and last*/ ++ ADJ_ID_BEGIN = ADJ_ID_CONTRAST, ++ ADJ_ID_END = ADJ_ID_BACKLIGHT_OPTIMIZATION, ++}; ++ ++enum adjustment_data_type { ++ ADJ_RANGED, ++ ADJ_BITVECTOR, ++ ADJ_LUT /* not handled currently */ ++}; ++ ++union adjustment_property { ++ uint32_t u32all; ++ struct { ++ /*per mode adjustment*/ ++ uint32_t SAVED_WITHMODE:1; ++ /*per edid adjustment*/ ++ uint32_t SAVED_WITHEDID:1; ++ /*adjustment not visible to HWSS*/ ++ uint32_t CALCULATE:1; ++ /*explisit adjustment applied by HWSS*/ ++ uint32_t INC_IN_SET_MODE:1; ++ /*adjustment requires set mode to be applied*/ ++ uint32_t SETMODE_REQ:1; ++ /*adjustment is applied at the end of set mode*/ ++ uint32_t POST_SET:1; ++/*when adjustment is applied its value should be stored ++in place and not wait for flush call*/ ++ uint32_t SAVE_IN_PLACE:1; ++ /*adjustment is always apply*/ ++ uint32_t FORCE_SET:1; ++ /*this adjustment is specific to individual display path.*/ ++ uint32_t SAVED_WITH_DISPLAY_IDX:1; ++ uint32_t RESERVED_23:23; ++ } bits; ++}; ++ ++enum adjustment_state { ++ ADJUSTMENT_STATE_INVALID, ++ ADJUSTMENT_STATE_VALID, ++ ADJUSTMENT_STATE_REQUESTED, ++ ADJUSTMENT_STATE_COMMITTED_TO_HW, ++}; ++ ++/* AdjustmentInfo structure - it keeps either ranged data or discrete*/ ++struct adjustment_info { ++ enum adjustment_data_type adj_data_type; ++ union adjustment_property adj_prop; ++ enum adjustment_state adj_state; ++ enum adjustment_id adj_id; ++ ++ union data { ++ struct ranged { ++ int32_t min; ++ int32_t max; ++ int32_t def; ++ int32_t step; ++ int32_t cur; ++ } ranged; ++ struct bit_vector { ++ int32_t system_supported; ++ int32_t current_supported; ++ int32_t default_val; ++ } bit_vector; ++ } adj_data; ++}; ++ ++/* adjustment category ++this should be a MASK struct with the bitfileds!!! ++since it could be crt and cv and dfp!!! ++the only fit is for overlay!!!*/ ++enum adjustment_category { ++ CAT_ALL, ++ CAT_CRT, ++ CAT_DFP, ++ CAT_LCD, ++ CAT_OVERLAY, ++ CAT_INVALID ++}; ++ ++enum raw_gamma_ramp_type { ++ GAMMA_RAMP_TYPE_UNINITIALIZED, ++ GAMMA_RAMP_TYPE_DEFAULT, ++ GAMMA_RAMP_TYPE_RGB256, ++ GAMMA_RAMP_TYPE_FIXED_POINT ++}; ++ ++struct raw_gamma_ramp_rgb { ++ uint32_t red; ++ uint32_t green; ++ uint32_t blue; ++}; ++ ++#define NUM_OF_RAW_GAMMA_RAMP_RGB_256 256 ++struct raw_gamma_ramp { ++ enum raw_gamma_ramp_type type; ++ struct raw_gamma_ramp_rgb rgb_256[NUM_OF_RAW_GAMMA_RAMP_RGB_256]; ++ uint32_t size; ++}; ++ ++struct ds_underscan_info { ++ uint32_t default_width; ++ uint32_t default_height; ++ uint32_t max_width; ++ uint32_t max_height; ++ uint32_t min_width; ++ uint32_t min_height; ++ uint32_t h_step; ++ uint32_t v_step; ++ uint32_t default_x_pos; ++ uint32_t default_y_pos; ++}; ++ ++struct ds_overscan { ++ uint32_t left; ++ uint32_t right; ++ uint32_t top; ++ uint32_t bottom; ++}; ++ ++enum ds_color_space { ++ DS_COLOR_SPACE_UNKNOWN = 0, ++ DS_COLOR_SPACE_SRGB_FULLRANGE = 1, ++ DS_COLOR_SPACE_SRGB_LIMITEDRANGE, ++ DS_COLOR_SPACE_YPBPR601, ++ DS_COLOR_SPACE_YPBPR709, ++ DS_COLOR_SPACE_YCBCR601, ++ DS_COLOR_SPACE_YCBCR709, ++ DS_COLOR_SPACE_NMVPU_SUPERAA, ++ DS_COLOR_SPACE_YCBCR601_YONLY, ++ DS_COLOR_SPACE_YCBCR709_YONLY/*same as YCbCr, but Y in Full range*/ ++}; ++ ++enum ds_underscan_options { ++ DS_UNDERSCAN_OPTION_DEFAULT = 0, ++ DS_UNDERSCAN_OPTION_USECEA861D ++}; ++ ++enum dpms_state { ++ DPMS_NONE = 0, ++ DPMS_ON, ++ DPMS_OFF, ++}; ++ ++enum ds_gamut_reference { ++ DS_GAMUT_REFERENCE_DESTINATION = 0, ++ DS_GAMUT_REFERENCE_SOURCE, ++}; ++ ++enum ds_gamut_content { ++ DS_GAMUT_CONTENT_GRAPHICS = 0, ++ DS_GAMUT_CONTENT_VIDEO, ++}; ++ ++struct ds_gamut_reference_data { ++ enum ds_gamut_reference gamut_ref; ++ enum ds_gamut_content gamut_content; ++}; ++ ++union ds_custom_gamut_type { ++ uint32_t u32all; ++ struct { ++ uint32_t CUSTOM_WHITE_POINT:1; ++ uint32_t CUSTOM_GAMUT_SPACE:1; ++ uint32_t reserved:30; ++ } bits; ++}; ++ ++union ds_gamut_spaces { ++ uint32_t u32all; ++ struct { ++ uint32_t GAMUT_SPACE_CCIR709:1; ++ uint32_t GAMUT_SPACE_CCIR601:1; ++ uint32_t GAMUT_SPACE_ADOBERGB:1; ++ uint32_t GAMUT_SPACE_CIERGB:1; ++ uint32_t GAMUT_SPACE_CUSTOM:1; ++ uint32_t reserved:27; ++ } bits; ++}; ++ ++union ds_gamut_white_point { ++ uint32_t u32all; ++ struct { ++ uint32_t GAMUT_WHITE_POINT_5000:1; ++ uint32_t GAMUT_WHITE_POINT_6500:1; ++ uint32_t GAMUT_WHITE_POINT_7500:1; ++ uint32_t GAMUT_WHITE_POINT_9300:1; ++ uint32_t GAMUT_WHITE_POINT_CUSTOM:1; ++ uint32_t reserved:27; ++ } bits; ++}; ++ ++struct ds_gamut_space_coordinates { ++ int32_t red_x; ++ int32_t red_y; ++ int32_t green_x; ++ int32_t green_y; ++ int32_t blue_x; ++ int32_t blue_y; ++ ++}; ++ ++struct ds_white_point_coordinates { ++ int32_t white_x; ++ int32_t white_y; ++}; ++ ++struct ds_gamut_data { ++ union ds_custom_gamut_type feature; ++ union { ++ uint32_t predefined; ++ struct ds_white_point_coordinates custom; ++ ++ } white_point; ++ ++ union { ++ uint32_t predefined; ++ struct ds_gamut_space_coordinates custom; ++ ++ } gamut; ++}; ++ ++struct ds_set_gamut_data { ++ struct ds_gamut_reference_data ref; ++ struct ds_gamut_data gamut; ++ ++}; ++ ++struct ds_get_gamut_data { ++ struct ds_gamut_data gamut; ++}; ++ ++struct ds_gamut_info { ++/*mask of supported predefined gamuts ,started from DI_GAMUT_SPACE_CCIR709 ...*/ ++ union ds_gamut_spaces gamut_space; ++/*mask of supported predefined white points,started from DI_WHITE_POINT_5000K */ ++ union ds_gamut_white_point white_point; ++ ++}; ++ ++union ds_regamma_flags { ++ uint32_t u32all; ++ struct { ++ /*custom/user gamam array is in use*/ ++ uint32_t GAMMA_RAMP_ARRAY:1; ++ /*gamma from edid is in use*/ ++ uint32_t GAMMA_FROM_EDID:1; ++ /*gamma from edid is in use , but only for Display Id 1.2*/ ++ uint32_t GAMMA_FROM_EDID_EX:1; ++ /*user custom gamma is in use*/ ++ uint32_t GAMMA_FROM_USER:1; ++ /*coeff. A0-A3 from user is in use*/ ++ uint32_t COEFF_FROM_USER:1; ++ /*coeff. A0-A3 from edid is in use only for Display Id 1.2*/ ++ uint32_t COEFF_FROM_EDID:1; ++ /*which ROM to choose for graphics*/ ++ uint32_t GRAPHICS_DEGAMMA_SRGB:1; ++ /*which ROM to choose for video overlay*/ ++ uint32_t OVERLAY_DEGAMMA_SRGB:1; ++ /*apply degamma removal in driver*/ ++ uint32_t APPLY_DEGAMMA:1; ++ ++ uint32_t reserved:23; ++ } bits; ++}; ++ ++struct ds_regamma_ramp { ++ uint16_t gamma[256 * 3]; /* gamma ramp packed as RGB */ ++ ++}; ++ ++struct ds_regamma_coefficients_ex { ++ int32_t gamma[3];/*2400 use divider 1 000*/ ++ int32_t coeff_a0[3];/*31308 divider 10 000 000,0-red, 1-green, 2-blue*/ ++ int32_t coeff_a1[3];/*12920 use divider 1 000*/ ++ int32_t coeff_a2[3];/*55 use divider 1 000*/ ++ int32_t coeff_a3[3];/*55 use divider 1 000*/ ++}; ++ ++struct ds_regamma_lut { ++ union ds_regamma_flags flags; ++ union { ++ struct ds_regamma_ramp gamma; ++ struct ds_regamma_coefficients_ex coeff; ++ }; ++}; ++ ++enum ds_backlight_optimization { ++ DS_BACKLIGHT_OPTIMIZATION_DISABLE = 0, ++ DS_BACKLIGHT_OPTIMIZATION_DESKTOP, ++ DS_BACKLIGHT_OPTIMIZATION_DYNAMIC, ++ DS_BACKLIGHT_OPTIMIZATION_DIMMED ++}; ++ ++struct ds_adj_id_value { ++ enum adjustment_id adj_id; ++ enum adjustment_data_type adj_type; ++ union adjustment_property adj_prop; ++ int32_t value; ++}; ++ ++struct gamut_data { ++ union ds_custom_gamut_type option; ++ union { ++ union ds_gamut_white_point predefined; ++ struct ds_white_point_coordinates custom; ++ ++ } white_point; ++ ++ union { ++ union ds_gamut_spaces predefined; ++ struct ds_gamut_space_coordinates custom; ++ ++ } gamut; ++}; ++#endif /* __DAL_ADJUSTMENT_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/asic_capability_interface.h b/drivers/gpu/drm/amd/dal/include/asic_capability_interface.h +new file mode 100644 +index 0000000..bdeaaf9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/asic_capability_interface.h +@@ -0,0 +1,58 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of enc software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and enc permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ASIC_CAPABILITY_INTERFACE_H__ ++#define __DAL_ASIC_CAPABILITY_INTERFACE_H__ ++ ++/* Include */ ++#include "include/asic_capability_types.h" ++ ++/* Forward declaration */ ++struct hw_asic_id; ++ ++ ++/* ASIC capability */ ++struct asic_capability { ++ struct dc_context *ctx; ++ struct asic_caps caps; ++ struct asic_stereo_3d_caps stereo_3d_caps; ++ struct asic_bugs bugs; ++ struct dal_asic_runtime_flags runtime_flags; ++ uint32_t data[ASIC_DATA_MAX_NUMBER]; ++}; ++ ++ ++/** ++ * Interfaces ++ */ ++ ++/* Create and initialize ASIC capability */ ++struct asic_capability *dal_asic_capability_create(struct hw_asic_id *init, ++ struct dc_context *ctx); ++ ++/* Destroy ASIC capability and free memory space */ ++void dal_asic_capability_destroy(struct asic_capability **cap); ++ ++#endif /* __DAL_ASIC_CAPABILITY_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/asic_capability_types.h b/drivers/gpu/drm/amd/dal/include/asic_capability_types.h +new file mode 100644 +index 0000000..1cb9776 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/asic_capability_types.h +@@ -0,0 +1,134 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_ASIC_CAPABILITY_TYPES_H__ ++#define __DAL_ASIC_CAPABILITY_TYPES_H__ ++ ++/* ++ * ASIC Capabilities ++ */ ++struct asic_caps { ++ bool CONSUMER_SINGLE_SELECTED_TIMING:1; ++ bool UNDERSCAN_ADJUST:1; ++ bool DELTA_SIGMA_SUPPORT:1; ++ bool PANEL_SELF_REFRESH_SUPPORTED:1; ++ bool IS_FUSION:1; ++ bool DP_MST_SUPPORTED:1; ++ bool UNDERSCAN_FOR_HDMI_ONLY:1; ++ bool DVI_CLOCK_SHARE_CAPABILITY:1; ++ bool SUPPORT_CEA861E_FINAL:1; ++ bool MIRABILIS_SUPPORTED:1; ++ bool MIRABILIS_ENABLED_BY_DEFAULT:1; ++ bool DEVICE_TAG_REMAP_SUPPORTED:1; ++ bool HEADLESS_NO_OPM_SUPPORTED:1; ++ bool WIRELESS_LIMIT_TO_720P:1; ++ bool WIRELESS_FULL_TIMING_ADJUSTMENT:1; ++ bool WIRELESS_TIMING_ADJUSTMENT:1; ++ bool WIRELESS_COMPRESSED_AUDIO:1; ++ bool VCE_SUPPORTED:1; ++ bool HPD_CHECK_FOR_EDID:1; ++ bool NO_VCC_OFF_HPD_POLLING:1; ++ bool NEED_MC_TUNING:1; ++ bool SKIP_PSR_WAIT_FOR_PLL_LOCK_BIT:1; ++ bool DFSBYPASS_DYNAMIC_SUPPORT:1; ++ bool SUPPORT_8BPP:1; ++}; ++ ++ ++/* ++ * ASIC Stereo 3D Caps ++ */ ++struct asic_stereo_3d_caps { ++ bool SUPPORTED:1; ++ bool DISPLAY_BASED_ON_WS:1; ++ bool HDMI_FRAME_PACK:1; ++ bool INTERLACE_FRAME_PACK:1; ++ bool DISPLAYPORT_FRAME_PACK:1; ++ bool DISPLAYPORT_FRAME_ALT:1; ++ bool INTERLEAVE:1; ++}; ++ ++ ++/* ++ * ASIC Bugs ++ */ ++struct asic_bugs { ++ bool MST_SYMBOL_MISALIGNMENT:1; ++ bool PSR_2X_LANE_GANGING:1; ++ bool LB_WA_IS_SUPPORTED:1; ++ bool ROM_REGISTER_ACCESS:1; ++ bool PSR_WA_OVERSCAN_CRC_ERROR:1; ++}; ++ ++ ++/* ++ * ASIC Data ++ */ ++enum asic_data { ++ ASIC_DATA_FIRST = 0, ++ ASIC_DATA_CONTROLLERS_NUM = ASIC_DATA_FIRST, ++ ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM, ++ ASIC_DATA_DCE_VERSION, ++ ASIC_DATA_DCE_VERSION_MINOR, ++ ASIC_DATA_VRAM_TYPE, ++ ASIC_DATA_VRAM_BITWIDTH, ++ ASIC_DATA_FEATURE_FLAGS, ++ ASIC_DATA_LINEBUFFER_NUM, ++ ASIC_DATA_LINEBUFFER_SIZE, ++ ASIC_DATA_DRAM_BANDWIDTH_EFFICIENCY, ++ ASIC_DATA_MC_LATENCY, ++ ASIC_DATA_MC_LATENCY_SLOW, ++ ASIC_DATA_CLOCKSOURCES_NUM, ++ ASIC_DATA_MEMORYTYPE_MULTIPLIER, ++ ASIC_DATA_STUTTERMODE, ++ ASIC_DATA_PATH_NUM_PER_DPMST_CONNECTOR, ++ ASIC_DATA_MAX_COFUNC_NONDP_DISPLAYS, ++ ASIC_DATA_REVISION_ID, ++ ASIC_DATA_MAX_UNDERSCAN_PERCENTAGE, ++ ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY, ++ ASIC_DATA_DIGFE_NUM, ++ ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM, ++ ASIC_DATA_MIN_DISPCLK_FOR_UNDERSCAN, ++ ASIC_DATA_NUM_OF_VIDEO_PLANES, ++ ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ, ++ ASIC_DATA_MAX_NUMBER /* end of enum */ ++}; ++ ++ ++/* ++ * ASIC Feature Flags ++ */ ++struct asic_feature_flags { ++ union { ++ uint32_t raw; ++ struct { ++ uint32_t LEGACY_CLIENT:1; ++ uint32_t PACKED_PIXEL_FORMAT:1; ++ uint32_t WORKSTATION_STEREO:1; ++ uint32_t WORKSTATION:1; ++ } bits; ++ }; ++}; ++ ++#endif /* __DAL_ASIC_CAPABILITY_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/audio_interface.h b/drivers/gpu/drm/amd/dal/include/audio_interface.h +new file mode 100644 +index 0000000..bf21762 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/audio_interface.h +@@ -0,0 +1,184 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_AUDIO_INTERFACE_H__ ++#define __DAL_AUDIO_INTERFACE_H__ ++ ++#include "audio_types.h" ++#include "adapter_service_interface.h" ++#include "signal_types.h" ++#include "link_service_types.h" ++ ++/* forward declaration */ ++struct audio; ++struct dal_adapter_service; ++ ++/***** audio initialization data *****/ ++/* ++ * by audio, it means audio endpoint id. ASIC may have many endpoints. ++ * upper sw layer will create one audio object instance for each endpoints. ++ * ASIC support internal audio only. So enum number is used to differ ++ * each endpoint ++ */ ++struct audio_init_data { ++ struct adapter_service *as; ++ struct graphics_object_id audio_stream_id; ++ struct dc_context *ctx; ++}; ++ ++enum audio_result { ++ AUDIO_RESULT_OK, ++ AUDIO_RESULT_ERROR, ++}; ++ ++/****** audio object create, destroy ******/ ++struct audio *dal_audio_create( ++ const struct audio_init_data *init_data); ++ ++void dal_audio_destroy( ++ struct audio **audio); ++ ++/****** graphics object interface ******/ ++const struct graphics_object_id dal_audio_get_graphics_object_id( ++ const struct audio *audio); ++ ++/* Enumerate Graphics Object supported Input/Output Signal Types */ ++uint32_t dal_audio_enumerate_input_signals( ++ struct audio *audio); ++ ++uint32_t dal_audio_enumerate_output_signals( ++ struct audio *audio); ++ ++/* Check if signal supported by GraphicsObject */ ++bool dal_audio_is_input_signal_supported( ++ struct audio *audio, ++ enum signal_type signal); ++ ++bool dal_audio_is_output_signal_supported( ++ struct audio *audio, ++ enum signal_type signal); ++ ++ ++/***** programming interface *****/ ++ ++/* perform power up sequence (boot up, resume, recovery) */ ++enum audio_result dal_audio_power_up( ++ struct audio *audio); ++ ++/* perform power down (shut down, stand by) */ ++enum audio_result dal_audio_power_down( ++ struct audio *audio); ++ ++/* setup audio */ ++enum audio_result dal_audio_setup( ++ struct audio *audio, ++ struct audio_output *output, ++ struct audio_info *info); ++ ++/* enable audio */ ++enum audio_result dal_audio_enable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++/* disable audio */ ++enum audio_result dal_audio_disable_output( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++/* enable azalia audio endpoint */ ++enum audio_result dal_audio_enable_azalia_audio_jack_presence( ++ struct audio *audio, ++ enum engine_id engine_id); ++ ++/* disable azalia audio endpoint */ ++enum audio_result dal_audio_disable_azalia_audio_jack_presence( ++ struct audio *audio, ++ enum engine_id engine_id); ++ ++/* unmute audio */ ++enum audio_result dal_audio_unmute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++/* mute audio */ ++enum audio_result dal_audio_mute( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal); ++ ++ ++/***** information interface *****/ ++ ++struct audio_feature_support dal_audio_get_supported_features( ++ struct audio *audio); ++ ++/* get audio bandwidth information */ ++void dal_audio_check_audio_bandwidth( ++ struct audio *audio, ++ const struct audio_crtc_info *info, ++ uint32_t channel_count, ++ enum signal_type signal, ++ union audio_sample_rates *sample_rates); ++ ++/* Enable multi channel split */ ++void dal_audio_enable_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ enum signal_type signal, ++ const struct audio_channel_associate_info *audio_mapping, ++ bool enable); ++ ++/* get current multi channel split. */ ++enum audio_result dal_audio_get_channel_splitting_mapping( ++ struct audio *audio, ++ enum engine_id engine_id, ++ struct audio_channel_associate_info *audio_mapping); ++ ++/* set payload value for the unsolicited response */ ++void dal_audio_set_unsolicited_response_payload( ++ struct audio *audio, ++ enum audio_payload payload); ++ ++/*Assign GTC group and enable GTC value embedding*/ ++void dal_audio_enable_gtc_embedding_with_group( ++ struct audio *audio, ++ uint32_t group_num, ++ uint32_t audio_latency); ++ ++/* Disable GTC value embedding */ ++void dal_audio_disable_gtc_embedding( ++ struct audio *audio); ++ ++/* Update audio wall clock source */ ++void dal_audio_setup_audio_wall_dto( ++ struct audio *audio, ++ enum signal_type signal, ++ const struct audio_crtc_info *crtc_info, ++ const struct audio_pll_info *pll_info); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/audio_types.h b/drivers/gpu/drm/amd/dal/include/audio_types.h +new file mode 100644 +index 0000000..e9c2ab3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/audio_types.h +@@ -0,0 +1,275 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __AUDIO_TYPES_H__ ++#define __AUDIO_TYPES_H__ ++ ++#include "grph_object_defs.h" ++#include "signal_types.h" ++ ++#define AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS 20 ++#define MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS 18 ++#define MULTI_CHANNEL_SPLIT_NO_ASSO_INFO 0xFFFFFFFF ++ ++ ++struct audio_pll_hw_settings { ++ uint32_t feed_back_divider; ++ uint32_t step_size_integer; ++ uint32_t step_size_fraction; ++ uint32_t step_range; ++}; ++ ++struct audio_clock_info { ++ /* pixel clock frequency*/ ++ uint32_t pixel_clock_in_10khz; ++ /* N - 32KHz audio */ ++ uint32_t n_32khz; ++ /* CTS - 32KHz audio*/ ++ uint32_t cts_32khz; ++ uint32_t n_44khz; ++ uint32_t cts_44khz; ++ uint32_t n_48khz; ++ uint32_t cts_48khz; ++}; ++ ++struct azalia_clock_info { ++ uint32_t pixel_clock_in_10khz; ++ uint32_t audio_dto_phase; ++ uint32_t audio_dto_module; ++ uint32_t audio_dto_wall_clock_ratio; ++}; ++ ++enum audio_dto_source { ++ DTO_SOURCE_UNKNOWN = 0, ++ DTO_SOURCE_ID0, ++ DTO_SOURCE_ID1, ++ DTO_SOURCE_ID2, ++ DTO_SOURCE_ID3, ++ DTO_SOURCE_ID4, ++ DTO_SOURCE_ID5 ++}; ++ ++union audio_sample_rates { ++ struct sample_rates { ++ uint8_t RATE_32:1; ++ uint8_t RATE_44_1:1; ++ uint8_t RATE_48:1; ++ uint8_t RATE_88_2:1; ++ uint8_t RATE_96:1; ++ uint8_t RATE_176_4:1; ++ uint8_t RATE_192:1; ++ } rate; ++ ++ uint8_t all; ++}; ++ ++enum audio_format_code { ++ AUDIO_FORMAT_CODE_FIRST = 1, ++ AUDIO_FORMAT_CODE_LINEARPCM = AUDIO_FORMAT_CODE_FIRST, ++ ++ AUDIO_FORMAT_CODE_AC3, ++ /*Layers 1 & 2 */ ++ AUDIO_FORMAT_CODE_MPEG1, ++ /*MPEG1 Layer 3 */ ++ AUDIO_FORMAT_CODE_MP3, ++ /*multichannel */ ++ AUDIO_FORMAT_CODE_MPEG2, ++ AUDIO_FORMAT_CODE_AAC, ++ AUDIO_FORMAT_CODE_DTS, ++ AUDIO_FORMAT_CODE_ATRAC, ++ AUDIO_FORMAT_CODE_1BITAUDIO, ++ AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS, ++ AUDIO_FORMAT_CODE_DTS_HD, ++ AUDIO_FORMAT_CODE_MAT_MLP, ++ AUDIO_FORMAT_CODE_DST, ++ AUDIO_FORMAT_CODE_WMAPRO, ++ AUDIO_FORMAT_CODE_LAST, ++ AUDIO_FORMAT_CODE_COUNT = ++ AUDIO_FORMAT_CODE_LAST - AUDIO_FORMAT_CODE_FIRST ++}; ++ ++struct audio_mode { ++ /* ucData[0] [6:3] */ ++ enum audio_format_code format_code; ++ /* ucData[0] [2:0] */ ++ uint8_t channel_count; ++ /* ucData[1] */ ++ union audio_sample_rates sample_rates; ++ union { ++ /* for LPCM */ ++ uint8_t sample_size; ++ /* for Audio Formats 2-8 (Max bit rate divided by 8 kHz) */ ++ uint8_t max_bit_rate; ++ /* for Audio Formats 9-15 */ ++ uint8_t vendor_specific; ++ }; ++}; ++ ++struct audio_info_flags { ++ ++ union { ++ ++ struct audio_speaker_flags { ++ uint32_t FL_FR:1; ++ uint32_t LFE:1; ++ uint32_t FC:1; ++ uint32_t RL_RR:1; ++ uint32_t RC:1; ++ uint32_t FLC_FRC:1; ++ uint32_t RLC_RRC:1; ++ uint32_t SUPPORT_AI:1; ++ } speaker_flags; ++ ++ struct audio_speaker_info { ++ uint32_t ALLSPEAKERS:7; ++ uint32_t SUPPORT_AI:1; ++ } info; ++ ++ uint8_t all; ++ }; ++}; ++ ++ ++/*struct audio_info_flags { ++ struct audio_speaker_flags { ++ uint32_t FL_FR:1; ++ uint32_t LFE:1; ++ uint32_t FC:1; ++ uint32_t RL_RR:1; ++ uint32_t RC:1; ++ uint32_t FLC_FRC:1; ++ uint32_t RLC_RRC:1; ++ uint32_t SUPPORT_AI:1; ++ }; ++ ++ struct audio_speaker_info { ++ uint32_t ALLSPEAKERS:7; ++ uint32_t SUPPORT_AI:1; ++ }; ++ ++ union { ++ struct audio_speaker_flags speaker_flags; ++ struct audio_speaker_info info; ++ }; ++}; ++*/ ++ ++union audio_cea_channels { ++ uint8_t all; ++ struct audio_cea_channels_bits { ++ uint32_t FL:1; ++ uint32_t FR:1; ++ uint32_t LFE:1; ++ uint32_t FC:1; ++ uint32_t RL_RC:1; ++ uint32_t RR:1; ++ uint32_t RC_RLC_FLC:1; ++ uint32_t RRC_FRC:1; ++ } channels; ++}; ++ ++struct audio_info { ++ struct audio_info_flags flags; ++ uint32_t video_latency; ++ uint32_t audio_latency; ++ uint32_t display_index; ++ uint8_t display_name[AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS]; ++ uint32_t manufacture_id; ++ uint32_t product_id; ++ /* PortID used for ContainerID when defined */ ++ uint32_t port_id[2]; ++ uint32_t mode_count; ++ /* this field must be last in this struct */ ++ struct audio_mode modes[DC_MAX_AUDIO_DESC_COUNT]; ++}; ++ ++struct audio_crtc_info { ++ uint32_t h_total; ++ uint32_t h_active; ++ uint32_t v_active; ++ uint32_t pixel_repetition; ++ uint32_t requested_pixel_clock; /* in KHz */ ++ uint32_t calculated_pixel_clock; /* in KHz */ ++ uint32_t refresh_rate; ++ enum dc_color_depth color_depth; ++ bool interlaced; ++}; ++ ++/* PLL information required for AZALIA DTO calculation */ ++ ++struct audio_pll_info { ++ uint32_t dp_dto_source_clock_in_khz; ++ uint32_t feed_back_divider; ++ enum audio_dto_source dto_source; ++ bool ss_enabled; ++ uint32_t ss_percentage; ++ uint32_t ss_percentage_divider; ++}; ++ ++struct audio_channel_associate_info { ++ union { ++ struct { ++ uint32_t ALL_CHANNEL_FL:4; ++ uint32_t ALL_CHANNEL_FR:4; ++ uint32_t ALL_CHANNEL_FC:4; ++ uint32_t ALL_CHANNEL_Sub:4; ++ uint32_t ALL_CHANNEL_SL:4; ++ uint32_t ALL_CHANNEL_SR:4; ++ uint32_t ALL_CHANNEL_BL:4; ++ uint32_t ALL_CHANNEL_BR:4; ++ } bits; ++ uint32_t u32all; ++ }; ++}; ++ ++struct audio_output { ++ /* Front DIG id. */ ++ enum engine_id engine_id; ++ /* encoder output signal */ ++ enum signal_type signal; ++ /* video timing */ ++ struct audio_crtc_info crtc_info; ++ /* PLL for audio */ ++ struct audio_pll_info pll_info; ++}; ++ ++struct audio_feature_support { ++ /* supported engines*/ ++ uint32_t ENGINE_DIGA:1; ++ uint32_t ENGINE_DIGB:1; ++ uint32_t ENGINE_DIGC:1; ++ uint32_t ENGINE_DIGD:1; ++ uint32_t ENGINE_DIGE:1; ++ uint32_t ENGINE_DIGF:1; ++ uint32_t ENGINE_DIGG:1; ++ uint32_t ENGINE_DVO:1; ++ uint32_t MULTISTREAM_AUDIO:1; ++}; ++ ++enum audio_payload { ++ CHANNEL_SPLIT_MAPPINGCHANG = 0x9, ++}; ++ ++#endif /* __AUDIO_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/bios_parser_interface.h b/drivers/gpu/drm/amd/dal/include/bios_parser_interface.h +new file mode 100644 +index 0000000..6269164 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/bios_parser_interface.h +@@ -0,0 +1,294 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_BIOS_PARSER_INTERFACE_H__ ++#define __DAL_BIOS_PARSER_INTERFACE_H__ ++ ++#include "bios_parser_types.h" ++#include "adapter_service_types.h" ++#include "gpio_types.h" ++ ++struct adapter_service; ++struct bios_parser; ++ ++struct bp_gpio_cntl_info { ++ uint32_t id; ++ enum gpio_pin_output_state state; ++}; ++ ++enum bp_result { ++ BP_RESULT_OK = 0, /* There was no error */ ++ BP_RESULT_BADINPUT, /*Bad input parameter */ ++ BP_RESULT_BADBIOSTABLE, /* Bad BIOS table */ ++ BP_RESULT_UNSUPPORTED, /* BIOS Table is not supported */ ++ BP_RESULT_NORECORD, /* Record can't be found */ ++ BP_RESULT_FAILURE ++}; ++ ++struct bp_init_data { ++ struct dc_context *ctx; ++ uint8_t *bios; ++}; ++ ++struct bios_parser *dal_bios_parser_create( ++ struct bp_init_data *init, ++ struct adapter_service *as); ++void dal_bios_parser_destroy( ++ struct bios_parser **bp); ++void dal_bios_parser_power_down( ++ struct bios_parser *bp); ++void dal_bios_parser_power_up( ++ struct bios_parser *bp); ++ ++uint8_t dal_bios_parser_get_encoders_number( ++ struct bios_parser *bp); ++uint8_t dal_bios_parser_get_connectors_number( ++ struct bios_parser *bp); ++uint32_t dal_bios_parser_get_oem_ddc_lines_number( ++ struct bios_parser *bp); ++struct graphics_object_id dal_bios_parser_get_encoder_id( ++ struct bios_parser *bp, ++ uint32_t i); ++struct graphics_object_id dal_bios_parser_get_connector_id( ++ struct bios_parser *bp, ++ uint8_t connector_index); ++uint32_t dal_bios_parser_get_src_number( ++ struct bios_parser *bp, ++ struct graphics_object_id id); ++uint32_t dal_bios_parser_get_dst_number( ++ struct bios_parser *bp, ++ struct graphics_object_id id); ++uint32_t dal_bios_parser_get_gpio_record( ++ struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct bp_gpio_cntl_info *gpio_record, ++ uint32_t record_size); ++enum bp_result dal_bios_parser_get_src_obj( ++ struct bios_parser *bp, ++ struct graphics_object_id object_id, uint32_t index, ++ struct graphics_object_id *src_object_id); ++enum bp_result dal_bios_parser_get_dst_obj( ++ struct bios_parser *bp, ++ struct graphics_object_id object_id, uint32_t index, ++ struct graphics_object_id *dest_object_id); ++enum bp_result dal_bios_parser_get_i2c_info( ++ struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct graphics_object_i2c_info *info); ++enum bp_result dal_bios_parser_get_oem_ddc_info( ++ struct bios_parser *bp, ++ uint32_t index, ++ struct graphics_object_i2c_info *info); ++enum bp_result dal_bios_parser_get_voltage_ddc_info( ++ struct bios_parser *bp, ++ uint32_t index, ++ struct graphics_object_i2c_info *info); ++enum bp_result dal_bios_parser_get_thermal_ddc_info( ++ struct bios_parser *bp, ++ uint32_t i2c_channel_id, ++ struct graphics_object_i2c_info *info); ++enum bp_result dal_bios_parser_get_hpd_info( ++ struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct graphics_object_hpd_info *info); ++enum bp_result dal_bios_parser_get_device_tag( ++ struct bios_parser *bp, ++ struct graphics_object_id connector_object_id, ++ uint32_t device_tag_index, ++ struct connector_device_tag_info *info); ++enum bp_result dal_bios_parser_get_firmware_info( ++ struct bios_parser *bp, ++ struct firmware_info *info); ++enum bp_result dal_bios_parser_get_spread_spectrum_info( ++ struct bios_parser *bp, ++ enum as_signal_type signal, ++ uint32_t index, ++ struct spread_spectrum_info *ss_info); ++uint32_t dal_bios_parser_get_ss_entry_number( ++ struct bios_parser *bp, ++ enum as_signal_type signal); ++enum bp_result dal_bios_parser_get_embedded_panel_info( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info); ++enum bp_result dal_bios_parser_enum_embedded_panel_patch_mode( ++ struct bios_parser *bp, ++ uint32_t index, ++ struct embedded_panel_patch_mode *mode); ++enum bp_result dal_bios_parser_get_gpio_pin_info( ++ struct bios_parser *bp, ++ uint32_t gpio_id, ++ struct gpio_pin_info *info); ++enum bp_result dal_bios_parser_get_embedded_panel_info( ++ struct bios_parser *bp, ++ struct embedded_panel_info *info); ++enum bp_result dal_bios_parser_get_gpio_pin_info( ++ struct bios_parser *bp, ++ uint32_t gpio_id, ++ struct gpio_pin_info *info); ++enum bp_result dal_bios_parser_get_faked_edid_len( ++ struct bios_parser *bp, ++ uint32_t *len); ++enum bp_result dal_bios_parser_get_faked_edid_buf( ++ struct bios_parser *bp, ++ uint8_t *buff, ++ uint32_t len); ++enum bp_result dal_bios_parser_get_encoder_cap_info( ++ struct bios_parser *bp, ++ struct graphics_object_id object_id, ++ struct bp_encoder_cap_info *info); ++enum bp_result dal_bios_parser_get_din_connector_info( ++ struct bios_parser *bp, ++ struct graphics_object_id id, ++ struct din_connector_info *info); ++ ++bool dal_bios_parser_is_lid_open( ++ struct bios_parser *bp); ++bool dal_bios_parser_is_lid_status_changed( ++ struct bios_parser *bp); ++bool dal_bios_parser_is_display_config_changed( ++ struct bios_parser *bp); ++bool dal_bios_parser_is_accelerated_mode( ++ struct bios_parser *bp); ++void dal_bios_parser_set_scratch_lcd_scale( ++ struct bios_parser *bp, ++ enum lcd_scale scale); ++enum lcd_scale dal_bios_parser_get_scratch_lcd_scale( ++ struct bios_parser *bp); ++void dal_bios_parser_get_bios_event_info( ++ struct bios_parser *bp, ++ struct bios_event_info *info); ++void dal_bios_parser_update_requested_backlight_level( ++ struct bios_parser *bp, ++ uint32_t backlight_8bit); ++uint32_t dal_bios_parser_get_requested_backlight_level( ++ struct bios_parser *bp); ++void dal_bios_parser_take_backlight_control( ++ struct bios_parser *bp, ++ bool cntl); ++bool dal_bios_parser_is_active_display( ++ struct bios_parser *bp, ++ enum signal_type signal, ++ const struct connector_device_tag_info *device_tag); ++enum controller_id dal_bios_parser_get_embedded_display_controller_id( ++ struct bios_parser *bp); ++uint32_t dal_bios_parser_get_embedded_display_refresh_rate( ++ struct bios_parser *bp); ++void dal_bios_parser_set_scratch_connected( ++ struct bios_parser *bp, ++ struct graphics_object_id connector_id, ++ bool connected, ++ const struct connector_device_tag_info *device_tag); ++void dal_bios_parser_prepare_scratch_active_and_requested( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ enum signal_type signal, ++ const struct connector_device_tag_info *device_tag); ++void dal_bios_parser_set_scratch_active_and_requested( ++ struct bios_parser *bp); ++void dal_bios_parser_set_scratch_critical_state( ++ struct bios_parser *bp, ++ bool state); ++void dal_bios_parser_set_scratch_acc_mode_change( ++ struct bios_parser *bp); ++ ++bool dal_bios_parser_is_device_id_supported( ++ struct bios_parser *bp, ++ struct device_id id); ++ ++/* COMMANDS */ ++ ++enum bp_result dal_bios_parser_encoder_control( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++enum bp_result dal_bios_parser_transmitter_control( ++ struct bios_parser *bp, ++ struct bp_transmitter_control *cntl); ++enum bp_result dal_bios_parser_crt_control( ++ struct bios_parser *bp, ++ enum engine_id engine_id, ++ bool enable, ++ uint32_t pixel_clock); ++enum bp_result dal_bios_parser_dvo_encoder_control( ++ struct bios_parser *bp, ++ struct bp_dvo_encoder_control *cntl); ++enum bp_result dal_bios_parser_enable_crtc( ++ struct bios_parser *bp, ++ enum controller_id id, ++ bool enable); ++enum bp_result dal_bios_parser_adjust_pixel_clock( ++ struct bios_parser *bp, ++ struct bp_adjust_pixel_clock_parameters *bp_params); ++enum bp_result dal_bios_parser_set_pixel_clock( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++enum bp_result dal_bios_parser_enable_spread_spectrum_on_ppll( ++ struct bios_parser *bp, ++ struct bp_spread_spectrum_parameters *bp_params, ++ bool enable); ++enum bp_result dal_bios_parser_program_crtc_timing( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_timing_parameters *bp_params); ++enum bp_result dal_bios_parser_blank_crtc( ++ struct bios_parser *bp, ++ struct bp_blank_crtc_parameters *bp_params, ++ bool blank); ++enum bp_result dal_bios_parser_set_overscan( ++ struct bios_parser *bp, ++ struct bp_hw_crtc_overscan_parameters *bp_params); ++enum bp_result dal_bios_parser_crtc_source_select( ++ struct bios_parser *bp, ++ struct bp_crtc_source_select *bp_params); ++enum bp_result dal_bios_parser_program_display_engine_pll( ++ struct bios_parser *bp, ++ struct bp_pixel_clock_parameters *bp_params); ++enum bp_result dal_bios_parser_get_divider_for_target_display_clock( ++ struct bios_parser *bp, ++ struct bp_display_clock_parameters *bp_params); ++enum signal_type dal_bios_parser_dac_load_detect( ++ struct bios_parser *bp, ++ struct graphics_object_id encoder, ++ struct graphics_object_id connector, ++ enum signal_type display_signal); ++enum bp_result dal_bios_parser_enable_memory_requests( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ bool enable); ++enum bp_result dal_bios_parser_external_encoder_control( ++ struct bios_parser *bp, ++ struct bp_external_encoder_control *cntl); ++enum bp_result dal_bios_parser_enable_disp_power_gating( ++ struct bios_parser *bp, ++ enum controller_id controller_id, ++ enum bp_pipe_control_action action); ++ ++void dal_bios_parser_post_init(struct bios_parser *bp); ++ ++/* Parse integrated BIOS info */ ++struct integrated_info *dal_bios_parser_create_integrated_info( ++ struct bios_parser *bp); ++ ++/* Destroy provided integrated info */ ++void dal_bios_parser_destroy_integrated_info(struct dc_context *ctx, struct integrated_info **info); ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/bios_parser_types.h b/drivers/gpu/drm/amd/dal/include/bios_parser_types.h +new file mode 100644 +index 0000000..da7d5f2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/bios_parser_types.h +@@ -0,0 +1,305 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_BIOS_PARSER_TYPES_H__ ++#define __DAL_BIOS_PARSER_TYPES_H__ ++ ++#include "include/signal_types.h" ++#include "include/grph_object_ctrl_defs.h" ++#include "link_service_types.h" ++ ++enum bp_encoder_control_action { ++ /* direct VBIOS translation! Just to simplify the translation */ ++ ENCODER_CONTROL_DISABLE = 0, ++ ENCODER_CONTROL_ENABLE, ++ ENCODER_CONTROL_SETUP, ++ ENCODER_CONTROL_INIT ++}; ++ ++enum bp_transmitter_control_action { ++ /* direct VBIOS translation! Just to simplify the translation */ ++ TRANSMITTER_CONTROL_DISABLE = 0, ++ TRANSMITTER_CONTROL_ENABLE, ++ TRANSMITTER_CONTROL_BACKLIGHT_OFF, ++ TRANSMITTER_CONTROL_BACKLIGHT_ON, ++ TRANSMITTER_CONTROL_BACKLIGHT_BRIGHTNESS, ++ TRANSMITTER_CONTROL_LCD_SETF_TEST_START, ++ TRANSMITTER_CONTROL_LCD_SELF_TEST_STOP, ++ TRANSMITTER_CONTROL_INIT, ++ TRANSMITTER_CONTROL_DEACTIVATE, ++ TRANSMITTER_CONTROL_ACTIAVATE, ++ TRANSMITTER_CONTROL_SETUP, ++ TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS, ++ /* ATOM_TRANSMITTER_ACTION_POWER_ON. This action is for eDP only ++ * (power up the panel) ++ */ ++ TRANSMITTER_CONTROL_POWER_ON, ++ /* ATOM_TRANSMITTER_ACTION_POWER_OFF. This action is for eDP only ++ * (power down the panel) ++ */ ++ TRANSMITTER_CONTROL_POWER_OFF ++}; ++ ++enum bp_external_encoder_control_action { ++ EXTERNAL_ENCODER_CONTROL_DISABLE = 0, ++ EXTERNAL_ENCODER_CONTROL_ENABLE = 1, ++ EXTERNAL_ENCODER_CONTROL_INIT = 0x7, ++ EXTERNAL_ENCODER_CONTROL_SETUP = 0xf, ++ EXTERNAL_ENCODER_CONTROL_UNBLANK = 0x10, ++ EXTERNAL_ENCODER_CONTROL_BLANK = 0x11, ++ EXTERNAL_ENCODER_CONTROL_DAC_LOAD_DETECT = 0x12 ++}; ++ ++enum bp_pipe_control_action { ++ ASIC_PIPE_DISABLE = 0, ++ ASIC_PIPE_ENABLE, ++ ASIC_PIPE_INIT ++}; ++ ++struct bp_encoder_control { ++ enum bp_encoder_control_action action; ++ enum engine_id engine_id; ++ enum transmitter transmitter; ++ enum signal_type signal; ++ enum lane_count lanes_number; ++ enum dc_color_depth color_depth; ++ bool enable_dp_audio; ++ uint32_t pixel_clock; /* khz */ ++}; ++ ++struct bp_external_encoder_control { ++ enum bp_external_encoder_control_action action; ++ enum engine_id engine_id; ++ enum link_rate link_rate; ++ enum lane_count lanes_number; ++ enum signal_type signal; ++ enum dc_color_depth color_depth; ++ bool coherent; ++ struct graphics_object_id encoder_id; ++ struct graphics_object_id connector_obj_id; ++ uint32_t pixel_clock; /* in KHz */ ++}; ++ ++struct bp_crtc_source_select { ++ enum engine_id engine_id; ++ enum controller_id controller_id; ++ /* from GPU Tx aka asic_signal */ ++ enum signal_type signal; ++ /* sink_signal may differ from asicSignal if Translator encoder */ ++ enum signal_type sink_signal; ++ enum display_output_bit_depth display_output_bit_depth; ++ bool enable_dp_audio; ++}; ++ ++struct bp_transmitter_control { ++ enum bp_transmitter_control_action action; ++ enum engine_id engine_id; ++ enum transmitter transmitter; /* PhyId */ ++ enum lane_count lanes_number; ++ enum clock_source_id pll_id; /* needed for DCE 4.0 */ ++ enum signal_type signal; ++ enum dc_color_depth color_depth; /* not used for DCE6.0 */ ++ enum hpd_source_id hpd_sel; /* ucHPDSel, used for DCe6.0 */ ++ struct graphics_object_id connector_obj_id; ++ /* symClock; in 10kHz, pixel clock, in HDMI deep color mode, it should ++ * be pixel clock * deep_color_ratio (in KHz) ++ */ ++ uint32_t pixel_clock; ++ uint32_t lane_select; ++ uint32_t lane_settings; ++ bool coherent; ++ bool multi_path; ++ bool single_pll_mode; ++}; ++ ++enum dvo_encoder_memory_rate { ++ DVO_ENCODER_MEMORY_RATE_DDR, ++ DVO_ENCODER_MEMORY_RATE_SDR ++}; ++ ++enum dvo_encoder_interface_width { ++ DVO_ENCODER_INTERFACE_WIDTH_LOW12BIT, ++ DVO_ENCODER_INTERFACE_WIDTH_HIGH12BIT, ++ DVO_ENCODER_INTERFACE_WIDTH_FULL24BIT ++}; ++ ++struct bp_dvo_encoder_control { ++ enum bp_encoder_control_action action; ++ enum dvo_encoder_memory_rate memory_rate; ++ enum dvo_encoder_interface_width interface_width; ++ uint32_t pixel_clock; /* in KHz */ ++}; ++ ++struct bp_blank_crtc_parameters { ++ enum controller_id controller_id; ++ uint32_t black_color_rcr; ++ uint32_t black_color_gy; ++ uint32_t black_color_bcb; ++}; ++ ++struct bp_hw_crtc_timing_parameters { ++ enum controller_id controller_id; ++ /* horizontal part */ ++ uint32_t h_total; ++ uint32_t h_addressable; ++ uint32_t h_overscan_left; ++ uint32_t h_overscan_right; ++ uint32_t h_sync_start; ++ uint32_t h_sync_width; ++ ++ /* vertical part */ ++ uint32_t v_total; ++ uint32_t v_addressable; ++ uint32_t v_overscan_top; ++ uint32_t v_overscan_bottom; ++ uint32_t v_sync_start; ++ uint32_t v_sync_width; ++ ++ struct timing_flags { ++ uint32_t INTERLACE:1; ++ uint32_t PIXEL_REPETITION:4; ++ uint32_t HSYNC_POSITIVE_POLARITY:1; ++ uint32_t VSYNC_POSITIVE_POLARITY:1; ++ uint32_t HORZ_COUNT_BY_TWO:1; ++ } flags; ++}; ++ ++struct bp_hw_crtc_overscan_parameters { ++ enum controller_id controller_id; ++ uint32_t h_overscan_left; ++ uint32_t h_overscan_right; ++ uint32_t v_overscan_top; ++ uint32_t v_overscan_bottom; ++}; ++ ++struct bp_adjust_pixel_clock_parameters { ++ /* Input: Signal Type - to be converted to Encoder mode */ ++ enum signal_type signal_type; ++ /* Input: required by V3, display pll configure parameter defined as ++ * following DISPPLL_CONFIG_XXXX */ ++ enum disp_pll_config display_pll_config; ++ /* Input: Encoder object id */ ++ struct graphics_object_id encoder_object_id; ++ /* Input: Pixel Clock (requested Pixel clock based on Video timing ++ * standard used) in KHz ++ */ ++ uint32_t pixel_clock; ++ union { ++ /* Input: If DVO, need passing link rate and output 12bit low or ++ * 24bit to VBIOS Exec table */ ++ uint32_t dvo_config; ++ /* Input: If non DVO, not defined yet */ ++ uint32_t non_dvo_undefined; ++ }; ++ /* Output: Adjusted Pixel Clock (after VBIOS exec table) in KHz */ ++ uint32_t adjusted_pixel_clock; ++ /* Output: If non-zero, this refDiv value should be used to calculate ++ * other ppll params */ ++ uint32_t reference_divider; ++ /* Output: If non-zero, this postDiv value should be used to calculate ++ * other ppll params */ ++ uint32_t pixel_clock_post_divider; ++ /* Input: Enable spread spectrum */ ++ bool ss_enable; ++}; ++ ++struct bp_pixel_clock_parameters { ++ enum controller_id controller_id; /* (Which CRTC uses this PLL) */ ++ enum clock_source_id pll_id; /* Clock Source Id */ ++ /* signal_type -> Encoder Mode - needed by VBIOS Exec table */ ++ enum signal_type signal_type; ++ /* Adjusted Pixel Clock (after VBIOS exec table) ++ * that becomes Target Pixel Clock (KHz) */ ++ uint32_t target_pixel_clock; ++ /* Calculated Reference divider of Display PLL */ ++ uint32_t reference_divider; ++ /* Calculated Feedback divider of Display PLL */ ++ uint32_t feedback_divider; ++ /* Calculated Fractional Feedback divider of Display PLL */ ++ uint32_t fractional_feedback_divider; ++ /* Calculated Pixel Clock Post divider of Display PLL */ ++ uint32_t pixel_clock_post_divider; ++ struct graphics_object_id encoder_object_id; /* Encoder object id */ ++ /* If DVO, need passing link rate and output 12bit low or ++ * 24bit to VBIOS Exec table */ ++ uint32_t dvo_config; ++ /* VBIOS returns a fixed display clock when DFS-bypass feature ++ * is enabled (KHz) */ ++ uint32_t dfs_bypass_display_clock; ++ struct program_pixel_clock_flags { ++ uint32_t FORCE_PROGRAMMING_OF_PLL:1; ++ /* Use Engine Clock as source for Display Clock when ++ * programming PLL */ ++ uint32_t USE_E_CLOCK_AS_SOURCE_FOR_D_CLOCK:1; ++ /* Use external reference clock (refDivSrc for PLL) */ ++ uint32_t SET_EXTERNAL_REF_DIV_SRC:1; ++ } flags; ++}; ++ ++struct bp_display_clock_parameters { ++ uint32_t target_display_clock; /* KHz */ ++ /* Actual Display Clock set due to clock divider granularity KHz */ ++ uint32_t actual_display_clock; ++ /* Actual Post Divider ID used to generate the actual clock */ ++ uint32_t actual_post_divider_id; ++}; ++ ++struct spread_spectrum_flags { ++ /* 1 = Center Spread; 0 = down spread */ ++ uint32_t CENTER_SPREAD:1; ++ /* 1 = external; 0 = internal */ ++ uint32_t EXTERNAL_SS:1; ++ /* 1 = delta-sigma type parameter; 0 = ver1 */ ++ uint32_t DS_TYPE:1; ++}; ++ ++struct bp_spread_spectrum_parameters { ++ enum clock_source_id pll_id; ++ uint32_t percentage; ++ uint32_t ds_frac_amount; ++ ++ union { ++ struct { ++ uint32_t step; ++ uint32_t delay; ++ uint32_t range; /* In Hz unit */ ++ } ver1; ++ struct { ++ uint32_t feedback_amount; ++ uint32_t nfrac_amount; ++ uint32_t ds_frac_size; ++ } ds; ++ }; ++ ++ struct spread_spectrum_flags flags; ++}; ++ ++struct bp_encoder_cap_info { ++ uint32_t DP_HBR2_CAP:1; ++ uint32_t DP_HBR2_EN:1; ++ uint32_t RESERVED:30; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/bit_set.h b/drivers/gpu/drm/amd/dal/include/bit_set.h +new file mode 100644 +index 0000000..3cd8d32 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/bit_set.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_BIT_SET_H__ ++#define __DAL_BIT_SET_H__ ++ ++struct bit_set_iterator_32 { ++ uint32_t value; ++}; ++ ++static inline uint32_t least_significant_bit(uint32_t bs32_container) ++{ ++ return bs32_container & (0 - bs32_container); ++} ++/* iterates over bit_set_iterator by means of least significant bit purge*/ ++static inline uint32_t get_next_significant_bit( ++ struct bit_set_iterator_32 *bs32) ++{ ++ uint32_t lsb = least_significant_bit(bs32->value); ++ ++ bs32->value &= ~lsb; ++ return lsb; ++} ++ ++static inline void bit_set_iterator_reset_to_mask( ++ struct bit_set_iterator_32 *bs32, ++ uint32_t mask) ++{ ++ bs32->value = mask; ++} ++ ++static inline void bit_set_iterator_construct( ++ struct bit_set_iterator_32 *bs32, ++ uint32_t mask) ++{ ++ bit_set_iterator_reset_to_mask(bs32, mask); ++} ++ ++#endif /* __DAL_BIT_SET_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/clock_source_interface.h b/drivers/gpu/drm/amd/dal/include/clock_source_interface.h +new file mode 100644 +index 0000000..bea4c2b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/clock_source_interface.h +@@ -0,0 +1,89 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CLOCK_SOURCE_INTERFACE__ ++#define __DAL_CLOCK_SOURCE_INTERFACE__ ++ ++#include "include/clock_source_types.h" ++ ++struct clock_source; ++struct clock_source_init_data { ++ struct adapter_service *as; ++ struct graphics_object_id clk_src_id; ++ struct dc_context *ctx; ++}; ++ ++struct clock_source *dal_clock_source_create(struct clock_source_init_data *); ++ ++void dal_clock_source_destroy(struct clock_source **clk_src); ++ ++enum clock_source_id dal_clock_source_get_id( ++ const struct clock_source *clk_src); ++ ++bool dal_clock_source_is_clk_src_with_fixed_freq( ++ const struct clock_source *clk_src); ++ ++const struct graphics_object_id dal_clock_source_get_graphics_object_id( ++ const struct clock_source *clk_src); ++ ++enum clock_sharing_level dal_clock_souce_get_clk_sharing_lvl( ++ const struct clock_source *clk_src); ++ ++uint32_t dal_clock_source_get_pix_clk_dividers( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++ ++bool dal_clock_source_program_pix_clk( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings); ++ ++bool dal_clock_source_adjust_pxl_clk_by_ref_pixel_rate( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ uint32_t pix_clk_hz); ++ ++bool dal_clock_source_adjust_pxl_clk_by_pix_amount( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ int32_t pix_num); ++ ++uint32_t dal_clock_source_retreive_pix_rate_hz( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params); ++ ++bool dal_clock_source_power_down_pll(struct clock_source *clk_src, ++ enum controller_id); ++ ++bool dal_clock_source_is_clk_in_reset(struct clock_source *clk_src); ++ ++bool dal_clock_source_is_gen_lock_capable(struct clock_source *clk_src); ++ ++bool dal_clock_source_is_output_signal_supported( ++ const struct clock_source *clk_src, ++ enum signal_type signal_type); ++ ++#endif /*__DAL_CLOCK_SOURCE_INTERFACE__*/ +diff --git a/drivers/gpu/drm/amd/dal/include/clock_source_types.h b/drivers/gpu/drm/amd/dal/include/clock_source_types.h +new file mode 100644 +index 0000000..3883216 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/clock_source_types.h +@@ -0,0 +1,118 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CLOCK_SOURCE_TYPES_H__ ++#define __DAL_CLOCK_SOURCE_TYPES_H__ ++ ++#include "include/signal_types.h" ++#include "include/grph_object_ctrl_defs.h" ++ ++/** ++ * ClockSharingLevel ++ * Enumeration for clock sharing support. ++ * Level <x> means sharing supported on all levels below and including <x> ++ */ ++enum clock_sharing_level { ++ CLOCK_SHARING_LEVEL_NOT_SHAREABLE = 0, ++ CLOCK_SHARING_LEVEL_DP_MST_SHAREABLE, ++ CLOCK_SHARING_LEVEL_DISPLAY_PORT_SHAREABLE ++}; ++ ++/** ++ * Display Port HW De spread of Reference Clock related Parameters structure ++ * Store it once at boot for later usage ++ */ ++struct csdp_ref_clk_ds_params { ++ bool hw_dso_n_dp_ref_clk; ++/* Flag for HW De Spread enabled (if enabled SS on DP Reference Clock)*/ ++ uint32_t avg_dp_ref_clk_khz; ++/* Average DP Reference clock (in KHz)*/ ++ uint32_t ss_percentage_on_dp_ref_clk; ++/* DP Reference clock SS percentage ++ * (not to be mixed with DP IDCLK SS from PLL Settings)*/ ++ uint32_t ss_percentage_divider; ++/* DP Reference clock SS percentage divider */ ++}; ++ ++/** ++ * Pixel Clock Parameters structure ++ * These parameters are required as input ++ * when calculating Pixel Clock Dividers for requested Pixel Clock ++ */ ++struct pixel_clk_flags { ++ uint32_t ENABLE_SS:1; ++ uint32_t DISPLAY_BLANKED:1; ++ uint32_t PROGRAM_PIXEL_CLOCK:1; ++ uint32_t PROGRAM_ID_CLOCK:1; ++}; ++ ++struct pixel_clk_params { ++ uint32_t requested_pix_clk; /* in KHz */ ++/*> Requested Pixel Clock ++ * (based on Video Timing standard used for requested mode)*/ ++ uint32_t requested_sym_clk; /* in KHz */ ++/*> Requested Sym Clock (relevant only for display port)*/ ++ uint32_t dp_ref_clk; /* in KHz */ ++/*> DP reference clock - calculated only for DP signal for specific cases*/ ++ struct graphics_object_id encoder_object_id; ++/*> Encoder object Id - needed by VBIOS Exec table*/ ++ enum signal_type signal_type; ++/*> signalType -> Encoder Mode - needed by VBIOS Exec table*/ ++ enum controller_id controller_id; ++/*> ControllerId - which controller using this PLL*/ ++ enum dc_color_depth color_depth; ++ struct csdp_ref_clk_ds_params de_spread_params; ++/*> de-spread info, relevant only for on-the-fly tune-up pixel rate*/ ++ ++ uint32_t dvo_cfg; ++/*> If DVO, need passing link rate ++ * and output 12bit low or 24bit to VBIOS Exec table*/ ++ ++ enum disp_pll_config disp_pll_cfg; ++ struct pixel_clk_flags flags; ++}; ++ ++/** ++ * Pixel Clock Dividers structure with desired Pixel Clock ++ * (adjusted after VBIOS exec table), ++ * with actually calculated Clock and reference Crystal frequency ++ */ ++struct pll_settings { ++ uint32_t actual_pix_clk; ++ uint32_t adjusted_pix_clk; ++ uint32_t calculated_pix_clk; ++ uint32_t vco_freq; ++ uint32_t reference_freq; ++ uint32_t reference_divider; ++ uint32_t feedback_divider; ++ uint32_t fract_feedback_divider; ++ uint32_t pix_clk_post_divider; ++ uint32_t ss_percentage; ++ bool use_external_clk; ++}; ++ ++#define MAX_PLL_CALC_ERROR 0xFFFFFFFF ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/connector_interface.h b/drivers/gpu/drm/amd/dal/include/connector_interface.h +new file mode 100644 +index 0000000..e09af7e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/connector_interface.h +@@ -0,0 +1,82 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_CONNECTOR_INTERFACE_H__ ++#define __DAL_CONNECTOR_INTERFACE_H__ ++ ++#include "adapter_service_interface.h" ++#include "signal_types.h" ++ ++/* forward declaration */ ++struct connector; ++ ++struct connector_signals { ++ const enum signal_type *signal; ++ uint32_t number_of_signals; ++}; ++ ++struct connector_feature_support { ++ bool HPD_FILTERING:1; ++ bool HW_DDC_POLLING:1; ++ enum hpd_source_id hpd_line; ++ enum channel_id ddc_line; ++}; ++ ++void dal_connector_get_features( ++ const struct connector *con, ++ struct connector_feature_support *cfs); ++ ++struct connector *dal_connector_create( ++ struct dc_context *ctx, ++ struct adapter_service *as, ++ struct graphics_object_id go_id); ++ ++void dal_connector_destroy(struct connector **connector); ++ ++void dal_connector_destroy(struct connector **connector); ++ ++const struct graphics_object_id dal_connector_get_graphics_object_id( ++ const struct connector *connector); ++ ++uint32_t dal_connector_enumerate_output_signals( ++ const struct connector *connector); ++uint32_t dal_connector_enumerate_input_signals( ++ const struct connector *connector); ++ ++struct connector_signals dal_connector_get_default_signals( ++ const struct connector *connector); ++ ++bool dal_connector_program_hpd_filter( ++ const struct connector *connector, ++ const uint32_t delay_on_connect_in_ms, ++ const uint32_t delay_on_disconnect_in_ms); ++ ++bool dal_connector_enable_ddc_polling( ++ const struct connector *connector, ++ const bool is_poll_for_connect); ++ ++bool dal_connector_disable_ddc_polling(const struct connector *connector); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/dal_asic_id.h b/drivers/gpu/drm/amd/dal/include/dal_asic_id.h +new file mode 100644 +index 0000000..fa04f80 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dal_asic_id.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ASIC_ID_H__ ++#define __DAL_ASIC_ID_H__ ++ ++/* ++ * ASIC internal revision ID ++ */ ++ ++/* DCE80 (based on ci_id.h in Perforce) */ ++ ++#define CI_BONAIRE_M_A0 0x14 ++#define CI_BONAIRE_M_A1 0x15 ++#define CI_HAWAII_P_A0 0x28 ++ ++#define CI_UNKNOWN 0xFF ++ ++#define ASIC_REV_IS_BONAIRE_M(rev) \ ++ ((rev >= CI_BONAIRE_M_A0) && (rev < CI_HAWAII_P_A0)) ++ ++#define ASIC_REV_IS_HAWAII_P(rev) \ ++ (rev >= CI_HAWAII_P_A0) ++ ++/* KV1 with Spectre GFX core, 8-8-1-2 (CU-Pix-Primitive-RB) */ ++#define KV_SPECTRE_A0 0x01 ++ ++/* KV2 with Spooky GFX core, including downgraded from Spectre core, ++ * 3-4-1-1 (CU-Pix-Primitive-RB) */ ++#define KV_SPOOKY_A0 0x41 ++ ++/* KB with Kalindi GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ ++#define KB_KALINDI_A0 0x81 ++ ++/* KB with Kalindi GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ ++#define KB_KALINDI_A1 0x82 ++ ++/* BV with Kalindi GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ ++#define BV_KALINDI_A2 0x85 ++ ++/* ML with Godavari GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ ++#define ML_GODAVARI_A0 0xA1 ++ ++/* ML with Godavari GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ ++#define ML_GODAVARI_A1 0xA2 ++ ++#define KV_UNKNOWN 0xFF ++ ++#define ASIC_REV_IS_KALINDI(rev) \ ++ ((rev >= KB_KALINDI_A0) && (rev < KV_UNKNOWN)) ++ ++#define ASIC_REV_IS_BHAVANI(rev) \ ++ ((rev >= BV_KALINDI_A2) && (rev < ML_GODAVARI_A0)) ++ ++#define ASIC_REV_IS_GODAVARI(rev) \ ++ ((rev >= ML_GODAVARI_A0) && (rev < KV_UNKNOWN)) ++ ++/* DCE11 */ ++#define CZ_CARRIZO_A0 0x01 ++ ++#define STONEY_A0 0x61 ++#define CZ_UNKNOWN 0xFF ++ ++#define ASIC_REV_IS_STONEY(rev) \ ++ ((rev >= STONEY_A0) && (rev < CZ_UNKNOWN)) ++ ++/* ++ * ASIC chip ID ++ */ ++/* DCE80 */ ++#define DEVICE_ID_KALINDI_9834 0x9834 ++#define DEVICE_ID_TEMASH_9839 0x9839 ++#define DEVICE_ID_TEMASH_983D 0x983D ++ ++ ++/* Asic Family IDs for different asic family. */ ++#define FAMILY_CI 120 /* Sea Islands: Hawaii (P), Bonaire (M) */ ++#define FAMILY_KV 125 /* Fusion => Kaveri: Spectre, Spooky; Kabini: Kalindi */ ++#define FAMILY_VI 130 /* Volcanic Islands: Iceland (V), Tonga (M) */ ++#define FAMILY_CZ 135 /* Carrizo */ ++ ++#define FAMILY_UNKNOWN 0xFF ++ ++#endif /* __DAL_ASIC_ID_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dal_register_logger.h b/drivers/gpu/drm/amd/dal/include/dal_register_logger.h +new file mode 100644 +index 0000000..176d811 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dal_register_logger.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_REGISTER_LOGGER__ ++#define __DAL_REGISTER_LOGGER__ ++ ++/**************** ++ * API functions ++ ***************/ ++ ++/* dal_reg_logger_push - begin Register Logging */ ++void dal_reg_logger_push(const char *caller_func); ++/* dal_reg_logger_pop - stop Register Logging */ ++void dal_reg_logger_pop(void); ++ ++ ++/* for internal use of the Logger only */ ++void dal_reg_logger_rw_count_increment(void); ++bool dal_reg_logger_should_dump_register(void); ++ ++#endif /* __DAL_REGISTER_LOGGER__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dal_types.h b/drivers/gpu/drm/amd/dal/include/dal_types.h +new file mode 100644 +index 0000000..d3c91b9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dal_types.h +@@ -0,0 +1,292 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_TYPES_H__ ++#define __DAL_TYPES_H__ ++ ++#include "dcs_types.h" ++struct dal_logger; ++ ++enum dce_version { ++ DCE_VERSION_UNKNOWN = (-1), ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ DCE_VERSION_11_0, ++#endif ++ DCE_VERSION_MAX ++}; ++ ++/* ++ * ASIC Runtime Flags ++ */ ++struct dal_asic_runtime_flags { ++ union { ++ uint32_t raw; ++ struct { ++ uint32_t EMULATE_REPLUG_ON_CAP_CHANGE:1; ++ uint32_t SUPPORT_XRBIAS:1; ++ uint32_t SKIP_POWER_DOWN_ON_RESUME:1; ++ uint32_t FULL_DETECT_ON_RESUME:1; ++ uint32_t GSL_FRAMELOCK:1; ++ uint32_t NO_LOW_BPP_MODES:1; ++ uint32_t BLOCK_ON_INITIAL_DETECTION:1; ++ uint32_t OPTIMIZED_DISPLAY_PROGRAMMING_ON_BOOT:1; ++ uint32_t DRIVER_CONTROLLED_BRIGHTNESS:1; ++ uint32_t MODIFIABLE_FRAME_DURATION:1; ++ uint32_t MIRACAST_SUPPORTED:1; ++ uint32_t CONNECTED_STANDBY_SUPPORTED:1; ++ uint32_t GNB_WAKEUP_SUPPORTED:1; ++ } bits; ++ } flags; ++}; ++ ++struct hw_asic_id { ++ uint32_t chip_id; ++ uint32_t chip_family; ++ uint32_t pci_revision_id; ++ uint32_t hw_internal_rev; ++ uint32_t vram_type; ++ uint32_t vram_width; ++ uint32_t feature_flags; ++ struct dal_asic_runtime_flags runtime_flags; ++ uint32_t fake_paths_num; ++ void *atombios_base_address; ++}; ++ ++/* this is pci information. BDF stands for BUS,DEVICE,FUNCTION*/ ++ ++struct bdf_info { ++ uint16_t BUS_NUMBER:8; ++ uint16_t DEVICE_NUMBER:5; ++ uint16_t FUNCTION_NUMBER:3; ++}; ++ ++#define DAL_PARAM_INVALID_INT 0x80000000 ++ ++/* shift values for bool override parameter mask ++ * bmask is for this struct,if we touch this feature ++ * bval indicates every bit fields for this struct too,1 is enable this feature ++ * amdgpu.disp_bval=1594, amdgpu.disp_bmask=1594 , ++ * finally will show log like this: ++ * Overridden FEATURE_LIGHT_SLEEP is enabled now ++ * Overridden FEATURE_USE_MAX_DISPLAY_CLK is enabled now ++ * Overridden FEATURE_ENABLE_DFS_BYPASS is enabled now ++ * Overridden FEATURE_POWER_GATING_PIPE_IN_TILE is enabled now ++ * Overridden FEATURE_USE_PPLIB is enabled now ++ * Overridden FEATURE_DISABLE_LPT_SUPPORT is enabled now ++ * Overridden FEATURE_DUMMY_FBC_BACKEND is enabled now */ ++enum bool_param_shift { ++ DAL_PARAM_MAXIMIZE_STUTTER_MARKS = 0, ++ DAL_PARAM_LIGHT_SLEEP, ++ DAL_PARAM_MAXIMIZE_URGENCY_WATERMARKS, ++ DAL_PARAM_USE_MAX_DISPLAY_CLK, ++ DAL_PARAM_ENABLE_DFS_BYPASS, ++ DAL_PARAM_POWER_GATING_PIPE_IN_TILE, ++ DAL_PARAM_POWER_GATING_LB_PORTION, ++ DAL_PARAM_PSR_ENABLE, ++ DAL_PARAM_VARI_BRIGHT_ENABLE, ++ DAL_PARAM_USE_PPLIB, ++ DAL_PARAM_DISABLE_LPT_SUPPORT, ++ DAL_PARAM_DUMMY_FBC_BACKEND, ++ DAL_PARAM_ENABLE_GPU_SCALING, ++ DAL_BOOL_PARAM_MAX ++}; ++ ++/* array index for integer override parameters*/ ++enum int_param_array_index { ++ DAL_PARAM_MAX_COFUNC_NON_DP_DISPLAYS = 0, ++ DAL_PARAM_DRR_SUPPORT, ++ DAL_INT_PARAM_MAX ++}; ++ ++struct dal_override_parameters { ++ uint32_t bool_param_enable_mask; ++ uint32_t bool_param_values; ++ uint32_t int_param_values[DAL_INT_PARAM_MAX]; ++}; ++ ++ ++struct dal_init_data { ++ struct hw_asic_id asic_id; ++ struct view_port_alignment vp_alignment; ++ struct bdf_info bdf_info; ++ struct dal_override_parameters display_param; ++ void *driver; /* ctx */ ++ void *cgs_device; ++}; ++ ++struct dal_dc_init_data { ++ struct dc_context *ctx; /* TODO: remove 'dal' when DC is complete. */ ++ struct adapter_service *adapter_srv; ++}; ++ ++struct dal_dev_c_lut { ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++ uint8_t reserved; ++}; ++ ++struct dal_dev_gamma_lut { ++ uint16_t red; ++ uint16_t green; ++ uint16_t blue; ++}; ++ ++struct dc_context { ++ struct dc *dc; ++ ++ void *driver_context; /* e.g. amdgpu_device */ ++ ++ struct dal_logger *logger; ++ void *cgs_device; ++}; ++ ++/* Wireless display structs */ ++ ++union dal_remote_display_cea_mode_bitmap { ++ struct { ++ uint32_t CEA_640X480_60P:1;/*0*/ ++ uint32_t CEA_720X480_60P:1;/*1*/ ++ uint32_t CEA_720X480_60I:1;/*2*/ ++ uint32_t CEA_720X576_50P:1;/*3*/ ++ uint32_t CEA_720X576_50I:1;/*4*/ ++ uint32_t CEA_1280X720_30P:1;/*5*/ ++ uint32_t CEA_1280X720_60P:1;/*6*/ ++ uint32_t CEA_1920X1080_30P:1;/*7*/ ++ uint32_t CEA_1920X1080_60P:1;/*8*/ ++ uint32_t CEA_1920X1080_60I:1;/*9*/ ++ uint32_t CEA_1280X720_25P:1;/*10*/ ++ uint32_t CEA_1280X728_50P:1;/*11*/ ++ uint32_t CEA_1920X1080_25P:1;/*12*/ ++ uint32_t CEA_1920X1080_50P:1;/*13*/ ++ uint32_t CEA_1920X1080_50I:1;/*14*/ ++ uint32_t CEA_1280X1024_24P:1;/*15*/ ++ uint32_t CEA_1920X1080_24P:1;/*16*/ ++ uint32_t RESERVED:15;/*[17-31]*/ ++ } flags; ++ uint32_t raw; ++}; ++ ++union dal_remote_display_vesa_mode_bitmap { ++ struct { ++ uint32_t VESA_800X600_30P:1;/*0*/ ++ uint32_t VESA_800X600_60P:1;/*1*/ ++ uint32_t VESA_1024X768_30P:1;/*2*/ ++ uint32_t VESA_1024X768_60P:1;/*3*/ ++ uint32_t VESA_1152X864_30P:1;/*4*/ ++ uint32_t VESA_1152X864_60P:1;/*5*/ ++ uint32_t VESA_1280X768_30P:1;/*6*/ ++ uint32_t VESA_1280X768_60P:1;/*7*/ ++ uint32_t VESA_1280X800_30P:1;/*8*/ ++ uint32_t VESA_1280X800_60P:1;/*9*/ ++ uint32_t VESA_1360X768_30P:1;/*10*/ ++ uint32_t VESA_1360X768_60P:1;/*11*/ ++ uint32_t VESA_1366X768_30P:1;/*12*/ ++ uint32_t VESA_1366X768_60P:1;/*13*/ ++ uint32_t VESA_1280X1024_30P:1;/*14*/ ++ uint32_t VESA_1280X1024_60P:1;/*15*/ ++ uint32_t VESA_1400X1050_30P:1;/*16*/ ++ uint32_t VESA_1400X1050_60P:1;/*17*/ ++ uint32_t VESA_1440X900_30P:1;/*18*/ ++ uint32_t VESA_1440X900_60P:1;/*19*/ ++ uint32_t VESA_1600X900_30P:1;/*20*/ ++ uint32_t VESA_1600X900_60P:1;/*21*/ ++ uint32_t VESA_1600X1200_30P:1;/*22*/ ++ uint32_t VESA_1600X1200_60P:1;/*23*/ ++ uint32_t VESA_1680X1024_30P:1;/*24*/ ++ uint32_t VESA_1680X1024_60P:1;/*25*/ ++ uint32_t VESA_1680X1050_30P:1;/*26*/ ++ uint32_t VESA_1680X1050_60P:1;/*27*/ ++ uint32_t VESA_1920X1200_30P:1;/*28*/ ++ uint32_t VESA_1920X1200_60P:1;/*29*/ ++ uint32_t RESERVED:2;/*[30-31]*/ ++ } flags; ++ uint32_t raw; ++}; ++ ++union dal_remote_display_hh_mode_bitmap { ++ struct { ++ uint32_t HH_800X480_30P:1;/*0*/ ++ uint32_t HH_800X480_60P:1;/*1*/ ++ uint32_t HH_854X480_30P:1;/*2*/ ++ uint32_t HH_854X480_60P:1;/*3*/ ++ uint32_t HH_864X480_30P:1;/*4*/ ++ uint32_t HH_864X480_60P:1;/*5*/ ++ uint32_t HH_640X360_30P:1;/*6*/ ++ uint32_t HH_640X360_60P:1;/*7*/ ++ uint32_t HH_960X540_30P:1;/*8*/ ++ uint32_t HH_960X540_60P:1;/*9*/ ++ uint32_t HH_848X480_30P:1;/*10*/ ++ uint32_t HH_848X480_60P:1;/*11*/ ++ uint32_t RESERVED:20;/*[12-31]*/ ++ } flags; ++ uint32_t raw; ++}; ++ ++union dal_remote_display_stereo_3d_mode_bitmap { ++ struct { ++ uint32_t STEREO_1920X1080_24P_TOP_AND_BOTTOM:1;/*0*/ ++ uint32_t STEREO_1280X720_60P_TOP_AND_BOTTOM:1;/*1*/ ++ uint32_t STEREO_1280X720_50P_TOP_AND_BOTTOM:1;/*2*/ ++ uint32_t STEREO_1920X1080_24X2P_FRAME_ALTERNATE:1;/*3*/ ++ uint32_t STEREO_1280X720_60X2P_FRAME_ALTERNATE:1;/*4*/ ++ uint32_t STEREO_1280X720_30X2P_FRAME_ALTERNATE:1;/*5*/ ++ uint32_t STEREO_1280X720_50X2P_FRAME_ALTERNATE:1;/*6*/ ++ uint32_t STEREO_1280X720_25X2P_FRAME_ALTERNATE:1;/*7*/ ++ uint32_t STEREO_1920X1080_24P_FRAME_PACKING:1;/* 8*/ ++ uint32_t STEREO_1280X720_60P_FRAME_PACKING:1;/* 9*/ ++ uint32_t STEREO_1280X720_30P_FRAME_PACKING:1;/*10*/ ++ uint32_t STEREO_1280X720_50P_FRAME_PACKING:1;/*11*/ ++ uint32_t STEREO_1280X720_25P_FRAME_PACKING:1;/*12*/ ++ uint32_t RESERVED:19; /*[13-31]*/ ++ } flags; ++ uint32_t raw; ++}; ++ ++union dal_remote_display_audio_bitmap { ++ struct { ++ uint32_t LPCM_44100HZ_16BITS_2_CHANNELS:1;/*0*/ ++ uint32_t LPCM_48000HZ_16BITS_2_CHANNELS:1;/*1*/ ++ uint32_t AAC_48000HZ_16BITS_2_CHANNELS:1;/*2*/ ++ uint32_t AAC_48000HZ_16BITS_4_CHANNELS:1;/*3*/ ++ uint32_t AAC_48000HZ_16BITS_6_CHANNELS:1;/*4*/ ++ uint32_t AAC_48000HZ_16BITS_8_CHANNELS:1;/*5*/ ++ uint32_t AC3_48000HZ_16BITS_2_CHANNELS:1;/*6*/ ++ uint32_t AC3_48000HZ_16BITS_4_CHANNELS:1;/*7*/ ++ uint32_t AC3_48000HZ_16BITS_6_CHANNELS:1;/*8*/ ++ uint32_t RESERVED:23;/*[9-31]*/ ++ } flags; ++ uint32_t raw; ++}; ++ ++struct dal_remote_display_receiver_capability { ++ union dal_remote_display_cea_mode_bitmap cea_mode; ++ union dal_remote_display_vesa_mode_bitmap vesa_mode; ++ union dal_remote_display_hh_mode_bitmap hh_mode; ++ union dal_remote_display_stereo_3d_mode_bitmap stereo_3d_mode; ++ union dal_remote_display_audio_bitmap audio; ++}; ++ ++#endif /* __DAL_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dc_clock_generator_interface.h b/drivers/gpu/drm/amd/dal/include/dc_clock_generator_interface.h +new file mode 100644 +index 0000000..b7fb9ff +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dc_clock_generator_interface.h +@@ -0,0 +1,77 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_CLOCK_GENERATOR_INTERFACE_H__ ++#define __DC_CLOCK_GENERATOR_INTERFACE_H__ ++ ++#include "grph_object_ctrl_defs.h" ++#include "set_mode_types.h" ++ ++/* Parameter for programming the DCCP_DISP_SLOW_SELECT*/ ++struct dccg_mapping_params { ++ uint32_t controllers_num; ++ enum controller_id *controllers; ++}; ++ ++/* Parameters related to HW DeSpread of DP Reference Clock*/ ++struct dccg_dp_ref_clk_ds_params { ++ struct { ++ /* Flag for Enabled SS on DP Reference Clock*/ ++ bool SS_ENABLED:1; ++ /* Flag for HW De Spread enabled ++ * (if enabled SS on DP Reference Clock)*/ ++ bool DS_ENABLED:1; ++ /* Flag for HW De Spread Calculations enabled for DS_DTO_INCR ++ * and DS_DTO_MODULO (if 0 SW programs DS_DTO_INCR and ++ * DS_DTO_MODULO)*/ ++ bool DS_CALCULATIONS:1; ++ } flags; ++ /*DP Reference clock SS percentage ++ * (if enabled downspread on DP Reference Clock)*/ ++ uint32_t ss_percentage; ++ /*DP Reference clock SS percentage Divider (1000 or 100)*/ ++ uint32_t ss_percentage_divider; ++}; ++ ++struct dc_clock_generator; ++ ++void dal_dc_clock_generator_destroy(struct dc_clock_generator **dc); ++void dal_dc_clock_generator_set_display_pipe_mapping( ++ struct dc_clock_generator *dc_clk_gen, ++ struct dccg_mapping_params *params); ++bool dal_dc_clock_generator_get_dp_ref_clk_ds_params( ++ struct dc_clock_generator *dc_clk_gen, ++ struct dccg_dp_ref_clk_ds_params *params); ++bool dal_dc_clock_generator_enable_gtc_counter( ++ struct dc_clock_generator *dc_clk_gen, ++ uint32_t dprefclk); ++void dal_dc_clock_generator_disable_gtc_counter( ++ struct dc_clock_generator *dc_clk_gen); ++void dal_dc_clock_generator_set_gtc_group_offset( ++ struct dc_clock_generator *dc_clk_gen, ++ enum gtc_group group_num, ++ uint32_t offset); ++ ++#endif /* __DC_CLOCK_GENERATOR_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dcs_interface.h b/drivers/gpu/drm/amd/dal/include/dcs_interface.h +new file mode 100644 +index 0000000..b3474cf +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dcs_interface.h +@@ -0,0 +1,351 @@ ++/* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_DCS_INTERFACE_H__ ++#define __DAL_DCS_INTERFACE_H__ ++ ++#include "dcs_types.h" ++#include "grph_object_id.h" ++ ++struct dal_context; ++struct dcs; ++struct ddc_service; ++enum ddc_transaction_type; ++enum ddc_result; ++struct display_sink_capability; ++enum dc_timing_3d_format; ++ ++struct dcs_cea_audio_mode_list; ++struct dcs_customized_mode_list; ++ ++struct dcs_init_data { ++ struct dal_context *dal; ++ struct adapter_service *as; ++ struct timing_service *ts; ++ enum dcs_interface_type interface_type; ++ struct graphics_object_id grph_obj_id; ++}; ++ ++struct dcs_cea_audio_mode_list *dal_dcs_cea_audio_mode_list_create( ++ uint32_t list_size); ++ ++void dal_dcs_cea_audio_mode_list_destroy( ++ struct dcs_cea_audio_mode_list **list); ++ ++bool dal_dcs_cea_audio_mode_list_append( ++ struct dcs_cea_audio_mode_list *list, ++ struct cea_audio_mode *cea_audio_mode); ++uint32_t dal_dcs_cea_audio_mode_list_get_count( ++ const struct dcs_cea_audio_mode_list *list); ++void dal_dcs_cea_audio_mode_list_clear( ++ struct dcs_cea_audio_mode_list *list); ++ ++struct cea_audio_mode *dal_dcs_cea_audio_mode_list_at_index( ++ const struct dcs_cea_audio_mode_list *list, ++ uint32_t index); ++ ++struct dcs *dal_dcs_create(const struct dcs_init_data *init_data); ++ ++void dal_dcs_destroy(struct dcs **dcs); ++ ++enum edid_retrieve_status dal_dcs_retrieve_raw_edid(struct dcs *dcs); ++ ++uint32_t dal_dcs_get_edid_raw_data_size(struct dcs *dcs); ++ ++enum edid_retrieve_status dal_dcs_override_raw_edid( ++ struct dcs *dcs, ++ uint32_t len, ++ uint8_t *data); ++ ++const uint8_t *dal_dcs_get_edid_raw_data( ++ struct dcs *dcs, ++ uint32_t *buff_size); ++ ++enum edid_retrieve_status dal_dcs_update_edid_from_last_retrieved( ++ struct dcs *dcs); ++ ++/*Update DDC Service. returns the old DdcService being replaced*/ ++struct ddc_service *dal_dcs_update_ddc( ++ struct dcs *dcs, ++ struct ddc_service *ddc); ++ ++void dal_dcs_set_transaction_type( ++ struct dcs *dcs, ++ enum ddc_transaction_type type); ++ ++/*updates the ModeTimingList of given path with ++ModeTiming reported by this DCS*/ ++void dal_dcs_update_ts_timing_list_on_display( ++ struct dcs *dcs, ++ uint32_t display_index); ++ ++/* DDC query on generic slave address*/ ++bool dal_dcs_query_ddc_data( ++ struct dcs *dcs, ++ uint32_t address, ++ uint8_t *write_buf, ++ uint32_t write_buff_size, ++ uint8_t *read_buff, ++ uint32_t read_buff_size); ++ ++bool dal_dcs_get_vendor_product_id_info( ++ struct dcs *dcs, ++ struct vendor_product_id_info *info); ++ ++bool dal_dcs_get_display_name(struct dcs *dcs, uint8_t *name, uint32_t size); ++ ++bool dal_dcs_get_display_characteristics( ++ struct dcs *dcs, ++ struct display_characteristics *characteristics); ++ ++bool dal_dcs_get_screen_info( ++ struct dcs *dcs, ++ struct edid_screen_info *info); ++ ++enum dcs_edid_connector_type dal_dcs_get_connector_type(struct dcs *dcs); ++ ++bool dal_dcs_get_display_pixel_encoding( ++ struct dcs *dcs, ++ struct display_pixel_encoding_support *pe); ++ ++enum display_dongle_type dal_dcs_get_dongle_type(struct dcs *dcs); ++ ++void dal_dcs_query_sink_capability( ++ struct dcs *dcs, ++ struct display_sink_capability *sink_cap, ++ bool hpd_sense_bit); ++ ++void dal_dcs_reset_sink_capability(struct dcs *dcs); ++ ++bool dal_dcs_get_sink_capability( ++ struct dcs *dcs, ++ struct display_sink_capability *sink_cap); ++ ++bool dal_dcs_emulate_sink_capability( ++ struct dcs *dcs, ++ struct display_sink_capability *sink_cap); ++ ++bool dal_dcs_get_display_color_depth( ++ struct dcs *dcs, ++ struct display_color_depth_support *color_depth); ++ ++bool dal_dcs_get_display_pixel_encoding( ++ struct dcs *dcs, ++ struct display_pixel_encoding_support *pixel_encoding); ++ ++bool dal_dcs_get_cea861_support( ++ struct dcs *dcs, ++ struct cea861_support *cea861_support); ++ ++bool dal_dcs_get_cea_vendor_specific_data_block( ++ struct dcs *dcs, ++ struct cea_vendor_specific_data_block *vendor_block); ++ ++bool dal_dcs_get_cea_speaker_allocation_data_block( ++ struct dcs *dcs, ++ enum signal_type signal, ++ union cea_speaker_allocation_data_block *spkr_data); ++ ++bool dal_dcs_get_cea_colorimetry_data_block( ++ struct dcs *dcs, ++ struct cea_colorimetry_data_block *colorimetry_data_block); ++ ++bool dal_dcs_get_cea_video_capability_data_block( ++ struct dcs *dcs, ++ union cea_video_capability_data_block *video_capability_data_block); ++ ++uint32_t dal_dcs_get_extensions_num(struct dcs *dcs); ++ ++const struct dcs_cea_audio_mode_list *dal_dcs_get_cea_audio_modes( ++ struct dcs *dcs, ++ enum signal_type signal); ++ ++bool dal_dcs_is_audio_supported(struct dcs *dcs); ++ ++bool dal_dcs_validate_customized_mode( ++ struct dcs *dcs, ++ const struct dcs_customized_mode *customized_mode); ++ ++bool dal_dcs_add_customized_mode( ++ struct dcs *dcs, ++ struct dcs_customized_mode *customized_mode); ++ ++bool dal_dcs_delete_customized_mode(struct dcs *dcs, uint32_t index); ++ ++const struct dcs_customized_mode_list *dal_dcs_get_customized_modes( ++ struct dcs *dcs); ++ ++bool dal_dcs_delete_mode_timing_override( ++ struct dcs *dcs, ++ struct dcs_override_mode_timing *dcs_mode_timing); ++ ++bool dal_dcs_set_mode_timing_override( ++ struct dcs *dcs, ++ uint32_t display_index, ++ struct dcs_override_mode_timing *dcs_mode_timing); ++ ++bool dal_dcs_get_timing_override_for_mode( ++ struct dcs *dcs, ++ uint32_t display_index, ++ struct dc_mode_info *mode_info, ++ struct dcs_override_mode_timing_list *dcs_mode_timing_list); ++ ++uint32_t dal_dcs_get_num_mode_timing_overrides(struct dcs *dcs); ++ ++bool dal_dcs_get_timing_override_list( ++ struct dcs *dcs, ++ uint32_t display_index, ++ struct dcs_override_mode_timing_list *dcs_mode_timing_list, ++ uint32_t size); ++ ++bool dal_dcs_get_supported_force_hdtv_mode( ++ struct dcs *dcs, ++ union hdtv_mode_support *hdtv_mode); ++ ++bool dal_dcs_get_user_force_hdtv_mode( ++ struct dcs *dcs, ++ union hdtv_mode_support *hdtv_mode); ++ ++bool dal_dcs_set_user_force_hdtv_mode( ++ struct dcs *dcs, ++ const union hdtv_mode_support *hdtv_mode); ++ ++bool dal_dcs_get_fid9204_allow_ce_mode_only_option( ++ struct dcs *dcs, ++ bool is_hdmi, ++ bool *enable); ++ ++bool dal_dcs_set_fid9204_allow_ce_mode_only_option( ++ struct dcs *dcs, ++ bool is_hdmi, ++ bool enable); ++ ++bool dal_dcs_get_panel_misc_info( ++ struct dcs *dcs, ++ union panel_misc_info *panel_info); ++ ++enum ddc_result dal_dcs_dpcd_read( ++ struct dcs *dcs, ++ uint32_t address, ++ uint8_t *buffer, ++ uint32_t length); ++ ++enum ddc_result dal_dcs_dpcd_write( ++ struct dcs *dcs, ++ uint32_t address, ++ const uint8_t *buffer, ++ uint32_t length); ++ ++bool dal_dcs_get_range_limit( ++ struct dcs *dcs, ++ struct display_range_limits *limit); ++ ++bool dal_dcs_set_range_limit_override( ++ struct dcs *dcs, ++ struct display_range_limits *limit); ++ ++bool dal_dcs_get_user_select_limit( ++ struct dcs *dcs, ++ struct monitor_user_select_limits *limit); ++ ++bool dal_dcs_set_user_select_limit( ++ struct dcs *dcs, ++ struct monitor_user_select_limits *limit); ++ ++bool dal_dcs_get_dongle_mode_support( ++ struct dcs *dcs, ++ union hdtv_mode_support *hdtv_mode); ++ ++bool dal_dcs_get_timing_limits( ++ struct dcs *dcs, ++ struct timing_limits *timing_limits); ++ ++bool dal_dcs_get_drr_config( ++ struct dcs *dcs, ++ struct drr_config *config); ++ ++bool dal_dcs_force_dp_audio(struct dcs *dcs, bool force_audio_on); ++ ++bool dal_dcs_is_dp_audio_forced(struct dcs *dcs); ++ ++const struct monitor_patch_info *dal_dcs_get_monitor_patch_info( ++ struct dcs *dcs, ++ enum monitor_patch_type patch_type); ++ ++bool dal_dcs_set_monitor_patch_info( ++ struct dcs *dcs, ++ struct monitor_patch_info *patch_info); ++ ++union dcs_monitor_patch_flags dal_dcs_get_monitor_patch_flags(struct dcs *dcs); ++ ++enum dcs_packed_pixel_format dal_dcs_get_enabled_packed_pixel_format( ++ struct dcs *dcs); ++ ++enum dcs_packed_pixel_format dal_dcs_get_monitor_packed_pixel_format( ++ struct dcs *dcs); ++ ++bool dal_dcs_report_single_selected_timing(struct dcs *dcs); ++ ++bool dal_dcs_can_tile_scale(struct dcs *dcs); ++ ++void dal_dcs_set_single_selected_timing_restriction( ++ struct dcs *dcs, ++ bool value); ++ ++const struct dcs_edid_supported_max_bw *dal_dcs_get_edid_supported_max_bw( ++ struct dcs *dcs); ++ ++bool dal_dcs_is_non_continous_frequency(struct dcs *dcs); ++ ++struct dcs_stereo_3d_features dal_dcs_get_stereo_3d_features( ++ struct dcs *dcs, ++ enum dc_timing_3d_format format); ++ ++union stereo_3d_support dal_dcs_get_stereo_3d_support(struct dcs *dcs); ++ ++void dal_dcs_override_stereo_3d_support( ++ struct dcs *dcs, ++ union stereo_3d_support support); ++ ++void dal_dcs_set_remote_display_receiver_capabilities( ++ struct dcs *dcs, ++ const struct dal_remote_display_receiver_capability *cap); ++ ++void dal_dcs_clear_remote_display_receiver_capabilities(struct dcs *dcs); ++ ++bool dal_dcs_get_display_tile_info( ++ struct dcs *dcs, ++ struct dcs_display_tile *display_tile, ++ bool first_display); ++ ++bool dal_dcs_get_container_id(struct dcs *dcs, ++ struct dcs_container_id *container_id); ++ ++bool dal_dcs_set_container_id(struct dcs *dcs, ++ struct dcs_container_id *container_id); ++ ++void dal_dcs_invalidate_container_id(struct dcs *dcs); ++ ++union dcs_monitor_patch_flags dal_dcs_get_monitor_patch_flags(struct dcs *dcs); ++ ++#endif /* __DAL_DCS_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dcs_types.h b/drivers/gpu/drm/amd/dal/include/dcs_types.h +new file mode 100644 +index 0000000..8c65057 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dcs_types.h +@@ -0,0 +1,742 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DCS_TYPES_H__ ++#define __DAL_DCS_TYPES_H__ ++ ++#include "signal_types.h" ++ ++#include "dc_types.h" ++ ++#define NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS 10 ++#define MAX_NUMBER_OF_HDMI_VSDB_3D_EXTENDED_SUPPORT 21 ++#define MAX_NUMBER_OF_HDMI_VSDB_VICS 7 ++ ++struct drr_config { ++ /* minimum frame per second for dynamic ++ * refresh rate feature; 0 if drr support not found*/ ++ uint32_t min_fps_in_microhz; ++ bool force_lock_on_event; ++ bool lock_to_master_vsync; ++ ++ struct { ++ uint8_t FORCED_BY_REGKEY_OR_ESCAPE:1; ++ uint8_t FORCED_BY_VBIOS:1; ++ uint8_t SUPPORTED_BY_EDID:1; ++ } support_method; ++}; ++ ++struct timing_limits { ++ uint32_t min_pixel_clock_in_khz; ++ uint32_t max_pixel_clock_in_khz; ++}; ++ ++struct vendor_product_id_info { ++ uint32_t manufacturer_id; ++ uint32_t product_id; ++ uint32_t serial_id; ++ uint32_t manufacture_week; ++ uint32_t manufacture_year; ++}; ++ ++struct display_range_limits { ++ uint32_t min_v_rate_hz; ++ uint32_t max_v_rate_hz; ++ uint32_t min_h_rate_khz; ++ uint32_t max_h_rateIn_khz; ++ uint32_t max_pix_clk_khz; ++ bool use_override; ++}; ++ ++struct monitor_user_select_limits { ++ bool use_ati_override; ++ uint32_t max_h_res; ++ uint32_t max_v_res; ++ uint32_t max_refresh_rate; ++}; ++ ++enum edid_screen_aspect_ratio { ++ EDID_SCREEN_AR_UNKNOWN = 0, ++ EDID_SCREEN_AR_PROJECTOR, ++ EDID_SCREEN_AR_16X9, ++ EDID_SCREEN_AR_16X10, ++ EDID_SCREEN_AR_4X3, ++ EDID_SCREEN_AR_5X4, ++ EDID_SCREEN_AR_9X16, ++ EDID_SCREEN_AR_10X16, ++ EDID_SCREEN_AR_3X4, ++ EDID_SCREEN_AR_4X5 ++}; ++ ++struct edid_screen_info { ++ enum edid_screen_aspect_ratio aspect_ratio; ++ uint32_t width; ++ uint32_t height; ++}; ++ ++struct display_characteristics { ++ uint8_t gamma; ++ uint8_t color_characteristics[NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS]; ++}; ++ ++union cv_smart_dongle_modes { ++ uint8_t all; ++ struct cv_smart_dongle_switches { ++ uint8_t MODE_1080I:1; ++ uint8_t MODE_720P:1; ++ uint8_t MODE_540P:1; ++ uint8_t MODE_480P:1; ++ uint8_t MODE_480I:1; ++ uint8_t MODE_16_9:1; ++ } switches; ++}; ++ ++struct cea_audio_mode { ++ uint8_t format_code; /* ucData[0] [6:3]*/ ++ uint8_t channel_count; /* ucData[0] [2:0]*/ ++ uint8_t sample_rate; /* ucData[1]*/ ++ union { ++ uint8_t sample_size; /* for LPCM*/ ++ /* for Audio Formats 2-8 (Max bit rate divided by 8 kHz)*/ ++ uint8_t max_bit_rate; ++ uint8_t audio_codec_vendor_specific; /* for Audio Formats 9-15*/ ++ }; ++}; ++ ++union cea_speaker_allocation_data_block { ++ struct { ++ uint32_t FL_FR:1; ++ uint32_t LFE:1; ++ uint32_t FC:1; ++ uint32_t RL_RR:1; ++ uint32_t RC:1; ++ uint32_t FLC_FRC:1; ++ uint32_t RLC_RRC:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++struct cea_colorimetry_data_block { ++ struct { ++ uint32_t XV_YCC601:1; ++ uint32_t XV_YCC709:1; ++ uint32_t S_YCC601:1; ++ uint32_t ADOBE_YCC601:1; ++ uint32_t ADOBE_RGB:1; ++ ++ } flag; ++ struct { ++ uint32_t MD0:1; ++ uint32_t MD1:1; ++ uint32_t MD2:1; ++ uint32_t MD3:1; ++ } metadata_flag; ++}; ++ ++union cea_video_capability_data_block { ++ struct { ++ uint8_t S_CE0:1; ++ uint8_t S_CE1:1; ++ uint8_t S_IT0:1; ++ uint8_t S_IT1:1; ++ uint8_t S_PT0:1; ++ uint8_t S_PT1:1; ++ uint8_t QS:1; ++ uint8_t QY:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++enum stereo_3d_multi_presence { ++ STEREO_3D_MULTI_NOT_PRESENT = 0, ++ STEREO_3D_MULTI_ALL_FORMATS, ++ STEREO_3D_MULTI_MASKED_FORMATS, ++ STEREO_3D_MULTI_RESERVED ++}; ++ ++enum cea_hdmi_vic { ++ CEA_HDMI_VIC_RESERVED = 0, ++ CEA_HDMI_VIC_4KX2K_30, ++ CEA_HDMI_VIC_4KX2K_25, ++ CEA_HDMI_VIC_4KX2K_24, ++ CEA_HDMI_VIC_4KX2K_24_SMPTE ++}; ++ ++struct cea_hdmi_vsdb_extended_caps { ++ uint32_t reserved; ++ uint32_t image_size; ++ enum stereo_3d_multi_presence stereo_3d_multi_present; ++ bool stereo_3d_present; ++ uint32_t hdmi_3d_len; ++ uint32_t hdmi_vic_len; ++}; ++ ++struct cea_vendor_specific_data_block { ++ ++ uint32_t ieee_id; ++ ++ struct commonent_phy { ++ uint32_t PHY_ADDR_A:4; ++ uint32_t PHY_ADDR_B:4; ++ uint32_t PHY_ADDR_C:4; ++ uint32_t PHY_ADDR_D:4; ++ } commonent_phy_addr; ++ ++ struct byte6 { ++ uint32_t SUPPORTS_AI:1; ++ uint32_t DC_48BIT:1; ++ uint32_t DC_36BIT:1; ++ uint32_t DC_30BIT:1; ++ uint32_t DC_Y444:1; ++ uint32_t DVI_DUAL:1; ++ uint32_t RESERVED:2; ++ } byte6;/* link capabilities*/ ++ bool byte6_valid; ++ ++ uint32_t max_tmds_clk_mhz; ++ ++ struct byte8 { ++ uint32_t LATENCY_FIELDS_PRESENT:1; ++ uint32_t ILATENCY_FIELDS_PRESENT:1; ++ uint32_t HDMI_VIDEO_PRESENT:1; ++ uint32_t RESERVED:1; ++ uint32_t CNC3_GAME:1; ++ uint32_t CNC2_CINEMA:1; ++ uint32_t CNC1_PHOTO:1; ++ uint32_t CNC0_GRAPHICS:1; ++ } byte8; ++ /*bit 6-7: latency flags to indicate valid latency fields*/ ++ /*bit 5: support of additional video format capabilities*/ ++ /* bit 0-3: flags indicating which content type is supported*/ ++ uint32_t video_latency; ++ uint32_t audio_latency; ++ uint32_t i_video_latency; ++ uint32_t i_audio_latency; ++ ++ struct cea_hdmi_vsdb_extended_caps hdmi_vsdb_extended_caps; ++ ++ enum stereo_3d_multi_presence stereo_3d_multi_present; ++ ++ struct { ++ bool FRAME_PACKING:1; ++ bool SIDE_BY_SIDE_HALF:1; ++ bool TOP_AND_BOTTOM:1; ++ } stereo_3d_all_support; ++ uint16_t stereo_3d_mask; ++ ++ enum cea_hdmi_vic hdmi_vic[MAX_NUMBER_OF_HDMI_VSDB_VICS]; ++ struct stereo_3d_extended_support { ++ struct { ++ bool FRAME_PACKING:1; ++ bool SIDE_BY_SIDE_HALF:1; ++ bool TOP_AND_BOTTOM:1; ++ } format; ++ uint32_t vic_index; ++ uint32_t value; ++ uint32_t size; ++ } stereo_3d_extended_support[MAX_NUMBER_OF_HDMI_VSDB_3D_EXTENDED_SUPPORT]; ++}; ++ ++struct cea861_support { ++ ++ uint32_t revision; ++ union { ++ struct { ++ uint32_t NATIVE_COUNT:4; ++ uint32_t BASE_AUDIO:1; ++ uint32_t YCRCB444:1; ++ uint32_t YCRCB422:1; ++ uint32_t UNDER_SCAN:1; ++ } features; ++ uint8_t raw_features; ++ }; ++}; ++ ++struct dcs_customized_mode { ++ struct { ++ uint32_t READ_ONLY:1; ++ uint32_t ADD_BY_DRIVER:1; ++ uint32_t INTERLACED:1; ++ uint32_t BASE_MODE:1; ++ } flags; ++ struct dc_mode_info base_mode_info; ++ struct dc_mode_info customized_mode_info; ++}; ++ ++struct dcs_override_mode_timing { ++ /* possible timing standards, bit vector of TimingStandard*/ ++ uint32_t possible_timing_standards; ++ /* indicate driver default timing is used , no override*/ ++ bool use_driver_default_timing; ++ struct dc_mode_timing mode_timing; ++}; ++ ++struct dcs_override_mode_timing_list { ++ uint32_t max_num_overrides; ++ uint32_t num_overrides; ++ struct dcs_override_mode_timing mode_timings[1]; ++}; ++ ++/* "interface type" is different from Signal Type because ++ * an "interface type" can be driven by more than one Signal Type. ++ * For example, INTERFACE_TYPE_DVI can be driven by ++ * Single or Dual link DVI signal. */ ++enum dcs_interface_type { ++ INTERFACE_TYPE_VGA = 0, ++ INTERFACE_TYPE_DVI, ++ INTERFACE_TYPE_CV, ++ INTERFACE_TYPE_TV, ++ INTERFACE_TYPE_LVDS, ++ INTERFACE_TYPE_DP, ++ INTERFACE_TYPE_WIRELESS, ++ INTERFACE_TYPE_CF, ++ INTERFACE_TYPE_EDP ++}; ++ ++ ++union panel_misc_info { ++ struct { ++ uint32_t H_CUT_OFF:1; ++ uint32_t H_SYNC_POLARITY:1;/*0=Active High, 1=Active Low*/ ++ uint32_t V_SYNC_POLARITY:1; /*0=Active High, 1=Active Low*/ ++ uint32_t V_CUT_OFF:1; ++ uint32_t H_REPLICATION_BY_2:1; ++ uint32_t V_REPLICATION_BY_2:1; ++ uint32_t COMPOSITE_SYNC:1; ++ uint32_t INTERLACE:1; ++ uint32_t DOUBLE_CLOCK:1; ++ uint32_t RGB888:1; ++ uint32_t GREY_LEVEL:2; ++ uint32_t SPATIAL:1; ++ uint32_t TEMPORAL:1; ++ uint32_t API_ENABLED:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++union hdtv_mode_support { ++ struct { ++ uint32_t HDTV_SUPPORT_480I:1; ++ uint32_t HDTV_SUPPORT_480P:1; ++ uint32_t HDTV_SUPPORT_576I25:1; ++ uint32_t HDTV_SUPPORT_576P50:1; ++ uint32_t HDTV_SUPPORT_720P:1; ++ uint32_t HDTV_SUPPORT_720P50:1; ++ uint32_t HDTV_SUPPORT_1080I:1; ++ uint32_t HDTV_SUPPORT_1080I25:1; ++ uint32_t HDTV_SUPPORT_1080P:1; ++ uint32_t HDTV_SUPPORT_1080P50:1; ++ uint32_t HDTV_SUPPORT_1080P24:1; ++ uint32_t HDTV_SUPPORT_1080P25:1; ++ uint32_t HDTV_SUPPORT_1080P30:1; ++ uint32_t HDTV_SUPPORT_16X9:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++enum edid_retrieve_status { ++ EDID_RETRIEVE_SUCCESS = 0, ++ EDID_RETRIEVE_FAIL, ++ EDID_RETRIEVE_SAME_EDID, ++ EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS ++}; ++ ++#define DCS_DECODE_EDID_RETRIEVE_STATUS(status) \ ++ (status == EDID_RETRIEVE_SUCCESS) ? "EDID_RETRIEVE_SUCCESS" : \ ++ (status == EDID_RETRIEVE_FAIL) ? "EDID_RETRIEVE_FAIL" : \ ++ (status == EDID_RETRIEVE_SAME_EDID) ? "EDID_RETRIEVE_SAME_EDID" : \ ++ (status == EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS) ? \ ++ "EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS" : "Unknown" ++ ++ ++#ifndef TV_SIGNALFORMAT_DEFINED ++#define TV_SIGNALFORMAT_DEFINED ++enum tv_signal_format { ++ TV_SIGNAL_FORMAT_UNKNOWN, ++ TV_SIGNAL_FORMAT_NTSC, ++ TV_SIGNAL_FORMAT_NTSC_J, ++ TV_SIGNAL_FORMAT_PAL, ++ TV_SIGNAL_FORMAT_PAL_M, ++ TV_SIGNAL_FORMAT_PAL_CN, ++ TV_SIGNAL_FORMAT_SECAM ++}; ++#endif ++ ++enum tv_signal_format_result { ++ TV_SIGNAL_FORMAT_RESULT_OK, ++ TV_SIGNAL_FORMAT_SET_MODE_REQ, ++ TV_SIGNAL_FORMAT_REBOOT_REQ, ++ TV_SIGNAL_FORMAT_ERROR ++}; ++ ++enum pixel_encoding_mask { ++ PIXEL_ENCODING_MASK_YCBCR444 = 0x01, ++ PIXEL_ENCODING_MASK_YCBCR422 = 0x02, ++ PIXEL_ENCODING_MASK_RGB = 0x04, ++}; ++ ++struct display_pixel_encoding_support { ++ uint32_t mask; ++}; ++ ++enum color_depth_index { ++ COLOR_DEPTH_INDEX_UNKNOWN, ++ COLOR_DEPTH_INDEX_666 = 0x01, ++ COLOR_DEPTH_INDEX_888 = 0x02, ++ COLOR_DEPTH_INDEX_101010 = 0x04, ++ COLOR_DEPTH_INDEX_121212 = 0x08, ++ COLOR_DEPTH_INDEX_141414 = 0x10, ++ COLOR_DEPTH_INDEX_161616 = 0x20, ++ COLOR_DEPTH_INDEX_LAST = 0x40, ++}; ++ ++struct display_color_depth_support { ++ uint32_t mask; ++ bool deep_color_native_res_only; ++}; ++ ++struct display_color_and_pixel_support { ++ struct display_color_depth_support color_depth_support; ++ struct display_pixel_encoding_support pixel_encoding_support; ++ bool deep_color_y444_support; ++}; ++ ++enum dcs_packed_pixel_format { ++ DCS_PACKED_PIXEL_FORMAT_NOT_PACKED = 0, ++ DCS_PACKED_PIXEL_FORMAT_SPLIT_G70_B54_R70_B10 = 1, ++ DCS_PACKED_PIXEL_FORMAT_R70_G76 = 2, ++ DCS_PACKED_PIXEL_FORMAT_SPLIT_B70_G10_R70_G76 = 3, ++ DCS_PACKED_PIXEL_FORMAT_G70_B54_R70_B10 = 4, ++ DCS_PACKED_PIXEL_FORMAT_G70_B54 = 5, ++ DCS_PACKED_PIXEL_FORMAT_B70_R30_G70_R74 = 6, ++ DCS_PACKED_PIXEL_FORMAT_B70_G70_R70 = 7, ++ DCS_PACKED_PIXEL_FORMAT_B70_R32_G70_R76 = 8, ++}; ++ ++enum monitor_manufacturer_id { ++ MONITOR_MANUFACTURER_ID_0 = 0x0000, ++ MONITOR_MANUFACTURER_ID_1 = 0x3834, ++ MONITOR_MANUFACTURER_ID_2 = 0x4d24, ++ MONITOR_MANUFACTURER_ID_3 = 0x293E, ++ MONITOR_MANUFACTURER_ID_4 = 0x635a, ++ MONITOR_MANUFACTURER_ID_5 = 0x1006, ++ MONITOR_MANUFACTURER_ID_6 = 0xc32a, ++ MONITOR_MANUFACTURER_ID_7 = 0x4d24, ++ MONITOR_MANUFACTURER_ID_8 = 0x110e, ++ MONITOR_MANUFACTURER_ID_9 = 0xaf0d, ++ MONITOR_MANUFACTURER_ID_10 = 0x6D1E, ++ MONITOR_MANUFACTURER_ID_11 = 0xA338, ++ MONITOR_MANUFACTURER_ID_12 = 0xC315, ++ MONITOR_MANUFACTURER_ID_13 = 0xD94D, ++ MONITOR_MANUFACTURER_ID_14 = 0x104D, ++ MONITOR_MANUFACTURER_ID_15 = 0x855C, ++ MONITOR_MANUFACTURER_ID_16 = 0x4251, ++ MONITOR_MANUFACTURER_ID_17 = 0xA934, ++ MONITOR_MANUFACTURER_ID_18 = 0x0C41, ++ /* TODO: Update when EDID is available */ ++ MONITOR_MANUFACTURER_ID_19 = 0xDEAD, ++ MONITOR_MANUFACTURER_ID_20 = 0x6904, ++ MONITOR_MANUFACTURER_ID_21 = 0xAC10, ++ MONITOR_MANUFACTURER_ID_22 = 0x2D4C, ++ MONITOR_MANUFACTURER_ID_23 = 0x144E, ++ MONITOR_MANUFACTURER_ID_24 = 0x6c50, ++ MONITOR_MANUFACTURER_ID_26 = 0x0c41, ++ MONITOR_MANUFACTURER_ID_27 = 0x143E, ++ MONITOR_MANUFACTURER_ID_25 = 0xffff, ++ MONITOR_MANUFACTURER_ID_28 = 0x3421, ++ MONITOR_MANUFACTURER_ID_29 = 0x2D19, ++ MONITOR_MANUFACTURER_ID_30 = 0x8B52, ++ MONITOR_MANUFACTURER_ID_31 = 0x7204, ++ MONITOR_MANUFACTURER_ID_32 = 0xF022, ++ MONITOR_MANUFACTURER_ID_33 = 0x0E11, ++ MONITOR_MANUFACTURER_ID_34 = 0xD241, ++ MONITOR_MANUFACTURER_ID_35 = 0xAE30, ++ MONITOR_MANUFACTURER_ID_36 = 0xF91E, ++ MONITOR_MANUFACTURER_ID_37 = 0xAB4C, ++}; ++ ++enum monitor_product_id { ++ MONITOR_PRODUCT_ID_0 = 0x0000, ++ MONITOR_PRODUCT_ID_1 = 0x0BCC, ++ MONITOR_PRODUCT_ID_2 = 0x251F, ++ MONITOR_PRODUCT_ID_3 = 0x5241, ++ MONITOR_PRODUCT_ID_4 = 0x6919, ++ MONITOR_PRODUCT_ID_5 = 0xee18, ++ MONITOR_PRODUCT_ID_6 = 0xf008, ++ MONITOR_PRODUCT_ID_7 = 0x2f0c, ++ MONITOR_PRODUCT_ID_7_2 = 0x3411, ++ MONITOR_PRODUCT_ID_9 = 0x4208, ++ MONITOR_PRODUCT_ID_10 = 0xE51D, ++ MONITOR_PRODUCT_ID_11 = 0x7E22, ++ MONITOR_PRODUCT_ID_12 = 0x0E23, ++ MONITOR_PRODUCT_ID_13 = 0x9d08, ++ MONITOR_PRODUCT_ID_14 = 0x9236, ++ MONITOR_PRODUCT_ID_15 = 0x9227, ++ MONITOR_PRODUCT_ID_16 = 0x0220, ++ MONITOR_PRODUCT_ID_17 = 0x4920, ++ MONITOR_PRODUCT_ID_18 = 0x251f, ++ MONITOR_PRODUCT_ID_19 = 0x1395, ++ MONITOR_PRODUCT_ID_20 = 0xc04e, ++ MONITOR_PRODUCT_ID_21 = 0x5787, ++ MONITOR_PRODUCT_ID_22 = 0x5A71, ++ MONITOR_PRODUCT_ID_23 = 0x6622, ++ MONITOR_PRODUCT_ID_24 = 0x20C1, ++ MONITOR_PRODUCT_ID_25 = 0x2110, ++ MONITOR_PRODUCT_ID_26 = 0x2006, ++ MONITOR_PRODUCT_ID_27 = 0x1827, ++ MONITOR_PRODUCT_ID_28 = 0x0EA0, ++ MONITOR_PRODUCT_ID_29 = 0x03D0, ++ MONITOR_PRODUCT_ID_30 = 0x01D2, ++ MONITOR_PRODUCT_ID_31 = 0x2801, ++ MONITOR_PRODUCT_ID_32 = 0x0FB3, ++ MONITOR_PRODUCT_ID_33 = 0x0FB1, ++ MONITOR_PRODUCT_ID_34 = 0xA045, ++ MONITOR_PRODUCT_ID_35 = 0x0001, ++ MONITOR_PRODUCT_ID_36 = 0xA296, ++ MONITOR_PRODUCT_ID_38 = 0x21DC, ++ MONITOR_PRODUCT_ID_37 = 0x21EA, ++ MONITOR_PRODUCT_ID_39 = 0x4093, ++ MONITOR_PRODUCT_ID_40 = 0x4094, ++ MONITOR_PRODUCT_ID_41 = 0x4094, ++ MONITOR_PRODUCT_ID_42 = 0x32A2, ++ MONITOR_PRODUCT_ID_43 = 0xE009, ++ MONITOR_PRODUCT_ID_44 = 0xA010, ++ MONITOR_PRODUCT_ID_45 = 0x405C, ++ MONITOR_PRODUCT_ID_46 = 0xF017, ++ MONITOR_PRODUCT_ID_47 = 0xD026, ++ MONITOR_PRODUCT_ID_48 = 0x4036, ++ MONITOR_PRODUCT_ID_49 = 0x4065, ++ MONITOR_PRODUCT_ID_50 = 0xA02A, ++ MONITOR_PRODUCT_ID_51 = 0xA02C, ++ MONITOR_PRODUCT_ID_46_HDMI = 0xF016, ++ MONITOR_PRODUCT_ID_53 = 0xF048, ++ MONITOR_PRODUCT_ID_54 = 0xA0A2, ++ MONITOR_PRODUCT_ID_55 = 0x4083, ++ MONITOR_PRODUCT_ID_56 = 0x0E74, ++ MONITOR_PRODUCT_ID_57 = 0x2771, ++ MONITOR_PRODUCT_ID_58 = 0x0814, ++ MONITOR_PRODUCT_ID_59 = 0xffff, ++ MONITOR_PRODUCT_ID_60 = 0x3339, ++ MONITOR_PRODUCT_ID_61 = 0x01F5, ++ MONITOR_PRODUCT_ID_62 = 0x02A5, ++ MONITOR_PRODUCT_ID_63 = 0x06AC, ++ MONITOR_PRODUCT_ID_64 = 0x04D5, ++ MONITOR_PRODUCT_ID_65 = 0x079D, ++ MONITOR_PRODUCT_ID_66 = 0x079F, ++ MONITOR_PRODUCT_ID_67 = 0x0797, ++ MONITOR_PRODUCT_ID_68 = 0x0B80, ++ MONITOR_PRODUCT_ID_69 = 0x7D06, ++ MONITOR_PRODUCT_ID_70 = 0x0131, ++ MONITOR_PRODUCT_ID_71 = 0x8545, ++ MONITOR_PRODUCT_ID_72 = 0x0002, ++ MONITOR_PRODUCT_ID_73 = 0x0125, ++ MONITOR_PRODUCT_ID_74 = 0x00D0, ++ MONITOR_PRODUCT_ID_75 = 0x26F7, ++ MONITOR_PRODUCT_ID_76 = 0x26F9, ++ MONITOR_PRODUCT_ID_77 = 0x2807, ++ MONITOR_PRODUCT_ID_78 = 0x26F3, ++ MONITOR_PRODUCT_ID_79 = 0x2676, ++ MONITOR_PRODUCT_ID_80 = 0x0A72, ++ MONITOR_PRODUCT_ID_81 = 0x2693, ++ MONITOR_PRODUCT_ID_82 = 0x2615, ++ MONITOR_PRODUCT_ID_83 = 0x2613, ++ MONITOR_PRODUCT_ID_84 = 0x262D, ++ MONITOR_PRODUCT_ID_85 = 0x264B, ++ MONITOR_PRODUCT_ID_86 = 0x2869, ++ MONITOR_PRODUCT_ID_87 = 0x286C, ++ MONITOR_PRODUCT_ID_88 = 0x288F, ++ MONITOR_PRODUCT_ID_89 = 0x2954, ++ MONITOR_PRODUCT_ID_90 = 0x6522, ++ MONITOR_PRODUCT_ID_91 = 0x0FAE, ++ MONITOR_PRODUCT_ID_92 = 0x0A0C, ++ MONITOR_PRODUCT_ID_93 = 0x00BF, ++ MONITOR_PRODUCT_ID_94 = 0x0, ++}; ++ ++enum monitor_patch_type { ++ MONITOR_PATCH_TYPE_NONE, ++ MONITOR_PATCH_TYPE_ERROR_CHECKSUM, ++ MONITOR_PATCH_TYPE_HDTV_WITH_PURE_DFP_EDID, ++ MONITOR_PATCH_TYPE_DO_NOT_USE_DETAILED_TIMING, ++ MONITOR_PATCH_TYPE_DO_NOT_USE_RANGE_LIMITATION, ++ MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECK_SUM, ++ MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, ++ MONITOR_PATCH_TYPE_RESTRICT_VESA_MODE_TIMING, ++ MONITOR_PATCH_TYPE_DO_NOT_USE_EDID_MAX_PIX_CLK, ++ MONITOR_PATCH_TYPE_VENDOR_0, ++ MONITOR_PATCH_TYPE_RANDOM_CRT, ++ MONITOR_PATCH_TYPE_VENDOR_1, ++ MONITOR_PATCH_TYPE_LIMIT_PANEL_SUPPORT_RGB_ONLY, ++ MONITOR_PATCH_TYPE_PACKED_PIXEL_FORMAT, ++ MONITOR_PATCH_TYPE_LARGE_PANEL, ++ MONITOR_PATCH_TYPE_STEREO_SUPPORT, ++ /* 0 (default) - mean patch will not be applied, however it can be ++ * explicitly set to 1 ++ */ ++ MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, ++ MONITOR_PATCH_TYPE_IGNORE_19X12_STD_TIMING, ++ MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE, ++ MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, ++ MONITOR_PATCH_TYPE_VENDOR_2, ++ MONITOR_PATCH_TYPE_RESTRICT_PROT_DUAL_LINK_DVI, ++ MONITOR_PATCH_TYPE_FORCE_LINK_RATE, ++ MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP, ++ MONITOR_PATCH_TYPE_KEEP_DP_RECEIVER_POWERED, ++ MONITOR_PATCH_TYPE_DELAY_BEFORE_READ_EDID, ++ MONITOR_PATCH_TYPE_DELAY_AFTER_PIXEL_FORMAT_CHANGE, ++ MONITOR_PATCH_TYPE_INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX, ++ MONITOR_PATCH_TYPE_NO_DEFAULT_TIMINGS, ++ MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC16, ++ MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC31, ++ MONITOR_PATCH_TYPE_DELAY_BEFORE_UNMUTE, ++ MONITOR_PATCH_TYPE_RETRY_LINK_TRAINING_ON_FAILURE, ++ MONITOR_PATCH_TYPE_ALLOW_AUX_WHEN_HPD_LOW, ++ MONITOR_PATCH_TYPE_TILED_DISPLAY, ++ MONITOR_PATCH_TYPE_DISABLE_PSR_ENTRY_ABORT, ++ MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECKSUM, ++ MONITOR_PATCH_TYPE_ALLOW_ONLY_CE_MODE, ++ MONITOR_PATCH_TYPE_VID_STREAM_DIFFER_TO_SYNC, ++ MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, ++ MONITOR_PATCH_TYPE_DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS, ++ MONITOR_PATCH_TYPE_SINGLE_MODE_PACKED_PIXEL ++}; ++ ++/* Single entry in the monitor table */ ++struct monitor_patch_info { ++ enum monitor_manufacturer_id manufacturer_id; ++ enum monitor_product_id product_id; ++ enum monitor_patch_type type; ++ uint32_t param; ++}; ++ ++union dcs_monitor_patch_flags { ++ struct { ++ bool ERROR_CHECKSUM:1; ++ bool HDTV_WITH_PURE_DFP_EDID:1; ++ bool DO_NOT_USE_DETAILED_TIMING:1; ++ bool DO_NOT_USE_RANGE_LIMITATION:1; ++ bool EDID_EXTENTION_ERROR_CHECKSUM:1; ++ bool TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE:1; ++ bool RESTRICT_VESA_MODE_TIMING:1; ++ bool DO_NOT_USE_EDID_MAX_PIX_CLK:1; ++ bool VENDOR_0:1; ++ bool RANDOM_CRT:1;/* 10 bits used including this one-*/ ++ bool VENDOR_1:1; ++ bool LIMIT_PANEL_SUPPORT_RGB_ONLY:1; ++ bool PACKED_PIXEL_FORMAT:1; ++ bool LARGE_PANEL:1; ++ bool STEREO_SUPPORT:1; ++ bool DUAL_EDID_PANEL:1; ++ bool IGNORE_19X12_STD_TIMING:1; ++ bool MULTIPLE_PACKED_TYPE:1; ++ bool RESET_TX_ON_DISPLAY_POWER_ON:1; ++ bool ALLOW_ONLY_CE_MODE:1;/* 20 bits used including this one*/ ++ bool RESTRICT_PROT_DUAL_LINK_DVI:1; ++ bool FORCE_LINK_RATE:1; ++ bool DELAY_AFTER_DP_RECEIVER_POWER_UP:1; ++ bool KEEP_DP_RECEIVER_POWERED:1; ++ bool DELAY_BEFORE_READ_EDID:1; ++ bool DELAY_AFTER_PIXEL_FORMAT_CHANGE:1; ++ bool INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX:1; ++ bool NO_DEFAULT_TIMINGS:1; ++ bool ADD_CEA861_DETAILED_TIMING_VIC16:1; ++ bool ADD_CEA861_DETAILED_TIMING_VIC31:1; /* 30 bits*/ ++ bool DELAY_BEFORE_UNMUTE:1; ++ bool RETRY_LINK_TRAINING_ON_FAILURE:1; ++ bool ALLOW_AUX_WHEN_HPD_LOW:1; ++ bool TILED_DISPLAY:1; ++ bool DISABLE_PSR_ENTRY_ABORT:1; ++ bool INTERMITTENT_EDID_ERROR:1;/* 36 bits total*/ ++ bool VID_STREAM_DIFFER_TO_SYNC:1;/* 37 bits total*/ ++ bool EXTRA_DELAY_ON_DISCONNECT:1;/* 38 bits total*/ ++ bool DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS:1;/* 39 bits total*/ ++ } flags; ++ uint64_t raw; ++}; ++ ++struct dcs_edid_supported_max_bw { ++ uint32_t pix_clock_khz; ++ uint32_t bits_per_pixel; ++}; ++ ++struct dcs_stereo_3d_features { ++ struct { ++/* 3D Format supported by monitor (implies supported by driver)*/ ++ uint32_t SUPPORTED:1; ++/* 3D Format supported on all timings ++(no need to check every timing for 3D support)*/ ++ uint32_t ALL_TIMINGS:1; ++/* 3D Format supported in clone mode*/ ++ uint32_t CLONE_MODE:1; ++/* Scaling allowed when driving 3D Format*/ ++ uint32_t SCALING:1; ++/* Left and right images packed by SW within single frame*/ ++ uint32_t SINGLE_FRAME_SW_PACKED:1; ++ } flags; ++}; ++ ++struct dcs_container_id { ++ /*128bit GUID in binary form*/ ++ uint8_t guid[16]; ++ /* 8 byte port ID -> ELD.PortID*/ ++ uint32_t port_id[2]; ++ /* 2 byte manufacturer name -> ELD.ManufacturerName*/ ++ uint16_t manufacturer_name; ++ /* 2 byte product code -> ELD.ProductCode*/ ++ uint16_t product_code; ++}; ++ ++struct dcs_display_tile { ++/*unique Id of Tiled Display. 0 - means display is not part in Tiled Display*/ ++ uint64_t id; ++ uint32_t rows;/* size of Tiled Display in tiles*/ ++ uint32_t cols;/* size of Tiled Display in tiles*/ ++ uint32_t width;/* size of current Tile in pixels*/ ++ uint32_t height;/* size of current Tile in pixels*/ ++ uint32_t row;/* location of current Tile*/ ++ uint32_t col;/* location of current Tile*/ ++ struct { ++ /*in pixels*/ ++ uint32_t left; ++ uint32_t right; ++ uint32_t top; ++ uint32_t bottom; ++ } bezel;/* bezel information of current tile*/ ++ ++ struct { ++ uint32_t SEPARATE_ENCLOSURE:1; ++ uint32_t BEZEL_INFO_PRESENT:1; ++ uint32_t CAN_SCALE:1; ++ } flags; ++ ++ struct { ++ uint32_t manufacturer_id; ++ uint32_t product_id; ++ uint32_t serial_id; ++ } topology_id; ++}; ++ ++#endif /* __DAL_DCS_TYPES_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/include/ddc_interface.h b/drivers/gpu/drm/amd/dal/include/ddc_interface.h +new file mode 100644 +index 0000000..22fd31f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/ddc_interface.h +@@ -0,0 +1,74 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DDC_INTERFACE_H__ ++#define __DAL_DDC_INTERFACE_H__ ++ ++#include "gpio_types.h" ++ ++struct ddc; ++ ++enum gpio_result dal_ddc_open( ++ struct ddc *ddc, ++ enum gpio_mode mode, ++ enum gpio_ddc_config_type config_type); ++ ++enum gpio_result dal_ddc_get_clock( ++ const struct ddc *ddc, ++ uint32_t *value); ++ ++enum gpio_result dal_ddc_set_clock( ++ const struct ddc *ddc, ++ uint32_t value); ++ ++enum gpio_result dal_ddc_get_data( ++ const struct ddc *ddc, ++ uint32_t *value); ++ ++enum gpio_result dal_ddc_set_data( ++ const struct ddc *ddc, ++ uint32_t value); ++ ++enum gpio_result dal_ddc_change_mode( ++ struct ddc *ddc, ++ enum gpio_mode mode); ++ ++bool dal_ddc_is_hw_supported( ++ const struct ddc *ddc); ++ ++enum gpio_ddc_line dal_ddc_get_line( ++ const struct ddc *ddc); ++ ++bool dal_ddc_check_line_aborted( ++ const struct ddc *ddc); ++ ++enum gpio_result dal_ddc_set_config( ++ struct ddc *ddc, ++ enum gpio_ddc_config_type config_type); ++ ++void dal_ddc_close( ++ struct ddc *ddc); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/ddc_service_interface.h b/drivers/gpu/drm/amd/dal/include/ddc_service_interface.h +new file mode 100644 +index 0000000..ca3e6ce +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/ddc_service_interface.h +@@ -0,0 +1,100 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_DDC_SERVICE_INTERFACE_H__ ++#define __DAL_DDC_SERVICE_INTERFACE_H__ ++ ++#include "ddc_service_types.h" ++ ++struct ddc_service; ++struct adapter_service; ++struct graphics_object_id; ++enum ddc_result; ++struct av_sync_data; ++struct dp_receiver_id_info; ++ ++struct ddc_service_init_data { ++ struct adapter_service *as; ++ struct graphics_object_id id; ++ struct dc_context *ctx; ++}; ++struct ddc_service *dal_ddc_service_create( ++ struct ddc_service_init_data *ddc_init_data); ++ ++void dal_ddc_service_destroy(struct ddc_service **ddc); ++ ++enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc); ++ ++void dal_ddc_service_set_transaction_type( ++ struct ddc_service *ddc, ++ enum ddc_transaction_type type); ++ ++bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc); ++ ++uint32_t dal_ddc_service_edid_query(struct ddc_service *ddc); ++ ++uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc); ++ ++void dal_ddc_service_get_edid_buf(struct ddc_service *ddc, uint8_t *edid_buf); ++ ++void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( ++ struct ddc_service *ddc, ++ struct display_sink_capability *sink_cap); ++ ++bool dal_ddc_service_query_ddc_data( ++ struct ddc_service *ddc, ++ uint32_t address, ++ uint8_t *write_buf, ++ uint32_t write_size, ++ uint8_t *read_buf, ++ uint32_t read_size); ++ ++bool dal_ddc_service_get_dp_receiver_id_info( ++ struct ddc_service *ddc, ++ struct dp_receiver_id_info *info); ++ ++enum ddc_result dal_ddc_service_read_dpcd_data( ++ struct ddc_service *ddc, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t len); ++ ++enum ddc_result dal_ddc_service_write_dpcd_data( ++ struct ddc_service *ddc, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t len); ++ ++void dal_ddc_service_write_scdc_data( ++ struct ddc_service *ddc_service, ++ uint32_t pix_clk, ++ bool lte_340_scramble); ++ ++void dal_ddc_service_read_scdc_data( ++ struct ddc_service *ddc_service); ++ ++void ddc_service_set_dongle_type(struct ddc_service *ddc, ++ enum display_dongle_type dongle_type); ++ ++#endif /* __DAL_DDC_SERVICE_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/ddc_service_types.h b/drivers/gpu/drm/amd/dal/include/ddc_service_types.h +new file mode 100644 +index 0000000..47ad2ed +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/ddc_service_types.h +@@ -0,0 +1,220 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef __DAL_DDC_SERVICE_TYPES_H__ ++#define __DAL_DDC_SERVICE_TYPES_H__ ++ ++#include "include/hw_sequencer_types.h" ++ ++#define DP_BRANCH_DEVICE_ID_1 0x0010FA ++#define DP_BRANCH_DEVICE_ID_2 0x0022B9 ++#define DP_SINK_DEVICE_ID_1 0x4CE000 ++#define DP_BRANCH_DEVICE_ID_3 0x00001A ++#define DP_BRANCH_DEVICE_ID_4 0x0080e1 ++#define DP_BRANCH_DEVICE_ID_5 0x006037 ++#define DP_SINK_DEVICE_ID_2 0x001CF8 ++ ++ ++enum ddc_result { ++ DDC_RESULT_UNKNOWN = 0, ++ DDC_RESULT_SUCESSFULL, ++ DDC_RESULT_FAILED_CHANNEL_BUSY, ++ DDC_RESULT_FAILED_TIMEOUT, ++ DDC_RESULT_FAILED_PROTOCOL_ERROR, ++ DDC_RESULT_FAILED_NACK, ++ DDC_RESULT_FAILED_INCOMPLETE, ++ DDC_RESULT_FAILED_OPERATION, ++ DDC_RESULT_FAILED_INVALID_OPERATION, ++ DDC_RESULT_FAILED_BUFFER_OVERFLOW ++}; ++ ++enum ddc_service_type { ++ DDC_SERVICE_TYPE_CONNECTOR, ++ DDC_SERVICE_TYPE_DISPLAY_PORT_MST, ++}; ++ ++enum ddc_transaction_type { ++ DDC_TRANSACTION_TYPE_NONE = 0, ++ DDC_TRANSACTION_TYPE_I2C, ++ DDC_TRANSACTION_TYPE_I2C_OVER_AUX, ++ DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER, ++ DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER ++}; ++ ++enum display_dongle_type { ++ DISPLAY_DONGLE_NONE = 0, ++ /* Active converter types*/ ++ DISPLAY_DONGLE_DP_VGA_CONVERTER, ++ DISPLAY_DONGLE_DP_DVI_CONVERTER, ++ DISPLAY_DONGLE_DP_HDMI_CONVERTER, ++ /* DP-HDMI/DVI passive dongles (Type 1 and Type 2)*/ ++ DISPLAY_DONGLE_DP_DVI_DONGLE, ++ DISPLAY_DONGLE_DP_HDMI_DONGLE, ++ /* Other types of dongle*/ ++ DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE, ++}; ++ ++enum dcs_dpcd_revision { ++ DCS_DPCD_REV_10 = 0x10, ++ DCS_DPCD_REV_11 = 0x11, ++ DCS_DPCD_REV_12 = 0x12 ++}; ++ ++/** ++ * display sink capability ++ */ ++struct display_sink_capability { ++ /* dongle type (DP converter, CV smart dongle) */ ++ enum display_dongle_type dongle_type; ++ ++ /********************************************************** ++ capabilities going INTO SINK DEVICE (stream capabilities) ++ **********************************************************/ ++ /* Dongle's downstream count. */ ++ uint32_t downstrm_sink_count; ++ /* Is dongle's downstream count info field (downstrm_sink_count) ++ * valid. */ ++ bool downstrm_sink_count_valid; ++ ++ /* Maximum additional audio delay in microsecond (us) */ ++ uint32_t additional_audio_delay; ++ /* Audio latency value in microsecond (us) */ ++ uint32_t audio_latency; ++ /* Interlace video latency value in microsecond (us) */ ++ uint32_t video_latency_interlace; ++ /* Progressive video latency value in microsecond (us) */ ++ uint32_t video_latency_progressive; ++ /* Dongle caps: Maximum pixel clock supported over dongle for HDMI */ ++ uint32_t max_hdmi_pixel_clock; ++ /* Dongle caps: Maximum deep color supported over dongle for HDMI */ ++ enum dc_color_depth max_hdmi_deep_color; ++ ++ /************************************************************ ++ capabilities going OUT OF SOURCE DEVICE (link capabilities) ++ ************************************************************/ ++ /* support for Spread Spectrum(SS) */ ++ bool ss_supported; ++ /* DP link settings (laneCount, linkRate, Spread) */ ++ uint32_t dp_link_lane_count; ++ uint32_t dp_link_rate; ++ uint32_t dp_link_spead; ++ ++ enum dcs_dpcd_revision dpcd_revision; ++ /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, ++ indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ ++ bool is_dp_hdmi_s3d_converter; ++ /* to check if we have queried the display capability ++ * for eDP panel already. */ ++ bool is_edp_sink_cap_valid; ++}; ++ ++struct dp_receiver_id_info { ++ uint32_t dpcd_rev; ++ uint32_t sink_id; ++ int8_t sink_id_str[6]; ++ int8_t sink_hw_revision; ++ int8_t sink_fw_revision[2]; ++ uint32_t branch_id; ++ int8_t branch_name[6]; ++ enum display_dongle_type dongle_type; ++}; ++ ++struct av_sync_data { ++ uint8_t av_granularity;/* DPCD 00023h */ ++ uint8_t aud_dec_lat1;/* DPCD 00024h */ ++ uint8_t aud_dec_lat2;/* DPCD 00025h */ ++ uint8_t aud_pp_lat1;/* DPCD 00026h */ ++ uint8_t aud_pp_lat2;/* DPCD 00027h */ ++ uint8_t vid_inter_lat;/* DPCD 00028h */ ++ uint8_t vid_prog_lat;/* DPCD 00029h */ ++ uint8_t aud_del_ins1;/* DPCD 0002Bh */ ++ uint8_t aud_del_ins2;/* DPCD 0002Ch */ ++ uint8_t aud_del_ins3;/* DPCD 0002Dh */ ++}; ++ ++/** EDID retrieval related constants, also used by MstMgr **/ ++ ++#define DDC_EDID_SEGMENT_SIZE 256 ++#define DDC_EDID_BLOCK_SIZE 128 ++#define DDC_EDID_BLOCKS_PER_SEGMENT \ ++ (DDC_EDID_SEGMENT_SIZE / DDC_EDID_BLOCK_SIZE) ++ ++#define DDC_EDID_EXT_COUNT_OFFSET 0x7E ++ ++#define DDC_EDID_ADDRESS_START 0x50 ++#define DDC_EDID_ADDRESS_END 0x52 ++#define DDC_EDID_SEGMENT_ADDRESS 0x30 ++ ++/* signatures for Edid 1x */ ++#define DDC_EDID1X_VENDORID_SIGNATURE_OFFSET 8 ++#define DDC_EDID1X_VENDORID_SIGNATURE_LEN 4 ++#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_OFFSET 126 ++#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN 2 ++#define DDC_EDID1X_CHECKSUM_OFFSET 127 ++/* signatures for Edid 20*/ ++#define DDC_EDID_20_SIGNATURE_OFFSET 0 ++#define DDC_EDID_20_SIGNATURE 0x20 ++ ++#define DDC_EDID20_VENDORID_SIGNATURE_OFFSET 1 ++#define DDC_EDID20_VENDORID_SIGNATURE_LEN 4 ++#define DDC_EDID20_CHECKSUM_OFFSET 255 ++#define DDC_EDID20_CHECKSUM_LEN 1 ++ ++/*DP to VGA converter*/ ++static const uint8_t DP_VGA_CONVERTER_ID_1[] = "mVGAa"; ++/*DP to Dual link DVI converter*/ ++static const uint8_t DP_DVI_CONVERTER_ID_1[] = "m2DVIa"; ++/*Travis*/ ++static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT"; ++/*Nutmeg*/ ++static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA"; ++/*DP to VGA converter*/ ++static const uint8_t DP_VGA_CONVERTER_ID_4[] = "DpVga"; ++/*DP to Dual link DVI converter*/ ++static const uint8_t DP_DVI_CONVERTER_ID_4[] = "m2DVIa"; ++/*DP to Dual link DVI converter 2*/ ++static const uint8_t DP_DVI_CONVERTER_ID_42[] = "v2DVIa"; ++ ++static const uint8_t DP_SINK_DEV_STRING_ID2_REV0[] = "\0\0\0\0\0\0"; ++ ++/* Identifies second generation PSR TCON from Parade: Device ID string: ++ * yy-xx-**-**-**-** ++ */ ++/* xx - Hw ID high byte */ ++static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_HIGH_BYTE = ++ 0x06; ++ ++/* yy - HW ID low byte, the same silicon has several package/feature flavors */ ++static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE1 = ++ 0x61; ++static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE2 = ++ 0x62; ++static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE3 = ++ 0x63; ++static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE4 = ++ 0x72; ++static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE5 = ++ 0x73; ++ ++#endif /* __DAL_DDC_SERVICE_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h b/drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h +new file mode 100644 +index 0000000..35a5695 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DEFAULT_MODE_LIST_INTERFACE_H__ ++#define __DAL_DEFAULT_MODE_LIST_INTERFACE_H__ ++ ++struct default_mode_list; ++ ++uint32_t dal_default_mode_list_get_count(const struct default_mode_list *dml); ++ ++struct dc_mode_info *dal_default_mode_list_get_mode_info_at_index( ++ const struct default_mode_list *dml, ++ uint32_t index); ++ ++#endif /*__DAL_DEFAULT_MODE_LIST_INTERFACE_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/include/display_clock_interface.h b/drivers/gpu/drm/amd/dal/include/display_clock_interface.h +new file mode 100644 +index 0000000..2f48b8a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/display_clock_interface.h +@@ -0,0 +1,189 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DISPLAY_CLOCK_INTERFACE_H__ ++#define __DISPLAY_CLOCK_INTERFACE_H__ ++ ++#include "hw_sequencer_types.h" ++#include "grph_object_defs.h" ++#include "signal_types.h" ++#include "scaler_types.h" ++ ++/* Timing related information*/ ++struct dc_timing_params { ++ uint32_t INTERLACED:1; ++ uint32_t HCOUNT_BY_TWO:1; ++ uint32_t PIXEL_REPETITION:4; /*< values 1 to 10 supported*/ ++ uint32_t PREFETCH:1; ++ ++ uint32_t h_total; ++ uint32_t h_addressable; ++ uint32_t h_sync_width; ++}; ++ ++/* Scaling related information*/ ++struct dc_scaling_params { ++ uint32_t h_overscan_right; ++ uint32_t h_overscan_left; ++ uint32_t h_taps; ++ uint32_t v_taps; ++}; ++ ++/*Display Request Mode (1 and 2 valid when scaler is OFF)*/ ++enum display_request_mode { ++ REQUEST_ONLY_AT_EVERY_READ_POINTER_INCREMENT = 0, ++ REQUEST_WAITING_FOR_THE_FIRST_READ_POINTER_ONLY, ++ REQUEST_WITHOUT_WAITING_FOR_READ_POINTER ++}; ++ ++/* FBC minimum CompressionRatio*/ ++enum fbc_compression_ratio { ++ FBC_COMPRESSION_NOT_USED = 0, ++ FBC_MINIMUM_COMPRESSION_RATIO_1 = 1, ++ FBC_MINIMUM_COMPRESSION_RATIO_2 = 2, ++ FBC_MINIMUM_COMPRESSION_RATIO_4 = 4, ++ FBC_MINIMUM_COMPRESSION_RATIO_8 = 8 ++}; ++ ++/* VScalerEfficiency */ ++enum v_scaler_efficiency { ++ V_SCALER_EFFICIENCY_LB36BPP = 0, ++ V_SCALER_EFFICIENCY_LB30BPP = 1, ++ V_SCALER_EFFICIENCY_LB24BPP = 2, ++ V_SCALER_EFFICIENCY_LB18BPP = 3 ++}; ++ ++/* Parameters required for minimum Engine ++ * and minimum Display clock calculations*/ ++struct min_clock_params { ++ uint32_t id; ++ uint32_t requested_pixel_clock; /* in KHz */ ++ uint32_t actual_pixel_clock; /* in KHz */ ++ struct view source_view; ++ struct view dest_view; ++ struct dc_timing_params timing_info; ++ struct dc_scaling_params scaling_info; ++ struct color_quality color_info; ++ enum signal_type signal_type; ++ enum dc_color_depth deep_color_depth; ++ enum v_scaler_efficiency scaler_efficiency; ++ bool line_buffer_prefetch_enabled; ++}; ++ ++/* Enumerations for Source selection of the Display clock */ ++enum display_clock_source_select { ++ USE_PIXEL_CLOCK_PLL = 0, ++ USE_EXTERNAL_CLOCK, ++ USE_ENGINE_CLOCK ++}; ++ ++/* Result of Minimum System and Display clock calculations. ++ * Minimum System clock and Display clock, source and path to be used ++ * for Display clock*/ ++struct minimum_clocks_calculation_result { ++ uint32_t min_sclk_khz; ++ uint32_t min_dclk_khz; ++ uint32_t min_mclk_khz; ++ uint32_t min_deep_sleep_sclk; ++}; ++ ++/* Enumeration of all clocks states */ ++enum clocks_state { ++ CLOCKS_STATE_INVALID, ++ CLOCKS_STATE_ULTRA_LOW, ++ CLOCKS_STATE_LOW, ++ CLOCKS_STATE_NOMINAL, ++ CLOCKS_STATE_PERFORMANCE ++}; ++ ++/* Structure containing all state-dependent clocks ++ * (dependent on "enum clocks_state") */ ++struct state_dependent_clocks { ++ uint32_t display_clk_khz; ++ uint32_t pixel_clk_khz; ++ uint32_t dvo_clk_khz; ++}; ++ ++struct display_clock_state { ++ uint32_t DFS_BYPASS_ACTIVE:1; ++}; ++ ++struct display_clock; ++ ++struct display_clock *dal_display_clock_dce110_create( ++ struct dc_context *ctx, ++ struct adapter_service *as); ++ ++struct display_clock *dal_display_clock_dce80_create( ++ struct dc_context *ctx, ++ struct adapter_service *as); ++ ++void dal_display_clock_destroy(struct display_clock **to_destroy); ++bool dal_display_clock_validate( ++ struct display_clock *disp_clk, ++ struct min_clock_params *params); ++uint32_t dal_display_clock_calculate_min_clock( ++ struct display_clock *disp_clk, ++ uint32_t path_num, ++ struct min_clock_params *params); ++uint32_t dal_display_clock_get_validation_clock(struct display_clock *disp_clk); ++void dal_display_clock_set_clock( ++ struct display_clock *disp_clk, ++ uint32_t requested_clock_khz); ++uint32_t dal_display_clock_get_clock(struct display_clock *disp_clk); ++enum clocks_state dal_display_clock_get_min_clocks_state( ++ struct display_clock *disp_clk); ++enum clocks_state dal_display_clock_get_required_clocks_state( ++ struct display_clock *disp_clk, ++ struct state_dependent_clocks *req_clocks); ++bool dal_display_clock_set_min_clocks_state( ++ struct display_clock *disp_clk, ++ enum clocks_state clocks_state); ++uint32_t dal_display_clock_get_dp_ref_clk_frequency( ++ struct display_clock *disp_clk); ++/*the second parameter of "switchreferenceclock" is ++ * a dummy argument for all pre dce 6.0 versions*/ ++void dal_display_clock_switch_reference_clock( ++ struct display_clock *disp_clk, ++ bool use_external_ref_clk, ++ uint32_t requested_clock_khz); ++void dal_display_clock_set_dp_ref_clock_source( ++ struct display_clock *disp_clk, ++ enum clock_source_id clk_src); ++void dal_display_clock_store_max_clocks_state( ++ struct display_clock *disp_clk, ++ enum clocks_state max_clocks_state); ++void dal_display_clock_set_clock_state( ++ struct display_clock *disp_clk, ++ struct display_clock_state clk_state); ++struct display_clock_state dal_display_clock_get_clock_state( ++ struct display_clock *disp_clk); ++uint32_t dal_display_clock_get_dfs_bypass_threshold( ++ struct display_clock *disp_clk); ++void dal_display_clock_invalid_clock_state( ++ struct display_clock *disp_clk); ++ ++ ++#endif /* __DISPLAY_CLOCK_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/display_path_interface.h b/drivers/gpu/drm/amd/dal/include/display_path_interface.h +new file mode 100644 +index 0000000..7bf2ef2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/display_path_interface.h +@@ -0,0 +1,436 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DISPLAY_PATH_INTERFACE_H__ ++#define __DISPLAY_PATH_INTERFACE_H__ ++ ++#include "display_path_types.h" ++#include "dcs_types.h" ++#include "grph_object_ctrl_defs.h" ++#include "signal_types.h" ++#include "controller_types.h" ++ ++struct encoder; ++struct controller; ++struct connector; ++struct audio; ++struct clock_source; ++struct link_service; ++struct goc_link_service_data; ++struct drr_config; ++ ++struct display_path; ++ ++struct display_path *dal_display_path_create(void); ++ ++struct display_path *dal_display_path_clone( ++ const struct display_path *self, ++ bool copy_active_state); ++ ++void dal_display_path_destroy( ++ struct display_path **to_destroy); ++ ++bool dal_display_path_validate( ++ struct display_path *path, ++ enum signal_type sink_signal); ++ ++bool dal_display_path_add_link( ++ struct display_path *path, ++ struct encoder *encoder); ++ ++bool dal_display_path_add_connector( ++ struct display_path *path, ++ struct connector *connector); ++ ++struct connector *dal_display_path_get_connector( ++ struct display_path *path); ++ ++int32_t dal_display_path_acquire( ++ struct display_path *path); ++ ++bool dal_display_path_is_acquired( ++ const struct display_path *path); ++ ++int32_t dal_display_path_get_ref_counter( ++ const struct display_path *path); ++ ++int32_t dal_display_path_release( ++ struct display_path *path); ++ ++void dal_display_path_release_resources( ++ struct display_path *path); ++ ++void dal_display_path_acquire_links( ++ struct display_path *path); ++ ++bool dal_display_path_is_source_blanked( ++ const struct display_path *path); ++ ++bool dal_display_path_is_source_unblanked( ++ const struct display_path *path); ++ ++void dal_display_path_set_source_blanked( ++ struct display_path *path, ++ enum display_tri_state state); ++ ++bool dal_display_path_is_target_blanked( ++ const struct display_path *path); ++ ++bool dal_display_path_is_target_unblanked( ++ const struct display_path *path); ++ ++void dal_display_path_set_target_blanked( ++ struct display_path *path, ++ enum display_tri_state state); ++ ++bool dal_display_path_is_target_powered_on( ++ const struct display_path *path); ++ ++bool dal_display_path_is_target_powered_off( ++ const struct display_path *path); ++ ++void dal_display_path_set_target_powered_on( ++ struct display_path *path, ++ enum display_tri_state state); ++ ++bool dal_display_path_is_target_connected( ++ const struct display_path *path); ++ ++void dal_display_path_set_target_connected( ++ struct display_path *path, ++ bool c); ++ ++uint32_t dal_display_path_get_display_index( ++ const struct display_path *path); ++ ++void dal_display_path_set_display_index( ++ struct display_path *path, ++ uint32_t index); ++ ++struct connector_device_tag_info *dal_display_path_get_device_tag( ++ struct display_path *path); ++ ++void dal_display_path_set_device_tag( ++ struct display_path *path, ++ struct connector_device_tag_info tag); ++ ++enum clock_sharing_group dal_display_path_get_clock_sharing_group( ++ const struct display_path *path); ++ ++void dal_display_path_set_clock_sharing_group( ++ struct display_path *path, ++ enum clock_sharing_group clock); ++ ++union display_path_properties dal_display_path_get_properties( ++ const struct display_path *path); ++ ++void dal_display_path_set_properties( ++ struct display_path *path, ++ union display_path_properties p); ++ ++struct dcs *dal_display_path_get_dcs( ++ const struct display_path *path); ++ ++void dal_display_path_set_dcs( ++ struct display_path *path, ++ struct dcs *dcs); ++ ++uint32_t dal_display_path_get_number_of_links( ++ const struct display_path *path); ++ ++void dal_display_path_set_controller( ++ struct display_path *path, ++ struct controller *controller); ++ ++struct controller *dal_display_path_get_controller( ++ const struct display_path *path); ++ ++void dal_display_path_set_clock_source( ++ struct display_path *path, ++ struct clock_source *clock); ++ ++struct clock_source *dal_display_path_get_clock_source( ++ const struct display_path *path); ++ ++void dal_display_path_set_alt_clock_source( ++ struct display_path *path, ++ struct clock_source *clock); ++ ++struct clock_source *dal_display_path_get_alt_clock_source( ++ const struct display_path *path); ++ ++void dal_display_path_set_fbc_info( ++ struct display_path *path, ++ struct fbc_info *clock); ++ ++struct fbc_info *dal_display_path_get_fbc_info( ++ struct display_path *path); ++ ++void dal_display_path_set_drr_config( ++ struct display_path *path, ++ struct drr_config *clock); ++ ++void dal_display_path_get_drr_config( ++ const struct display_path *path, ++ struct drr_config *clock); ++ ++void dal_display_path_set_static_screen_triggers( ++ struct display_path *path, ++ const struct static_screen_events *events); ++ ++void dal_display_path_get_static_screen_triggers( ++ const struct display_path *path, ++ struct static_screen_events *events); ++ ++bool dal_display_path_is_psr_supported( ++ const struct display_path *path); ++ ++bool dal_display_path_is_drr_supported( ++ const struct display_path *path); ++ ++void dal_display_path_set_link_service_data( ++ struct display_path *path, ++ uint32_t idx, ++ const struct goc_link_service_data *data); ++ ++bool dal_display_path_get_link_service_data( ++ const struct display_path *path, ++ uint32_t idx, ++ struct goc_link_service_data *data); ++ ++struct link_service *dal_display_path_get_link_query_interface( ++ const struct display_path *path, ++ uint32_t idx); ++ ++void dal_display_path_set_link_query_interface( ++ struct display_path *path, ++ uint32_t idx, ++ struct link_service *link); ++ ++struct link_service *dal_display_path_get_link_config_interface( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct link_service *dal_display_path_get_link_service_interface( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct encoder *dal_display_path_get_upstream_encoder( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct encoder *dal_display_path_get_upstream_object( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct encoder *dal_display_path_get_downstream_encoder( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct encoder *dal_display_path_get_downstream_object( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct audio *dal_display_path_get_audio( ++ const struct display_path *path, ++ uint32_t idx); ++ ++void dal_display_path_set_audio( ++ struct display_path *path, ++ uint32_t idx, ++ struct audio *a); ++ ++struct audio *dal_display_path_get_audio_object( ++ const struct display_path *path, ++ uint32_t idx); ++ ++void dal_display_path_set_audio_active_state( ++ struct display_path *path, ++ uint32_t idx, ++ bool state); ++ ++enum engine_id dal_display_path_get_stream_engine( ++ const struct display_path *path, ++ uint32_t idx); ++ ++void dal_display_path_set_stream_engine( ++ struct display_path *path, ++ uint32_t idx, ++ enum engine_id id); ++ ++bool dal_display_path_is_link_active( ++ const struct display_path *path, ++ uint32_t idx); ++ ++void dal_display_path_set_link_active_state( ++ struct display_path *path, ++ uint32_t idx, ++ bool state); ++ ++enum signal_type dal_display_path_get_config_signal( ++ const struct display_path *path, ++ uint32_t idx); ++ ++enum signal_type dal_display_path_get_query_signal( ++ const struct display_path *path, ++ uint32_t idx); ++ ++struct link_service *dal_display_path_get_mst_link_service( ++ const struct display_path *path); ++ ++void dal_display_path_set_sync_output_object( ++ struct display_path *path, ++ enum sync_source o_source, ++ struct encoder *o_object); ++ ++struct encoder *dal_display_path_get_sync_output_object( ++ const struct display_path *path); ++ ++void dal_display_path_set_stereo_sync_object( ++ struct display_path *path, ++ struct encoder *stereo_sync); ++ ++struct encoder *dal_display_path_get_stereo_sync_object( ++ const struct display_path *path); ++ ++void dal_display_path_set_sync_input_source( ++ struct display_path *path, ++ enum sync_source s); ++ ++enum sync_source dal_display_path_get_sync_input_source( ++ const struct display_path *path); ++ ++void dal_display_path_set_sync_output_source( ++ struct display_path *path, ++ enum sync_source s); ++ ++enum sync_source dal_display_path_get_sync_output_source( ++ const struct display_path *path); ++ ++bool dal_display_path_set_pixel_clock_safe_range( ++ struct display_path *path, ++ struct pixel_clock_safe_range *range); ++ ++bool dal_display_path_get_pixel_clock_safe_range( ++ const struct display_path *path, ++ struct pixel_clock_safe_range *range); ++ ++void dal_display_path_set_ddi_channel_mapping( ++ struct display_path *path, ++ union ddi_channel_mapping mapping); ++ ++bool dal_display_path_set_sink_signal( ++ struct display_path *path, ++ enum signal_type sink_signal); ++ ++enum signal_type dal_display_path_sink_signal_to_asic_signal( ++ struct display_path *path, ++ enum signal_type sink_signal); ++ ++enum signal_type dal_display_path_sink_signal_to_link_signal( ++ struct display_path *path, ++ enum signal_type sink_signal, ++ uint32_t idx); ++ ++enum signal_type dal_display_path_downstream_to_upstream_signal( ++ struct display_path *path, ++ enum signal_type signal, ++ uint32_t idx); ++ ++bool dal_display_path_is_audio_present( ++ const struct display_path *path, ++ uint32_t *audio_pin); ++ ++bool dal_display_path_is_dp_auth_supported( ++ struct display_path *path); ++ ++bool dal_display_path_is_vce_supported( ++ const struct display_path *path); ++ ++bool dal_display_path_is_sls_capable( ++ const struct display_path *path); ++ ++bool dal_display_path_is_gen_lock_capable( ++ const struct display_path *path); ++ ++struct transmitter_configuration dal_display_path_get_transmitter_configuration( ++ const struct display_path *path, ++ bool physical); ++ ++bool dal_display_path_is_ss_supported( ++ const struct display_path *path); ++ ++bool dal_display_path_is_ss_configurable( ++ const struct display_path *path); ++ ++void dal_display_path_set_ss_support( ++ struct display_path *path, ++ bool s); ++ ++enum signal_type dal_display_path_get_active_signal( ++ struct display_path *path, ++ uint32_t idx); ++ ++bool dal_display_path_contains_object( ++ struct display_path *path, ++ struct graphics_object_id id); ++ ++/* Multi-plane declarations. ++ * This structure should also be used for Stereo. */ ++struct display_path_plane { ++ struct controller *controller; ++ /* During dal_tm_acquire_plane_resources() set blnd_mode, because ++ * "layer index" is known at that point, and we must decide how ++ * "controller" should do the blending */ ++ enum blender_mode blnd_mode; ++ /* Some use-cases allow to power-gate FE. ++ * For example, with Full Screen Video on Underlay we can ++ * disable the 'root' plane. ++ * This flag indicates that FE should be power-gated */ ++ bool disabled; ++}; ++ ++bool dal_display_path_add_plane( ++ struct display_path *path, ++ struct display_path_plane *plane); ++ ++uint8_t dal_display_path_get_number_of_planes( ++ const struct display_path *path); ++ ++struct display_path_plane *dal_display_path_get_plane_at_index( ++ const struct display_path *path, ++ uint8_t index); ++ ++struct controller *dal_display_path_get_controller_for_layer_index( ++ const struct display_path *path, ++ uint8_t layer_index); ++ ++void dal_display_path_release_planes( ++ struct display_path *path); ++ ++void dal_display_path_release_non_root_planes( ++ struct display_path *path); ++ ++#endif /* __DISPLAY_PATH_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/display_path_types.h b/drivers/gpu/drm/amd/dal/include/display_path_types.h +new file mode 100644 +index 0000000..8aac46d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/display_path_types.h +@@ -0,0 +1,132 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DISPLAY_PATH_TYPES_H__ ++#define __DAL_DISPLAY_PATH_TYPES_H__ ++ ++#include "grph_object_defs.h" ++ ++enum { ++ CONTROLLER_HANDLE_INVALID = (uint32_t) (-1) ++}; ++ ++/*Limit maximum number of cofunctional paths*/ ++enum { ++ MAX_COFUNCTIONAL_PATHS = 6 ++}; ++ ++struct pixel_clock_safe_range { ++ uint32_t min_frequency; ++ uint32_t max_frequency; ++}; ++ ++/** ++ * ClockSharingGroup ++ * Enumeration of Clock Source Sharing categories ++ * Instead using enum we define valid range for clock sharing group values ++ * This is because potential num of group can be pretty big ++ */ ++ ++enum clock_sharing_group { ++ /* Default group for display paths that cannot share clock source. ++ * Display path in such group will aqcuire clock source exclusively*/ ++ CLOCK_SHARING_GROUP_EXCLUSIVE = 0, ++ /* DisplayPort paths will have this group if clock sharing ++ * level is DisplayPortShareable*/ ++ CLOCK_SHARING_GROUP_DISPLAY_PORT = 1, ++ /* Mst paths will have this group if clock sharing ++ * level is DpMstShareable*/ ++ CLOCK_SHARING_GROUP_DP_MST = 2, ++ /* Display paths will have this group when ++ * desired to use alternative DPRef clock source.*/ ++ CLOCK_SHARING_GROUP_ALTERNATIVE_DP_REF = 3, ++ /* Start of generic SW sharing groups.*/ ++ CLOCK_SHARING_GROUP_GROUP1 = 4, ++ /* Total number of clock sharing groups.*/ ++ CLOCK_SHARING_GROUP_MAX = 32, ++}; ++/* Should be around maximal number of ever connected displays (since boot :)*/ ++/*TEMP*/ ++enum goc_link_settings_type { ++ GOC_LINK_SETTINGS_TYPE_PREFERRED = 0, ++ GOC_LINK_SETTINGS_TYPE_REPORTED, ++ GOC_LINK_SETTINGS_TYPE_TRAINED, ++ GOC_LINK_SETTINGS_TYPE_OVERRIDEN_TRAINED, ++ GOC_LINK_SETTINGS_TYPE_MAX ++}; ++ ++struct dp_audio_test_data { ++ ++ struct dp_audio_test_data_flags { ++ uint32_t test_requested:1; ++ uint32_t disable_video:1; ++ } flags; ++ ++ /*struct dp_audio_test_data_flags flags;*/ ++ uint32_t sampling_rate; ++ uint32_t channel_count; ++ uint32_t pattern_type; ++ uint8_t pattern_period[8]; ++}; ++ ++struct goc_link_service_data { ++ struct dp_audio_test_data dp_audio_test_data; ++}; ++/* END-OF-TEMP*/ ++ ++ ++union display_path_properties { ++ struct bit_map { ++ uint32_t ALWAYS_CONNECTED:1; ++ uint32_t HPD_SUPPORTED:1; ++ uint32_t NON_DESTRUCTIVE_POLLING:1; ++ uint32_t FORCE_CONNECT_SUPPORTED:1; ++ uint32_t FAKED_PATH:1; ++ uint32_t IS_BRANCH_DP_MST_PATH:1; ++ uint32_t IS_ROOT_DP_MST_PATH:1; ++ uint32_t IS_DP_AUDIO_SUPPORTED:1; ++ uint32_t IS_HDMI_AUDIO_SUPPORTED:1; ++ } bits; ++ ++ uint32_t raw; ++}; ++ ++enum display_tri_state { ++ DISPLAY_TRI_STATE_UNKNOWN = 0, ++ DISPLAY_TRI_STATE_TRUE, ++ DISPLAY_TRI_STATE_FALSE ++}; ++ ++enum { ++ MAX_NUM_OF_LINKS_PER_PATH = 2 ++}; ++enum { ++ SINK_LINK_INDEX = (uint32_t) (-1) ++}; ++enum { ++ ASIC_LINK_INDEX = 0 ++}; ++ ++#endif /* __DAL_DISPLAY_PATH_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/display_service_interface.h b/drivers/gpu/drm/amd/dal/include/display_service_interface.h +new file mode 100644 +index 0000000..b602bca +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/display_service_interface.h +@@ -0,0 +1,165 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DISPLAY_SERVICE_INTERFACE_H__ ++#define __DISPLAY_SERVICE_INTERFACE_H__ ++ ++#include "include/display_service_types.h" ++#include "include/display_path_types.h" ++#include "include/grph_object_ctrl_defs.h" ++ ++struct display_service; ++struct ds_overlay; ++struct ds_dispatch; ++struct ds_synchronization; ++struct path_mode_set; ++ ++struct display_service *dal_display_service_create( ++ struct ds_init_data *data); ++ ++void dal_display_service_destroy( ++ struct display_service **ds); ++ ++struct ds_dispatch *dal_display_service_get_adjustment_interface( ++ struct display_service *ds); ++ ++struct ds_overlay *dal_display_service_get_overlay_interface( ++ struct display_service *ds); ++ ++struct ds_dispatch *dal_display_service_get_set_mode_interface( ++ struct display_service *ds); ++ ++struct ds_dispatch *dal_display_service_get_reset_mode_interface( ++ struct display_service *ds); ++ ++struct ds_synchronization *dal_display_service_get_synchronization_interface( ++ struct display_service *ds); ++ ++enum ds_return dal_display_service_notify_v_sync_int_state( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool maintain_v_sync_phase); ++ ++enum ds_return dal_display_service_target_power_control( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool power_on); ++ ++enum ds_return dal_display_service_power_down_active_hw( ++ struct display_service *ds, ++ enum dc_video_power_state state); ++ ++enum ds_return dal_display_service_mem_request_control( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool enable); ++ ++enum ds_return dal_display_service_set_multimedia_pass_through_mode( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool passThrough); ++ ++enum ds_return dal_display_service_set_palette( ++ struct display_service *ds, ++ uint32_t display_index, ++ const struct ds_devclut *palette, ++ const uint32_t start, ++ const uint32_t length); ++ ++enum ds_return dal_display_service_apply_pix_clk_range( ++ struct display_service *ds, ++ uint32_t display_index, ++ struct pixel_clock_safe_range *range); ++ ++enum ds_return dal_display_service_get_safe_pix_clk( ++ struct display_service *ds, ++ uint32_t display_index, ++ uint32_t *pix_clk_khz); ++ ++enum ds_return dal_display_service_apply_refreshrate_adjustment( ++ struct display_service *ds, ++ uint32_t display_index, ++ enum ds_refreshrate_adjust_action action, ++ struct ds_refreshrate *refreshrate); ++ ++enum ds_return dal_display_service_pre_ddc( ++ struct display_service *ds, ++ uint32_t display_index); ++ ++enum ds_return dal_display_service_post_ddc( ++ struct display_service *ds, ++ uint32_t display_index); ++ ++enum ds_return dal_display_service_backlight_control( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool enable); ++ ++enum ds_return dal_display_service_get_backlight_user_level( ++ struct display_service *ds, ++ uint32_t display_index, ++ uint32_t *level); ++ ++enum ds_return dal_display_service_get_backlight_effective_level( ++ struct display_service *ds, ++ uint32_t display_index, ++ uint32_t *level); ++ ++enum ds_return dal_display_service_enable_hpd( ++ struct display_service *ds, ++ uint32_t display_index); ++ ++enum ds_return dal_display_service_disable_hpd( ++ struct display_service *ds, ++ uint32_t display_index); ++ ++enum ds_return dal_display_service_get_min_mem_channels( ++ struct display_service *ds, ++ const struct path_mode_set *path_mode_set, ++ uint32_t mem_channels_num, ++ uint32_t *min_mem_channels_num); ++ ++enum ds_return dal_display_service_enable_advanced_request( ++ struct display_service *ds, ++ bool enable); ++ ++/*Audio related*/ ++enum ds_return dal_display_service_enable_audio_endpoint( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool enable); ++ ++enum ds_return dal_display_service_mute_audio_endpoint( ++ struct display_service *ds, ++ uint32_t display_index, ++ bool mute); ++ ++bool dal_display_service_calc_view_port_for_wide_display( ++ struct display_service *ds, ++ uint32_t display_index, ++ const struct ds_view_port *set_view_port, ++ struct ds_get_view_port *get_view_port); ++ ++#endif /* __DISPLAY_SERVICE_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/display_service_types.h b/drivers/gpu/drm/amd/dal/include/display_service_types.h +new file mode 100644 +index 0000000..4f27f59 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/display_service_types.h +@@ -0,0 +1,167 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DISPLAY_SERVICE_TYPES_H__ ++#define __DAL_DISPLAY_SERVICE_TYPES_H__ ++struct ds_dispatch { ++ ++}; ++ ++struct ds_view_port_alignment { ++ uint8_t x_width_size_alignment; ++ uint8_t y_height_size_alignment; ++ uint8_t x_start_alignment; ++ uint8_t y_start_alignment; ++}; ++ ++struct hw_sequencer; ++struct topology_mgr; ++struct adapter_service; ++struct timing_service; ++ ++struct ds_init_data { ++ struct dal_context *dal_context; ++ struct hw_sequencer *hwss; ++ struct topology_mgr *tm; ++ struct adapter_service *as; ++ struct timing_service *ts; ++ struct ds_view_port_alignment view_port_alignment; ++}; ++ ++enum ds_return { ++ DS_SUCCESS, ++ DS_SUCCESS_FALLBACK, ++ DS_ERROR, ++ DS_SET_MODE_REQUIRED, ++ DS_REBOOT_REQUIRED, ++ DS_OUT_OF_RANGE, ++ DS_RESOURCE_UNAVAILABLE, ++ DS_NOT_SUPPORTED ++}; ++ ++struct ds_devclut { ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++ uint8_t reserved; ++}; ++ ++enum ds_refreshrate_adjust_action { ++ DS_REFRESHRATE_ADJUST_ACTION_SET, ++ DS_REFRESHRATE_ADJUST_ACTION_RESET, ++ DS_REFRESHRATE_ADJUST_ACTION_UPDATE, ++}; ++ ++struct ds_refreshrate { ++ uint32_t numerator; ++ uint32_t denominator; ++}; ++ ++/*Contains delta in pixels between two active CRTC timings and relevant timing ++details. Delta will be positive if CRTC1 timing running before CRTC2 and ++negative otherwise (CRTC2 timing running before CRTC1)*/ ++/*CRTC1 running before CRTC2 = CRTC1 pixel position in ++frame smaller then CRTC2 position*/ ++struct ds_timings_delta_info { ++ int32_t delta_in_pixels; ++ uint32_t pix_clk_khz; ++ uint32_t h_total; ++ uint32_t v_total; ++}; ++ ++enum ds_audio_os_channel_name { ++ DS_AUDIO_OS_CHANNEL_L = 0, ++ DS_AUDIO_OS_CHANNEL_R = 1, ++ DS_AUDIO_OS_CHANNEL_C = 2, ++ DS_AUDIO_OS_CHANNEL_SUB = 3, ++ DS_AUDIO_OS_CHANNEL_RL = 4, ++ DS_AUDIO_OS_CHANNEL_RR = 5, ++ DS_AUDIO_OS_CHANNEL_SL = 6, ++ DS_AUDIO_OS_CHANNEL_SR = 7, ++ DS_AUDIO_OS_CHANNEL_SILENT = 8, ++ DS_AUDIO_OS_CHANNEL_NO_ASSOCIATION = 15 ++}; ++ ++enum ds_audio_azalia_channel_name { ++ DS_AUDIO_AZALIA_CHANNEL_FL = 0, ++ DS_AUDIO_AZALIA_CHANNEL_FR = 1, ++ DS_AUDIO_AZALIA_CHANNEL_FC = 2, ++ DS_AUDIO_AZALIA_CHANNEL_SUB = 3, ++ DS_AUDIO_AZALIA_CHANNEL_SL = 4, ++ DS_AUDIO_AZALIA_CHANNEL_SR = 5, ++ DS_AUDIO_AZALIA_CHANNEL_BL = 6, ++ DS_AUDIO_AZALIA_CHANNEL_BR = 7, ++ DS_AUDIO_AZALIA_CHANNEL_SILENT = 8, ++ DS_AUDIO_AZALIA_CHANNEL_NO_ASSOCIATION = 15 ++}; ++ ++enum ds_audio_channel_format { ++ DS_AUDIO_CHANNEL_FORMAT_2P0 = 0, ++ DS_AUDIO_CHANNEL_FORMAT_2P1, ++ DS_AUDIO_CHANNEL_FORMAT_5P1, ++ DS_AUDIO_CHANNEL_FORMAT_7P1 ++}; ++ ++/*Used for get/set Mirabilis*/ ++enum ds_mirabilis_control_option { ++ DS_MIRABILIS_UNINITIALIZE = 0, ++ DS_MIRABILIS_DISABLE, ++ DS_MIRABILIS_ENABLE, ++ DS_MIRABILIS_SAVE_PROFILE ++}; ++ ++struct ds_disp_identifier { ++ uint32_t display_index; ++ uint32_t manufacture_id; ++ uint32_t product_id; ++ uint32_t serial_no; ++}; ++ ++struct ds_view_port { ++ uint32_t x_start; ++ uint32_t y_start; ++ uint32_t width; ++ uint32_t height; ++ uint32_t controller; ++}; ++ ++#define DS_MAX_NUM_VIEW_PORTS 2 ++struct ds_get_view_port { ++ uint32_t num_of_view_ports; ++ struct ds_view_port view_ports[DS_MAX_NUM_VIEW_PORTS]; ++}; ++ ++struct ranged_timing_preference_flags { ++ union { ++ struct { ++ uint32_t prefer_enable_drr:1; ++ uint32_t force_disable_drr:1; ++ ++ } bits; ++ uint32_t u32all; ++ }; ++}; ++ ++#endif /* __DAL_DISPLAY_SERVICE_TYPE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dmcu_interface.h b/drivers/gpu/drm/amd/dal/include/dmcu_interface.h +new file mode 100644 +index 0000000..c712cc2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dmcu_interface.h +@@ -0,0 +1,87 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DMCU_INTERFACE_H__ ++#define __DAL_DMCU_INTERFACE_H__ ++ ++#include "grph_object_defs.h" ++#include "dmcu_types.h" ++ ++/* Interface functions */ ++ ++/* DMCU setup related interface functions */ ++struct dmcu *dal_dmcu_create( ++ struct dmcu_init_data *init_data); ++void dal_dmcu_destroy(struct dmcu **dmcu); ++void dal_dmcu_release_hw(struct dmcu *dmcu); ++ ++void dal_dmcu_power_up(struct dmcu *dmcu); ++void dal_dmcu_power_down(struct dmcu *dmcu); ++ ++void dal_dmcu_configure_wait_loop( ++ struct dmcu *dmcu, ++ uint32_t display_clock); ++ ++/* PSR feature related interface functions */ ++void dal_dmcu_psr_setup( ++ struct dmcu *dmcu, ++ struct dmcu_context *dmcu_context); ++void dal_dmcu_psr_enable(struct dmcu *dmcu); ++void dal_dmcu_psr_disable(struct dmcu *dmcu); ++void dal_dmcu_psr_block(struct dmcu *dmcu, bool block_psr); ++bool dal_dmcu_psr_is_blocked(struct dmcu *dmcu); ++void dal_dmcu_psr_set_level( ++ struct dmcu *dmcu, ++ union dmcu_psr_level psr_level); ++void dal_dmcu_psr_allow_power_down_crtc( ++ struct dmcu *dmcu, ++ bool should_allow_crtc_power_down); ++bool dal_dmcu_psr_submit_command( ++ struct dmcu *dmcu, ++ struct dmcu_context *dmcu_context, ++ struct dmcu_config_data *config_data); ++void dal_dmcu_psr_get_config_data( ++ struct dmcu *dmcu, ++ uint32_t v_total, ++ struct dmcu_config_data *config_data); ++ ++/* ABM feature related interface functions */ ++void dal_dmcu_abm_enable( ++ struct dmcu *dmcu, ++ enum controller_id controller_id, ++ uint32_t vsync_rate_hz); ++void dal_dmcu_abm_disable(struct dmcu *dmcu); ++bool dal_dmcu_abm_enable_smooth_brightness(struct dmcu *dmcu); ++bool dal_dmcu_abm_disable_smooth_brightness(struct dmcu *dmcu); ++void dal_dmcu_abm_varibright_control( ++ struct dmcu *dmcu, ++ const struct varibright_control *varibright_control); ++bool dal_dmcu_abm_set_backlight_level( ++ struct dmcu *dmcu, ++ uint8_t backlight_8_bit); ++uint8_t dal_dmcu_abm_get_user_backlight_level(struct dmcu *dmcu); ++uint8_t dal_dmcu_abm_get_current_backlight_level(struct dmcu *dmcu); ++ ++#endif /* __DAL_DMCU_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dmcu_types.h b/drivers/gpu/drm/amd/dal/include/dmcu_types.h +new file mode 100644 +index 0000000..1f3107d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dmcu_types.h +@@ -0,0 +1,199 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DMCU_TYPES_H__ ++#define __DAL_DMCU_TYPES_H__ ++ ++/* Forward declaration */ ++struct dmcu; ++ ++/* Required information for creation and initialization of a controller */ ++struct dmcu_init_data { ++ struct dal_context *dal_context; ++ struct adapter_service *as; ++ uint32_t max_engine_clock_in_khz; ++}; ++ ++/* Interface structure defines */ ++ ++enum dmcu_action { ++ DMCU_ACTION_PSR_ENABLE, ++ DMCU_ACTION_PSR_EXIT, ++ DMCU_ACTION_PSR_RFB_UPDATE, ++ DMCU_ACTION_PSR_SET, ++ DMCU_ACTION_PSR_CLEAR_COUNT, ++ DMCU_ACTION_PSR_COUNT_REQUEST, ++ DMCU_ACTION_PSR_STATE_REQUEST, ++ DMCU_ACTION_PSR_SET_LEVEL, ++ DMCU_ACTION_PSR_ADVANCE_STATE, ++ DMCU_ACTION_PSR_SET_WAITLOOP ++}; ++ ++enum dmcu_output { ++ DMCU_OUTPUT_PSR_ACK, ++ DMCU_OUTPUT_PSR_NACK, ++ DMCU_OUTPUT_PSR_AUX_ERR, ++ DMCU_OUTPUT_PSR_COUNT_STATUS, ++ DMCU_OUTPUT_PSR_STATE_STATUS, ++ DMCU_OUTPUT_PSR_RFB_UPDATE_ERR, ++ DMCU_OUTPUT_PSR_ERR, ++ DMCU_OUTPUT_PSR_GET_REPLY, ++ DMCU_OUTPUT_PSR_ENTRY_ERROR, ++ DMCU_OUTPUT_PSR_LT_ERROR, ++ DMCU_OUTPUT_PSR_FORCE_SR_ERROR, ++ DMCU_OUTPUT_PSR_SDP_SEND_TIMEOUT ++}; ++ ++/* PSR states, based similarly on states defined in eDP specification. */ ++enum psr_state { ++ STATE0, /* PSR is disabled */ ++ STATE1, /* PSR is enabled, but inactive */ ++ STATE1A, ++ STATE2, /* PSR is transitioning to active state */ ++ STATE2A, ++ STATE3, /* PSR is active; Display is in self refresh */ ++ STATE3INIT, ++ STATE4, /* RFB single frame update */ ++ STATE4A, ++ STATE4B, ++ STATE4C, ++ STATE4D, ++ STATE5, /* Exiting from PSR active state */ ++ STATE5A, ++ STATE5B, ++ STATE5C ++}; ++ ++enum phy_type { ++ PHY_TYPE_UNKNOWN = 1, ++ PHY_TYPE_PCIE_PHY = 2, ++ PHY_TYPE_UNIPHY = 3, ++}; ++ ++struct dmcu_context { ++ enum channel_id channel; ++ enum transmitter transmitter_id; ++ enum engine_id engine_id; ++ enum controller_id controller_id; ++ enum phy_type phy_type; ++ enum physical_phy_id smu_physical_phy_id; ++ ++ /* Vertical total pixels from crtc timing. ++ * This is used for static screen detection. ++ * ie. If we want to detect half a frame, ++ * we use this to determine the hyst lines.*/ ++ uint32_t crtc_timing_vertical_total; ++ ++ /* PSR supported from panel capabilities ++ * and current display configuration */ ++ bool psr_supported_display_config; ++ ++ /* Whether fast link training is supported by the panel */ ++ bool psr_exit_link_training_required; ++ ++ /* If RFB setup time is greater than the total VBLANK time, it is not ++ * possible for the sink to capture the video frame in the same frame ++ * the SDP is sent. In this case, the frame capture indication bit ++ * should be set and an extra static frame should be transmitted to ++ * the sink */ ++ bool psr_frame_capture_indication_required; ++ ++ /* Set the last possible line SDP may be transmitted without violating ++ * the RFB setup time */ ++ bool sdp_transmit_line_num_deadline; ++ ++ /* The VSync rate in Hz used to calculate the step size ++ * for smooth brightness feature */ ++ uint32_t vsync_rate_hz; ++}; ++ ++union dmcu_psr_level { ++ struct { ++ bool SKIP_CRC:1; ++ bool SKIP_DP_VID_STREAM_DISABLE:1; ++ bool SKIP_PHY_POWER_DOWN:1; ++ bool SKIP_AUX_ACK_CHECK:1; ++ bool SKIP_CRTC_DISABLE:1; ++ bool SKIP_AUX_RFB_CAPTURE_CHECK:1; ++ bool SKIP_SMU_NOTIFICATION:1; ++ bool SKIP_AUTO_STATE_ADVANCE:1; ++ bool DISABLE_PSR_ENTRY_ABORT:1; ++ } bits; ++ uint32_t u32all; ++}; ++ ++struct dmcu_config_data { ++ /* Command sent to DMCU. */ ++ enum dmcu_action action; ++ /* PSR Level controls which HW blocks to power down during PSR active, ++ * and also other sequence modifications. */ ++ union dmcu_psr_level psr_level; ++ /* To indicate that first changed frame from active state should not ++ * result in exit to inactive state, but instead perform an automatic ++ * single frame RFB update. */ ++ bool rfb_update_auto_en; ++ /* Number of consecutive static frames to detect before entering PSR ++ * active state. */ ++ uint32_t hyst_frames; ++ /* Partial frames before entering PSR active. Note this parameter is in ++ * units of 100 lines. i.e. Wait a value of 5 means wait 500 additional ++ * lines. */ ++ uint32_t hyst_lines; ++ /* Number of repeated AUX retries before indicating failure to driver. ++ * In a working case, first attempt to write/read AUX should pass. */ ++ uint32_t aux_repeat; ++ /* Additional delay after remote frame capture before continuing to ++ * power down. This is mainly for debug purposes to identify timing ++ * issues. */ ++ uint32_t frame_delay; ++ /* Controls how long the delay of a wait loop is. It should be tuned ++ * to 1 us, and needs to be reconfigured every time DISPCLK changes. */ ++ uint32_t wait_loop_num; ++}; ++ ++struct dmcu_output_data { ++ /* DMCU reply */ ++ enum dmcu_output output; ++ /* The current PSR state. */ ++ uint32_t psr_state; ++ /* The number of frames during PSR active state. */ ++ uint32_t psr_count; ++}; ++ ++enum varibright_command { ++ VARIBRIGHT_CMD_SET_VB_LEVEL = 0, ++ VARIBRIGHT_CMD_USER_ENABLE, ++ VARIBRIGHT_CMD_POST_DISPLAY_CONFIG, ++ VARIBRIGHT_CMD_UNKNOWN ++}; ++ ++struct varibright_control { ++ enum varibright_command command; ++ uint8_t level; ++ bool enable; ++ bool activate; ++}; ++ ++#endif /* __DAL_DMCU_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dpcd_access_service_interface.h b/drivers/gpu/drm/amd/dal/include/dpcd_access_service_interface.h +new file mode 100644 +index 0000000..a942c77 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dpcd_access_service_interface.h +@@ -0,0 +1,65 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifdef __DPCD_ACCESS_SERVICE_INTERFACE_HPP__ ++#define __DPCD_ACCESS_SERVICE_INTERFACE_HPP__ ++ ++/* DDC service transaction error codes ++ * depends on transaction status ++ */ ++enum ddc_result { ++ DDCRESULT_UNKNOWN = 0, ++ DDCRESULT_SUCESSFULL, ++ DDCRESULT_FAILEDCHANNELBUSY, ++ DDCRESULT_FAILEDTIMEOUT, ++ DDCRESULT_FAILEDPROTOCOLERROR, ++ DDCRESULT_FAILEDNACK, ++ DDCRESULT_FAILEDINCOMPLETE, ++ DDCRESULT_FAILEDOPERATION, ++ DDCRESULT_FAILEDINVALIDOPERATION, ++ DDCRESULT_FAILEDBUFFEROVERFLOW ++}; ++ ++enum { ++ MaxNativeAuxTransactionSize = 16 ++}; ++ ++struct display_sink_capability; ++ ++/* TO DO: below functions can be moved to ddc_service (think about it)*/ ++enum ddc_result dal_ddc_read_dpcd_data( ++ uint32_t address, ++ unsigned char *data, ++ uint32_t size); ++ ++enum ddc_result dal_ddc_write_dpcd_data( ++ uint32_t address, ++ const unsigned char *data uint32_t size); ++ ++bool dal_aux_query_dp_sink_capability(display_sink_capability *sink_cap); ++bool start_gtc_sync(void); ++bool stop_gtc_sync(void); ++ ++#endif /*__DPCD_ACCESS_SERVICE_INTERFACE_HPP__*/ +diff --git a/drivers/gpu/drm/amd/dal/include/dpcd_defs.h b/drivers/gpu/drm/amd/dal/include/dpcd_defs.h +new file mode 100644 +index 0000000..cefa1fc +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dpcd_defs.h +@@ -0,0 +1,869 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DPCD_DEFS_H__ ++#define __DAL_DPCD_DEFS_H__ ++ ++enum dpcd_address { ++/* addresses marked with 1.2 are only defined since DP 1.2 spec */ ++ ++ /* Reciever Capability Field */ ++ DPCD_ADDRESS_DPCD_REV = 0x00000, ++ DPCD_ADDRESS_MAX_LINK_RATE = 0x00001, ++ DPCD_ADDRESS_MAX_LANE_COUNT = 0x00002, ++ DPCD_ADDRESS_MAX_DOWNSPREAD = 0x00003, ++ DPCD_ADDRESS_NORP = 0x00004, ++ DPCD_ADDRESS_DOWNSTREAM_PORT_PRESENT = 0x00005, ++ DPCD_ADDRESS_MAIN_LINK_CHANNEL_CODING = 0x00006, ++ DPCD_ADDRESS_DOWNSTREAM_PORT_COUNT = 0x00007, ++ DPCD_ADDRESS_RECEIVE_PORT0_CAP0 = 0x00008, ++ DPCD_ADDRESS_RECEIVE_PORT0_CAP1 = 0x00009, ++ DPCD_ADDRESS_RECEIVE_PORT1_CAP0 = 0x0000A, ++ DPCD_ADDRESS_RECEIVE_PORT1_CAP1 = 0x0000B, ++ ++ DPCD_ADDRESS_I2C_SPEED_CNTL_CAP = 0x0000C,/*1.2*/ ++ DPCD_ADDRESS_EDP_CONFIG_CAP = 0x0000D,/*1.2*/ ++ DPCD_ADDRESS_TRAINING_AUX_RD_INTERVAL = 0x000E,/*1.2*/ ++ ++ DPCD_ADDRESS_MSTM_CAP = 0x00021,/*1.2*/ ++ ++ /* Audio Video Sync Data Feild */ ++ DPCD_ADDRESS_AV_GRANULARITY = 0x0023, ++ DPCD_ADDRESS_AUDIO_DECODE_LATENCY1 = 0x0024, ++ DPCD_ADDRESS_AUDIO_DECODE_LATENCY2 = 0x0025, ++ DPCD_ADDRESS_AUDIO_POSTPROCESSING_LATENCY1 = 0x0026, ++ DPCD_ADDRESS_AUDIO_POSTPROCESSING_LATENCY2 = 0x0027, ++ DPCD_ADDRESS_VIDEO_INTERLACED_LATENCY = 0x0028, ++ DPCD_ADDRESS_VIDEO_PROGRESSIVE_LATENCY = 0x0029, ++ DPCD_ADDRESS_AUDIO_DELAY_INSERT1 = 0x0002B, ++ DPCD_ADDRESS_AUDIO_DELAY_INSERT2 = 0x0002C, ++ DPCD_ADDRESS_AUDIO_DELAY_INSERT3 = 0x0002D, ++ ++ /* Audio capability */ ++ DPCD_ADDRESS_NUM_OF_AUDIO_ENDPOINTS = 0x00022, ++ ++ DPCD_ADDRESS_GUID_START = 0x00030,/*1.2*/ ++ DPCD_ADDRESS_GUID_END = 0x0003f,/*1.2*/ ++ ++ DPCD_ADDRESS_PSR_SUPPORT_VER = 0x00070, ++ DPCD_ADDRESS_PSR_CAPABILITY = 0x00071, ++ ++ DPCD_ADDRESS_DWN_STRM_PORT0_CAPS = 0x00080,/*1.2a*/ ++ ++ /* Link Configuration Field */ ++ DPCD_ADDRESS_LINK_BW_SET = 0x00100, ++ DPCD_ADDRESS_LANE_COUNT_SET = 0x00101, ++ DPCD_ADDRESS_TRAINING_PATTERN_SET = 0x00102, ++ DPCD_ADDRESS_LANE0_SET = 0x00103, ++ DPCD_ADDRESS_LANE1_SET = 0x00104, ++ DPCD_ADDRESS_LANE2_SET = 0x00105, ++ DPCD_ADDRESS_LANE3_SET = 0x00106, ++ DPCD_ADDRESS_DOWNSPREAD_CNTL = 0x00107, ++ DPCD_ADDRESS_I2C_SPEED_CNTL = 0x00109,/*1.2*/ ++ ++ DPCD_ADDRESS_EDP_CONFIG_SET = 0x0010A, ++ DPCD_ADDRESS_LINK_QUAL_LANE0_SET = 0x0010B, ++ DPCD_ADDRESS_LINK_QUAL_LANE1_SET = 0x0010C, ++ DPCD_ADDRESS_LINK_QUAL_LANE2_SET = 0x0010D, ++ DPCD_ADDRESS_LINK_QUAL_LANE3_SET = 0x0010E, ++ ++ DPCD_ADDRESS_LANE0_SET2 = 0x0010F,/*1.2*/ ++ DPCD_ADDRESS_LANE2_SET2 = 0x00110,/*1.2*/ ++ ++ DPCD_ADDRESS_MSTM_CNTL = 0x00111,/*1.2*/ ++ ++ DPCD_ADDRESS_PSR_ENABLE_CFG = 0x0170, ++ ++ /* Payload Table Configuration Field 1.2 */ ++ DPCD_ADDRESS_PAYLOAD_ALLOCATE_SET = 0x001C0, ++ DPCD_ADDRESS_PAYLOAD_ALLOCATE_START_TIMESLOT = 0x001C1, ++ DPCD_ADDRESS_PAYLOAD_ALLOCATE_TIMESLOT_COUNT = 0x001C2, ++ ++ DPCD_ADDRESS_SINK_COUNT = 0x0200, ++ DPCD_ADDRESS_DEVICE_SERVICE_IRQ_VECTOR = 0x0201, ++ ++ /* Link / Sink Status Field */ ++ DPCD_ADDRESS_LANE_01_STATUS = 0x00202, ++ DPCD_ADDRESS_LANE_23_STATUS = 0x00203, ++ DPCD_ADDRESS_LANE_ALIGN_STATUS_UPDATED = 0x0204, ++ DPCD_ADDRESS_SINK_STATUS = 0x0205, ++ ++ /* Adjust Request Field */ ++ DPCD_ADDRESS_ADJUST_REQUEST_LANE0_1 = 0x0206, ++ DPCD_ADDRESS_ADJUST_REQUEST_LANE2_3 = 0x0207, ++ DPCD_ADDRESS_ADJUST_REQUEST_POST_CURSOR2 = 0x020C, ++ ++ /* Test Request Field */ ++ DPCD_ADDRESS_TEST_REQUEST = 0x0218, ++ DPCD_ADDRESS_TEST_LINK_RATE = 0x0219, ++ DPCD_ADDRESS_TEST_LANE_COUNT = 0x0220, ++ DPCD_ADDRESS_TEST_PATTERN = 0x0221, ++ DPCD_ADDRESS_TEST_MISC1 = 0x0232, ++ ++ /* Phy Test Pattern Field */ ++ DPCD_ADDRESS_TEST_PHY_PATTERN = 0x0248, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_7_0 = 0x0250, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_15_8 = 0x0251, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_23_16 = 0x0252, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_31_24 = 0x0253, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_39_32 = 0x0254, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_47_40 = 0x0255, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_55_48 = 0x0256, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_63_56 = 0x0257, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_71_64 = 0x0258, ++ DPCD_ADDRESS_TEST_80BIT_CUSTOM_PATTERN_79_72 = 0x0259, ++ ++ /* Test Response Field*/ ++ DPCD_ADDRESS_TEST_RESPONSE = 0x0260, ++ ++ /* Audio Test Pattern Field 1.2*/ ++ DPCD_ADDRESS_TEST_AUDIO_MODE = 0x0271, ++ DPCD_ADDRESS_TEST_AUDIO_PATTERN_TYPE = 0x0272, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_1 = 0x0273, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_2 = 0x0274, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_3 = 0x0275, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_4 = 0x0276, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_5 = 0x0277, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_6 = 0x0278, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_7 = 0x0279, ++ DPCD_ADDRESS_TEST_AUDIO_PERIOD_CH_8 = 0x027A, ++ ++ /* Payload Table Status Field */ ++ DPCD_ADDRESS_PAYLOAD_TABLE_UPDATE_STATUS = 0x002C0,/*1.2*/ ++ DPCD_ADDRESS_VC_PAYLOAD_ID_SLOT1 = 0x002C1,/*1.2*/ ++ DPCD_ADDRESS_VC_PAYLOAD_ID_SLOT63 = 0x002FF,/*1.2*/ ++ ++ /* Source Device Specific Field */ ++ DPCD_ADDRESS_SOURCE_DEVICE_ID_START = 0x0300, ++ DPCD_ADDRESS_SOURCE_DEVICE_ID_END = 0x0301, ++ DPCD_ADDRESS_SOURCE_RESERVED_START = 0x030C, ++ DPCD_ADDRESS_SOURCE_RESERVED_END = 0x03FF, ++ ++ /* Sink Device Specific Field */ ++ DPCD_ADDRESS_SINK_DEVICE_ID_START = 0x0400, ++ DPCD_ADDRESS_SINK_DEVICE_ID_END = 0x0402, ++ DPCD_ADDRESS_SINK_DEVICE_STR_START = 0x0403, ++ DPCD_ADDRESS_SINK_DEVICE_STR_END = 0x0408, ++ DPCD_ADDRESS_SINK_REVISION_START = 0x409, ++ DPCD_ADDRESS_SINK_REVISION_END = 0x40B, ++ ++ /* Branch Device Specific Field */ ++ DPCD_ADDRESS_BRANCH_DEVICE_ID_START = 0x0500, ++ DPCD_ADDRESS_BRANCH_DEVICE_ID_END = 0x0502, ++ DPCD_ADDRESS_BRANCH_DEVICE_STR_START = 0x0503, ++ DPCD_ADDRESS_BRANCH_DEVICE_STR_END = 0x0508, ++ ++ DPCD_ADDRESS_POWER_STATE = 0x0600, ++ ++ /* EDP related */ ++ DPCD_ADDRESS_EDP_REV = 0x0700, ++ DPCD_ADDRESS_EDP_CAPABILITY = 0x0701, ++ DPCD_ADDRESS_EDP_BACKLIGHT_ADJUST_CAP = 0x0702, ++ DPCD_ADDRESS_EDP_GENERAL_CAP2 = 0x0703, ++ ++ DPCD_ADDRESS_EDP_DISPLAY_CONTROL = 0x0720, ++ DPCD_ADDRESS_EDP_BACKLIGHT_SET = 0x0721, ++ DPCD_ADDRESS_EDP_BACKLIGHT_BRIGHTNESS_MSB = 0x0722, ++ DPCD_ADDRESS_EDP_BACKLIGHT_BRIGHTNESS_LSB = 0x0723, ++ DPCD_ADDRESS_EDP_PWMGEN_BIT_COUNT = 0x0724, ++ DPCD_ADDRESS_EDP_PWMGEN_BIT_COUNT_CAP_MIN = 0x0725, ++ DPCD_ADDRESS_EDP_PWMGEN_BIT_COUNT_CAP_MAX = 0x0726, ++ DPCD_ADDRESS_EDP_BACKLIGHT_CONTROL_STATUS = 0x0727, ++ DPCD_ADDRESS_EDP_BACKLIGHT_FREQ_SET = 0x0728, ++ DPCD_ADDRESS_EDP_REVERVED = 0x0729, ++ DPCD_ADDRESS_EDP_BACKLIGNT_FREQ_CAP_MIN_MSB = 0x072A, ++ DPCD_ADDRESS_EDP_BACKLIGNT_FREQ_CAP_MIN_MID = 0x072B, ++ DPCD_ADDRESS_EDP_BACKLIGNT_FREQ_CAP_MIN_LSB = 0x072C, ++ DPCD_ADDRESS_EDP_BACKLIGNT_FREQ_CAP_MAX_MSB = 0x072D, ++ DPCD_ADDRESS_EDP_BACKLIGNT_FREQ_CAP_MAX_MID = 0x072E, ++ DPCD_ADDRESS_EDP_BACKLIGNT_FREQ_CAP_MAX_LSB = 0x072F, ++ ++ DPCD_ADDRESS_EDP_DBC_MINIMUM_BRIGHTNESS_SET = 0x0732, ++ DPCD_ADDRESS_EDP_DBC_MAXIMUM_BRIGHTNESS_SET = 0x0733, ++ ++ /* Sideband MSG Buffers 1.2 */ ++ DPCD_ADDRESS_DOWN_REQ_START = 0x01000, ++ DPCD_ADDRESS_DOWN_REQ_END = 0x011ff, ++ ++ DPCD_ADDRESS_UP_REP_START = 0x01200, ++ DPCD_ADDRESS_UP_REP_END = 0x013ff, ++ ++ DPCD_ADDRESS_DOWN_REP_START = 0x01400, ++ DPCD_ADDRESS_DOWN_REP_END = 0x015ff, ++ ++ DPCD_ADDRESS_UP_REQ_START = 0x01600, ++ DPCD_ADDRESS_UP_REQ_END = 0x017ff, ++ ++ /* ESI (Event Status Indicator) Field 1.2 */ ++ DPCD_ADDRESS_SINK_COUNT_ESI = 0x02002, ++ DPCD_ADDRESS_DEVICE_IRQ_ESI0 = 0x02003, ++ DPCD_ADDRESS_DEVICE_IRQ_ESI1 = 0x02004, ++ /*@todo move dpcd_address_Lane01Status back here*/ ++ ++ DPCD_ADDRESS_PSR_ERROR_STATUS = 0x2006, ++ DPCD_ADDRESS_PSR_EVENT_STATUS = 0x2007, ++ DPCD_ADDRESS_PSR_SINK_STATUS = 0x2008, ++ DPCD_ADDRESS_PSR_DBG_REGISTER0 = 0x2009, ++ DPCD_ADDRESS_PSR_DBG_REGISTER1 = 0x200A, ++ ++ /* Travis specific addresses */ ++ DPCD_ADDRESS_TRAVIS_SINK_DEV_SEL = 0x5f0, ++ DPCD_ADDRESS_TRAVIS_SINK_ACCESS_OFFSET = 0x5f1, ++ DPCD_ADDRESS_TRAVIS_SINK_ACCESS_REG = 0x5f2, ++}; ++ ++enum dpcd_revision { ++ DPCD_REV_10 = 0x10, ++ DPCD_REV_11 = 0x11, ++ DPCD_REV_12 = 0x12 ++}; ++ ++enum dp_pwr_state { ++ DP_PWR_STATE_D0 = 1,/* direct HW translation! */ ++ DP_PWR_STATE_D3 ++}; ++ ++/* these are the types stored at DOWNSTREAMPORT_PRESENT */ ++enum dpcd_downstream_port_type { ++ DOWNSTREAM_DP = 0, ++ DOWNSTREAM_VGA, ++ DOWNSTREAM_DVI_HDMI, ++ DOWNSTREAM_NONDDC /* has no EDID (TV,CV) */ ++}; ++ ++enum dpcd_link_test_patterns { ++ LINK_TEST_PATTERN_NONE = 0, ++ LINK_TEST_PATTERN_COLOR_RAMP, ++ LINK_TEST_PATTERN_VERTICAL_BARS, ++ LINK_TEST_PATTERN_COLOR_SQUARES ++}; ++ ++enum dpcd_test_color_format { ++ TEST_COLOR_FORMAT_RGB = 0, ++ TEST_COLOR_FORMAT_YCBCR422, ++ TEST_COLOR_FORMAT_YCBCR444 ++}; ++ ++enum dpcd_test_bit_depth { ++ TEST_BIT_DEPTH_6 = 0, ++ TEST_BIT_DEPTH_8, ++ TEST_BIT_DEPTH_10, ++ TEST_BIT_DEPTH_12, ++ TEST_BIT_DEPTH_16 ++}; ++ ++/* PHY (encoder) test patterns ++The order of test patterns follows DPCD register PHY_TEST_PATTERN (0x248) */ ++enum dpcd_phy_test_patterns { ++ PHY_TEST_PATTERN_NONE = 0, ++ PHY_TEST_PATTERN_D10_2, ++ PHY_TEST_PATTERN_SYMBOL_ERROR, ++ PHY_TEST_PATTERN_PRBS7, ++ PHY_TEST_PATTERN_80BIT_CUSTOM,/* For DP1.2 only */ ++ PHY_TEST_PATTERN_HBR2_COMPLIANCE_EYE/* For DP1.2 only */ ++}; ++ ++enum dpcd_test_dyn_range { ++ TEST_DYN_RANGE_VESA = 0, ++ TEST_DYN_RANGE_CEA ++}; ++ ++enum dpcd_audio_test_pattern { ++ AUDIO_TEST_PATTERN_OPERATOR_DEFINED = 0,/* direct HW translation */ ++ AUDIO_TEST_PATTERN_SAWTOOTH ++}; ++ ++enum dpcd_audio_sampling_rate { ++ AUDIO_SAMPLING_RATE_32KHZ = 0,/* direct HW translation */ ++ AUDIO_SAMPLING_RATE_44_1KHZ, ++ AUDIO_SAMPLING_RATE_48KHZ, ++ AUDIO_SAMPLING_RATE_88_2KHZ, ++ AUDIO_SAMPLING_RATE_96KHZ, ++ AUDIO_SAMPLING_RATE_176_4KHZ, ++ AUDIO_SAMPLING_RATE_192KHZ ++}; ++ ++enum dpcd_audio_channels { ++ AUDIO_CHANNELS_1 = 0,/* direct HW translation */ ++ AUDIO_CHANNELS_2, ++ AUDIO_CHANNELS_3, ++ AUDIO_CHANNELS_4, ++ AUDIO_CHANNELS_5, ++ AUDIO_CHANNELS_6, ++ AUDIO_CHANNELS_7, ++ AUDIO_CHANNELS_8, ++ ++ AUDIO_CHANNELS_COUNT ++}; ++ ++enum dpcd_audio_test_pattern_periods { ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_NOTUSED = 0,/* direct HW translation */ ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_3, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_6, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_12, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_24, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_48, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_96, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_192, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_384, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_768, ++ DPCD_AUDIO_TEST_PATTERN_PERIOD_1536 ++}; ++ ++/* This enum is for programming DPCD TRAINING_PATTERN_SET */ ++enum dpcd_training_patterns { ++ DPCD_TRAINING_PATTERN_VIDEOIDLE = 0,/* direct HW translation! */ ++ DPCD_TRAINING_PATTERN_1, ++ DPCD_TRAINING_PATTERN_2, ++ DPCD_TRAINING_PATTERN_3 ++}; ++ ++/* This enum is for use with PsrSinkPsrStatus.bits.sinkSelfRefreshStatus ++It defines the possible PSR states. */ ++enum dpcd_psr_sink_states { ++ PSR_SINK_STATE_INACTIVE = 0, ++ PSR_SINK_STATE_ACTIVE_CAPTURE_DISPLAY_ON_SOURCE_TIMING = 1, ++ PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB = 2, ++ PSR_SINK_STATE_ACTIVE_CAPTURE_DISPLAY_ON_SINK_TIMING = 3, ++ PSR_SINK_STATE_ACTIVE_CAPTURE_TIMING_RESYNC = 4, ++ PSR_SINK_STATE_SINK_INTERNAL_ERROR = 7, ++}; ++ ++/* This enum defines the Panel's eDP revision at DPCD 700h ++ * 00h = eDP v1.1 or lower ++ * 01h = eDP v1.2 ++ * 02h = eDP v1.3 (PSR support starts here) ++ * 03h = eDP v1.4 ++ * If unknown revision, treat as eDP v1.1, meaning least functionality set. ++ * This enum has values matched to eDP spec, thus values should not change. ++ */ ++enum dpcd_edp_revision { ++ DPCD_EDP_REVISION_EDP_V1_1 = 0, ++ DPCD_EDP_REVISION_EDP_V1_2 = 1, ++ DPCD_EDP_REVISION_EDP_V1_3 = 2, ++ DPCD_EDP_REVISION_EDP_V1_4 = 3, ++ DPCD_EDP_REVISION_EDP_UNKNOWN = DPCD_EDP_REVISION_EDP_V1_1, ++}; ++ ++union dpcd_rev { ++ struct { ++ uint8_t MINOR:4; ++ uint8_t MAJOR:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++union max_lane_count { ++ struct { ++ uint8_t MAX_LANE_COUNT:5; ++ uint8_t POST_LT_ADJ_REQ_SUPPORTED:1; ++ uint8_t TPS3_SUPPORTED:1; ++ uint8_t ENHANCED_FRAME_CAP:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union max_down_spread { ++ struct { ++ uint8_t MAX_DOWN_SPREAD:1; ++ uint8_t RESERVED:5; ++ uint8_t NO_AUX_HANDSHAKE_LINK_TRAINING:1; ++ uint8_t RESERVED1:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union mstm_cap { ++ struct { ++ uint8_t MST_CAP:1; ++ uint8_t RESERVED:7; ++ } bits; ++ uint8_t raw; ++}; ++ ++union mstm_cntl { ++ struct { ++ uint8_t MST_EN:1; ++ uint8_t UP_REQ_EN:1; ++ uint8_t UPSTREAM_IS_SRC:1; ++ uint8_t RESERVED:5; ++ } bits; ++ uint8_t raw; ++}; ++ ++union lane_count_set { ++ struct { ++ uint8_t LANE_COUNT_SET:5; ++ uint8_t POST_LT_ADJ_REQ_GRANTED:1; ++ uint8_t RESERVED:1; ++ uint8_t ENHANCED_FRAMING:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++/* for DPCD_ADDRESS_I2C_SPEED_CNTL_CAP ++ * and DPCD_ADDRESS_I2C_SPEED_CNTL ++ */ ++union i2c_speed { ++ struct { ++ uint8_t _1KBPS:1; ++ uint8_t _5KBPS:1; ++ uint8_t _10KBPS:1; ++ uint8_t _100KBPS:1; ++ uint8_t _400KBPS:1; ++ uint8_t _1MBPS:1; ++ uint8_t reserved:2; ++ } bits; ++ uint8_t raw; ++}; ++ ++union payload_table_update_status { ++ struct { ++ uint8_t VC_PAYLOAD_TABLE_UPDATED:1; ++ uint8_t ACT_HANDLED:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union device_irq_esi_0 { ++ struct { ++ uint8_t REMOTE_CONTROL_CMD_PENDING:1; ++ uint8_t AUTOMATED_TEST_REQUEST:1; ++ uint8_t CP_IRQ:1; ++ uint8_t MCCS_IRQ:1; ++ uint8_t DOWN_REP_MSG_RDY:1; ++ uint8_t UP_REQ_MSG_RDY:1; ++ uint8_t SINK_SPECIFIC_IRQ:1; ++ uint8_t RESERVED:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union lane_status { ++ struct { ++ uint8_t CR_DONE_0:1; ++ uint8_t CHANNEL_EQ_DONE_0:1; ++ uint8_t SYMBOL_LOCKED_0:1; ++ uint8_t RESERVED0:1; ++ uint8_t CR_DONE_1:1; ++ uint8_t CHANNEL_EQ_DONE_1:1; ++ uint8_t SYMBOL_LOCKED_1:1; ++ uint8_t RESERVED_1:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union device_service_irq { ++ struct { ++ uint8_t REMOTE_CONTROL_CMD_PENDING:1; ++ uint8_t AUTOMATED_TEST:1; ++ uint8_t CP_IRQ:1; ++ uint8_t MCCS_IRQ:1; ++ uint8_t DOWN_REP_MSG_RDY:1; ++ uint8_t UP_REQ_MSG_RDY:1; ++ uint8_t SINK_SPECIFIC:1; ++ uint8_t reserved:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union downstream_port { ++ struct { ++ uint8_t PRESENT:1; ++ uint8_t TYPE:2; ++ uint8_t FORMAT_CONV:1; ++ uint8_t DETAILED_CAPS:1; ++ uint8_t RESERVED:3; ++ } bits; ++ uint8_t raw; ++}; ++ ++union sink_count { ++ struct { ++ uint8_t SINK_COUNT:6; ++ uint8_t CPREADY:1; ++ uint8_t RESERVED:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union lane_align_status_updated { ++ struct { ++ uint8_t INTERLANE_ALIGN_DONE:1; ++ uint8_t POST_LT_ADJ_REQ_IN_PROGRESS:1; ++ uint8_t RESERVED:4; ++ uint8_t DOWNSTREAM_PORT_STATUS_CHANGED:1; ++ uint8_t LINK_STATUS_UPDATED:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union lane_adjust { ++ struct { ++ uint8_t VOLTAGE_SWING_LANE:2; ++ uint8_t PRE_EMPHASIS_LANE:2; ++ uint8_t RESERVED:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++/* Automated test structures */ ++union test_request { ++ struct { ++ uint8_t LINK_TRAINING:1; ++ uint8_t LINK_TEST_PATTERN:1; ++ uint8_t EDID_READ:1; ++ uint8_t PHY_TEST_PATTERN:1; ++ uint8_t AUDIO_TEST_PATTERN:1; ++ uint8_t AUDIO_TEST_NO_VIDEO:1; ++ uint8_t RESERVED:1; ++ uint8_t TEST_STEREO_3D:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union test_response { ++ struct { ++ uint8_t ACK:1; ++ uint8_t NO_ACK:1; ++ uint8_t RESERVED:6; ++ } bits; ++ uint8_t raw; ++}; ++ ++union link_test_pattern { ++ struct { ++ uint8_t PATTERN:2;/*DpcdLinkTestPatterns*/ ++ uint8_t RESERVED:6; ++ } bits; ++ uint8_t raw; ++}; ++ ++union test_misc { ++ struct dpcd_test_misc_bits { ++ uint8_t SYNC_CLOCK:1; ++ uint8_t CLR_FORMAT:2;/*DpcdTestColorFormat*/ ++ uint8_t DYN_RANGE:1;/*DpcdTestDynRange*/ ++ uint8_t YCBCR:1;/*DpcdTestYCbCrStandard*/ ++ uint8_t BPC:3;/*DpcdTestBitDepth*/ ++ } bits; ++ uint8_t raw; ++}; ++ ++union phy_test_pattern { ++ struct { ++ /* This field is 2 bits for DP1.1 and 3 bits for DP1.2.*/ ++ uint8_t PATTERN:3; ++ uint8_t RESERVED:5;/* BY spec, bit7:2 is 0 for DP1.1.*/ ++ } bits; ++ uint8_t raw; ++}; ++ ++union audio_test_mode { ++ struct { ++ uint8_t SAMPLING_RATE:4; ++ uint8_t CHANNEL_COUNT:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++union audio_tes_tpattern_period { ++ struct { ++ uint8_t PATTERN_PERIOD:4; ++ uint8_t RESERVED:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++struct audio_test_pattern_type { ++ uint8_t value; ++}; ++ ++union dpcd_training_pattern { ++ struct { ++ uint8_t TRAINING_PATTERN_SET:2; ++ uint8_t LINK_QUAL_PATTERN_SET:2; ++ uint8_t RECOVERED_CLOCK_OUT_EN:1; ++ uint8_t SCRAMBLING_DISABLE:1; ++ uint8_t RESERVED:2; ++ } bits; ++ uint8_t raw; ++}; ++ ++/* Training Lane is used to configure downstream DP device's voltage swing ++and pre-emphasis levels*/ ++/* The DPCD addresses are from 0x103 to 0x106*/ ++union dpcd_training_lane { ++ struct { ++ uint8_t VOLTAGE_SWING_SET:2; ++ uint8_t MAX_SWING_REACHED:1; ++ uint8_t PRE_EMPHASIS_SET:2; ++ uint8_t MAX_PRE_EMPHASIS_REACHED:1; ++ uint8_t RESERVED:2; ++ } bits; ++ uint8_t raw; ++}; ++ ++/*Training Lane Set 2 is used to configure downstream DP device's ++post cursor 2 level of Training Pattern 2 or 3*/ ++/* The DPCD addresses are 0x10F (TRAINING_LANE0_1_SET2) ++and 0x110 (TRAINING_LANE2_3_SET2)*/ ++union dpcd_training_lane_set2 { ++ struct { ++ uint8_t POST_CURSOR2_SET:2; ++ uint8_t MAX_POST_CURSOR2_REACHED:1; ++ uint8_t RESERVED:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union dpcd_psr_configuration { ++ struct { ++ uint8_t ENABLE:1; ++ uint8_t TRANSMITTER_ACTIVE_IN_PSR:1; ++ uint8_t CRC_VERIFICATION:1; ++ uint8_t FRAME_CAPTURE_INDICATION:1; ++ uint8_t LINE_CAPTURE_INDICATION:1; ++ uint8_t IRQ_HPD_WITH_CRC_ERROR:1; ++ uint8_t RESERVED:2; ++ } bits; ++ uint8_t raw; ++}; ++ ++union psr_error_status { ++ struct { ++ uint8_t LINK_CRC_ERROR:1; ++ uint8_t RFB_STORAGE_ERROR:1; ++ uint8_t RESERVED:6; ++ } bits; ++ uint8_t raw; ++}; ++ ++union psr_event_status_ind { ++ struct { ++ uint8_t SINK_PSR_CAP_CHANGE:1; ++ uint8_t RESERVED:7; ++ } bits; ++ uint8_t raw; ++}; ++ ++union psr_sink_psr_status { ++ struct { ++ uint8_t SINK_SELF_REFRESH_STATUS:3; ++ uint8_t RESERVED:5; ++ } bits; ++ uint8_t raw; ++}; ++ ++/* EDP related 0x701 */ ++union edp_generial_cap1 { ++ struct { ++ uint8_t TCON_BACKLIGHT_ADJUSTMENT_CAPABLE:1; ++ uint8_t BACKLIGHT_PIN_ENABLE_CAPABLE:1; ++ uint8_t BACKLIGHT_AUX_ENABLE_CAPABLE:1; ++ uint8_t PANEL_SELFTEST_PIN_ENABLE_CAPABLE:1; ++ uint8_t BACKLIGHT_SELFTEST_AUX_ENABLE_CAPABLE:1; ++ uint8_t FRC_ENABLE_CAPABLE:1; ++ uint8_t COLOR_ENGINE_CAPABLE:1; ++ /*bit 7, pane can be controlled by 0x600*/ ++ uint8_t SET_POWER_CAPABLE:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++/* TMDS-converter related */ ++union dwnstream_port_caps_byte0 { ++ struct { ++ uint8_t DWN_STRM_PORTX_TYPE:3; ++ uint8_t DWN_STRM_PORTX_HPD:1; ++ uint8_t RESERVERD:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++/* these are the detailed types stored at DWN_STRM_PORTX_CAP (00080h)*/ ++enum dpcd_downstream_port_detailed_type { ++ DOWN_STREAM_DETAILED_DP = 0, ++ DOWN_STREAM_DETAILED_VGA, ++ DOWN_STREAM_DETAILED_DVI, ++ DOWN_STREAM_DETAILED_HDMI, ++ DOWN_STREAM_DETAILED_NONDDC,/* has no EDID (TV,CV)*/ ++ DOWN_STREAM_DETAILED_DP_PLUS_PLUS ++}; ++ ++union dwnstream_port_caps_byte2 { ++ struct { ++ uint8_t MAX_BITS_PER_COLOR_COMPONENT:2; ++ uint8_t RESERVED:6; ++ } bits; ++ uint8_t raw; ++}; ++ ++union dp_downstream_port_present { ++ uint8_t byte; ++ struct { ++ uint8_t PORT_PRESENT:1; ++ uint8_t PORT_TYPE:2; ++ uint8_t FMT_CONVERSION:1; ++ uint8_t DETAILED_CAPS:1; ++ uint8_t RESERVED:3; ++ } fields; ++}; ++ ++ ++union dwnstream_port_caps_byte3_dvi { ++ struct { ++ uint8_t RESERVED1:1; ++ uint8_t DUAL_LINK:1; ++ uint8_t HIGH_COLOR_DEPTH:1; ++ uint8_t RESERVED2:5; ++ } bits; ++ uint8_t raw; ++}; ++ ++union dwnstream_port_caps_byte3_hdmi { ++ struct { ++ uint8_t FRAME_SEQ_TO_FRAME_PACK:1; ++ uint8_t RESERVED:7; ++ } bits; ++ uint8_t raw; ++}; ++ ++/*4-byte structure for detailed capabilities of a down-stream port ++(DP-to-TMDS converter).*/ ++union dwnstream_portx_caps { ++ struct { ++ union dwnstream_port_caps_byte0 byte0; ++ uint8_t max_tmds_clk;/* byte1 */ ++ union dwnstream_port_caps_byte2 byte2; ++ ++ union { ++ union dwnstream_port_caps_byte3_dvi byte_dvi; ++ union dwnstream_port_caps_byte3_hdmi byte_hdmi; ++ } byte3; ++ } bytes; ++ uint8_t raw[4]; ++}; ++ ++union sink_status { ++ struct { ++ uint8_t RX_PORT0_STATUS:1; ++ uint8_t RX_PORT1_STATUS:1; ++ uint8_t RESERVED:6; ++ } bits; ++ uint8_t raw; ++}; ++ ++/*6-byte structure corresponding to 6 registers (200h-205h) ++read during handling of HPD-IRQ*/ ++union hpd_irq_data { ++ struct { ++ union sink_count sink_cnt;/* 200h */ ++ union device_service_irq device_service_irq;/* 201h */ ++ union lane_status lane01_status;/* 202h */ ++ union lane_status lane23_status;/* 203h */ ++ union lane_align_status_updated lane_status_updated;/* 204h */ ++ union sink_status sink_status; ++ } bytes; ++ uint8_t raw[6]; ++}; ++ ++union down_stream_port_count { ++ struct { ++ uint8_t DOWN_STR_PORT_COUNT:4; ++ uint8_t RESERVED:2; /*Bits 5:4 = RESERVED. Read all 0s.*/ ++ /*Bit 6 = MSA_TIMING_PAR_IGNORED ++ 0 = Sink device requires the MSA timing parameters ++ 1 = Sink device is capable of rendering incoming video ++ stream without MSA timing parameters*/ ++ uint8_t IGNORE_MSA_TIMING_PARAM:1; ++ /*Bit 7 = OUI Support ++ 0 = OUI not supported ++ 1 = OUI supported ++ (OUI and Device Identification mandatory for DP 1.2)*/ ++ uint8_t OUI_SUPPORT:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union down_spread_ctrl { ++ struct { ++ uint8_t RESERVED1:4;/* Bit 3:0 = RESERVED. Read all 0s*/ ++ /* Bits 4 = SPREAD_AMP. Spreading amplitude ++ 0 = Main link signal is not downspread ++ 1 = Main link signal is downspread <= 0.5% ++ with frequency in the range of 30kHz ~ 33kHz*/ ++ uint8_t SPREAD_AMP:1; ++ uint8_t RESERVED2:2;/*Bit 6:5 = RESERVED. Read all 0s*/ ++ /*Bit 7 = MSA_TIMING_PAR_IGNORE_EN ++ 0 = Source device will send valid data for the MSA Timing Params ++ 1 = Source device may send invalid data for these MSA Timing Params*/ ++ uint8_t IGNORE_MSA_TIMING_PARAM:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++union dpcd_edp_config { ++ struct { ++ uint8_t PANEL_MODE_EDP:1; ++ uint8_t FRAMING_CHANGE_ENABLE:1; ++ uint8_t RESERVED:5; ++ uint8_t PANEL_SELF_TEST_ENABLE:1; ++ } bits; ++ uint8_t raw; ++}; ++ ++struct dp_device_vendor_id { ++ uint8_t ieee_oui[3];/*24-bit IEEE OUI*/ ++ uint8_t ieee_device_id[6];/*usually 6-byte ASCII name*/ ++}; ++ ++struct dp_sink_hw_fw_revision { ++ uint8_t ieee_hw_rev; ++ uint8_t ieee_fw_rev[2]; ++}; ++ ++/*DPCD register of DP receiver capability field bits-*/ ++union edp_configuration_cap { ++ struct { ++ uint8_t ALT_SCRAMBLER_RESET:1; ++ uint8_t FRAMING_CHANGE:1; ++ uint8_t RESERVED:1; ++ uint8_t DPCD_DISPLAY_CONTROL_CAPABLE:1; ++ uint8_t RESERVED2:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++union psr_capabilities { ++ struct { ++ uint8_t EXIT_LT_NOT_REQ:1; ++ uint8_t RFB_SETUP_TIME:3; ++ uint8_t RESERVED:4; ++ } bits; ++ uint8_t raw; ++}; ++ ++#endif /* __DAL_DPCD_DEFS_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/dvo_interface.h b/drivers/gpu/drm/amd/dal/include/dvo_interface.h +new file mode 100644 +index 0000000..58d2c6f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/dvo_interface.h +@@ -0,0 +1,48 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_DVO_INTERFACE_H__ ++#define __DAL_DVO_INTERFACE_H__ ++ ++#include "gpio_types.h" ++ ++struct dvo; ++ ++enum gpio_result dal_dvo_open( ++ struct dvo *dvo, ++ enum gpio_mode mode); ++ ++enum gpio_result dal_dvo_get_value( ++ const struct dvo *dvo, ++ uint32_t *value); ++ ++enum gpio_result dal_dvo_set_value( ++ const struct dvo *dvo, ++ uint32_t value); ++ ++void dal_dvo_close( ++ struct dvo *dvo); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/encoder_interface.h b/drivers/gpu/drm/amd/dal/include/encoder_interface.h +new file mode 100644 +index 0000000..5fbf816 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/encoder_interface.h +@@ -0,0 +1,278 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of enc software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and enc permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ENCODER_INTERFACE_H__ ++#define __DAL_ENCODER_INTERFACE_H__ ++ ++#include "encoder_types.h" ++#include "adapter_service_interface.h" ++#include "fixed31_32.h" ++ ++enum encoder_result { ++ ENCODER_RESULT_OK, ++ ENCODER_RESULT_ERROR, ++ ENCODER_RESULT_NOBANDWIDTH, ++ ENCODER_RESULT_SINKCONNECTIVITYCHANGED, ++}; ++ ++struct encoder_init_data { ++ struct adapter_service *adapter_service; ++ enum channel_id channel; ++ struct graphics_object_id connector; ++ enum hpd_source_id hpd_source; ++ /* TODO: in DAL2, here was pointer to EventManagerInterface */ ++ struct graphics_object_id encoder; ++ struct dc_context *ctx; ++}; ++ ++/* forward declaration */ ++struct encoder; ++ ++struct encoder *dal_encoder_create( ++ const struct encoder_init_data *init_data); ++ ++/* access graphics object base */ ++const struct graphics_object_id dal_encoder_get_graphics_object_id( ++ const struct encoder *enc); ++ ++/* ++ * Signal types support ++ */ ++uint32_t dal_encoder_enumerate_input_signals( ++ const struct encoder *enc); ++uint32_t dal_encoder_enumerate_output_signals( ++ const struct encoder *enc); ++bool dal_encoder_is_input_signal_supported( ++ const struct encoder *enc, ++ enum signal_type signal); ++bool dal_encoder_is_output_signal_supported( ++ const struct encoder *enc, ++ enum signal_type signal); ++void dal_encoder_set_input_signals( ++ struct encoder *enc, ++ uint32_t signals); ++void dal_encoder_set_output_signals( ++ struct encoder *enc, ++ uint32_t signals); ++ ++/* ++ * Programming interface ++ */ ++/* perform power-up sequence (boot up, resume, recovery) */ ++enum encoder_result dal_encoder_power_up( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* perform power-down (shut down, stand-by */ ++enum encoder_result dal_encoder_power_down( ++ struct encoder *enc, ++ const struct encoder_output *output); ++/* setup encoder block (DIG, DVO, DAC), does not enables encoder */ ++enum encoder_result dal_encoder_setup( ++ struct encoder *enc, ++ const struct encoder_output *output); ++/* activate transmitter, ++ * do preparation before enables the actual stream output */ ++enum encoder_result dal_encoder_pre_enable_output( ++ struct encoder *enc, ++ const struct encoder_pre_enable_output_param *param); ++/* activate transmitter, enables actual stream output */ ++enum encoder_result dal_encoder_enable_output( ++ struct encoder *enc, ++ const struct encoder_output *output); ++/* deactivate transmitter, disables stream output */ ++enum encoder_result dal_encoder_disable_output( ++ struct encoder *enc, ++ const struct encoder_output *output); ++/* output blank data, ++ *prevents output of the actual surface data on active transmitter */ ++enum encoder_result dal_encoder_blank( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* stop sending blank data, ++ * output the actual surface data on active transmitter */ ++enum encoder_result dal_encoder_unblank( ++ struct encoder *enc, ++ const struct encoder_unblank_param *param); ++/* setup stereo signal from given controller */ ++enum encoder_result dal_encoder_setup_stereo( ++ struct encoder *enc, ++ const struct encoder_3d_setup *setup); ++/* enable HSync/VSync output from given controller */ ++enum encoder_result dal_encoder_enable_sync_output( ++ struct encoder *enc, ++ enum sync_source src); ++/* disable HSync/VSync output */ ++enum encoder_result dal_encoder_disable_sync_output( ++ struct encoder *enc); ++/* action of encoder before DDC transaction */ ++enum encoder_result dal_encoder_pre_ddc( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* action of encoder after DDC transaction */ ++enum encoder_result dal_encoder_post_ddc( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* CRT DDC EDID polling interrupt interface */ ++enum encoder_result dal_encoder_update_implementation( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* set test pattern signal */ ++enum encoder_result dal_encoder_set_dp_phy_pattern( ++ struct encoder *enc, ++ const struct encoder_set_dp_phy_pattern_param *param); ++ ++void dal_encoder_release_hw(struct encoder *enc); ++/* ++ * Information interface ++ */ ++/* check whether sink is present based on SENSE detection, ++ * analog encoders will return true */ ++bool dal_encoder_is_sink_present( ++ struct encoder *enc, ++ struct graphics_object_id downstream); ++/* detect load on the sink, ++ * for analog signal, ++ * load detection will be called for the specified signal */ ++enum signal_type dal_encoder_detect_load( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* detect output sink type, ++ * for digital perform sense detection, ++ * for analog return encoder's signal type */ ++enum signal_type dal_encoder_detect_sink( ++ struct encoder *enc, ++ struct graphics_object_id downstream); ++/* get transmitter id */ ++enum transmitter dal_encoder_get_transmitter( ++ const struct encoder *enc); ++/* */ ++enum transmitter dal_encoder_get_paired_transmitter( ++ const struct encoder *enc); ++/* */ ++enum physical_phy_id dal_encoder_get_phy( ++ const struct encoder *enc); ++/* */ ++enum physical_phy_id dal_encoder_get_paired_phy( ++ const struct encoder *enc); ++/* reports if the encoder supports given link settings */ ++bool dal_encoder_is_link_settings_supported( ++ struct encoder *enc, ++ const struct link_settings *link_settings); ++/* options and features supported by encoder */ ++struct encoder_feature_support dal_encoder_get_supported_features( ++ const struct encoder *enc); ++/* reports list of supported stream engines */ ++union supported_stream_engines dal_encoder_get_supported_stream_engines( ++ const struct encoder *enc); ++/* reports preferred stream engine */ ++enum engine_id dal_encoder_get_preferred_stream_engine( ++ const struct encoder *enc); ++/* reports whether clock source can be used with enc encoder */ ++bool dal_encoder_is_clock_source_supported( ++ const struct encoder *enc, ++ enum clock_source_id clock_source); ++/* check encoder capabilities to confirm ++ * specified timing is in the encoder limits ++ * when outputting certain signal */ ++enum encoder_result dal_encoder_validate_output( ++ struct encoder *enc, ++ const struct encoder_output *output); ++/* retrieves sync source which outputs VSync signal from encoder */ ++enum sync_source dal_encoder_get_vsync_output_source( ++ const struct encoder *enc); ++/* ++ * Adjustments ++ */ ++/* update AVI info frame */ ++void dal_encoder_update_info_frame( ++ struct encoder *enc, ++ const struct encoder_info_frame_param *param); ++/* */ ++void dal_encoder_stop_info_frame( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* */ ++enum encoder_result dal_encoder_set_lcd_backlight_level( ++ struct encoder *enc, ++ uint32_t level); ++/* backlight control interface */ ++enum encoder_result dal_encoder_backlight_control( ++ struct encoder *enc, ++ bool enable); ++/* ++ * DP MST programming ++ */ ++/* update payload slot allocation for each DP MST stream */ ++enum encoder_result dal_encoder_update_mst_alloc_table( ++ struct encoder *enc, ++ const struct dp_mst_stream_allocation_table *table, ++ bool is_removal); ++/* enable virtual channel stream with throttled value X.Y */ ++enum encoder_result dal_encoder_enable_stream( ++ struct encoder *enc, ++ enum engine_id engine, ++ struct fixed31_32 throttled_vcp_size); ++/* disable virtual channel stream */ ++enum encoder_result dal_encoder_disable_stream( ++ struct encoder *enc, ++ enum engine_id engine); ++void dal_encoder_set_multi_path(struct encoder *enc, bool is_multi_path); ++/* ++ * Test harness ++ */ ++/* check whether Test Pattern enabled */ ++bool dal_encoder_is_test_pattern_enabled( ++ struct encoder *enc, ++ enum engine_id engine); ++/* set lane parameters */ ++enum encoder_result dal_encoder_set_lane_settings( ++ struct encoder *enc, ++ const struct encoder_context *ctx, ++ const struct link_training_settings *link_settings); ++/* get lane parameters */ ++enum encoder_result dal_encoder_get_lane_settings( ++ struct encoder *enc, ++ const struct encoder_context *ctx, ++ struct link_training_settings *link_settings); ++/* enable master clock of HPD interrupt */ ++void dal_encoder_enable_hpd( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++/* disable all HPD interrupts */ ++void dal_encoder_disable_hpd( ++ struct encoder *enc, ++ const struct encoder_context *ctx); ++ ++/* get current HW state - used for optimization code path only */ ++enum clock_source_id dal_encoder_get_active_clock_source( ++ const struct encoder *enc); ++enum engine_id dal_encoder_get_active_engine( ++ const struct encoder *enc); ++ ++/* destroy encoder instance */ ++void dal_encoder_destroy( ++ struct encoder **ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/encoder_types.h b/drivers/gpu/drm/amd/dal/include/encoder_types.h +new file mode 100644 +index 0000000..2897a1d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/encoder_types.h +@@ -0,0 +1,216 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ENCODER_TYPES_H__ ++#define __DAL_ENCODER_TYPES_H__ ++ ++#include "grph_object_defs.h" ++#include "signal_types.h" ++#include "hw_sequencer_types.h" ++#include "link_service_types.h" ++ ++struct encoder_context { ++ /* ++ * HW programming context ++ */ ++ /* DIG id. Also used as AC context */ ++ enum engine_id engine; ++ /* DDC line */ ++ enum channel_id channel; ++ /* HPD line */ ++ enum hpd_source_id hpd_source; ++ /* ++ * ASIC Control (VBIOS) context ++ */ ++ /* encoder output signal */ ++ enum signal_type signal; ++ /* native connector id */ ++ struct graphics_object_id connector; ++ /* downstream object (can be connector or downstream encoder) */ ++ struct graphics_object_id downstream; ++}; ++ ++union encoder_flags { ++ struct { ++ /* enable audio (DP/eDP only) */ ++ uint32_t ENABLE_AUDIO:1; ++ /* coherency */ ++ uint32_t COHERENT:1; ++ /* delay after Pixel Format change before enable transmitter */ ++ uint32_t DELAY_AFTER_PIXEL_FORMAT_CHANGE:1; ++ /* by default, do not turn off VCC when disabling output */ ++ uint32_t TURN_OFF_VCC:1; ++ /* by default, do wait for HPD low after turn of panel VCC */ ++ uint32_t NO_WAIT_FOR_HPD_LOW:1; ++ /* slow DP panels don't reset internal fifo */ ++ uint32_t VID_STREAM_DIFFER_TO_SYNC:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++struct encoder_info_packet { ++ bool valid; ++ uint8_t hb0; ++ uint8_t hb1; ++ uint8_t hb2; ++ uint8_t hb3; ++ uint8_t sb[28]; ++}; ++ ++struct encoder_info_frame { ++ /* auxiliary video information */ ++ struct encoder_info_packet avi; ++ struct encoder_info_packet gamut; ++ struct encoder_info_packet vendor; ++ /* source product description */ ++ struct encoder_info_packet spd; ++ /* video stream configuration */ ++ struct encoder_info_packet vsc; ++}; ++ ++struct encoder_info_frame_param { ++ struct encoder_info_frame packets; ++ struct encoder_context enc_ctx; ++}; ++ ++/*TODO: cleanup pending encoder cleanup*/ ++struct encoder_output { ++ /* encoder AC & HW programming context */ ++ struct encoder_context enc_ctx; ++ /* requested timing */ ++ struct hw_crtc_timing crtc_timing; ++ /* clock source id (PLL or external) */ ++ enum clock_source_id clock_source; ++ /* link settings (DP/eDP only) */ ++ struct link_settings link_settings; ++ /* info frame packets */ ++ struct encoder_info_frame info_frame; ++ /* timing validation (HDMI only) */ ++ uint32_t max_tmds_clk_from_edid_in_mhz; ++ /* edp panel mode */ ++ enum dp_panel_mode dp_panel_mode; ++ /* delay in milliseconds after powering up DP receiver (DP/eDP only) */ ++ uint32_t delay_after_dp_receiver_power_up; ++ /* various flags for features and workarounds */ ++ union encoder_flags flags; ++ /* delay after pixel format change */ ++ uint32_t delay_after_pixel_format_change; ++ /* controller id */ ++ enum controller_id controller; ++ /* maximum supported deep color depth for HDMI */ ++ enum dc_color_depth max_hdmi_deep_color; ++ /* maximum supported pixel clock for HDMI */ ++ uint32_t max_hdmi_pixel_clock; ++}; ++ ++struct encoder_pre_enable_output_param { ++ struct hw_crtc_timing crtc_timing; ++ struct link_settings link_settings; ++ struct encoder_context enc_ctx; ++}; ++ ++struct encoder_unblank_param { ++ struct hw_crtc_timing crtc_timing; ++ struct link_settings link_settings; ++ enum signal_type signal; ++}; ++ ++/* ++ * @brief ++ * Parameters to setup stereo 3D mode in Encoder: ++ * - source: used for side-band stereo sync (DVO/DAC); ++ * - engine_id: defines engine for this Encoder; ++ * - enable_inband: in-band stereo sync should be enabled; ++ * - enable_sideband: side-band stereo sync should be enabled. ++ */ ++struct encoder_3d_setup { ++ enum engine_id engine; ++ enum sync_source source; ++ union { ++ struct { ++ uint32_t SETUP_SYNC_SOURCE:1; ++ uint32_t ENABLE_INBAND:1; ++ uint32_t ENABLE_SIDEBAND:1; ++ uint32_t DISABLE_INBAND:1; ++ uint32_t DISABLE_SIDEBAND:1; ++ } bits; ++ uint32_t raw; ++ } flags; ++}; ++ ++struct encoder_set_dp_phy_pattern_param { ++ enum dp_test_pattern dp_phy_pattern; ++ const uint8_t *custom_pattern; ++ uint32_t custom_pattern_size; ++ enum dp_panel_mode dp_panel_mode; ++}; ++ ++struct encoder_feature_support { ++ union { ++ struct { ++ /* 1 - external encoder; 0 - internal encoder */ ++ uint32_t EXTERNAL_ENCODER:1; ++ uint32_t ANALOG_ENCODER:1; ++ uint32_t STEREO_SYNC:1; ++ /* check the DDC data pin ++ * when performing DP Sink detection */ ++ uint32_t DP_SINK_DETECT_POLL_DATA_PIN:1; ++ /* CPLIB authentication ++ * for external DP chip supported */ ++ uint32_t CPLIB_DP_AUTHENTICATION:1; ++ uint32_t IS_HBR2_CAPABLE:1; ++ uint32_t IS_HBR2_VALIDATED:1; ++ uint32_t IS_TPS3_CAPABLE:1; ++ uint32_t IS_AUDIO_CAPABLE:1; ++ uint32_t IS_VCE_SUPPORTED:1; ++ uint32_t IS_CONVERTER:1; ++ uint32_t IS_Y_ONLY_CAPABLE:1; ++ uint32_t IS_YCBCR_CAPABLE:1; ++ } bits; ++ uint32_t raw; ++ } flags; ++ /* maximum supported deep color depth */ ++ enum dc_color_depth max_deep_color; ++ /* maximum supported clock */ ++ uint32_t max_pixel_clock; ++}; ++ ++enum dig_encoder_mode { ++ DIG_ENCODER_MODE_DP, ++ DIG_ENCODER_MODE_LVDS, ++ DIG_ENCODER_MODE_DVI, ++ DIG_ENCODER_MODE_HDMI, ++ DIG_ENCODER_MODE_SDVO, ++ DIG_ENCODER_MODE_DP_WITH_AUDIO, ++ DIG_ENCODER_MODE_DP_MST, ++ ++ /* direct HW translation ! */ ++ DIG_ENCODER_MODE_TV = 13, ++ DIG_ENCODER_MODE_CV, ++ DIG_ENCODER_MODE_CRT ++}; ++ ++#endif ++ +diff --git a/drivers/gpu/drm/amd/dal/include/fixed31_32.h b/drivers/gpu/drm/amd/dal/include/fixed31_32.h +new file mode 100644 +index 0000000..507f9f6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/fixed31_32.h +@@ -0,0 +1,389 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_FIXED31_32_H__ ++#define __DAL_FIXED31_32_H__ ++ ++/* ++ * @brief ++ * Arithmetic operations on real numbers ++ * represented as fixed-point numbers. ++ * There are: 1 bit for sign, ++ * 31 bit for integer part, ++ * 32 bits for fractional part. ++ * ++ * @note ++ * Currently, overflows and underflows are asserted; ++ * no special result returned. ++ */ ++ ++struct fixed31_32 { ++ int64_t value; ++}; ++ ++/* ++ * @brief ++ * Useful constants ++ */ ++ ++static const struct fixed31_32 dal_fixed31_32_zero = { 0 }; ++static const struct fixed31_32 dal_fixed31_32_epsilon = { 1LL }; ++static const struct fixed31_32 dal_fixed31_32_half = { 0x80000000LL }; ++static const struct fixed31_32 dal_fixed31_32_one = { 0x100000000LL }; ++ ++static const struct fixed31_32 dal_fixed31_32_pi = { 13493037705LL }; ++static const struct fixed31_32 dal_fixed31_32_two_pi = { 26986075409LL }; ++static const struct fixed31_32 dal_fixed31_32_e = { 11674931555LL }; ++static const struct fixed31_32 dal_fixed31_32_ln2 = { 2977044471LL }; ++static const struct fixed31_32 dal_fixed31_32_ln2_div_2 = { 1488522236LL }; ++ ++/* ++ * @brief ++ * Initialization routines ++ */ ++ ++/* ++ * @brief ++ * result = numerator / denominator ++ */ ++struct fixed31_32 dal_fixed31_32_from_fraction( ++ int64_t numerator, ++ int64_t denominator); ++ ++/* ++ * @brief ++ * result = arg ++ */ ++struct fixed31_32 dal_fixed31_32_from_int( ++ int64_t arg); ++ ++/* ++ * @brief ++ * Unary operators ++ */ ++ ++/* ++ * @brief ++ * result = -arg ++ */ ++struct fixed31_32 dal_fixed31_32_neg( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = abs(arg) := (arg >= 0) ? arg : -arg ++ */ ++struct fixed31_32 dal_fixed31_32_abs( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * Binary relational operators ++ */ ++ ++/* ++ * @brief ++ * result = arg1 < arg2 ++ */ ++bool dal_fixed31_32_lt( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * result = arg1 <= arg2 ++ */ ++bool dal_fixed31_32_le( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * result = arg1 == arg2 ++ */ ++bool dal_fixed31_32_eq( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * result = min(arg1, arg2) := (arg1 <= arg2) ? arg1 : arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_min( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * result = max(arg1, arg2) := (arg1 <= arg2) ? arg2 : arg1 ++ */ ++struct fixed31_32 dal_fixed31_32_max( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * | min_value, when arg <= min_value ++ * result = | arg, when min_value < arg < max_value ++ * | max_value, when arg >= max_value ++ */ ++struct fixed31_32 dal_fixed31_32_clamp( ++ struct fixed31_32 arg, ++ struct fixed31_32 min_value, ++ struct fixed31_32 max_value); ++ ++/* ++ * @brief ++ * Binary shift operators ++ */ ++ ++/* ++ * @brief ++ * result = arg << shift ++ */ ++struct fixed31_32 dal_fixed31_32_shl( ++ struct fixed31_32 arg, ++ uint8_t shift); ++ ++/* ++ * @brief ++ * result = arg >> shift ++ */ ++struct fixed31_32 dal_fixed31_32_shr( ++ struct fixed31_32 arg, ++ uint8_t shift); ++ ++/* ++ * @brief ++ * Binary additive operators ++ */ ++ ++/* ++ * @brief ++ * result = arg1 + arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_add( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * result = arg1 - arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_sub_int( ++ struct fixed31_32 arg1, ++ int32_t arg2); ++ ++/* ++ * @brief ++ * result = arg1 - arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_sub( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * Binary multiplicative operators ++ */ ++ ++/* ++ * @brief ++ * result = arg1 * arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_mul_int( ++ struct fixed31_32 arg1, ++ int32_t arg2); ++ ++/* ++ * @brief ++ * result = arg1 * arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_mul( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * result = square(arg) := arg * arg ++ */ ++struct fixed31_32 dal_fixed31_32_sqr( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = arg1 / arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_div_int( ++ struct fixed31_32 arg1, ++ int64_t arg2); ++ ++/* ++ * @brief ++ * result = arg1 / arg2 ++ */ ++struct fixed31_32 dal_fixed31_32_div( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * Reciprocal function ++ */ ++ ++/* ++ * @brief ++ * result = reciprocal(arg) := 1 / arg ++ * ++ * @note ++ * No special actions taken in case argument is zero. ++ */ ++struct fixed31_32 dal_fixed31_32_recip( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * Trigonometric functions ++ */ ++ ++/* ++ * @brief ++ * result = sinc(arg) := sin(arg) / arg ++ * ++ * @note ++ * Argument specified in radians, ++ * internally it's normalized to [-2pi...2pi] range. ++ */ ++struct fixed31_32 dal_fixed31_32_sinc( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = sin(arg) ++ * ++ * @note ++ * Argument specified in radians, ++ * internally it's normalized to [-2pi...2pi] range. ++ */ ++struct fixed31_32 dal_fixed31_32_sin( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = cos(arg) ++ * ++ * @note ++ * Argument specified in radians ++ * and should be in [-2pi...2pi] range - ++ * passing arguments outside that range ++ * will cause incorrect result! ++ */ ++struct fixed31_32 dal_fixed31_32_cos( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * Transcendent functions ++ */ ++ ++/* ++ * @brief ++ * result = exp(arg) ++ * ++ * @note ++ * Currently, function is verified for abs(arg) <= 1. ++ */ ++struct fixed31_32 dal_fixed31_32_exp( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = log(arg) ++ * ++ * @note ++ * Currently, abs(arg) should be less than 1. ++ * No normalization is done. ++ * Currently, no special actions taken ++ * in case of invalid argument(s). Take care! ++ */ ++struct fixed31_32 dal_fixed31_32_log( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * Power function ++ */ ++ ++/* ++ * @brief ++ * result = pow(arg1, arg2) ++ * ++ * @note ++ * Currently, abs(arg1) should be less than 1. Take care! ++ */ ++struct fixed31_32 dal_fixed31_32_pow( ++ struct fixed31_32 arg1, ++ struct fixed31_32 arg2); ++ ++/* ++ * @brief ++ * Rounding functions ++ */ ++ ++/* ++ * @brief ++ * result = floor(arg) := greatest integer lower than or equal to arg ++ */ ++int32_t dal_fixed31_32_floor( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = round(arg) := integer nearest to arg ++ */ ++int32_t dal_fixed31_32_round( ++ struct fixed31_32 arg); ++ ++/* ++ * @brief ++ * result = ceil(arg) := lowest integer greater than or equal to arg ++ */ ++int32_t dal_fixed31_32_ceil( ++ struct fixed31_32 arg); ++ ++/* the following two function are used in scaler hw programming to convert fixed ++ * point value to format 2 bits from integer part and 19 bits from fractional ++ * part. The same applies for u0d19, 0 bits from integer part and 19 bits from ++ * fractional ++ */ ++ ++uint32_t dal_fixed31_32_u2d19( ++ struct fixed31_32 arg); ++ ++uint32_t dal_fixed31_32_u0d19( ++ struct fixed31_32 arg); ++ ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/fixed32_32.h b/drivers/gpu/drm/amd/dal/include/fixed32_32.h +new file mode 100644 +index 0000000..5fca957 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/fixed32_32.h +@@ -0,0 +1,80 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_FIXED32_32_H__ ++#define __DAL_FIXED32_32_H__ ++ ++struct fixed32_32 { ++ uint64_t value; ++}; ++ ++static const struct fixed32_32 dal_fixed32_32_zero = { 0 }; ++static const struct fixed32_32 dal_fixed32_32_one = { 0x100000000LL }; ++static const struct fixed32_32 dal_fixed32_32_half = { 0x80000000LL }; ++ ++struct fixed32_32 dal_fixed32_32_from_fraction(uint32_t n, uint32_t d); ++struct fixed32_32 dal_fixed32_32_from_int(uint32_t value); ++struct fixed32_32 dal_fixed32_32_add( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs); ++struct fixed32_32 dal_fixed32_32_add_int( ++ struct fixed32_32 lhs, ++ uint32_t rhs); ++struct fixed32_32 dal_fixed32_32_sub( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs); ++struct fixed32_32 dal_fixed32_32_sub_int( ++ struct fixed32_32 lhs, ++ uint32_t rhs); ++struct fixed32_32 dal_fixed32_32_mul( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs); ++struct fixed32_32 dal_fixed32_32_mul_int( ++ struct fixed32_32 lhs, ++ uint32_t rhs); ++struct fixed32_32 dal_fixed32_32_div( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs); ++struct fixed32_32 dal_fixed32_32_div_int( ++ struct fixed32_32 lhs, ++ uint32_t rhs); ++struct fixed32_32 dal_fixed32_32_min( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs); ++struct fixed32_32 dal_fixed32_32_max( ++ struct fixed32_32 lhs, ++ struct fixed32_32 rhs); ++bool dal_fixed32_32_gt(struct fixed32_32 lhs, struct fixed32_32 rhs); ++bool dal_fixed32_32_gt_int(struct fixed32_32 lhs, uint32_t rhs); ++bool dal_fixed32_32_lt(struct fixed32_32 lhs, struct fixed32_32 rhs); ++bool dal_fixed32_32_lt_int(struct fixed32_32 lhs, uint32_t rhs); ++bool dal_fixed32_32_le(struct fixed32_32 lhs, struct fixed32_32 rhs); ++bool dal_fixed32_32_le_int(struct fixed32_32 lhs, uint32_t rhs); ++bool dal_fixed32_32_eq(struct fixed32_32 lhs, struct fixed32_32 rhs); ++uint32_t dal_fixed32_32_ceil(struct fixed32_32 value); ++uint32_t dal_fixed32_32_floor(struct fixed32_32 value); ++uint32_t dal_fixed32_32_round(struct fixed32_32 value); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/gpio_interface.h b/drivers/gpu/drm/amd/dal/include/gpio_interface.h +new file mode 100644 +index 0000000..a084d79 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/gpio_interface.h +@@ -0,0 +1,93 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPIO_INTERFACE_H__ ++#define __DAL_GPIO_INTERFACE_H__ ++ ++#include "gpio_types.h" ++#include "grph_object_defs.h" ++ ++struct gpio; ++ ++/* Open the handle for future use */ ++enum gpio_result dal_gpio_open( ++ struct gpio *gpio, ++ enum gpio_mode mode); ++ ++enum gpio_result dal_gpio_open_ex( ++ struct gpio *gpio, ++ enum gpio_mode mode, ++ void *options); ++ ++/* Get high or low from the pin */ ++enum gpio_result dal_gpio_get_value( ++ const struct gpio *gpio, ++ uint32_t *value); ++ ++/* Set pin high or low */ ++enum gpio_result dal_gpio_set_value( ++ const struct gpio *gpio, ++ uint32_t value); ++ ++/* Get current mode */ ++enum gpio_mode dal_gpio_get_mode( ++ const struct gpio *gpio); ++ ++/* Change mode of the handle */ ++enum gpio_result dal_gpio_change_mode( ++ struct gpio *gpio, ++ enum gpio_mode mode); ++ ++/* Get the GPIO id */ ++enum gpio_id dal_gpio_get_id( ++ const struct gpio *gpio); ++ ++/* Get the GPIO enum */ ++uint32_t dal_gpio_get_enum( ++ const struct gpio *gpio); ++ ++/* Set the GPIO pin configuration */ ++enum gpio_result dal_gpio_set_config( ++ struct gpio *gpio, ++ const struct gpio_config_data *config_data); ++ ++/* Obtain GPIO pin info */ ++enum gpio_result dal_gpio_get_pin_info( ++ const struct gpio *gpio, ++ struct gpio_pin_info *pin_info); ++ ++/* Obtain GPIO sync source */ ++enum sync_source dal_gpio_get_sync_source( ++ const struct gpio *gpio); ++ ++/* Obtain GPIO pin output state (active low or active high) */ ++enum gpio_pin_output_state dal_gpio_get_output_state( ++ const struct gpio *gpio); ++ ++/* Close the handle */ ++void dal_gpio_close( ++ struct gpio *gpio); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/gpio_service_interface.h b/drivers/gpu/drm/amd/dal/include/gpio_service_interface.h +new file mode 100644 +index 0000000..b22bb1b +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/gpio_service_interface.h +@@ -0,0 +1,94 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPIO_SERVICE_INTERFACE_H__ ++#define __DAL_GPIO_SERVICE_INTERFACE_H__ ++ ++#include "gpio_types.h" ++#include "gpio_interface.h" ++#include "dvo_interface.h" ++#include "ddc_interface.h" ++#include "irq_interface.h" ++ ++struct gpio_service; ++ ++struct gpio_service *dal_gpio_service_create( ++ enum dce_version dce_version, ++ struct dc_context *ctx); ++ ++struct gpio *dal_gpio_service_create_gpio( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask, ++ enum gpio_pin_output_state output_state); ++ ++struct gpio *dal_gpio_service_create_gpio_ex( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en, ++ enum gpio_pin_output_state output_state); ++ ++void dal_gpio_service_destroy_gpio( ++ struct gpio **gpio); ++ ++struct ddc *dal_gpio_service_create_ddc( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask, ++ struct gpio_ddc_hw_info *info); ++ ++void dal_gpio_service_destroy_ddc( ++ struct ddc **ddc); ++ ++struct dvo *dal_gpio_service_create_dvo( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask); ++ ++struct dvo *dal_gpio_service_create_dvo_ex( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en); ++ ++void dal_gpio_service_destroy_dvo( ++ struct dvo **ptr); ++ ++struct irq *dal_gpio_service_create_irq( ++ struct gpio_service *service, ++ uint32_t offset, ++ uint32_t mask); ++ ++struct irq *dal_gpio_service_create_irq_ex( ++ struct gpio_service *service, ++ enum gpio_id id, ++ uint32_t en); ++ ++void dal_gpio_service_destroy_irq( ++ struct irq **ptr); ++ ++void dal_gpio_service_destroy( ++ struct gpio_service **ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/gpio_types.h b/drivers/gpu/drm/amd/dal/include/gpio_types.h +new file mode 100644 +index 0000000..d616d62 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/gpio_types.h +@@ -0,0 +1,393 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPIO_TYPES_H__ ++#define __DAL_GPIO_TYPES_H__ ++ ++#define BUNDLE_A_MASK 0x00FFF000L ++#define BUNDLE_B_MASK 0x00000FFFL ++ ++/* ++ * gpio_result ++ * ++ * @brief ++ * The possible return codes that the GPIO object can return. ++ * These return codes can be generated ++ * directly by the GPIO object or from the GPIOPin object. ++ */ ++enum gpio_result { ++ GPIO_RESULT_OK, ++ GPIO_RESULT_NULL_HANDLE, ++ GPIO_RESULT_INVALID_DATA, ++ GPIO_RESULT_DEVICE_BUSY, ++ GPIO_RESULT_OPEN_FAILED, ++ GPIO_RESULT_ALREADY_OPENED, ++ GPIO_RESULT_NON_SPECIFIC_ERROR ++}; ++ ++/* ++ * @brief ++ * Used to identify the specific GPIO device ++ * ++ * @notes ++ * These constants are used as indices in a vector. ++ * Thus they should start from zero and be contiguous. ++ */ ++enum gpio_id { ++ GPIO_ID_UNKNOWN = (-1), ++ GPIO_ID_DVO1, ++ GPIO_ID_DVO12, ++ GPIO_ID_DVO24, ++ GPIO_ID_DDC_DATA, ++ GPIO_ID_DDC_CLOCK, ++ GPIO_ID_GENERIC, ++ GPIO_ID_HPD, ++ GPIO_ID_GPIO_PAD, ++ GPIO_ID_VIP_PAD, ++ GPIO_ID_SYNC, ++ GPIO_ID_GSL, /* global swap lock */ ++ GPIO_ID_COUNT, ++ GPIO_ID_MIN = GPIO_ID_DVO1, ++ GPIO_ID_MAX = GPIO_ID_GSL ++}; ++ ++#define GPIO_ENUM_UNKNOWN \ ++ 32 ++ ++struct gpio_pin_info { ++ uint32_t offset; ++ uint32_t offset_y; ++ uint32_t offset_en; ++ uint32_t offset_mask; ++ ++ uint32_t mask; ++ uint32_t mask_y; ++ uint32_t mask_en; ++ uint32_t mask_mask; ++}; ++ ++enum gpio_pin_output_state { ++ GPIO_PIN_OUTPUT_STATE_ACTIVE_LOW, ++ GPIO_PIN_OUTPUT_STATE_ACTIVE_HIGH, ++ GPIO_PIN_OUTPUT_STATE_DEFAULT = GPIO_PIN_OUTPUT_STATE_ACTIVE_LOW ++}; ++ ++enum gpio_dvo1 { ++ GPIO_DVO1_UNKNOWN = (-1), ++ GPIO_DVO1_0, ++ GPIO_DVO1_1, ++ GPIO_DVO1_2, ++ GPIO_DVO1_3, ++ GPIO_DVO1_4, ++ GPIO_DVO1_5, ++ GPIO_DVO1_6, ++ GPIO_DVO1_7, ++ GPIO_DVO1_8, ++ GPIO_DVO1_9, ++ GPIO_DVO1_10, ++ GPIO_DVO1_11, ++ GPIO_DVO1_12, ++ GPIO_DVO1_13, ++ GPIO_DVO1_14, ++ GPIO_DVO1_15, ++ GPIO_DVO1_16, ++ GPIO_DVO1_17, ++ GPIO_DVO1_18, ++ GPIO_DVO1_19, ++ GPIO_DVO1_20, ++ GPIO_DVO1_21, ++ GPIO_DVO1_22, ++ GPIO_DVO1_23, ++ GPIO_DVO1_COUNT, ++ GPIO_DVO1_MIN = GPIO_DVO1_0, ++ GPIO_DVO1_MAX = GPIO_DVO1_23 ++}; ++ ++enum gpio_dvo12 { ++ GPIO_DVO12_UNKNOWN = (-1), ++ GPIO_DVO12_A, ++ GPIO_DVO12_B, ++ GPIO_DVO12_COUNT, ++ GPIO_DVO12_MIN = GPIO_DVO12_A, ++ GPIO_DVO12_MAX = GPIO_DVO12_B ++}; ++ ++enum gpio_dvo24 { ++ GPIO_DVO24_UNKNOWN = (-1), ++ GPIO_DVO24_A, ++ GPIO_DVO24_COUNT, ++ GPIO_DVO24_MIN = GPIO_DVO24_A, ++ GPIO_DVO24_MAX = GPIO_DVO24_A ++}; ++ ++enum gpio_generic { ++ GPIO_GENERIC_UNKNOWN = (-1), ++ GPIO_GENERIC_A, ++ GPIO_GENERIC_B, ++ GPIO_GENERIC_C, ++ GPIO_GENERIC_D, ++ GPIO_GENERIC_E, ++ GPIO_GENERIC_F, ++ GPIO_GENERIC_G, ++ GPIO_GENERIC_COUNT, ++ GPIO_GENERIC_MIN = GPIO_GENERIC_A, ++ GPIO_GENERIC_MAX = GPIO_GENERIC_B ++}; ++ ++enum gpio_hpd { ++ GPIO_HPD_UNKNOWN = (-1), ++ GPIO_HPD_1, ++ GPIO_HPD_2, ++ GPIO_HPD_3, ++ GPIO_HPD_4, ++ GPIO_HPD_5, ++ GPIO_HPD_6, ++ GPIO_HPD_COUNT, ++ GPIO_HPD_MIN = GPIO_HPD_1, ++ GPIO_HPD_MAX = GPIO_HPD_6 ++}; ++ ++enum gpio_gpio_pad { ++ GPIO_GPIO_PAD_UNKNOWN = (-1), ++ GPIO_GPIO_PAD_0, ++ GPIO_GPIO_PAD_1, ++ GPIO_GPIO_PAD_2, ++ GPIO_GPIO_PAD_3, ++ GPIO_GPIO_PAD_4, ++ GPIO_GPIO_PAD_5, ++ GPIO_GPIO_PAD_6, ++ GPIO_GPIO_PAD_7, ++ GPIO_GPIO_PAD_8, ++ GPIO_GPIO_PAD_9, ++ GPIO_GPIO_PAD_10, ++ GPIO_GPIO_PAD_11, ++ GPIO_GPIO_PAD_12, ++ GPIO_GPIO_PAD_13, ++ GPIO_GPIO_PAD_14, ++ GPIO_GPIO_PAD_15, ++ GPIO_GPIO_PAD_16, ++ GPIO_GPIO_PAD_17, ++ GPIO_GPIO_PAD_18, ++ GPIO_GPIO_PAD_19, ++ GPIO_GPIO_PAD_20, ++ GPIO_GPIO_PAD_21, ++ GPIO_GPIO_PAD_22, ++ GPIO_GPIO_PAD_23, ++ GPIO_GPIO_PAD_24, ++ GPIO_GPIO_PAD_25, ++ GPIO_GPIO_PAD_26, ++ GPIO_GPIO_PAD_27, ++ GPIO_GPIO_PAD_28, ++ GPIO_GPIO_PAD_29, ++ GPIO_GPIO_PAD_30, ++ GPIO_GPIO_PAD_COUNT, ++ GPIO_GPIO_PAD_MIN = GPIO_GPIO_PAD_0, ++ GPIO_GPIO_PAD_MAX = GPIO_GPIO_PAD_30 ++}; ++ ++enum gpio_vip_pad { ++ GPIO_VIP_PAD_UNKNOWN = (-1), ++ /* following never used - ++ * GPIO_ID_DDC_CLOCK::GPIO_DDC_LINE_VIP_PAD defined instead */ ++ GPIO_VIP_PAD_SCL, ++ /* following never used - ++ * GPIO_ID_DDC_DATA::GPIO_DDC_LINE_VIP_PAD defined instead */ ++ GPIO_VIP_PAD_SDA, ++ GPIO_VIP_PAD_VHAD, ++ GPIO_VIP_PAD_VPHCTL, ++ GPIO_VIP_PAD_VIPCLK, ++ GPIO_VIP_PAD_VID, ++ GPIO_VIP_PAD_VPCLK0, ++ GPIO_VIP_PAD_DVALID, ++ GPIO_VIP_PAD_PSYNC, ++ GPIO_VIP_PAD_COUNT, ++ GPIO_VIP_PAD_MIN = GPIO_VIP_PAD_SCL, ++ GPIO_VIP_PAD_MAX = GPIO_VIP_PAD_PSYNC ++}; ++ ++enum gpio_sync { ++ GPIO_SYNC_UNKNOWN = (-1), ++ GPIO_SYNC_HSYNC_A, ++ GPIO_SYNC_VSYNC_A, ++ GPIO_SYNC_HSYNC_B, ++ GPIO_SYNC_VSYNC_B, ++ GPIO_SYNC_COUNT, ++ GPIO_SYNC_MIN = GPIO_SYNC_HSYNC_A, ++ GPIO_SYNC_MAX = GPIO_SYNC_VSYNC_B ++}; ++ ++enum gpio_gsl { ++ GPIO_GSL_UNKNOWN = (-1), ++ GPIO_GSL_GENLOCK_CLOCK, ++ GPIO_GSL_GENLOCK_VSYNC, ++ GPIO_GSL_SWAPLOCK_A, ++ GPIO_GSL_SWAPLOCK_B, ++ GPIO_GSL_COUNT, ++ GPIO_GSL_MIN = GPIO_GSL_GENLOCK_CLOCK, ++ GPIO_GSL_MAX = GPIO_GSL_SWAPLOCK_B ++}; ++ ++/* ++ * @brief ++ * Unique Id for DDC handle. ++ * Values are meaningful (used as indexes to array) ++ */ ++enum gpio_ddc_line { ++ GPIO_DDC_LINE_UNKNOWN = (-1), ++ GPIO_DDC_LINE_DDC1, ++ GPIO_DDC_LINE_DDC2, ++ GPIO_DDC_LINE_DDC3, ++ GPIO_DDC_LINE_DDC4, ++ GPIO_DDC_LINE_DDC5, ++ GPIO_DDC_LINE_DDC6, ++ GPIO_DDC_LINE_DDC_VGA, ++ GPIO_DDC_LINE_VIP_PAD, ++ GPIO_DDC_LINE_I2C_PAD = GPIO_DDC_LINE_VIP_PAD, ++ GPIO_DDC_LINE_COUNT, ++ GPIO_DDC_LINE_MIN = GPIO_DDC_LINE_DDC1, ++ GPIO_DDC_LINE_MAX = GPIO_DDC_LINE_I2C_PAD ++}; ++ ++/* ++ * @brief ++ * Identifies the mode of operation to open a GPIO device. ++ * A GPIO device (pin) can be programmed in only one of these modes at a time. ++ */ ++enum gpio_mode { ++ GPIO_MODE_UNKNOWN = (-1), ++ GPIO_MODE_INPUT, ++ GPIO_MODE_OUTPUT, ++ GPIO_MODE_FAST_OUTPUT, ++ GPIO_MODE_HARDWARE, ++ GPIO_MODE_INTERRUPT ++}; ++ ++/* ++ * @brief ++ * Identifies the source of the signal when GPIO is in HW mode. ++ * get_signal_source() will return GPIO_SYGNAL_SOURCE__UNKNOWN ++ * when one of the following holds: ++ * 1. GPIO is input GPIO ++ * 2. GPIO is not opened in HW mode ++ * 3. GPIO does not have fixed signal source ++ * (like DC_GenericA have mux instead fixed) ++ */ ++enum gpio_signal_source { ++ GPIO_SIGNAL_SOURCE_UNKNOWN = (-1), ++ GPIO_SIGNAL_SOURCE_DACA_STEREO_SYNC, ++ GPIO_SIGNAL_SOURCE_PASS_THROUGH_STEREO_SYNC, ++ GPIO_SIGNAL_SOURCE_DACB_STEREO_SYNC, ++ GPIO_SIGNAL_SOURCE_DACA_HSYNC, ++ GPIO_SIGNAL_SOURCE_DACB_HSYNC, ++ GPIO_SIGNAL_SOURCE_DACA_VSYNC, ++ GPIO_SIGNAL_SOURCE_DACB_VSYNC, ++ GPIO_SIGNAL_SOURCE_DVO_STEREO_SYNC ++}; ++ ++enum gpio_stereo_source { ++ GPIO_STEREO_SOURCE_UNKNOWN = (-1), ++ GPIO_STEREO_SOURCE_D1, ++ GPIO_STEREO_SOURCE_D2, ++ GPIO_STEREO_SOURCE_D3, ++ GPIO_STEREO_SOURCE_D4, ++ GPIO_STEREO_SOURCE_D5, ++ GPIO_STEREO_SOURCE_D6 ++}; ++ ++/* ++ * GPIO config ++ */ ++ ++enum gpio_config_type { ++ GPIO_CONFIG_TYPE_NONE, ++ GPIO_CONFIG_TYPE_DDC, ++ GPIO_CONFIG_TYPE_HPD, ++ GPIO_CONFIG_TYPE_GENERIC_MUX, ++ GPIO_CONFIG_TYPE_GSL_MUX, ++ GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE ++}; ++ ++/* DDC configuration */ ++ ++enum gpio_ddc_config_type { ++ GPIO_DDC_CONFIG_TYPE_MODE_AUX, ++ GPIO_DDC_CONFIG_TYPE_MODE_I2C, ++ GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT, ++ GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT, ++ GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING ++}; ++ ++struct gpio_ddc_config { ++ enum gpio_ddc_config_type type; ++ bool data_en_bit_present; ++ bool clock_en_bit_present; ++}; ++ ++/* HPD configuration */ ++ ++struct gpio_hpd_config { ++ uint32_t delay_on_connect; /* milliseconds */ ++ uint32_t delay_on_disconnect; /* milliseconds */ ++}; ++ ++struct gpio_generic_mux_config { ++ bool enable_output_from_mux; ++ enum gpio_signal_source mux_select; ++ enum gpio_stereo_source stereo_select; ++}; ++ ++enum gpio_gsl_mux_config_type { ++ GPIO_GSL_MUX_CONFIG_TYPE_DISABLE, ++ GPIO_GSL_MUX_CONFIG_TYPE_TIMING_SYNC, ++ GPIO_GSL_MUX_CONFIG_TYPE_FLIP_SYNC ++}; ++ ++struct gpio_gsl_mux_config { ++ enum gpio_gsl_mux_config_type type; ++ /* Actually sync_source type, ++ * however we want to avoid inter-component includes here */ ++ uint32_t gsl_group; ++}; ++ ++struct gpio_config_data { ++ enum gpio_config_type type; ++ union { ++ struct gpio_ddc_config ddc; ++ struct gpio_hpd_config hpd; ++ struct gpio_generic_mux_config generic_mux; ++ struct gpio_gsl_mux_config gsl_mux; ++ } config; ++}; ++ ++struct gpio_ddc_hw_info { ++ bool hw_supported; ++ uint32_t ddc_channel; ++}; ++ ++struct gpio_ddc_open_options { ++ bool en_bit_present; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/gpu_clock_info.h b/drivers/gpu/drm/amd/dal/include/gpu_clock_info.h +new file mode 100644 +index 0000000..c9b47b2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/gpu_clock_info.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPU_CLOCK_INFO__ ++#define __DAL_GPU_CLOCK_INFO__ ++ ++#include "include/gpu_interface.h" ++ ++/*TODO this structures should be defined*/ ++struct gpu_static_clk_info; ++struct gpu_dynamic_clk_info; ++ ++bool init_static_clk_info( ++ struct gpu *gpu, ++ struct gpu_static_clk_info *st_clk_info); ++ ++bool update_dynamic_clk_info( ++ struct gpu *gpu, ++ struct gpu_dynamic_clk_info *dyn_clk_info); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/gpu_interface.h b/drivers/gpu/drm/amd/dal/include/gpu_interface.h +new file mode 100644 +index 0000000..63262c3 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/gpu_interface.h +@@ -0,0 +1,91 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GPU_INTERFACE__ ++#define __DAL_GPU_INTERFACE__ ++ ++#include "include/adapter_service_interface.h" ++#include "include/grph_object_ctrl_defs.h" ++ ++enum gpu_clocks_state { ++ GPU_CLOCKS_STATE_INVALID, ++ GPU_CLOCKS_STATE_ULTRA_LOW, ++ GPU_CLOCKS_STATE_LOW, ++ GPU_CLOCKS_STATE_NOMINAL, ++ GPU_CLOCKS_STATE_PERFORMANCE ++}; ++ ++struct gpu_clock_info { ++ uint32_t min_sclk_khz; ++ uint32_t max_sclk_khz; ++ ++ uint32_t min_mclk_khz; ++ uint32_t max_mclk_khz; ++ ++ uint32_t min_dclk_khz; ++ uint32_t max_dclk_khz; ++}; ++ ++struct gpu; ++struct irq_manager; ++ ++struct gpu_init_data { ++ struct dc_context *ctx; ++ struct adapter_service *adapter_service; ++ struct irq_manager *irq_manager; ++}; ++ ++struct gpu *dal_gpu_create(struct gpu_init_data *init_data); ++void dal_gpu_destroy(struct gpu **); ++ ++void dal_gpu_power_up(struct gpu *); ++void dal_gpu_power_down( ++ struct gpu *gpu, ++ enum dc_video_power_state power_state); ++void dal_gpu_light_sleep_vbios_wa(struct gpu *gpu, bool enable); ++void dal_gpu_release_hw(struct gpu *gpu); ++ ++uint32_t dal_gpu_get_num_of_functional_controllers(const struct gpu *gpu); ++uint32_t dal_gpu_get_max_num_of_primary_controllers(const struct gpu *gpu); ++uint32_t dal_gpu_get_max_num_of_underlay_controllers(const struct gpu *gpu); ++struct controller *dal_gpu_create_controller( ++ struct gpu *gpu, ++ uint32_t index); ++uint32_t dal_gpu_get_num_of_clock_sources(const struct gpu *gpu); ++struct clock_source *dal_gpu_create_clock_source( ++ struct gpu *gpu, ++ uint32_t index); ++ ++/* gpu_clock_interface implementation */ ++bool dal_gpu_init_static_clock_info(struct gpu *gpu, ++ struct gpu_clock_info *gpu_clk_info); ++ ++bool dal_gpu_update_dynamic_clock_info(struct gpu *gpu, ++ struct gpu_clock_info *gpu_clk_info); ++ ++void dal_gpu_get_static_clock_info(struct gpu *gpu, ++ struct gpu_clock_info *gpu_clk_info); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/grph_csc_types.h b/drivers/gpu/drm/amd/dal/include/grph_csc_types.h +new file mode 100644 +index 0000000..711b458 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/grph_csc_types.h +@@ -0,0 +1,98 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GRPH_CSC_TYPES_H__ ++#define __DAL_GRPH_CSC_TYPES_H__ ++ ++#include "set_mode_types.h" ++ ++enum color_space { ++ COLOR_SPACE_UNKNOWN, ++ COLOR_SPACE_SRGB_FULL_RANGE, ++ COLOR_SPACE_SRGB_LIMITED_RANGE, ++ COLOR_SPACE_YPBPR601, ++ COLOR_SPACE_YPBPR709, ++ COLOR_SPACE_YCBCR601, ++ COLOR_SPACE_YCBCR709, ++ COLOR_SPACE_YCBCR601_YONLY, ++ COLOR_SPACE_YCBCR709_YONLY, ++ COLOR_SPACE_N_MVPU_SUPER_AA, ++}; ++ ++enum grph_color_adjust_option { ++ GRPH_COLOR_MATRIX_HW_DEFAULT = 1, ++ GRPH_COLOR_MATRIX_SW ++}; ++ ++enum grph_csc_adjust_item { ++ GRPH_ADJUSTMENT_CONTRAST = 1, ++ GRPH_ADJUSTMENT_SATURATION, ++ GRPH_ADJUSTMENT_BRIGHTNESS, ++ GRPH_ADJUSTMENT_HUE, ++ GRPH_ADJUSTMENT_COLOR_TEMPERATURE ++}; ++ ++#define CSC_TEMPERATURE_MATRIX_SIZE 9 ++ ++enum graphics_csc_adjust_type { ++ GRAPHICS_CSC_ADJUST_TYPE_BYPASS = 0, ++ GRAPHICS_CSC_ADJUST_TYPE_HW, /* without adjustments */ ++ GRAPHICS_CSC_ADJUST_TYPE_SW /*use adjustments */ ++}; ++ ++enum graphics_gamut_adjust_type { ++ GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS = 0, ++ GRAPHICS_GAMUT_ADJUST_TYPE_HW, /* without adjustments */ ++ GRAPHICS_GAMUT_ADJUST_TYPE_SW /* use adjustments */ ++}; ++ ++struct grph_csc_adjustment { ++ enum grph_color_adjust_option color_adjust_option; ++ enum color_space c_space; ++ int32_t grph_cont; ++ int32_t grph_sat; ++ int32_t grph_bright; ++ int32_t grph_hue; ++ int32_t adjust_divider; ++ int32_t temperature_matrix[CSC_TEMPERATURE_MATRIX_SIZE]; ++ int32_t temperature_divider; ++ uint32_t lb_color_depth; ++ uint8_t gamma; /* gamma from Edid */ ++ enum dc_color_depth color_depth; /* clean up to uint32_t */ ++ enum pixel_format surface_pixel_format; ++ enum graphics_csc_adjust_type csc_adjust_type; ++ enum graphics_gamut_adjust_type gamut_adjust_type; ++}; ++ ++struct default_adjustment { ++ uint32_t lb_color_depth; ++ enum color_space color_space; ++ enum dc_color_depth color_depth; ++ enum pixel_format surface_pixel_format; ++ enum graphics_csc_adjust_type csc_adjust_type; ++ bool force_hw_default; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/grph_object_ctrl_defs.h b/drivers/gpu/drm/amd/dal/include/grph_object_ctrl_defs.h +new file mode 100644 +index 0000000..d804109 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/grph_object_ctrl_defs.h +@@ -0,0 +1,598 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GRPH_OBJECT_CTRL_DEFS_H__ ++#define __DAL_GRPH_OBJECT_CTRL_DEFS_H__ ++ ++#include "grph_object_defs.h" ++ ++/* ++ * ##################################################### ++ * ##################################################### ++ * ++ * These defines shared between asic_control/bios_parser and other ++ * DAL components ++ * ++ * ##################################################### ++ * ##################################################### ++ */ ++ ++enum tv_standard { ++ TV_STANDARD_UNKNOWN = 0, /* direct HW (mmBIOS_SCRATCH_2) translation! */ ++ TV_STANDARD_NTSC, ++ TV_STANDARD_NTSCJ, ++ TV_STANDARD_PAL, ++ TV_STANDARD_PALM, ++ TV_STANDARD_PALCN, ++ TV_STANDARD_PALN, ++ TV_STANDARD_PAL60, ++ TV_STANDARD_SECAM ++}; ++ ++enum cv_standard { ++ CV_STANDARD_UNKNOWN = 0x0000, ++ CV_STANDARD_HD_MASK = 0x0800, /* Flag mask HDTV output */ ++ CV_STANDARD_SD_NTSC_MASK = 0x1000, /* Flag mask NTSC output */ ++ CV_STANDARD_SD_NTSC_M, /* NTSC (North America) output 1001 */ ++ CV_STANDARD_SD_NTSC_J, /* NTSC (Japan) output 1002 */ ++ CV_STANDARD_SD_480I, /* SDTV 480i output 1003 */ ++ CV_STANDARD_SD_480P, /* SDTV 480p output 1004 */ ++ CV_STANDARD_HD_720_60P = 0x1800,/* HDTV 720/60p output 1800 */ ++ CV_STANDARD_HD_1080_60I, /* HDTV 1080/60i output 1801 */ ++ CV_STANDARD_SD_PAL_MASK = 0x2000,/* Flag mask PAL output */ ++ CV_STANDARD_SD_PAL_B, /* PAL B output 2001 */ ++ CV_STANDARD_SD_PAL_D, /* PAL D output 2002 */ ++ CV_STANDARD_SD_PAL_G, /* PAL G output 2003 */ ++ CV_STANDARD_SD_PAL_H, /* PAL H output 2004 */ ++ CV_STANDARD_SD_PAL_I, /* PAL I output 2005 */ ++ CV_STANDARD_SD_PAL_M, /* PAL M output 2006 */ ++ CV_STANDARD_SD_PAL_N, /* PAL N output 2007 */ ++ CV_STANDARD_SD_PAL_N_COMB, /* PAL Combination N output 2008 */ ++ CV_STANDARD_SD_PAL_60, /* PAL 60 output (test mode) 2009 */ ++ CV_STANDARD_SD_576I, /* SDTV 576i output 2010 */ ++ CV_STANDARD_SD_576P, /* SDTV 576p output 2011 */ ++ CV_STANDARD_HD_720_50P = 0x2800,/* HDTV 720/50p output 2800 */ ++ CV_STANDARD_HD_1080_50I, /* HDTV 1080/50i output 2801 */ ++ CV_STANDARD_SD_SECAM_MASK = 0x4000, /* Flag mask SECAM output */ ++ CV_STANDARD_SD_SECAM_B, /* SECAM B output 4001 */ ++ CV_STANDARD_SD_SECAM_D, /* SECAM D output 4002 */ ++ CV_STANDARD_SD_SECAM_G, /* SECAM G output 4003 */ ++ CV_STANDARD_SD_SECAM_H, /* SECAM H output 4004 */ ++ CV_STANDARD_SD_SECAM_K, /* SECAM K output 4005 */ ++ CV_STANDARD_SD_SECAM_K1, /* SECAM K1 output 4006 */ ++ CV_STANDARD_SD_SECAM_L, /* SECAM L output 4007 */ ++ CV_STANDARD_SD_SECAM_L1 /* SECAM L1 output 4009 */ ++}; ++ ++enum disp_pll_config { ++ DISP_PLL_CONFIG_UNKNOWN = 0, ++ DISP_PLL_CONFIG_DVO_DDR_MODE_LOW_12BIT, ++ DISP_PLL_CONFIG_DVO_DDR_MODE_UPPER_12BIT, ++ DISP_PLL_CONFIG_DVO_DDR_MODE_24BIT ++}; ++ ++enum display_output_bit_depth { ++ PANEL_UNDEFINE = 0, ++ PANEL_6BIT_COLOR = 1, ++ PANEL_8BIT_COLOR = 2, ++ PANEL_10BIT_COLOR = 3, ++ PANEL_12BIT_COLOR = 4, ++ PANEL_16BIT_COLOR = 5, ++}; ++ ++enum lcd_scale { ++ /* No request to turn on LCD SCALER (Centering or Replication) */ ++ LCD_SCALE_NONE = 0, ++ /* Request LCD SCALER in full panel mode */ ++ LCD_SCALE_FULLPANEL, ++ /* Request LCD SCALER in aspect-ratio mode */ ++ LCD_SCALE_ASPECTRATIO, ++ ++ LCD_SCALE_UNKNOWN = (-1L), ++}; ++ ++/* Device type as abstracted by ATOM BIOS */ ++enum dal_device_type { ++ DEVICE_TYPE_UNKNOWN = 0, ++ DEVICE_TYPE_LCD, ++ DEVICE_TYPE_CRT, ++ DEVICE_TYPE_DFP, ++ DEVICE_TYPE_CV, ++ DEVICE_TYPE_TV, ++ DEVICE_TYPE_CF, ++ DEVICE_TYPE_WIRELESS ++}; ++ ++/* Device ID as abstracted by ATOM BIOS */ ++struct device_id { ++ enum dal_device_type device_type:16; ++ uint32_t enum_id:16; /* 1 based enum */ ++}; ++ ++struct graphics_object_i2c_info { ++ struct gpio_info { ++ uint32_t clk_mask_register_index; ++ uint32_t clk_en_register_index; ++ uint32_t clk_y_register_index; ++ uint32_t clk_a_register_index; ++ uint32_t data_mask_register_index; ++ uint32_t data_en_register_index; ++ uint32_t data_y_register_index; ++ uint32_t data_a_register_index; ++ ++ uint32_t clk_mask_shift; ++ uint32_t clk_en_shift; ++ uint32_t clk_y_shift; ++ uint32_t clk_a_shift; ++ uint32_t data_mask_shift; ++ uint32_t data_en_shift; ++ uint32_t data_y_shift; ++ uint32_t data_a_shift; ++ } gpio_info; ++ ++ bool i2c_hw_assist; ++ uint32_t i2c_line; ++ uint32_t i2c_engine_id; ++ uint32_t i2c_slave_address; ++}; ++ ++ ++struct graphics_object_hpd_info { ++ uint8_t hpd_int_gpio_uid; ++ uint8_t hpd_active; ++}; ++ ++struct connector_device_tag_info { ++ uint32_t acpi_device; ++ struct device_id dev_id; ++}; ++ ++struct device_timing { ++ struct misc_info { ++ uint32_t HORIZONTAL_CUT_OFF:1; ++ /* 0=Active High, 1=Active Low */ ++ uint32_t H_SYNC_POLARITY:1; ++ /* 0=Active High, 1=Active Low */ ++ uint32_t V_SYNC_POLARITY:1; ++ uint32_t VERTICAL_CUT_OFF:1; ++ uint32_t H_REPLICATION_BY2:1; ++ uint32_t V_REPLICATION_BY2:1; ++ uint32_t COMPOSITE_SYNC:1; ++ uint32_t INTERLACE:1; ++ uint32_t DOUBLE_CLOCK:1; ++ uint32_t RGB888:1; ++ uint32_t GREY_LEVEL:2; ++ uint32_t SPATIAL:1; ++ uint32_t TEMPORAL:1; ++ uint32_t API_ENABLED:1; ++ } misc_info; ++ ++ uint32_t pixel_clk; /* in KHz */ ++ uint32_t horizontal_addressable; ++ uint32_t horizontal_blanking_time; ++ uint32_t vertical_addressable; ++ uint32_t vertical_blanking_time; ++ uint32_t horizontal_sync_offset; ++ uint32_t horizontal_sync_width; ++ uint32_t vertical_sync_offset; ++ uint32_t vertical_sync_width; ++ uint32_t horizontal_border; ++ uint32_t vertical_border; ++}; ++ ++struct supported_refresh_rate { ++ uint32_t REFRESH_RATE_30HZ:1; ++ uint32_t REFRESH_RATE_40HZ:1; ++ uint32_t REFRESH_RATE_48HZ:1; ++ uint32_t REFRESH_RATE_50HZ:1; ++ uint32_t REFRESH_RATE_60HZ:1; ++}; ++ ++struct embedded_panel_info { ++ struct device_timing lcd_timing; ++ uint32_t ss_id; ++ struct supported_refresh_rate supported_rr; ++ uint32_t drr_enabled; ++ uint32_t min_drr_refresh_rate; ++}; ++ ++struct embedded_panel_patch_mode { ++ uint32_t width; ++ uint32_t height; ++}; ++ ++struct firmware_info { ++ struct pll_info { ++ uint32_t crystal_frequency; /* in KHz */ ++ uint32_t min_input_pxl_clk_pll_frequency; /* in KHz */ ++ uint32_t max_input_pxl_clk_pll_frequency; /* in KHz */ ++ uint32_t min_output_pxl_clk_pll_frequency; /* in KHz */ ++ uint32_t max_output_pxl_clk_pll_frequency; /* in KHz */ ++ } pll_info; ++ ++ struct firmware_feature { ++ uint32_t memory_clk_ss_percentage; ++ uint32_t engine_clk_ss_percentage; ++ } feature; ++ ++ uint32_t default_display_engine_pll_frequency; /* in KHz */ ++ uint32_t external_clock_source_frequency_for_dp; /* in KHz */ ++ uint32_t smu_gpu_pll_output_freq; /* in KHz */ ++ uint8_t min_allowed_bl_level; ++ uint8_t remote_display_config; ++}; ++ ++/* direct HW (mmBIOS_SCRATCH_2) translation! */ ++union tv_standard_support { ++ uint8_t u_all; ++ struct { ++ bool TV_SUPPORT_NTSC:1; ++ bool TV_SUPPORT_NTSCJ:1; ++ ++ bool TV_SUPPORT_PAL:1; ++ bool TV_SUPPORT_PALM:1; ++ bool TV_SUPPORT_PALCN:1; ++ bool TV_SUPPORT_PALN:1; ++ bool TV_SUPPORT_PAL60:1; ++ ++ bool TV_SUPPORT_SECAM:1; ++ } bits; ++}; ++ ++struct analog_tv_info { ++ union tv_standard_support tv_suppported; ++ union tv_standard_support tv_boot_up_default; ++}; ++ ++struct spread_spectrum_info { ++ struct spread_spectrum_type { ++ bool CENTER_MODE:1; ++ bool EXTERNAL:1; ++ bool STEP_AND_DELAY_INFO:1; ++ } type; ++ ++ /* in unit of 0.01% (spreadPercentageDivider = 100), ++ otherwise in 0.001% units (spreadPercentageDivider = 1000); */ ++ uint32_t spread_spectrum_percentage; ++ uint32_t spread_percentage_divider; /* 100 or 1000 */ ++ uint32_t spread_spectrum_range; /* modulation freq (HZ)*/ ++ ++ union { ++ struct step_and_delay_info { ++ uint32_t step; ++ uint32_t delay; ++ uint32_t recommended_ref_div; ++ } step_and_delay_info; ++ /* For mem/engine/uvd, Clock Out frequence (VCO ), ++ in unit of kHz. For TMDS/HDMI/LVDS, it is pixel clock, ++ for DP, it is link clock ( 270000 or 162000 ) */ ++ uint32_t target_clock_range; /* in KHz */ ++ }; ++ ++}; ++ ++struct graphics_object_encoder_cap_info { ++ uint32_t dp_hbr2_cap:1; ++ uint32_t dp_hbr2_validated:1; ++ uint32_t reserved:15; ++}; ++ ++struct din_connector_info { ++ uint32_t gpio_id; ++ bool gpio_tv_active_state; ++}; ++ ++/* Invalid channel mapping */ ++enum { INVALID_DDI_CHANNEL_MAPPING = 0x0 }; ++ ++/** ++ * DDI PHY channel mapping reflecting XBAR setting ++ */ ++union ddi_channel_mapping { ++ struct mapping { ++ uint8_t lane0:2; /* Mapping for lane 0 */ ++ uint8_t lane1:2; /* Mapping for lane 1 */ ++ uint8_t lane2:2; /* Mapping for lane 2 */ ++ uint8_t lane3:2; /* Mapping for lane 3 */ ++ } mapping; ++ uint8_t raw; ++}; ++ ++/** ++* Transmitter output configuration description ++*/ ++struct transmitter_configuration_info { ++ /* DDI PHY ID for the transmitter */ ++ enum transmitter transmitter_phy_id; ++ /* DDI PHY channel mapping reflecting crossbar setting */ ++ union ddi_channel_mapping output_channel_mapping; ++}; ++ ++struct transmitter_configuration { ++ /* Configuration for the primary transmitter */ ++ struct transmitter_configuration_info primary_transmitter_config; ++ /* Secondary transmitter configuration for Dual-link DVI */ ++ struct transmitter_configuration_info secondary_transmitter_config; ++}; ++ ++ ++/* These size should be sufficient to store info coming from BIOS */ ++#define NUMBER_OF_UCHAR_FOR_GUID 16 ++#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 ++#define NUMBER_OF_CSR_M3_ARB 10 ++#define NUMBER_OF_DISP_CLK_VOLTAGE 4 ++#define NUMBER_OF_AVAILABLE_SCLK 5 ++ ++/* V6 */ ++struct integrated_info { ++ struct clock_voltage_caps { ++ /* The Voltage Index indicated by FUSE, same voltage index ++ shared with SCLK DPM fuse table */ ++ uint32_t voltage_index; ++ /* Maximum clock supported with specified voltage index */ ++ uint32_t max_supported_clk; /* in KHz */ ++ } disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE]; ++ ++ struct display_connection_info { ++ struct external_display_path { ++ /* A bit vector to show what devices are supported */ ++ uint32_t device_tag; ++ /* 16bit device ACPI id. */ ++ uint32_t device_acpi_enum; ++ /* A physical connector for displays to plug in, ++ using object connector definitions */ ++ struct graphics_object_id device_connector_id; ++ /* An index into external AUX/DDC channel LUT */ ++ uint8_t ext_aux_ddc_lut_index; ++ /* An index into external HPD pin LUT */ ++ uint8_t ext_hpd_pin_lut_index; ++ /* external encoder object id */ ++ struct graphics_object_id ext_encoder_obj_id; ++ /* XBAR mapping of the PHY channels */ ++ union ddi_channel_mapping channel_mapping; ++ } path[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; ++ ++ uint8_t gu_id[NUMBER_OF_UCHAR_FOR_GUID]; ++ uint8_t checksum; ++ } ext_disp_conn_info; /* exiting long long time */ ++ ++ struct available_s_clk_list { ++ /* Maximum clock supported with specified voltage index */ ++ uint32_t supported_s_clk; /* in KHz */ ++ /* The Voltage Index indicated by FUSE for specified SCLK */ ++ uint32_t voltage_index; ++ /* The Voltage ID indicated by FUSE for specified SCLK */ ++ uint32_t voltage_id; ++ } avail_s_clk[NUMBER_OF_AVAILABLE_SCLK]; ++ ++ uint8_t memory_type; ++ uint8_t ma_channel_number; ++ uint32_t boot_up_engine_clock; /* in KHz */ ++ uint32_t dentist_vco_freq; /* in KHz */ ++ uint32_t boot_up_uma_clock; /* in KHz */ ++ uint32_t boot_up_req_display_vector; ++ uint32_t other_display_misc; ++ uint32_t gpu_cap_info; ++ uint32_t sb_mmio_base_addr; ++ uint32_t system_config; ++ uint32_t cpu_cap_info; ++ uint32_t max_nb_voltage; ++ uint32_t min_nb_voltage; ++ uint32_t boot_up_nb_voltage; ++ uint32_t ext_disp_conn_info_offset; ++ uint32_t csr_m3_arb_cntl_default[NUMBER_OF_CSR_M3_ARB]; ++ uint32_t csr_m3_arb_cntl_uvd[NUMBER_OF_CSR_M3_ARB]; ++ uint32_t csr_m3_arb_cntl_fs3d[NUMBER_OF_CSR_M3_ARB]; ++ uint32_t gmc_restore_reset_time; ++ uint32_t minimum_n_clk; ++ uint32_t idle_n_clk; ++ uint32_t ddr_dll_power_up_time; ++ uint32_t ddr_pll_power_up_time; ++ /* start for V6 */ ++ uint32_t pcie_clk_ss_type; ++ uint32_t lvds_ss_percentage; ++ uint32_t lvds_sspread_rate_in_10hz; ++ uint32_t hdmi_ss_percentage; ++ uint32_t hdmi_sspread_rate_in_10hz; ++ uint32_t dvi_ss_percentage; ++ uint32_t dvi_sspread_rate_in_10_hz; ++ uint32_t sclk_dpm_boost_margin; ++ uint32_t sclk_dpm_throttle_margin; ++ uint32_t sclk_dpm_tdp_limit_pg; ++ uint32_t sclk_dpm_tdp_limit_boost; ++ uint32_t boost_engine_clock; ++ uint32_t boost_vid_2bit; ++ uint32_t enable_boost; ++ uint32_t gnb_tdp_limit; ++ /* Start from V7 */ ++ uint32_t max_lvds_pclk_freq_in_single_link; ++ uint32_t lvds_misc; ++ uint32_t lvds_pwr_on_seq_dig_on_to_de_in_4ms; ++ uint32_t lvds_pwr_on_seq_de_to_vary_bl_in_4ms; ++ uint32_t lvds_pwr_off_seq_vary_bl_to_de_in4ms; ++ uint32_t lvds_pwr_off_seq_de_to_dig_on_in4ms; ++ uint32_t lvds_off_to_on_delay_in_4ms; ++ uint32_t lvds_pwr_on_seq_vary_bl_to_blon_in_4ms; ++ uint32_t lvds_pwr_off_seq_blon_to_vary_bl_in_4ms; ++ uint32_t lvds_reserved1; ++ uint32_t lvds_bit_depth_control_val; ++}; ++ ++/** ++* Power source ids. ++*/ ++enum power_source { ++ POWER_SOURCE_AC = 0, ++ POWER_SOURCE_DC, ++ POWER_SOURCE_LIMITED_POWER, ++ POWER_SOURCE_LIMITED_POWER_2, ++ POWER_SOURCE_MAX ++}; ++ ++struct bios_event_info { ++ uint32_t thermal_state; ++ uint32_t backlight_level; ++ enum power_source powerSource; ++ bool has_thermal_state_changed; ++ bool has_power_source_changed; ++ bool has_forced_mode_changed; ++ bool forced_mode; ++ bool backlight_changed; ++}; ++ ++union stereo_3d_support { ++ struct { ++ /* HW can alter left and right image sequentially */ ++ uint32_t FRAME_ALTERNATE:1; ++ /* Frame Alternate + HW can integrate stereosync ++ signal into TMDS stream */ ++ uint32_t DVI_FRAME_ALT:1; ++ /* Frame Alternate + HW can integrate stereosync ++ signal into DP stream */ ++ uint32_t DISPLAY_PORT_FRAME_ALT:1; ++ /* Frame Alternate + HW can drive stereosync signal ++ on separate line */ ++ uint32_t SIDEBAND_FRAME_ALT:1; ++ /* SW allowed to pack left and right image into single frame. ++ Used for HDMI only, DP has it's own flags. */ ++ uint32_t SW_FRAME_PACK:1; ++ /* HW can pack left and right image into single HDMI frame */ ++ uint32_t PROGRESSIVE_FRAME_PACK:1; ++ /* HW can pack left and right interlaced images into ++ single HDMI frame */ ++ uint32_t INTERLACE_FRAME_PACK:1; ++ /* HW can pack left and right images into single DP frame */ ++ uint32_t DISPLAY_PORT_FRAME_PACK:1; ++ /* SW can pack left and right images into single DP frame */ ++ uint32_t DISPLAY_PORT_SW_FRAME_PACK:1; ++ /* HW can mix left and right images into single image */ ++ uint32_t INTERLEAVE:1; ++ /* HW can mix left and right interlaced images ++ into single image */ ++ uint32_t INTERLACE_INTERLEAVE:1; ++ /* Allow display-based formats (whatever supported) ++ in WS stereo mode */ ++ uint32_t DISPLAY_3DIN_WS_MODE:1; ++ /* Side-by-side, packed by application/driver into 2D frame */ ++ uint32_t SIDE_BY_SIDE_SW_PACKED:1; ++ /* Top-and-bottom, packed by application/driver into 2D frame */ ++ uint32_t TOP_AND_BOTTOM_SW_PACKED:1; ++ } bits; ++ uint32_t u_all; ++}; ++ ++/* Bitvector and bitfields of possible optimizations ++ #IMPORTANT# Keep bitfields match bitvector! */ ++enum optimization_feature { ++ /* Don't do HW programming on panels that were enabled by VBIOS */ ++ OF_SKIP_HW_PROGRAMMING_ON_ENABLED_EMBEDDED_DISPLAY = 0x1, ++ OF_SKIP_RESET_OF_ALL_HW_ON_S3RESUME = 0x2, ++ OF_SKIP_HW_RESET_OF_EMBEDDED_DISPLAY_ON_S3RESUME = 0x4, ++ OF_SKIP_POWER_UP_VBIOS_ENABLED_ENCODER = 0x8, ++ /* Do not turn off VCC while powering down on boot or resume */ ++ OF_KEEP_VCC_DURING_POWER_DOWN_ON_BOOT_OR_RESUME = 0x10, ++ /* Do not turn off VCC while performing SetMode */ ++ OF_KEEP_VCC_DURING_SET_MODE = 0x20, ++ OF_DO_NOT_WAIT_FOR_HPD_LOW = 0x40, ++ OF_SKIP_POWER_DOWN_INACTIVE_ENCODER = 0x80 ++}; ++ ++union optimization_flags { ++ struct { ++ /* Don't do HW programming if panels were enabled by VBIOS */ ++ uint32_t SKIP_HW_PROGRAMMING_ON_ENABLED_EMBEDDED_DISPLAY:1; ++ uint32_t SKIP_RESET_OF_ALL_HW_ON_S3RESUME:1; ++ uint32_t SKIP_HW_RESET_OF_EMBEDDED_DISPLAY_ON_S3RESUME:1; ++ uint32_t SKIP_POWER_UP_VBIOS_ENABLED_ENCODER:1; ++ /* Do not turn off VCC while powering down on boot or resume */ ++ uint32_t KEEP_VCC_DURING_POWER_DOWN_ON_BOOT_OR_RESUME:1; ++ /* Do not turn off VCC while performing SetMode */ ++ uint32_t KEEP_VCC_DURING_SET_MODE:1; ++ uint32_t DO_NOT_WAIT_FOR_HPD_LOW:1; ++ } bits; ++ uint32_t u_all; ++}; ++ ++/* Bitvector and bitfields of performance measurements ++ #IMPORTANT# Keep bitfields match bitvector! */ ++enum perf_measure { ++ PERF_MEASURE_ADAPTER_POWER_STATE = 0x1, ++ PERF_MEASURE_DISPLAY_POWER_STATE = 0x2, ++ PERF_MEASURE_SET_MODE_SEQ = 0x4, ++ PERF_MEASURE_DETECT_AT_RESUME = 0x8, ++ PERF_MEASURE_MEMORY_READ_CONTROL = 0x10, ++}; ++ ++union perf_measure_flags { ++ struct { ++ uint32_t ADAPTER_POWER_STATE:1; ++ uint32_t DISPLAY_POWER_STATE:1; ++ uint32_t SET_MODE_SEQ:1; ++ uint32_t DETECT_AT_RESUME:1; ++ uint32_t MEMORY_READ_CONTROL:1; ++ ++ } bits; ++ uint32_t u_all; ++}; ++ ++enum { ++ PERF_MEASURE_POWERCODE_OFFSET = 0x0, ++ PERF_MEASURE_POWER_CODE_MASK = 0xFF, ++ PERF_MEASURE_POWER_STATE_OFFSET = 8, ++ PERF_MEASURE_POWER_STATE_MASK = 0x000FF00, ++ PERF_MEASURE_PREV_POWER_STATE_OFFSET = 16, ++ PERF_MEASURE_PREV_POWER_STATE_MASK = 0x00FF0000, ++ PERF_MEASURE_DISPLAY_INDEX_OFFSET = 24, ++ PERF_MEASURE_DISPLAY_INDEX_MASK = 0xFF000000 ++}; ++ ++enum { ++ HDMI_PIXEL_CLOCK_IN_KHZ_297 = 297000, ++ TMDS_PIXEL_CLOCK_IN_KHZ_165 = 165000 ++}; ++ ++/* ++ * DFS-bypass flag ++ */ ++/* Copy of SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS from atombios.h */ ++enum { ++ DFS_BYPASS_ENABLE = 0x10 ++}; ++ ++enum { ++ INVALID_BACKLIGHT = -1 ++}; ++ ++struct panel_backlight_boundaries { ++ uint32_t min_signal_level; ++ uint32_t max_signal_level; ++}; ++ ++struct panel_backlight_default_levels { ++ uint32_t ac_level_percentage; ++ uint32_t dc_level_percentage; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/grph_object_defs.h b/drivers/gpu/drm/amd/dal/include/grph_object_defs.h +new file mode 100644 +index 0000000..a1e468f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/grph_object_defs.h +@@ -0,0 +1,328 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GRPH_OBJECT_DEFS_H__ ++#define __DAL_GRPH_OBJECT_DEFS_H__ ++ ++#include "grph_object_id.h" ++ ++/* ******************************************************************** ++ * ******************************************************************** ++ * ++ * These defines shared between All Graphics Objects ++ * ++ * ******************************************************************** ++ * ******************************************************************** ++ */ ++ ++/* HPD unit id - HW direct translation */ ++enum hpd_source_id { ++ HPD_SOURCEID1 = 0, ++ HPD_SOURCEID2, ++ HPD_SOURCEID3, ++ HPD_SOURCEID4, ++ HPD_SOURCEID5, ++ HPD_SOURCEID6, ++ ++ HPD_SOURCEID_COUNT, ++ HPD_SOURCEID_UNKNOWN ++}; ++ ++/* DDC unit id - HW direct translation */ ++enum channel_id { ++ CHANNEL_ID_UNKNOWN = 0, ++ CHANNEL_ID_DDC1, ++ CHANNEL_ID_DDC2, ++ CHANNEL_ID_DDC3, ++ CHANNEL_ID_DDC4, ++ CHANNEL_ID_DDC5, ++ CHANNEL_ID_DDC6, ++ CHANNEL_ID_DDC_VGA, ++ CHANNEL_ID_I2C_PAD, ++ CHANNEL_ID_COUNT ++}; ++ ++#define DECODE_CHANNEL_ID(ch_id) \ ++ (ch_id) == CHANNEL_ID_DDC1 ? "CHANNEL_ID_DDC1" : \ ++ (ch_id) == CHANNEL_ID_DDC2 ? "CHANNEL_ID_DDC2" : \ ++ (ch_id) == CHANNEL_ID_DDC3 ? "CHANNEL_ID_DDC3" : \ ++ (ch_id) == CHANNEL_ID_DDC4 ? "CHANNEL_ID_DDC4" : \ ++ (ch_id) == CHANNEL_ID_DDC5 ? "CHANNEL_ID_DDC5" : \ ++ (ch_id) == CHANNEL_ID_DDC6 ? "CHANNEL_ID_DDC6" : \ ++ (ch_id) == CHANNEL_ID_DDC_VGA ? "CHANNEL_ID_DDC_VGA" : \ ++ (ch_id) == CHANNEL_ID_I2C_PAD ? "CHANNEL_ID_I2C_PAD" : "Invalid" ++ ++enum transmitter { ++ TRANSMITTER_UNKNOWN = (-1L), ++ TRANSMITTER_UNIPHY_A, ++ TRANSMITTER_UNIPHY_B, ++ TRANSMITTER_UNIPHY_C, ++ TRANSMITTER_UNIPHY_D, ++ TRANSMITTER_UNIPHY_E, ++ TRANSMITTER_UNIPHY_F, ++ TRANSMITTER_NUTMEG_CRT, ++ TRANSMITTER_TRAVIS_CRT, ++ TRANSMITTER_TRAVIS_LCD, ++ TRANSMITTER_UNIPHY_G, ++ TRANSMITTER_COUNT ++}; ++ ++enum physical_phy_id { ++ PHY_ID_UNKNOWN = (-1L), ++ PHY_ID_0, ++ PHY_ID_1, ++ PHY_ID_2, ++ PHY_ID_3, ++ PHY_ID_4, ++ PHY_ID_5, ++ PHY_ID_6, ++ PHY_ID_7, ++ PHY_ID_8, ++ PHY_ID_9, ++ PHY_ID_COUNT ++}; ++ ++/* Generic source of the synchronisation input/output signal */ ++/* Can be used for flow control, stereo sync, timing sync, frame sync, etc */ ++enum sync_source { ++ SYNC_SOURCE_NONE = 0, ++ ++ /* Source based on controllers */ ++ SYNC_SOURCE_CONTROLLER0, ++ SYNC_SOURCE_CONTROLLER1, ++ SYNC_SOURCE_CONTROLLER2, ++ SYNC_SOURCE_CONTROLLER3, ++ SYNC_SOURCE_CONTROLLER4, ++ SYNC_SOURCE_CONTROLLER5, ++ ++ /* Source based on GSL group */ ++ SYNC_SOURCE_GSL_GROUP0, ++ SYNC_SOURCE_GSL_GROUP1, ++ SYNC_SOURCE_GSL_GROUP2, ++ ++ /* Source based on GSL IOs */ ++ /* These IOs normally used as GSL input/output */ ++ SYNC_SOURCE_GSL_IO_FIRST, ++ SYNC_SOURCE_GSL_IO_GENLOCK_CLOCK = SYNC_SOURCE_GSL_IO_FIRST, ++ SYNC_SOURCE_GSL_IO_GENLOCK_VSYNC, ++ SYNC_SOURCE_GSL_IO_SWAPLOCK_A, ++ SYNC_SOURCE_GSL_IO_SWAPLOCK_B, ++ SYNC_SOURCE_GSL_IO_LAST = SYNC_SOURCE_GSL_IO_SWAPLOCK_B, ++ ++ /* Source based on regular IOs */ ++ SYNC_SOURCE_IO_FIRST, ++ SYNC_SOURCE_IO_GENERIC_A = SYNC_SOURCE_IO_FIRST, ++ SYNC_SOURCE_IO_GENERIC_B, ++ SYNC_SOURCE_IO_GENERIC_C, ++ SYNC_SOURCE_IO_GENERIC_D, ++ SYNC_SOURCE_IO_GENERIC_E, ++ SYNC_SOURCE_IO_GENERIC_F, ++ SYNC_SOURCE_IO_HPD1, ++ SYNC_SOURCE_IO_HPD2, ++ SYNC_SOURCE_IO_HSYNC_A, ++ SYNC_SOURCE_IO_VSYNC_A, ++ SYNC_SOURCE_IO_HSYNC_B, ++ SYNC_SOURCE_IO_VSYNC_B, ++ SYNC_SOURCE_IO_LAST = SYNC_SOURCE_IO_VSYNC_B, ++ ++ /* Misc. flow control sources */ ++ SYNC_SOURCE_DUAL_GPU_PIN ++}; ++ ++enum trigger_edge { ++ TRIGGER_EDGE_RISING = 0, ++ TRIGGER_EDGE_FALLING, ++ TRIGGER_EDGE_BOTH, ++ TRIGGER_EDGE_DEFAULT ++}; ++ ++/* Parameters to enable CRTC trigger */ ++struct trigger_params { ++ enum sync_source source; ++ enum trigger_edge edge; ++}; ++ ++/* CRTC Static Screen event triggers */ ++struct static_screen_events { ++ union { ++ /* event mask to enable/disable various ++ trigger sources for static screen detection */ ++ struct { ++ /* Force event high */ ++ uint32_t FRAME_START:1; ++ /* Cursor register change */ ++ uint32_t CURSOR_MOVE:1; ++ /* Memory write to any client other than MCIF */ ++ uint32_t MEM_WRITE:1; ++ /* Memory write to hit memory region 0 */ ++ uint32_t MEM_REGION0_WRITE:1; ++ /* Memory write to hit memory region 1 */ ++ uint32_t MEM_REGION1_WRITE:1; ++ /* Memory write to hit memory region 2 */ ++ uint32_t MEM_REGION2_WRITE:1; ++ /* Memory write to hit memory region 3 */ ++ uint32_t MEM_REGION3_WRITE:1; ++ /* Graphics Surface Update Pending */ ++ uint32_t GFX_UPDATE:1; ++ /* Overlay Surface Update Pending */ ++ uint32_t OVL_UPDATE:1; ++ /* Compressed surface invalidated in FBC */ ++ uint32_t INVALIDATE_FBC_SURFACE:1; ++ /* Register pending update in any double buffered ++ register group in the display pipe ++ (i.e. Blender, DCP, or SCL) */ ++ uint32_t REG_PENDING_UPDATE:1; ++ /* Crtc_trig_a: Based on signal from any other CRTC */ ++ uint32_t CRTC_TRIG_A:1; ++ /* Crtc_trig_b: Based on signal from any other CRTC */ ++ uint32_t CRTC_TRIG_B:1; ++ /* Readback of CRTC nominal vertical count register ++ by driver indicates that OS may be trying to change ++ mode or contents of the display therefore need to ++ switch to higher refresh rate */ ++ uint32_t READBACK_NOMINAL_VERTICAL:1; ++ /* Readback of CRTC dynamic vertical count register ++ by driver indicates that OS may be trying to change ++ mode or contents of the display therefore need to ++ switch to higher refresh rate */ ++ uint32_t READBACK_DYNAMIC_VERTICAL:1; ++ /* Reserved */ ++ uint32_t RESERVED:1; ++ } bits; ++ uint32_t u_all; ++ }; ++}; ++ ++ ++/* ++ * *************************************************************** ++ * ********************* Register programming sequences ******** ++ * *************************************************************** ++ */ ++ ++/* GPIO/Register access sequences */ ++enum io_register_sequence { ++ /* GLSync sequences to access SwapReady & SwapRequest ++ GPIOs - GLSync Connector parameter */ ++ IO_REG_SEQUENCE_SWAPREADY_SET = 0, ++ IO_REG_SEQUENCE_SWAPREADY_RESET, ++ IO_REG_SEQUENCE_SWAPREADY_READ, ++ IO_REG_SEQUENCE_SWAPREQUEST_SET, ++ IO_REG_SEQUENCE_SWAPREQUEST_RESET, ++ IO_REG_SEQUENCE_SWAPREQUEST_READ, ++ ++ /* Frame synchronization start/stop - display index parameter */ ++ IO_REG_SEQUENCE_FRAMELOCK_STOP, ++ IO_REG_SEQUENCE_FRAMELOCK_START, ++ ++ /* Flip lock/unlock - GLSync Connector parameter */ ++ IO_REG_SEQUENCE_GLOBALSWAP_LOCK, ++ IO_REG_SEQUENCE_GLOBALSWAP_UNLOCK, ++ ++ IO_REG_SEQUENCEENUM_MAX ++}; ++ ++#define IO_REGISTER_SEQUENCE_MAX_LENGTH 5 ++ ++/* ++ ***************************************************************************** ++ * struct io_register ++ ***************************************************************************** ++ * Generic struct for read/write register or GPIO. ++ * It allows controlling only some bit section of register, rather then the ++ * whole one. ++ * For write operation should be used as following: ++ * 1. data = READ(Base + RegisterOffset) ++ * 2. data &= ANDMask ++ * 3. data |= ORMask ++ * 4. WRITE(Base + RegisterOffset, data) ++ * ++ * Note: In case of regular register, ANDMask will be typically 0. ++ * In case of GPIO, ANDMask will have typically all bits set ++ * except the specific GPIO bit. ++ * ++ * For read operation should be used as following: ++ * 1. data = READ(Base + RegisterOffset) ++ * 2. data &= ANDMask ++ * 3. data >>= BitShift ++ * ++ * Note: In case of regular register, ANDMask will be typically 0xFFFFFFFF. ++ * In case of GPIO, ANDMask will have typically only specific GPIO bit set ++ * ++ * Note: Base Address is not exposed in this structure due to ++ * security consideration. ++ */ ++ ++/* ++ * The generic sequence to program/access registers or GPIOs. ++ * There could be 2 types of sequences - read and write. ++ * Read sequence may have 0 or more writes and in the end one read ++ * Write sequence may have 1 or more writes. ++ */ ++struct io_reg_sequence { ++ /* Ordered array of register to program */ ++ struct { ++ /* Offset of memory mapped register or GPIO */ ++ uint32_t register_offset; ++ /* Mask to use at AND operation (Mandatory, comes ++ before OR operation) */ ++ uint32_t and_mask; ++ union { ++ /* Mask to use at OR operation (For write ++ sequence only, comes after AND operation) */ ++ uint32_t or_mask; ++ /* Number of bits to shift to get the actual value ++ (For read sequence only, comes after AND operation) */ ++ uint32_t bit_shift; ++ }; ++ } io_registers[IO_REGISTER_SEQUENCE_MAX_LENGTH]; ++ ++ uint32_t steps_num; /* Total number of r/w steps in the sequence */ ++}; ++ ++/* Sequence ID - uniqly defines sequence on single adapter */ ++struct io_reg_sequence_id { ++ enum io_register_sequence sequence; /* Sequence enumeration Index/ID */ ++ union { ++ /* Refers to object to which the sequence applies.*/ ++ uint32_t index; ++ uint32_t display_index; ++ uint32_t controller_index; ++ uint32_t glsync_connector_index; ++ }; ++}; ++ ++struct fbc_info { ++ bool fbc_enable; ++ bool lpt_enable; ++}; ++ ++/* Event to request TM change IRQ registration state */ ++struct hotplug_irq_data { ++ bool disable; ++ struct graphics_object_id connector; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/grph_object_id.h b/drivers/gpu/drm/amd/dal/include/grph_object_id.h +new file mode 100644 +index 0000000..fcf3eea +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/grph_object_id.h +@@ -0,0 +1,285 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_GRPH_OBJECT_ID_H__ ++#define __DAL_GRPH_OBJECT_ID_H__ ++ ++/* Types of graphics objects */ ++enum object_type { ++ OBJECT_TYPE_UNKNOWN = 0, ++ ++ /* Direct ATOM BIOS translation */ ++ OBJECT_TYPE_GPU, ++ OBJECT_TYPE_ENCODER, ++ OBJECT_TYPE_CONNECTOR, ++ OBJECT_TYPE_ROUTER, ++ OBJECT_TYPE_GENERIC, ++ ++ /* Driver specific */ ++ OBJECT_TYPE_AUDIO, ++ OBJECT_TYPE_CONTROLLER, ++ OBJECT_TYPE_CLOCK_SOURCE, ++ OBJECT_TYPE_ENGINE, ++ ++ OBJECT_TYPE_COUNT ++}; ++ ++/* Enumeration inside one type of graphics objects */ ++enum object_enum_id { ++ ENUM_ID_UNKNOWN = 0, ++ ENUM_ID_1, ++ ENUM_ID_2, ++ ENUM_ID_3, ++ ENUM_ID_4, ++ ENUM_ID_5, ++ ENUM_ID_6, ++ ENUM_ID_7, ++ ++ ENUM_ID_COUNT ++}; ++ ++/* Generic object ids */ ++enum generic_id { ++ GENERIC_ID_UNKNOWN = 0, ++ GENERIC_ID_MXM_OPM, ++ GENERIC_ID_GLSYNC, ++ GENERIC_ID_STEREO, ++ ++ GENERIC_ID_COUNT ++}; ++ ++/* Controller object ids */ ++enum controller_id { ++ CONTROLLER_ID_UNDEFINED = 0, ++ CONTROLLER_ID_D0, ++ CONTROLLER_ID_D1, ++ CONTROLLER_ID_D2, ++ CONTROLLER_ID_D3, ++ CONTROLLER_ID_D4, ++ CONTROLLER_ID_D5, ++ CONTROLLER_ID_UNDERLAY0, ++ CONTROLLER_ID_MAX = CONTROLLER_ID_UNDERLAY0 ++}; ++ ++#define IS_UNDERLAY_CONTROLLER(ctrlr_id) (ctrlr_id >= CONTROLLER_ID_UNDERLAY0) ++ ++/* ++ * ClockSource object ids. ++ * We maintain the order matching (more or less) ATOM BIOS ++ * to improve optimized acquire ++ */ ++enum clock_source_id { ++ CLOCK_SOURCE_ID_UNDEFINED = 0, ++ CLOCK_SOURCE_ID_PLL0, ++ CLOCK_SOURCE_ID_PLL1, ++ CLOCK_SOURCE_ID_PLL2, ++ CLOCK_SOURCE_ID_EXTERNAL, /* ID (Phy) ref. clk. for DP */ ++ CLOCK_SOURCE_ID_DCPLL, ++ CLOCK_SOURCE_ID_DFS, /* DENTIST */ ++ CLOCK_SOURCE_ID_VCE, /* VCE does not need a real PLL */ ++ CLOCK_SOURCE_ID_DP_DTO, /* Used to distinguish between */ ++ /* programming pixel clock */ ++ /* and ID (Phy) clock */ ++}; ++ ++ ++/* Encoder object ids */ ++enum encoder_id { ++ ENCODER_ID_UNKNOWN = 0, ++ ++ /* Radeon Class Display Hardware */ ++ ENCODER_ID_INTERNAL_LVDS, ++ ENCODER_ID_INTERNAL_TMDS1, ++ ENCODER_ID_INTERNAL_TMDS2, ++ ENCODER_ID_INTERNAL_DAC1, ++ ENCODER_ID_INTERNAL_DAC2, /* TV/CV DAC */ ++ ENCODER_ID_INTERNAL_SDVOA, ++ ENCODER_ID_INTERNAL_SDVOB, ++ ++ /* External Third Party Encoders */ ++ ENCODER_ID_EXTERNAL_SI170B, ++ ENCODER_ID_EXTERNAL_CH7303, ++ ENCODER_ID_EXTERNAL_CH7301, /* 10 in decimal */ ++ ENCODER_ID_INTERNAL_DVO1, /* Belongs to Radeon Display Hardware */ ++ ENCODER_ID_EXTERNAL_SDVOA, ++ ENCODER_ID_EXTERNAL_SDVOB, ++ ENCODER_ID_EXTERNAL_TITFP513, ++ ENCODER_ID_INTERNAL_LVTM1, /* not used for Radeon */ ++ ENCODER_ID_EXTERNAL_VT1623, ++ ENCODER_ID_EXTERNAL_SI1930, /* HDMI */ ++ ENCODER_ID_INTERNAL_HDMI, ++ ++ /* Kaledisope (KLDSCP) Class Display Hardware */ ++ ENCODER_ID_INTERNAL_KLDSCP_TMDS1, ++ ENCODER_ID_INTERNAL_KLDSCP_DVO1, ++ ENCODER_ID_INTERNAL_KLDSCP_DAC1, ++ ENCODER_ID_INTERNAL_KLDSCP_DAC2, /* Shared with CV/TV and CRT */ ++ /* External TMDS (dual link) */ ++ ENCODER_ID_EXTERNAL_SI178, ++ ENCODER_ID_EXTERNAL_MVPU_FPGA, /* MVPU FPGA chip */ ++ ENCODER_ID_INTERNAL_DDI, ++ ENCODER_ID_EXTERNAL_VT1625, ++ ENCODER_ID_EXTERNAL_SI1932, ++ ENCODER_ID_EXTERNAL_AN9801, /* External Display Port */ ++ ENCODER_ID_EXTERNAL_DP501, /* External Display Port */ ++ ENCODER_ID_INTERNAL_UNIPHY, ++ ENCODER_ID_INTERNAL_KLDSCP_LVTMA, ++ ENCODER_ID_INTERNAL_UNIPHY1, ++ ENCODER_ID_INTERNAL_UNIPHY2, ++ ENCODER_ID_EXTERNAL_NUTMEG, ++ ENCODER_ID_EXTERNAL_TRAVIS, ++ ++ ENCODER_ID_INTERNAL_WIRELESS, /* Internal wireless display encoder */ ++ ENCODER_ID_INTERNAL_UNIPHY3, ++ ++ ENCODER_ID_EXTERNAL_GENERIC_DVO = 0xFF ++}; ++ ++ ++/* Connector object ids */ ++enum connector_id { ++ CONNECTOR_ID_UNKNOWN = 0, ++ CONNECTOR_ID_SINGLE_LINK_DVII, ++ CONNECTOR_ID_DUAL_LINK_DVII, ++ CONNECTOR_ID_SINGLE_LINK_DVID, ++ CONNECTOR_ID_DUAL_LINK_DVID, ++ CONNECTOR_ID_VGA, ++ CONNECTOR_ID_HDMI_TYPE_A, ++ CONNECTOR_ID_NOT_USED, ++ CONNECTOR_ID_LVDS, ++ CONNECTOR_ID_PCIE, ++ CONNECTOR_ID_HARDCODE_DVI, ++ CONNECTOR_ID_DISPLAY_PORT, ++ CONNECTOR_ID_EDP, ++ CONNECTOR_ID_MXM, ++ CONNECTOR_ID_WIRELESS, /* wireless display pseudo-connector */ ++ CONNECTOR_ID_MIRACAST, /* used for VCE encode display path ++ * for Miracast */ ++ ++ CONNECTOR_ID_COUNT ++}; ++ ++ ++/* Audio object ids */ ++enum audio_id { ++ AUDIO_ID_UNKNOWN = 0, ++ AUDIO_ID_INTERNAL_AZALIA ++}; ++ ++ ++/* Engine object ids */ ++enum engine_id { ++ ENGINE_ID_DIGA, ++ ENGINE_ID_DIGB, ++ ENGINE_ID_DIGC, ++ ENGINE_ID_DIGD, ++ ENGINE_ID_DIGE, ++ ENGINE_ID_DIGF, ++ ENGINE_ID_DIGG, ++ ENGINE_ID_DVO, ++ ENGINE_ID_DACA, ++ ENGINE_ID_DACB, ++ ENGINE_ID_VCE, /* wireless display pseudo-encoder */ ++ ++ ENGINE_ID_COUNT, ++ ENGINE_ID_UNKNOWN = (-1L) ++}; ++ ++union supported_stream_engines { ++ struct { ++ uint32_t ENGINE_ID_DIGA:1; ++ uint32_t ENGINE_ID_DIGB:1; ++ uint32_t ENGINE_ID_DIGC:1; ++ uint32_t ENGINE_ID_DIGD:1; ++ uint32_t ENGINE_ID_DIGE:1; ++ uint32_t ENGINE_ID_DIGF:1; ++ uint32_t ENGINE_ID_DIGG:1; ++ uint32_t ENGINE_ID_DVO:1; ++ uint32_t ENGINE_ID_DACA:1; ++ uint32_t ENGINE_ID_DACB:1; ++ uint32_t ENGINE_ID_VCE:1; ++ } engine; ++ uint32_t u_all; ++}; ++ ++ ++/* ++ ***************************************************************************** ++ * graphics_object_id struct ++ * ++ * graphics_object_id is a very simple struct wrapping 32bit Graphics ++ * Object identication ++ * ++ * This struct should stay very simple ++ * No dependencies at all (no includes) ++ * No debug messages or asserts ++ * No #ifndef and preprocessor directives ++ * No grow in space (no more data member) ++ ***************************************************************************** ++ */ ++ ++struct graphics_object_id { ++ uint32_t id:8; ++ uint32_t enum_id:4; ++ uint32_t type:4; ++ uint32_t reserved:16; /* for padding. total size should be u32 */ ++}; ++ ++/* some simple functions for convenient graphics_object_id handle */ ++ ++static inline struct graphics_object_id dal_graphics_object_id_init( ++ uint32_t id, ++ enum object_enum_id enum_id, ++ enum object_type type) ++{ ++ struct graphics_object_id result = { ++ id, enum_id, type, 0 ++ }; ++ ++ return result; ++} ++ ++bool dal_graphics_object_id_is_valid( ++ struct graphics_object_id id); ++bool dal_graphics_object_id_is_equal( ++ struct graphics_object_id id1, ++ struct graphics_object_id id2); ++uint32_t dal_graphics_object_id_to_uint( ++ struct graphics_object_id id); ++ ++ ++enum controller_id dal_graphics_object_id_get_controller_id( ++ struct graphics_object_id id); ++enum clock_source_id dal_graphics_object_id_get_clock_source_id( ++ struct graphics_object_id id); ++enum encoder_id dal_graphics_object_id_get_encoder_id( ++ struct graphics_object_id id); ++enum connector_id dal_graphics_object_id_get_connector_id( ++ struct graphics_object_id id); ++enum audio_id dal_graphics_object_id_get_audio_id( ++ struct graphics_object_id id); ++enum engine_id dal_graphics_object_id_get_engine_id( ++ struct graphics_object_id id); ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/hw_adjustment_set.h b/drivers/gpu/drm/amd/dal/include/hw_adjustment_set.h +new file mode 100644 +index 0000000..10fb8e2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/hw_adjustment_set.h +@@ -0,0 +1,50 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_ADJUSTMENT_SET_H__ ++#define __DAL_HW_ADJUSTMENT_SET_H__ ++ ++#include "include/hw_adjustment_types.h" ++ ++struct hw_adjustment_gamma_ramp; ++ ++struct hw_adjustment_set { ++ struct hw_adjustment_gamma_ramp *gamma_ramp; ++ struct hw_adjustment_deflicker *deflicker_filter; ++ struct hw_adjustment_value *coherent; ++ struct hw_adjustment_value *h_sync; ++ struct hw_adjustment_value *v_sync; ++ struct hw_adjustment_value *composite_sync; ++ struct hw_adjustment_value *backlight; ++ struct hw_adjustment_value *vb_level; ++ struct hw_adjustment_color_control *color_control; ++ union hw_adjustment_bit_depth_reduction *bit_depth; ++}; ++/* ++struct hw_adjustment *dal_adjustment_set_get_by_id( ++ struct hw_adjustment_set *adjustment_set, ++ enum hw_adjustment_id id);*/ ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/hw_adjustment_types.h b/drivers/gpu/drm/amd/dal/include/hw_adjustment_types.h +new file mode 100644 +index 0000000..cfae832 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/hw_adjustment_types.h +@@ -0,0 +1,205 @@ ++#ifndef __DAL_HW_ADJUSTMENT_TYPES_H__ ++#define __DAL_HW_ADJUSTMENT_TYPES_H__ ++ ++#include "hw_sequencer_types.h" ++ ++enum hw_adjustment_id { ++ HW_ADJUSTMENT_ID_COLOR_CONTROL, ++ HW_ADJUSTMENT_ID_GAMMA_LUT, ++ HW_ADJUSTMENT_ID_GAMMA_RAMP, ++ HW_ADJUSTMENT_ID_DEFLICKER, ++ HW_ADJUSTMENT_ID_SHARPNESS_CONTROL, ++ HW_ADJUSTMENT_ID_TIMING, ++ HW_ADJUSTMENT_ID_TIMING_AND_PIXEL_CLOCK, ++ HW_ADJUSTMENT_ID_OVERSCAN, ++ HW_ADJUSTMENT_ID_UNDERSCAN_TYPE, ++ HW_ADJUSTMENT_ID_VERTICAL_SYNC, ++ HW_ADJUSTMENT_ID_HORIZONTAL_SYNC, ++ HW_ADJUSTMENT_ID_COMPOSITE_SYNC, ++ HW_ADJUSTMENT_ID_VIDEO_STANDARD, ++ HW_ADJUSTMENT_ID_BACKLIGHT, ++ HW_ADJUSTMENT_ID_BIT_DEPTH_REDUCTION, ++ HW_ADJUSTMENT_ID_REDUCED_BLANKING, ++ HW_ADJUSTMENT_ID_COHERENT, ++ /* OVERLAY ADJUSTMENTS*/ ++ HW_ADJUSTMENT_ID_OVERLAY, ++ HW_ADJUSTMENT_ID_OVERLAY_ALPHA, ++ HW_ADJUSTMENT_ID_OVERLAY_VARIABLE_GAMMA, ++ HW_ADJUSTMENT_ID_COUNT, ++ HW_ADJUSTMENT_ID_UNDEFINED, ++}; ++ ++struct hw_adjustment_deflicker { ++ int32_t hp_factor; ++ uint32_t hp_divider; ++ int32_t lp_factor; ++ uint32_t lp_divider; ++ int32_t sharpness; ++ bool enable_sharpening; ++}; ++ ++struct hw_adjustment_value { ++ union { ++ uint32_t ui_value; ++ int32_t i_value; ++ }; ++}; ++ ++enum hw_color_adjust_option { ++ HWS_COLOR_MATRIX_HW_DEFAULT = 1, ++ HWS_COLOR_MATRIX_SW ++}; ++ ++enum { ++ HW_TEMPERATURE_MATRIX_SIZE = 9, ++ HW_TEMPERATURE_MATRIX_SIZE_WITH_OFFSET = 12 ++}; ++ ++struct hw_adjustment_color_control { ++ enum hw_color_space color_space; ++ enum hw_color_adjust_option option; ++ enum pixel_format surface_pixel_format; ++ enum dc_color_depth color_depth; ++ uint32_t lb_color_depth; ++ int32_t contrast; ++ int32_t saturation; ++ int32_t brightness; ++ int32_t hue; ++ uint32_t adjust_divider; ++ uint32_t temperature_divider; ++ uint32_t temperature_matrix[HW_TEMPERATURE_MATRIX_SIZE]; ++}; ++ ++struct hw_underscan_adjustment { ++ struct hw_adjustment_deflicker deflicker; ++ struct overscan_info hw_overscan; ++}; ++ ++struct hw_underscan_adjustment_data { ++ enum hw_adjustment_id hw_adj_id; ++ struct hw_underscan_adjustment hw_underscan_adj; ++}; ++ ++union hw_adjustment_bit_depth_reduction { ++ uint32_t raw; ++ struct { ++ uint32_t TRUNCATE_ENABLED:1; ++ uint32_t TRUNCATE_DEPTH:2; ++ uint32_t TRUNCATE_MODE:1; ++ uint32_t SPATIAL_DITHER_ENABLED:1; ++ uint32_t SPATIAL_DITHER_DEPTH:2; ++ uint32_t SPATIAL_DITHER_MODE:2; ++ uint32_t RGB_RANDOM:1; ++ uint32_t FRAME_RANDOM:1; ++ uint32_t HIGHPASS_RANDOM:1; ++ uint32_t FRAME_MODULATION_ENABLED:1; ++ uint32_t FRAME_MODULATION_DEPTH:2; ++ uint32_t TEMPORAL_LEVEL:1; ++ uint32_t FRC_25:2; ++ uint32_t FRC_50:2; ++ uint32_t FRC_75:2; ++ } bits; ++}; ++ ++struct hw_color_control_range { ++ struct hw_adjustment_range contrast; ++ struct hw_adjustment_range saturation; ++ struct hw_adjustment_range brightness; ++ struct hw_adjustment_range hue; ++ struct hw_adjustment_range temperature; ++}; ++ ++enum hw_surface_type { ++ HW_OVERLAY_SURFACE = 1, ++ HW_GRAPHIC_SURFACE ++}; ++ ++/* LUT type for GammaCorrection */ ++struct hw_gamma_lut { ++ uint32_t red; ++ uint32_t green; ++ uint32_t blue; ++}; ++ ++struct hw_devc_lut { ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++ uint8_t reserved; ++}; ++ ++struct hw_adjustment_gamma_lut { ++ struct hw_gamma_lut *pGammaLut; ++ uint32_t size_in_elements; ++ enum pixel_format surface_pixel_format; ++}; ++ ++ ++enum hw_gamma_ramp_type { ++ HW_GAMMA_RAMP_UNITIALIZED = 0, ++ HW_GAMMA_RAMP_DEFAULT, ++ HW_GAMMA_RAMP_RBG_256x3x16, ++ HW_GAMMA_RAMP_RBG_DXGI_1 ++}; ++ ++#define HW_GAMMA_RAMP_RBG_256 256 ++ ++struct hw_gamma_ramp_rgb256x3x16 { ++ unsigned short red[HW_GAMMA_RAMP_RBG_256]; ++ unsigned short green[HW_GAMMA_RAMP_RBG_256]; ++ unsigned short blue[HW_GAMMA_RAMP_RBG_256]; ++}; ++ ++union hw_gamma_flags { ++ uint32_t raw; ++ struct { ++ uint32_t gamma_ramp_array :1; ++ uint32_t graphics_degamma_srgb :1; ++ uint32_t overlay_degamma_srgb :1; ++ uint32_t apply_degamma :1; ++ uint32_t reserved :28; ++ } bits; ++}; ++ ++struct hw_regamma_coefficients { ++ int32_t gamma[3]; ++ int32_t a0[3]; ++ int32_t a1[3]; ++ int32_t a2[3]; ++ int32_t a3[3]; ++}; ++ ++struct hw_regamma_ramp { ++ /* Gamma ramp packed as RGB */ ++ unsigned short gamma[256 * 3]; ++}; ++ ++struct hw_regamma_lut { ++ union hw_gamma_flags flags; ++ union { ++ struct hw_regamma_ramp gamma; ++ struct hw_regamma_coefficients coeff; ++ }; ++}; ++ ++union hw_gamma_flag { ++ uint32_t uint; ++ struct { ++ uint32_t config_is_changed :1; ++ uint32_t regamma_update :1; ++ uint32_t gamma_update :1; ++ uint32_t reserved :29; ++ } bits; ++}; ++ ++struct hw_adjustment_gamma_ramp { ++ uint32_t size; ++ enum hw_gamma_ramp_type type; ++ enum pixel_format surface_pixel_format; ++ enum hw_color_space color_space; ++ struct hw_regamma_lut regamma; ++ union hw_gamma_flag flag; ++ struct hw_gamma_ramp_rgb256x3x16 gamma_ramp_rgb256x3x16; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/hw_path_mode_set_interface.h b/drivers/gpu/drm/amd/dal/include/hw_path_mode_set_interface.h +new file mode 100644 +index 0000000..28ac018 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/hw_path_mode_set_interface.h +@@ -0,0 +1,48 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_PATH_MODE_SET_INTERFACE_H__ ++#define __DAL_HW_PATH_MODE_SET_INTERFACE_H__ ++ ++struct hw_path_mode; ++struct hw_path_mode_set; ++ ++struct hw_path_mode_set *dal_hw_path_mode_set_create(void); ++ ++void dal_hw_path_mode_set_destroy(struct hw_path_mode_set **set); ++ ++bool dal_hw_path_mode_set_add( ++ struct hw_path_mode_set *set, ++ struct hw_path_mode *path_mode, ++ uint32_t *index); ++ ++struct hw_path_mode *dal_hw_path_mode_set_get_path_by_index( ++ const struct hw_path_mode_set *set, ++ uint32_t index); ++ ++uint32_t dal_hw_path_mode_set_get_paths_number( ++ const struct hw_path_mode_set *set); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/hw_sequencer_interface.h b/drivers/gpu/drm/amd/dal/include/hw_sequencer_interface.h +new file mode 100644 +index 0000000..ddd78d6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/hw_sequencer_interface.h +@@ -0,0 +1,388 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_SEQUENCER_INTERFACE_H__ ++#define __DAL_HW_SEQUENCER_INTERFACE_H__ ++ ++#include "hw_sequencer_types.h" ++#include "hw_adjustment_types.h" ++#include "include/display_clock_interface.h" ++#include "include/scaler_types.h" ++#include "include/grph_csc_types.h" ++#include "plane_types.h" ++ ++#include "adapter_service_interface.h" ++ ++enum hwss_result { ++ HWSS_RESULT_OK, ++ HWSS_RESULT_ERROR, ++ HWSS_RESULT_NO_BANDWIDTH, ++ HWSS_RESULT_OUT_OF_RANGE, ++ HWSS_RESULT_NOT_SUPPORTED, ++ HWSS_RESULT_UNKNOWN ++}; ++ ++struct hws_init_data { ++ struct adapter_service *as; ++ struct dal_context *dal_context; ++}; ++ ++/* TODO: below is three almost equal structures. ++ * We should decide what to do with them */ ++struct blank_stream_param { ++ struct display_path *display_path; ++ uint32_t link_idx; ++ struct hw_crtc_timing timing; ++ struct link_settings link_settings; ++}; ++ ++struct enable_stream_param { ++ struct display_path *display_path; ++ uint32_t link_idx; ++ struct hw_crtc_timing timing; ++ struct link_settings link_settings; ++ ++ const struct hw_path_mode *path_mode; ++}; ++ ++struct enable_link_param { ++ struct display_path *display_path; ++ uint32_t link_idx; ++ struct hw_crtc_timing timing; ++ struct link_settings link_settings; ++ ++ bool optimized_programming; ++ const struct hw_path_mode *path_mode; ++}; ++ ++struct validate_link_param { ++ const struct display_path *display_path; ++ uint32_t link_idx; ++ struct link_settings link_settings; ++}; ++ ++struct set_dp_phy_pattern_param { ++ struct display_path *display_path; ++ uint32_t link_idx; ++ enum dp_test_pattern test_pattern; ++ const uint8_t *custom_pattern; ++ uint32_t cust_pattern_size; ++}; ++ ++struct hw_global_objects; ++struct hw_sequencer; ++struct hw_adjustment; ++struct hw_path_mode_set; ++struct hw_path_mode; ++struct hwss_build_params; ++struct controller; ++ ++void dal_hw_sequencer_mute_audio_endpoint( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ bool mute); ++ ++void dal_hw_sequencer_enable_audio_endpoint( ++ struct hw_sequencer *hws, ++ struct link_settings *ls, ++ struct display_path *display_path, ++ bool enable); ++ ++enum hwss_result dal_hw_sequencer_reset_audio_device( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++enum hwss_result dal_hw_sequencer_validate_link( ++ struct hw_sequencer *hws, ++ const struct validate_link_param *param); ++ ++bool dal_hw_sequencer_is_supported_dp_training_pattern3( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ uint32_t link_idx); ++ ++enum hwss_result dal_hw_sequencer_set_dp_phy_pattern( ++ struct hw_sequencer *hws, ++ const struct set_dp_phy_pattern_param *param); ++ ++enum hwss_result dal_hw_sequencer_set_lane_settings( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ const struct link_training_settings *link_settings); ++ ++void dal_hw_sequencer_set_test_pattern( ++ struct hw_sequencer *hws, ++ struct hw_path_mode *path_mode, ++ enum dp_test_pattern test_pattern, ++ const struct link_training_settings *link_settings, ++ const uint8_t *custom_pattern, ++ uint8_t cust_pattern_size); ++ ++bool dal_hw_sequencer_has_audio_bandwidth_changed( ++ struct hw_sequencer *hws, ++ const struct hw_path_mode *old, ++ const struct hw_path_mode *new); ++ ++void dal_hw_sequencer_enable_azalia_audio_jack_presence( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++void dal_hw_sequencer_disable_azalia_audio_jack_presence( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++void dal_hw_sequencer_enable_memory_requests( ++ struct hw_sequencer *hws, ++ struct hw_path_mode *path_mode); ++ ++void dal_hw_sequencer_update_info_packets( ++ struct hw_sequencer *hws, ++ struct hw_path_mode *path_mode); ++ ++/* Static validation for a SINGLE path mode. ++ * Already "active" paths (if any) are NOT taken into account. */ ++enum hwss_result dal_hw_sequencer_validate_display_path_mode( ++ struct hw_sequencer *hws, ++ const struct hw_path_mode *path_mode); ++ ++/* Validation for a SET of path modes, including Video Memory Bandwidth ++ * validation. */ ++enum hwss_result dal_hw_sequencer_validate_display_hwpms( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *path_set); ++ ++struct hw_adjustment_gamma_ramp; ++ ++enum hwss_result dal_hw_sequencer_set_gamma_ramp_adjustment( ++ struct hw_sequencer *hws, ++ const struct display_path *display_path, ++ struct hw_adjustment_gamma_ramp *adjusment); ++ ++enum hwss_result dal_hw_sequencer_set_color_control_adjustment( ++ struct hw_sequencer *hws, ++ struct controller *crtc, ++ struct hw_adjustment_color_control *adjustment); ++ ++enum hwss_result dal_hw_sequencer_set_vertical_sync_adjustment( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ struct hw_adjustment_value *adjustment); ++ ++enum hwss_result dal_hw_sequencer_set_horizontal_sync_adjustment( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ struct hw_adjustment_value *adjustment); ++ ++enum hwss_result dal_hw_sequencer_set_composite_sync_adjustment( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ struct hw_adjustment_value *adjustment); ++ ++enum hwss_result dal_hw_sequencer_enable_sync_output( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++enum hwss_result dal_hw_sequencer_disable_sync_output( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++enum hwss_result dal_hw_sequencer_set_backlight_adjustment( ++ struct hw_sequencer *hws, ++ struct display_path *display_path, ++ struct hw_adjustment_value *adjustment); ++ ++void dal_hw_sequencer_disable_memory_requests( ++ struct hw_sequencer *hws, ++ const struct hw_path_mode *path_mode); ++ ++enum hwss_result dal_hw_sequencer_enable_link( ++ struct hw_sequencer *hws, ++ const struct enable_link_param *in); ++ ++void dal_hw_sequencer_disable_link( ++ struct hw_sequencer *hws, ++ const struct enable_link_param *in); ++ ++void dal_hw_sequencer_enable_stream( ++ struct hw_sequencer *hws, ++ const struct enable_stream_param *in); ++ ++void dal_hw_sequencer_disable_stream( ++ struct hw_sequencer *hws, ++ const struct enable_stream_param *in); ++ ++void dal_hw_sequencer_blank_stream( ++ struct hw_sequencer *hws, ++ const struct blank_stream_param *in); ++ ++void dal_hw_sequencer_unblank_stream( ++ struct hw_sequencer *hws, ++ const struct blank_stream_param *in); ++ ++enum hwss_result dal_hw_sequencer_set_clocks_and_clock_state( ++ struct hw_sequencer *hws, ++ struct hw_global_objects *g_obj, ++ const struct minimum_clocks_calculation_result *min_clk_in, ++ enum clocks_state required_clocks_state); ++ ++enum hwss_result dal_hw_sequencer_set_mode( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *path_set); ++ ++enum signal_type dal_hw_sequencer_detect_sink( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++enum signal_type dal_hw_sequencer_detect_load( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++bool dal_hw_sequencer_is_sink_present( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++void dal_hw_sequencer_psr_setup( ++ struct hw_sequencer *hws, ++ const struct hw_path_mode *path_mode, ++ const struct psr_caps *psr_caps); ++ ++void dal_hw_sequencer_psr_enable( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++void dal_hw_sequencer_psr_disable( ++ struct hw_sequencer *hws, ++ struct display_path *display_path); ++ ++void dal_hw_sequencer_program_drr( ++ struct hw_sequencer *hws, ++ const struct hw_path_mode *path_mode); ++ ++enum hwss_result dal_hw_sequencer_set_safe_displaymark( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *path_set); ++ ++enum hwss_result dal_hw_sequencer_set_displaymark( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *path_set); ++ ++void dal_hw_sequencer_destroy(struct hw_sequencer **hws); ++ ++struct hw_sequencer *dal_hw_sequencer_create( ++ struct hws_init_data *hws_init_data); ++ ++enum hwss_result dal_hw_sequencer_set_overscan_adj( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *set, ++ struct hw_underscan_adjustment_data *hw_underscan); ++ ++bool dal_hw_sequencer_enable_line_buffer_power_gating( ++ struct line_buffer *lb, ++ enum controller_id id, ++ enum pixel_type pixel_type, ++ uint32_t src_pixel_width, ++ uint32_t dst_pixel_width, ++ struct scaling_taps *taps, ++ enum lb_pixel_depth lb_depth, ++ uint32_t src_height, ++ uint32_t dst_height, ++ bool interlaced); ++ ++void dal_hw_sequencer_build_scaler_parameter( ++ const struct hw_path_mode *path_mode, ++ const struct scaling_taps *taps, ++ bool build_timing_required, ++ struct scaler_data *scaler_data); ++ ++void dal_hw_sequencer_update_info_frame( ++ const struct hw_path_mode *hw_path_mode); ++ ++enum hwss_result dal_hw_sequencer_set_bit_depth_reduction_adj( ++ struct hw_sequencer *hws, ++ struct display_path *disp_path, ++ union hw_adjustment_bit_depth_reduction *bit_depth); ++ ++bool dal_hw_sequencer_is_support_custom_gamut_adj( ++ struct hw_sequencer *hws, ++ struct display_path *disp_path, ++ enum hw_surface_type surface_type); ++ ++enum hwss_result dal_hw_sequencer_get_hw_color_adj_range( ++ struct hw_sequencer *hws, ++ struct display_path *disp_path, ++ struct hw_color_control_range *hw_color_range); ++ ++bool dal_hw_sequencer_is_support_custom_gamma_coefficients( ++ struct hw_sequencer *hws, ++ struct display_path *disp_path, ++ enum hw_surface_type surface_type); ++ ++enum hwss_result dal_hw_sequencer_build_csc_adjust( ++ struct hw_sequencer *hws, ++ struct hw_adjustment_color_control *color_control, ++ struct grph_csc_adjustment *adjust); ++ ++void dal_hw_sequencer_build_gamma_ramp_adj_params( ++ const struct hw_adjustment_gamma_ramp *adjusment, ++ struct gamma_parameters *gamma_param, ++ struct gamma_ramp *ramp); ++ ++void translate_from_hw_to_controller_regamma( ++ const struct hw_regamma_lut *hw_regamma, ++ struct regamma_lut *regamma); ++ ++void dal_hw_sequencer_enable_wireless_idle_detection( ++ struct hw_sequencer *hws, ++ bool enable); ++ ++/* Cursor interface */ ++enum hwss_result dal_hw_sequencer_set_cursor_position( ++ struct hw_sequencer *hws, ++ struct display_path *dp, ++ const struct dc_cursor_position *position); ++ ++enum hwss_result dal_hw_sequencer_set_cursor_attributes( ++ struct hw_sequencer *hws, ++ struct display_path *dp, ++ const struct dc_cursor_attributes *attributes); ++ ++/* Underlay/MPO interface */ ++enum hwss_result dal_hw_sequencer_set_plane_config( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *path_set, ++ uint32_t display_index); ++ ++bool dal_hw_sequencer_update_plane_address( ++ struct hw_sequencer *hws, ++ struct display_path *dp, ++ uint32_t num_planes, ++ struct plane_addr_flip_info *info); ++ ++void dal_hw_sequencer_prepare_to_release_planes( ++ struct hw_sequencer *hws, ++ struct hw_path_mode_set *path_set, ++ uint32_t display_index); ++ ++#endif /* __DAL_HW_SEQUENCER_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/hw_sequencer_types.h b/drivers/gpu/drm/amd/dal/include/hw_sequencer_types.h +new file mode 100644 +index 0000000..d5d7059 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/hw_sequencer_types.h +@@ -0,0 +1,305 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_HW_SEQUENCER_TYPES_H__ ++#define __DAL_HW_SEQUENCER_TYPES_H__ ++ ++#include "signal_types.h" ++#include "grph_object_defs.h" ++#include "link_service_types.h" ++#include "plane_types.h" ++ ++struct color_quality { ++ uint32_t bpp_graphics; ++ uint32_t bpp_backend_video; ++}; ++ ++enum { ++ HW_MAX_NUM_VIEWPORTS = 2, ++ HW_CURRENT_PIPE_INDEX = 0, ++ HW_OTHER_PIPE_INDEX = 1 ++}; ++ ++struct hw_view_port_adjustment { ++ int32_t start_adjustment; ++ int32_t width; ++ ++ enum controller_id controller_id; ++}; ++ ++struct hw_view_port_adjustments { ++ uint32_t view_ports_num; ++ struct hw_view_port_adjustment adjustments[HW_MAX_NUM_VIEWPORTS]; ++}; ++ ++/* Timing standard */ ++enum hw_timing_standard { ++ HW_TIMING_STANDARD_UNDEFINED, ++ HW_TIMING_STANDARD_DMT, ++ HW_TIMING_STANDARD_GTF, ++ HW_TIMING_STANDARD_CVT, ++ HW_TIMING_STANDARD_CVT_RB, ++ HW_TIMING_STANDARD_CEA770, ++ HW_TIMING_STANDARD_CEA861, ++ HW_TIMING_STANDARD_HDMI, ++ HW_TIMING_STANDARD_TV_NTSC, ++ HW_TIMING_STANDARD_TV_NTSC_J, ++ HW_TIMING_STANDARD_TV_PAL, ++ HW_TIMING_STANDARD_TV_PAL_M, ++ HW_TIMING_STANDARD_TV_PAL_CN, ++ HW_TIMING_STANDARD_TV_SECAM, ++ /* for explicit timings from VBIOS, EDID etc. */ ++ HW_TIMING_STANDARD_EXPLICIT ++}; ++ ++/* identical to struct crtc_ranged_timing_control ++ * defined in controller\timing_generator_types.h */ ++struct hw_ranged_timing_control { ++ /* set to 1 to force dynamic counter V_COUNT ++ * to lock to constant rate counter V_COUNT_NOM ++ * on page flip event in dynamic refresh mode ++ * when switching from a low refresh rate to nominal refresh rate */ ++ bool force_lock_on_event; ++ /* set to 1 to force CRTC2 (slave) to lock to CRTC1 (master) VSync ++ * in order to overlap their blank regions for MC clock changes */ ++ bool lock_to_master_vsync; ++ ++ /* set to 1 to program Static Screen Detection Masks ++ * without enabling dynamic refresh rate */ ++ bool program_static_screen_mask; ++ /* set to 1 to program Dynamic Refresh Rate */ ++ bool program_dynamic_refresh_rate; ++ /* set to 1 to force disable Dynamic Refresh Rate */ ++ bool force_disable_drr; ++ ++ /* event mask to enable/disable various trigger sources ++ * for static screen detection */ ++ struct static_screen_events event_mask; ++ ++ /* Number of consecutive static screen frames before static state is ++ * asserted. */ ++ uint32_t static_frame_count; ++}; ++ ++/* define the structure of Dynamic Refresh Mode */ ++struct hw_ranged_timing { ++ /* defines the minimum possible vertical dimension of display timing ++ * for CRTC as supported by the panel */ ++ uint32_t vertical_total_min; ++ /* defines the maximum possible vertical dimension of display timing ++ * for CRTC as supported by the panel */ ++ uint32_t vertical_total_max; ++ ++ struct hw_ranged_timing_control control; ++}; ++ ++/* CRTC timing structure */ ++struct hw_crtc_timing { ++ uint32_t h_total; ++ uint32_t h_addressable; ++ uint32_t h_overscan_left; ++ uint32_t h_overscan_right; ++ uint32_t h_sync_start; ++ uint32_t h_sync_width; ++ ++ uint32_t v_total; ++ uint32_t v_addressable; ++ uint32_t v_overscan_top; ++ uint32_t v_overscan_bottom; ++ uint32_t v_sync_start; ++ uint32_t v_sync_width; ++ ++ struct hw_ranged_timing ranged_timing; ++ ++ /* in KHz */ ++ uint32_t pixel_clock; ++ ++ enum hw_timing_standard timing_standard; ++ enum dc_color_depth color_depth; ++ enum dc_pixel_encoding pixel_encoding; ++ ++ struct { ++ uint32_t INTERLACED:1; ++ uint32_t DOUBLESCAN:1; ++ uint32_t PIXEL_REPETITION:4; /* 1...10 */ ++ uint32_t HSYNC_POSITIVE_POLARITY:1; ++ uint32_t VSYNC_POSITIVE_POLARITY:1; ++ /* frame should be packed for 3D ++ * (currently this refers to HDMI 1.4a FramePacking format */ ++ uint32_t HORZ_COUNT_BY_TWO:1; ++ uint32_t PACK_3D_FRAME:1; ++ /* 0 - left eye polarity, 1 - right eye polarity */ ++ uint32_t RIGHT_EYE_3D_POLARITY:1; ++ /* DVI-DL High-Color mode */ ++ uint32_t HIGH_COLOR_DL_MODE:1; ++ uint32_t Y_ONLY:1; ++ /* HDMI 2.0 - Support scrambling for TMDS character ++ * rates less than or equal to 340Mcsc */ ++ uint32_t LTE_340MCSC_SCRAMBLE:1; ++ } flags; ++}; ++ ++struct hw_scaling_info { ++ struct view src; ++ struct view dst; ++ enum signal_type signal; ++}; ++ ++enum hw_color_space { ++ HW_COLOR_SPACE_UNKNOWN = 0, ++ HW_COLOR_SPACE_SRGB_FULL_RANGE, ++ HW_COLOR_SPACE_SRGB_LIMITED_RANGE, ++ HW_COLOR_SPACE_YPBPR601, ++ HW_COLOR_SPACE_YPBPR709, ++ HW_COLOR_SPACE_YCBCR601, ++ HW_COLOR_SPACE_YCBCR709, ++ HW_COLOR_SPACE_YCBCR601_YONLY, ++ HW_COLOR_SPACE_YCBCR709_YONLY, ++ HW_COLOR_SPACE_NMVPU_SUPERAA, ++}; ++ ++enum hw_overlay_color_space { ++ HW_OVERLAY_COLOR_SPACE_UNKNOWN, ++ HW_OVERLAY_COLOR_SPACE_BT709, ++ HW_OVERLAY_COLOR_SPACE_BT601, ++ HW_OVERLAY_COLOR_SPACE_SMPTE240, ++ HW_OVERLAY_COLOR_SPACE_RGB ++}; ++ ++enum hw_overlay_backend_bpp { ++ HW_OVERLAY_BACKEND_BPP_UNKNOWN, ++ HW_OVERLAY_BACKEND_BPP32_FULL_BANDWIDTH, ++ HW_OVERLAY_BACKEND_BPP16_FULL_BANDWIDTH, ++ HW_OVERLAY_BACKEND_BPP32_HALF_BANDWIDTH, ++}; ++enum hw_overlay_format { ++ HW_OVERLAY_FORMAT_UNKNOWN, ++ HW_OVERLAY_FORMAT_YUY2, ++ HW_OVERLAY_FORMAT_UYVY, ++ HW_OVERLAY_FORMAT_RGB565, ++ HW_OVERLAY_FORMAT_RGB555, ++ HW_OVERLAY_FORMAT_RGB32, ++ HW_OVERLAY_FORMAT_YUV444, ++ HW_OVERLAY_FORMAT_RGB32_2101010 ++}; ++ ++enum hw_scale_options { ++ HW_SCALE_OPTION_UNKNOWN, ++ HW_SCALE_OPTION_OVERSCAN, /* multimedia pass through mode */ ++ HW_SCALE_OPTION_UNDERSCAN ++}; ++ ++enum hw_stereo_format { ++ HW_STEREO_FORMAT_NONE = 0, ++ HW_STEREO_FORMAT_SIDE_BY_SIDE = 1, ++ HW_STEREO_FORMAT_TOP_AND_BOTTOM = 2, ++ HW_STEREO_FORMAT_FRAME_ALTERNATE = 3, ++ HW_STEREO_FORMAT_ROW_INTERLEAVED = 5, ++ HW_STEREO_FORMAT_COLUMN_INTERLEAVED = 6, ++ HW_STEREO_FORMAT_CHECKER_BOARD = 7 /* the same as pixel interleave */ ++}; ++ ++enum hw_dithering_options { ++ HW_DITHERING_OPTION_UNKNOWN, ++ HW_DITHERING_OPTION_SKIP_PROGRAMMING, ++ HW_DITHERING_OPTION_ENABLE, ++ HW_DITHERING_OPTION_DISABLE ++}; ++ ++struct hw_stereo_mixer_params { ++ bool sub_sampling; ++ bool single_pipe; ++}; ++ ++ ++ ++struct hw_action_flags { ++ uint32_t RESYNC_PATH:1; ++ uint32_t TIMING_CHANGED:1; ++ uint32_t PIXEL_ENCODING_CHANGED:1; ++ uint32_t GAMUT_CHANGED:1; ++ uint32_t TURN_OFF_VCC:1; ++}; ++ ++enum hw_sync_request { ++ HW_SYNC_REQUEST_NONE = 0, ++ HW_SYNC_REQUEST_SET_INTERPATH, ++ HW_SYNC_REQUEST_SET_GL_SYNC_GENLOCK, ++ HW_SYNC_REQUEST_SET_GL_SYNC_FREE_RUN, ++ HW_SYNC_REQUEST_SET_GL_SYNC_SHADOW, ++ HW_SYNC_REQUEST_RESET_GLSYNC, ++ HW_SYNC_REQUEST_RESYNC_GLSYNC, ++ HW_SYNC_REQUEST_SET_STEREO3D ++}; ++ ++struct hw_sync_info { ++ enum hw_sync_request sync_request; ++ uint32_t target_pixel_clock; /* in KHz */ ++ enum sync_source sync_source; ++}; ++ ++/* TODO hw_info_frame and hw_info_packet structures are same as in encoder ++ * merge it*/ ++struct hw_info_packet { ++ bool valid; ++ uint8_t hb0; ++ uint8_t hb1; ++ uint8_t hb2; ++ uint8_t hb3; ++ uint8_t sb[28]; ++}; ++ ++struct hw_info_frame { ++ /* Auxiliary Video Information */ ++ struct hw_info_packet avi_info_packet; ++ struct hw_info_packet gamut_packet; ++ struct hw_info_packet vendor_info_packet; ++ /* Source Product Description */ ++ struct hw_info_packet spd_packet; ++ /* Video Stream Configuration */ ++ struct hw_info_packet vsc_packet; ++}; ++ ++ ++enum channel_command_type { ++ CHANNEL_COMMAND_I2C, ++ CHANNEL_COMMAND_I2C_OVER_AUX, ++ CHANNEL_COMMAND_AUX ++}; ++ ++ ++/* maximum TMDS transmitter pixel clock is 165 MHz. So it is KHz */ ++#define TMDS_MAX_PIXEL_CLOCK_IN_KHZ 165000 ++#define NATIVE_HDMI_MAX_PIXEL_CLOCK_IN_KHZ 297000 ++ ++struct hw_adjustment_range { ++ int32_t hw_default; ++ int32_t min; ++ int32_t max; ++ int32_t step; ++ uint32_t divider; /* (actually HW range is min/divider; divider !=0) */ ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/i2caux_interface.h b/drivers/gpu/drm/amd/dal/include/i2caux_interface.h +new file mode 100644 +index 0000000..b961d24 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/i2caux_interface.h +@@ -0,0 +1,127 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_I2CAUX_INTERFACE_H__ ++#define __DAL_I2CAUX_INTERFACE_H__ ++ ++#include "ddc_interface.h" ++#include "adapter_service_interface.h" ++ ++struct i2c_payload { ++ bool write; ++ uint8_t address; ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++enum i2c_command_engine { ++ I2C_COMMAND_ENGINE_DEFAULT, ++ I2C_COMMAND_ENGINE_SW, ++ I2C_COMMAND_ENGINE_HW ++}; ++ ++struct i2c_command { ++ struct i2c_payload *payloads; ++ uint8_t number_of_payloads; ++ ++ enum i2c_command_engine engine; ++ ++ /* expressed in KHz ++ * zero means "use default value" */ ++ uint32_t speed; ++}; ++ ++#define DEFAULT_AUX_MAX_DATA_SIZE 16 ++#define AUX_MAX_DEFER_WRITE_RETRY 20 ++ ++struct aux_payload { ++ /* set following flag to read/write I2C data, ++ * reset it to read/write DPCD data */ ++ bool i2c_over_aux; ++ /* set following flag to write data, ++ * reset it to read data */ ++ bool write; ++ uint32_t address; ++ uint8_t length; ++ uint8_t *data; ++}; ++ ++struct aux_command { ++ struct aux_payload *payloads; ++ uint8_t number_of_payloads; ++ ++ /* expressed in milliseconds ++ * zero means "use default value" */ ++ uint32_t defer_delay; ++ ++ /* zero means "use default value" */ ++ uint32_t max_defer_write_retry; ++}; ++ ++union aux_config { ++ struct { ++ uint32_t ALLOW_AUX_WHEN_HPD_LOW:1; ++ } bits; ++ uint32_t raw; ++}; ++ ++struct i2caux; ++ ++struct i2caux *dal_i2caux_create( ++ struct adapter_service *as, ++ struct dc_context *ctx); ++ ++bool dal_i2caux_submit_i2c_command( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ struct i2c_command *cmd); ++ ++bool dal_i2caux_submit_aux_command( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ struct aux_command *cmd); ++ ++void dal_i2caux_keep_engine_power_up( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ bool keep_power_up); ++ ++bool dal_i2caux_start_gtc_sync( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ ++bool dal_i2caux_stop_gtc_sync( ++ struct i2caux *i2caux, ++ struct ddc *ddc); ++ ++void dal_i2caux_configure_aux( ++ struct i2caux *i2caux, ++ struct ddc *ddc, ++ union aux_config cfg); ++ ++void dal_i2caux_destroy( ++ struct i2caux **ptr); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/irq_interface.h b/drivers/gpu/drm/amd/dal/include/irq_interface.h +new file mode 100644 +index 0000000..0faa48f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/irq_interface.h +@@ -0,0 +1,53 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IRQ_INTERFACE_H__ ++#define __DAL_IRQ_INTERFACE_H__ ++ ++#include "gpio_types.h" ++ ++struct irq; ++ ++enum gpio_result dal_irq_open( ++ struct irq *irq); ++ ++enum gpio_result dal_irq_get_value( ++ const struct irq *irq, ++ uint32_t *value); ++ ++enum dc_irq_source dal_irq_get_source( ++ const struct irq *irq); ++ ++enum dc_irq_source dal_irq_get_rx_source( ++ const struct irq *irq); ++ ++enum gpio_result dal_irq_setup_hpd_filter( ++ struct irq *irq, ++ struct gpio_hpd_config *config); ++ ++void dal_irq_close( ++ struct irq *irq); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/irq_service_interface.h b/drivers/gpu/drm/amd/dal/include/irq_service_interface.h +new file mode 100644 +index 0000000..7ae4aeb +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/irq_service_interface.h +@@ -0,0 +1,55 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_IRQ_SERVICE_INTERFACE_H__ ++#define __DAL_IRQ_SERVICE_INTERFACE_H__ ++ ++#include "include/adapter_service_types.h" ++ ++struct irq_service_init_data { ++ struct dc_context *ctx; ++}; ++ ++struct irq_service *dal_irq_service_create( ++ enum dce_version version, ++ struct irq_service_init_data *init_data); ++ ++void dal_irq_service_destroy(struct irq_service **irq_service); ++ ++bool dal_irq_service_set( ++ struct irq_service *irq_service, ++ enum dc_irq_source source, ++ bool enable); ++ ++bool dal_irq_service_ack( ++ struct irq_service *irq_service, ++ enum dc_irq_source source); ++ ++enum dc_irq_source dal_irq_service_to_irq_source( ++ struct irq_service *irq_service, ++ uint32_t src_id, ++ uint32_t ext_id); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/isr_config_types.h b/drivers/gpu/drm/amd/dal/include/isr_config_types.h +new file mode 100644 +index 0000000..2e822f0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/isr_config_types.h +@@ -0,0 +1,157 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ISR_TYPES_H__ ++#define __DAL_ISR_TYPES_H__ ++ ++#include "grph_object_id.h" ++#include "dc_types.h" ++ ++struct plane_config; ++enum { ++ /*move to common*/ ++ MAX_COFUNC_PATH_COMMON = 6, ++ /*CZ worst case*/ ++ MAX_NUM_PLANES = 4 ++}; ++ ++enum plane_type { ++ PLANE_TYPE_GRPH = 0, ++ PLANE_TYPE_VIDEO ++}; ++ ++struct plane_id { ++ enum plane_type select; ++ enum controller_id controller_id; ++}; ++ ++union display_plane_mask { ++ struct { ++ uint32_t CLONE_PRIMARY_CONTROLLER_ID_SET:1; ++ uint32_t INTERLEAVED_CONTROLLER_ID_SET:1; ++ uint32_t RESERVED:30; ++ } bits; ++ uint32_t value; ++}; ++ ++struct display_plane_format { ++ /* always valid */ ++ union display_plane_mask mask; ++ /* always valid */ ++ uint32_t display_index; ++ /* always valid */ ++ enum dc_timing_3d_format format; ++ /* always valid */ ++ enum controller_id controller_id; ++ /* valid only if CLONE_PRIMARY_CONTROLLER_ID_SET on */ ++ enum controller_id clone_primary_controller_id; ++ /* valid only if stereo interleave mode is on */ ++ enum controller_id interleave_controller_id; ++ /* valid only if crtc stereo is on */ ++ uint32_t right_eye_3d_polarity:1; ++}; ++ ++struct display_plane_set { ++ struct display_plane_format ++ set_mode_formats[MAX_COFUNC_PATH_COMMON]; ++ uint32_t reset_mode_index[ ++ MAX_COFUNC_PATH_COMMON]; ++ uint32_t num_set_mode_formats; ++ uint32_t num_reset_mode_index; ++}; ++ ++enum layers_setup { ++ LAYERS_SETUP_NOTHING = 0, ++ LAYERS_SETUP_SET, ++ LAYERS_SETUP_FREE ++}; ++ ++union plane_cfg_internal_flags { ++ struct { ++ uint32_t PLANE_OWNS_CRTC:1; ++ uint32_t RESERVED:31; ++ } bits; ++ uint32_t value; ++}; ++ ++ ++struct plane_cfg_internal { ++ const struct plane_config *config; ++ enum layers_setup setup; ++ union plane_cfg_internal_flags flags; ++}; ++ ++enum lock_type { ++ LOCK_TYPE_GRPH = 0, ++ LOCK_TYPE_SURF, ++ LOCK_TYPE_SCL, ++ LOCK_TYPE_BLND, ++ /* lock the given pipe with options above */ ++ LOCK_TYPE_THIS ++}; ++ ++enum alpha_mode { ++ ALPHA_MODE_PIXEL = 0, ++ ALPHA_MODE_PIXEL_AND_GLOBAL = 1, ++ ALPHA_MODE_GLOBAL = 2 ++}; ++ ++union alpha_mode_cfg_flag { ++ struct { ++ uint32_t MODE_IS_SET:1; ++ uint32_t MODE_MULTIPLIED_IS_SET:1; ++ uint32_t GLOBAL_ALPHA_IS_SET:1; ++ uint32_t GLOBAL_ALPHA_GAIN_IS_SET:1; ++ ++ uint32_t MULTIPLIED_MODE:1; ++ uint32_t GLOBAL_ALPHA:8; ++ /* total 21 bits! */ ++ uint32_t GLOBAL_ALPHA_GAIN:8; ++ } bits; ++ uint32_t value; ++}; ++ ++struct alpha_mode_cfg { ++ union alpha_mode_cfg_flag flags; ++ enum alpha_mode mode; ++}; ++ ++union pending_cfg_changes { ++ struct { ++ uint32_t SCL_UNLOCK_REQUIRED:1; ++ uint32_t BLND_UNLOCK_REQUIRED:1; ++ uint32_t INPUT_CSC_SWITCH_REQUIRED:1; ++ uint32_t OUTPUT_CSC_SWITCH_REQUIRED:1; ++ } bits; ++ uint32_t value; ++}; ++ ++struct pending_plane_changes { ++ union pending_cfg_changes changes; ++ struct plane_id id; ++}; ++ ++ ++#endif /* __DAL_ISR_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/link_encoder_types.h b/drivers/gpu/drm/amd/dal/include/link_encoder_types.h +new file mode 100644 +index 0000000..2a59902 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/link_encoder_types.h +@@ -0,0 +1,32 @@ ++/* ++ * link_encoder_types.h ++ * ++ * Created on: Oct 6, 2015 ++ * Author: yonsun ++ */ ++ ++#ifndef DRIVERS_GPU_DRM_AMD_DAL_DEV_INCLUDE_LINK_ENCODER_TYPES_H_ ++#define DRIVERS_GPU_DRM_AMD_DAL_DEV_INCLUDE_LINK_ENCODER_TYPES_H_ ++ ++#include "encoder_interface.h" ++ ++struct link_enc_status { ++ int dummy; /*TODO*/ ++}; ++struct link_encoder { ++ struct adapter_service *adapter_service; ++ int32_t be_engine_offset; ++ int32_t aux_channel_offset; ++ int32_t transmitter_offset; ++ struct dc_context *ctx; ++ struct graphics_object_id id; ++ struct graphics_object_id connector; ++ uint32_t input_signals; ++ uint32_t output_signals; ++ enum engine_id preferred_engine; ++ struct encoder_feature_support features; ++ enum transmitter transmitter; ++ enum hpd_source_id hpd_source; ++}; ++ ++#endif /* DRIVERS_GPU_DRM_AMD_DAL_DEV_INCLUDE_LINK_ENCODER_TYPES_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/include/link_service_interface.h b/drivers/gpu/drm/amd/dal/include/link_service_interface.h +new file mode 100644 +index 0000000..2ac9311 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/link_service_interface.h +@@ -0,0 +1,202 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_LINK_SERVICE_INTERFACE_H__ ++#define __DAL_LINK_SERVICE_INTERFACE_H__ ++ ++#include "include/link_service_types.h" ++ ++/* forward declaration */ ++struct link_service; ++struct hw_crtc_timing; ++struct hw_path_mode; ++struct display_path; ++struct hw_path_mode_set; ++struct link_training_preference; ++enum ddc_result; ++ ++struct link_service *dal_link_service_create( ++ struct link_service_init_data *init_data); ++ ++void dal_link_service_destroy( ++ struct link_service **ls); ++ ++enum link_service_type dal_ls_get_link_service_type( ++ struct link_service *link_service); ++ ++bool dal_ls_validate_mode_timing( ++ struct link_service *ls, ++ uint32_t display_index, ++ const struct hw_crtc_timing *timing, ++ struct link_validation_flags flags); ++ ++bool dal_ls_get_mst_sink_info( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct mst_sink_info *sink_info); ++ ++bool dal_ls_get_gtc_sync_status( ++ struct link_service *ls); ++ ++bool dal_ls_enable_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++bool dal_ls_disable_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *poath_mode); ++ ++bool dal_ls_optimized_enable_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct display_path *display_path); ++ ++void dal_ls_update_stream_features( ++ struct link_service *ls, ++ const struct hw_path_mode *path_mode); ++ ++bool dal_ls_blank_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++bool dal_ls_unblank_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++bool dal_ls_pre_mode_change( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++bool dal_ls_post_mode_change( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++bool dal_ls_power_on_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++bool dal_ls_power_off_stream( ++ struct link_service *ls, ++ uint32_t display_index, ++ struct hw_path_mode *path_mode); ++ ++void dal_ls_retrain_link( ++ struct link_service *ls, ++ struct hw_path_mode_set *path_set); ++ ++bool dal_ls_get_current_link_setting( ++ struct link_service *ls, ++ struct link_settings *link_settings); ++ ++void dal_ls_connect_link( ++ struct link_service *ls, ++ const struct display_path *display_path, ++ bool initial_detection); ++ ++void dal_ls_disconnect_link( ++ struct link_service *ls); ++ ++bool dal_ls_is_mst_network_present( ++ struct link_service *ls); ++ ++void dal_ls_invalidate_down_stream_devices( ++ struct link_service *ls); ++ ++bool dal_ls_are_mst_displays_cofunctional( ++ struct link_service *ls, ++ const uint32_t *array_display_index, ++ uint32_t len); ++ ++bool dal_ls_is_sink_present_at_display_index( ++ struct link_service *ls, ++ uint32_t display_index); ++ ++struct ddc_service *dal_ls_obtain_mst_ddc_service( ++ struct link_service *ls, ++ uint32_t display_index); ++ ++void dal_ls_release_mst_ddc_service( ++ struct link_service *ls, ++ struct ddc_service *ddc_service); ++ ++void dal_ls_release_hw( ++ struct link_service *ls); ++ ++bool dal_ls_associate_link( ++ struct link_service *ls, ++ uint32_t display_index, ++ uint32_t link_index, ++ bool is_internal_link); ++ ++bool dal_dpsst_ls_set_overridden_trained_link_settings( ++ struct link_service *ls, ++ const struct link_settings *link_settings); ++ ++void dal_dpsst_ls_set_link_training_preference( ++ struct link_service *ls, ++ const struct link_training_preference *ltp); ++ ++struct link_training_preference ++ dal_dpsst_ls_get_link_training_preference( ++ struct link_service *ls); ++ ++bool dal_ls_should_send_notification( ++ struct link_service *ls); ++ ++uint32_t dal_ls_get_notification_display_index( ++ struct link_service *ls); ++ ++enum ddc_result dal_dpsst_ls_read_dpcd_data( ++ struct link_service *ls, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size); ++ ++enum ddc_result dal_dpsst_ls_write_dpcd_data( ++ struct link_service *ls, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t size); ++ ++bool dal_ls_is_link_psr_supported(struct link_service *ls); ++ ++bool dal_ls_is_stream_drr_supported(struct link_service *ls); ++ ++void dal_ls_set_link_psr_capabilities( ++ struct link_service *ls, ++ struct psr_caps *psr_caps); ++ ++void dal_ls_get_link_psr_capabilities( ++ struct link_service *ls, ++ struct psr_caps *psr_caps); ++ ++#endif /* __DAL_LINK_SERVICE_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/link_service_types.h b/drivers/gpu/drm/amd/dal/include/link_service_types.h +new file mode 100644 +index 0000000..0df5687 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/link_service_types.h +@@ -0,0 +1,428 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_LINK_SERVICE_TYPES_H__ ++#define __DAL_LINK_SERVICE_TYPES_H__ ++ ++#include "dal_services_types.h" ++ ++#include "grph_object_id.h" ++#include "dpcd_defs.h" ++#include "dal_types.h" ++#include "irq_types.h" ++ ++/*struct mst_mgr_callback_object;*/ ++struct ddc; ++struct irq_manager; ++ ++enum link_service_type { ++ LINK_SERVICE_TYPE_LEGACY = 0, ++ LINK_SERVICE_TYPE_DP_SST, ++ LINK_SERVICE_TYPE_DP_MST, ++ LINK_SERVICE_TYPE_MAX ++}; ++ ++struct link_validation_flags { ++ uint32_t DYNAMIC_VALIDATION:1; ++ uint32_t CANDIDATE_TIMING:1; ++ uint32_t START_OF_VALIDATION:1; ++}; ++ ++/* Post Cursor 2 is optional for transmitter ++ * and it applies only to the main link operating at HBR2 ++ */ ++enum post_cursor2 { ++ POST_CURSOR2_DISABLED = 0, /* direct HW translation! */ ++ POST_CURSOR2_LEVEL1, ++ POST_CURSOR2_LEVEL2, ++ POST_CURSOR2_LEVEL3, ++ POST_CURSOR2_MAX_LEVEL = POST_CURSOR2_LEVEL3, ++}; ++ ++enum voltage_swing { ++ VOLTAGE_SWING_LEVEL0 = 0, /* direct HW translation! */ ++ VOLTAGE_SWING_LEVEL1, ++ VOLTAGE_SWING_LEVEL2, ++ VOLTAGE_SWING_LEVEL3, ++ VOLTAGE_SWING_MAX_LEVEL = VOLTAGE_SWING_LEVEL3 ++}; ++ ++enum pre_emphasis { ++ PRE_EMPHASIS_DISABLED = 0, /* direct HW translation! */ ++ PRE_EMPHASIS_LEVEL1, ++ PRE_EMPHASIS_LEVEL2, ++ PRE_EMPHASIS_LEVEL3, ++ PRE_EMPHASIS_MAX_LEVEL = PRE_EMPHASIS_LEVEL3 ++}; ++ ++enum dpcd_value_mask { ++ DPCD_VALUE_MASK_MAX_LANE_COUNT_LANE_COUNT = 0x1F, ++ DPCD_VALUE_MASK_MAX_LANE_COUNT_TPS3_SUPPORTED = 0x40, ++ DPCD_VALUE_MASK_MAX_LANE_COUNT_ENHANCED_FRAME_EN = 0x80, ++ DPCD_VALUE_MASK_MAX_DOWNSPREAD = 0x01, ++ DPCD_VALUE_MASK_LANE_ALIGN_STATUS_INTERLANE_ALIGN_DONE = 0x01 ++}; ++ ++enum dp_power_state { ++ DP_POWER_STATE_D0 = 1, ++ DP_POWER_STATE_D3 ++}; ++ ++enum dpcd_downstream_port_types { ++ DPCD_DOWNSTREAM_DP, ++ DPCD_DOWNSTREAM_VGA, ++ DPCD_DOWNSTREAM_DVI_HDMI, ++ /* has no EDID (TV, CV) */ ++ DPCD_DOWNSTREAM_NON_DDC ++}; ++ ++enum edp_revision { ++ /* eDP version 1.1 or lower */ ++ EDP_REVISION_11 = 0x00, ++ /* eDP version 1.2 */ ++ EDP_REVISION_12 = 0x01, ++ /* eDP version 1.3 */ ++ EDP_REVISION_13 = 0x02 ++}; ++ ++enum lane_count { ++ LANE_COUNT_UNKNOWN = 0, ++ LANE_COUNT_ONE = 1, ++ LANE_COUNT_TWO = 2, ++ LANE_COUNT_FOUR = 4, ++ LANE_COUNT_EIGHT = 8, ++ LANE_COUNT_DP_MAX = LANE_COUNT_FOUR ++}; ++ ++/* This is actually a reference clock (27MHz) multiplier ++ * 162MBps bandwidth for 1.62GHz like rate, ++ * 270MBps for 2.70GHz, ++ * 324MBps for 3.24Ghz, ++ * 540MBps for 5.40GHz ++ */ ++enum link_rate { ++ LINK_RATE_UNKNOWN = 0, ++ LINK_RATE_LOW = 0x06, ++ LINK_RATE_HIGH = 0x0A, ++ LINK_RATE_RBR2 = 0x0C, ++ LINK_RATE_HIGH2 = 0x14 ++}; ++ ++enum { ++ LINK_RATE_REF_FREQ_IN_KHZ = 27000 /*27MHz*/ ++}; ++ ++enum link_spread { ++ LINK_SPREAD_DISABLED = 0x00, ++ /* 0.5 % downspread 30 kHz */ ++ LINK_SPREAD_05_DOWNSPREAD_30KHZ = 0x10, ++ /* 0.5 % downspread 33 kHz */ ++ LINK_SPREAD_05_DOWNSPREAD_33KHZ = 0x11 ++}; ++ ++/* DPCD_ADDR_DOWNSTREAM_PORT_PRESENT register value */ ++union dpcd_downstream_port { ++ struct { ++#if defined(LITTLEENDIAN_CPU) ++ uint8_t PRESENT:1; ++ uint8_t TYPE:2; ++ uint8_t FORMAT_CONV:1; ++ uint8_t RESERVED:4; ++#elif defined(BIGENDIAN_CPU) ++ uint8_t RESERVED:4; ++ uint8_t FORMAT_CONV:1; ++ uint8_t TYPE:2; ++ uint8_t PRESENT:1; ++#else ++ #error ARCH not defined! ++#endif ++ } bits; ++ ++ uint8_t raw; ++}; ++ ++/* DPCD_ADDR_SINK_COUNT register value */ ++union dpcd_sink_count { ++ struct { ++#if defined(LITTLEENDIAN_CPU) ++ uint8_t SINK_COUNT:6; ++ uint8_t CP_READY:1; ++ uint8_t RESERVED:1; ++#elif defined(BIGENDIAN_CPU) ++ uint8_t RESERVED:1; ++ uint8_t CP_READY:1; ++ uint8_t SINK_COUNT:6; ++#else ++ #error ARCH not defined! ++#endif ++ } bits; ++ ++ uint8_t raw; ++}; ++ ++struct link_settings { ++ enum lane_count lane_count; ++ enum link_rate link_rate; ++ enum link_spread link_spread; ++}; ++ ++struct lane_settings { ++ enum voltage_swing VOLTAGE_SWING; ++ enum pre_emphasis PRE_EMPHASIS; ++ enum post_cursor2 POST_CURSOR2; ++}; ++ ++struct link_training_settings { ++ struct link_settings link_settings; ++ struct lane_settings lane_settings[LANE_COUNT_DP_MAX]; ++ bool allow_invalid_msa_timing_param; ++}; ++ ++enum hw_dp_training_pattern { ++ HW_DP_TRAINING_PATTERN_1 = 0, ++ HW_DP_TRAINING_PATTERN_2, ++ HW_DP_TRAINING_PATTERN_3 ++}; ++ ++/*TODO: Move this enum test harness*/ ++/* Test patterns*/ ++enum dp_test_pattern { ++ /* Input data is pass through Scrambler ++ * and 8b10b Encoder straight to output*/ ++ DP_TEST_PATTERN_VIDEO_MODE = 0, ++ /* phy test patterns*/ ++ DP_TEST_PATTERN_D102, ++ DP_TEST_PATTERN_SYMBOL_ERROR, ++ DP_TEST_PATTERN_PRBS7, ++ ++ DP_TEST_PATTERN_80BIT_CUSTOM, ++ DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE, ++ ++ /* Link Training Patterns */ ++ DP_TEST_PATTERN_TRAINING_PATTERN1, ++ DP_TEST_PATTERN_TRAINING_PATTERN2, ++ DP_TEST_PATTERN_TRAINING_PATTERN3, ++ ++ /* link test patterns*/ ++ DP_TEST_PATTERN_COLOR_SQUARES, ++ DP_TEST_PATTERN_COLOR_SQUARES_CEA, ++ DP_TEST_PATTERN_VERTICAL_BARS, ++ DP_TEST_PATTERN_HORIZONTAL_BARS, ++ DP_TEST_PATTERN_COLOR_RAMP, ++ ++ /* audio test patterns*/ ++ DP_TEST_PATTERN_AUDIO_OPERATOR_DEFINED, ++ DP_TEST_PATTERN_AUDIO_SAWTOOTH, ++ ++ DP_TEST_PATTERN_UNSUPPORTED ++}; ++ ++enum dp_panel_mode { ++ /* not required */ ++ DP_PANEL_MODE_DEFAULT, ++ /* standard mode for eDP */ ++ DP_PANEL_MODE_EDP, ++ /* external chips specific settings */ ++ DP_PANEL_MODE_SPECIAL ++}; ++ ++/** ++ * @brief LinkServiceInitOptions to set certain bits ++ */ ++struct link_service_init_options { ++ uint32_t APPLY_MISALIGNMENT_BUG_WORKAROUND:1; ++}; ++ ++/** ++ * @brief data required to initialize LinkService ++ */ ++struct link_service_init_data { ++ /* number of displays indices which the MST Mgr would manange*/ ++ uint32_t num_of_displays; ++ enum link_service_type link_type; ++ /*struct mst_mgr_callback_object*topology_change_callback;*/ ++ /* native aux access */ ++ struct ddc_service *dpcd_access_srv; ++ /* for calling HWSS to program HW */ ++ struct hw_sequencer *hwss; ++ /* the source which to register IRQ on */ ++ enum dc_irq_source irq_src_hpd_rx; ++ enum dc_irq_source irq_src_dp_sink; ++ /* other init options such as SW Workarounds */ ++ struct link_service_init_options init_options; ++ uint32_t connector_enum_id; ++ struct graphics_object_id connector_id; ++ struct adapter_service *adapter_service; ++ struct dc_context *ctx; ++ struct topology_mgr *tm; ++}; ++ ++/** ++ * @brief LinkServiceInitOptions to set certain bits ++ */ ++struct LinkServiceInitOptions { ++ uint32_t APPLY_MISALIGNMENT_BUG_WORKAROUND:1; ++}; ++ ++/* DPCD_ADDR_TRAINING_LANEx_SET registers value */ ++union dpcd_training_lane_set { ++ struct { ++#if defined(LITTLEENDIAN_CPU) ++ uint8_t VOLTAGE_SWING_SET:2; ++ uint8_t MAX_SWING_REACHED:1; ++ uint8_t PRE_EMPHASIS_SET:2; ++ uint8_t MAX_PRE_EMPHASIS_REACHED:1; ++ /* following is reserved in DP 1.1 */ ++ uint8_t POST_CURSOR2_SET:2; ++#elif defined(BIGENDIAN_CPU) ++ uint8_t POST_CURSOR2_SET:2; ++ uint8_t MAX_PRE_EMPHASIS_REACHED:1; ++ uint8_t PRE_EMPHASIS_SET:2; ++ uint8_t MAX_SWING_REACHED:1; ++ uint8_t VOLTAGE_SWING_SET:2; ++#else ++ #error ARCH not defined! ++#endif ++ } bits; ++ ++ uint8_t raw; ++}; ++ ++/* DPCD_ADDR_TRAINING_LANEx_SET2 registers value - since DP 1.2 */ ++union dpcd_training_lanes_set2 { ++ struct { ++#if defined(LITTLEENDIAN_CPU) ++ uint8_t LANE0_POST_CURSOR2_SET:2; ++ uint8_t LANE0_MAX_POST_CURSOR2_REACHED:1; ++ uint8_t LANE0_RESERVED:1; ++ uint8_t LANE1_POST_CURSOR2_SET:2; ++ uint8_t LANE1_MAX_POST_CURSOR2_REACHED:1; ++ uint8_t LANE1_RESERVED:1; ++#elif defined(BIGENDIAN_CPU) ++ uint8_t LANE1_RESERVED:1; ++ uint8_t LANE1_MAX_POST_CURSOR2_REACHED:1; ++ uint8_t LANE1_POST_CURSOR2_SET:2; ++ uint8_t LANE0_RESERVED:1; ++ uint8_t LANE0_MAX_POST_CURSOR2_REACHED:1; ++ uint8_t LANE0_POST_CURSOR2_SET:2; ++#else ++ #error ARCH not defined! ++#endif ++ } bits; ++ ++ uint8_t raw; ++}; ++ ++/** ++ * @brief represent the 16 byte ++ * global unique identifier ++ */ ++struct mst_guid { ++ uint8_t ids[16]; ++}; ++ ++/** ++ * @brief represents the relative address used ++ * to identify a node in MST topology network ++ */ ++struct mst_rad { ++ /* number of links. rad[0] up to ++ * rad [linkCount - 1] are valid. */ ++ uint32_t rad_link_count; ++ /* relative address. rad[0] is the ++ * first device connected to the source. */ ++ uint8_t rad[15]; ++ /* extra 10 bytes for underscores; for e.g.:2_1_8*/ ++ int8_t rad_str[25]; ++}; ++ ++/** ++ * @brief this structure is used to report ++ * properties associated to a sink device ++ */ ++struct mst_sink_info { ++ /* global unique identifier */ ++ struct mst_guid guid; ++ /* relative address */ ++ struct mst_rad rad; ++ /* total bandwidth available on the DP connector */ ++ uint32_t total_available_bandwidth_in_mbps; ++ /* bandwidth allocated to the sink device. */ ++ uint32_t allocated_bandwidth_in_mbps; ++ /* bandwidth consumed to support for the current mode. */ ++ uint32_t consumed_bandwidth_in_mbps; ++}; ++ ++/** ++ * @brief represent device information in MST topology ++ */ ++struct mst_device_info { ++ /* global unique identifier*/ ++ struct mst_guid guid; ++ /* relative address*/ ++ struct mst_rad rad; ++}; ++ ++/* DP MST stream allocation (payload bandwidth number) */ ++struct dp_mst_stream_allocation { ++ /* stream engine id (DIG) */ ++ enum engine_id engine; ++ /* number of slots required for the DP stream in ++ * transport packet */ ++ uint32_t slot_count; ++ uint32_t pbn; ++ uint32_t pbn_per_slot; ++}; ++ ++/* DP MST stream allocation table */ ++struct dp_mst_stream_allocation_table { ++ /* number of DP video streams */ ++ uint32_t stream_count; ++ /* array of stream allocations */ ++ struct dp_mst_stream_allocation stream_allocations[1]; ++}; ++ ++struct dp_test_event_data { ++ /*size of parameters (starting from params) in bytes*/ ++ uint32_t size; ++ /*parameters block*/ ++ uint32_t params[1]; ++}; ++ ++struct psr_caps { ++ /* These parameters are from PSR capabilities reported by Sink DPCD. */ ++ uint8_t psr_version; ++ uint32_t psr_rfb_setup_time; ++ bool psr_exit_link_training_req; ++ ++ /* These parameters are calculated in Driver, based on display timing ++ * and Sink capabilities. ++ * If VBLANK region is too small and Sink takes a long time to power up ++ * Remote Frame Buffer, it may take an extra frame to enter PSR */ ++ bool psr_frame_capture_indication_req; ++ uint32_t psr_sdp_transmit_line_num_deadline; ++}; ++ ++#endif /*__DAL_LINK_SERVICE_TYPES_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/include/logger_interface.h b/drivers/gpu/drm/amd/dal/include/logger_interface.h +new file mode 100644 +index 0000000..4d945ea +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/logger_interface.h +@@ -0,0 +1,153 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_LOGGER_INTERFACE_H__ ++#define __DAL_LOGGER_INTERFACE_H__ ++ ++#include "logger_types.h" ++ ++struct dal_logger; ++struct dc_context; ++union logger_flags; ++ ++/* ++ * TODO: This logger functionality needs to be implemented and reworked. ++ */ ++ ++ ++/* ++ * ++ * DAL logger functionality ++ * ++ */ ++ ++struct dal_logger *dal_logger_create(struct dc_context *ctx); ++ ++uint32_t dal_logger_destroy(struct dal_logger **logger); ++ ++uint32_t dal_logger_get_mask( ++ struct dal_logger *logger, ++ enum log_major lvl_major, enum log_minor lvl_minor); ++ ++uint32_t dal_logger_set_mask( ++ struct dal_logger *logger, ++ enum log_major lvl_major, enum log_minor lvl_minor); ++ ++uint32_t dal_logger_get_masks( ++ struct dal_logger *logger, ++ enum log_major lvl_major); ++ ++void dal_logger_set_masks( ++ struct dal_logger *logger, ++ enum log_major lvl_major, uint32_t log_mask); ++ ++uint32_t dal_logger_unset_mask( ++ struct dal_logger *logger, ++ enum log_major lvl_major, enum log_minor lvl_minor); ++ ++bool dal_logger_should_log( ++ struct dal_logger *logger, ++ enum log_major major, ++ enum log_minor minor); ++ ++uint32_t dal_logger_get_flags( ++ struct dal_logger *logger); ++ ++void dal_logger_set_flags( ++ struct dal_logger *logger, ++ union logger_flags flags); ++ ++void dal_logger_write( ++ struct dal_logger *logger, ++ enum log_major major, ++ enum log_minor minor, ++ const char *msg, ++ ...); ++ ++void dal_logger_append( ++ struct log_entry *entry, ++ const char *msg, ++ ...); ++ ++uint32_t dal_logger_read( ++ struct dal_logger *logger, ++ uint32_t output_buffer_size, ++ char *output_buffer, ++ uint32_t *bytes_read, ++ bool single_line); ++ ++void dal_logger_open( ++ struct dal_logger *logger, ++ struct log_entry *entry, ++ enum log_major major, ++ enum log_minor minor); ++ ++void dal_logger_close(struct log_entry *entry); ++ ++uint32_t dal_logger_get_buffer_size(struct dal_logger *logger); ++ ++uint32_t dal_logger_set_buffer_size( ++ struct dal_logger *logger, ++ uint32_t new_size); ++ ++const struct log_major_info *dal_logger_enum_log_major_info( ++ struct dal_logger *logger, ++ unsigned int enum_index); ++ ++const struct log_minor_info *dal_logger_enum_log_minor_info( ++ struct dal_logger *logger, ++ enum log_major major, ++ unsigned int enum_index); ++ ++/* Any function which is empty or have incomplete implementation should be ++ * marked by this macro. ++ * Note that the message will be printed exactly once for every function ++ * it is used in order to avoid repeating of the same message. */ ++#define DAL_LOGGER_NOT_IMPL(log_minor, fmt, ...) \ ++{ \ ++ static bool print_not_impl = true; \ ++\ ++ if (print_not_impl == true) { \ ++ print_not_impl = false; \ ++ dal_logger_write(ctx->logger, LOG_MAJOR_WARNING, \ ++ log_minor, "DAL_NOT_IMPL: " fmt, ##__VA_ARGS__); \ ++ } \ ++} ++ ++/****************************************************************************** ++ * Convenience macros to save on typing. ++ *****************************************************************************/ ++ ++#define DC_ERROR(...) \ ++ dal_logger_write(dc_ctx->logger, \ ++ LOG_MAJOR_ERROR, LOG_MINOR_COMPONENT_DC, \ ++ __VA_ARGS__); ++ ++#define DC_SYNC_INFO(...) \ ++ dal_logger_write(dc_ctx->logger, \ ++ LOG_MAJOR_SYNC, LOG_MINOR_SYNC_TIMING, \ ++ __VA_ARGS__); ++ ++#endif /* __DAL_LOGGER_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/logger_types.h b/drivers/gpu/drm/amd/dal/include/logger_types.h +new file mode 100644 +index 0000000..6147999 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/logger_types.h +@@ -0,0 +1,356 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_LOGGER_TYPES_H__ ++#define __DAL_LOGGER_TYPES_H__ ++ ++ ++/* ++ * TODO: This logger functionality needs to be implemented and reworked. ++ */ ++ ++ ++struct dal_logger; ++ ++enum log_major { ++/*00*/ ++ LOG_MAJOR_ERROR = 0, /*< DAL subcomponent error MSG*/ ++/*01*/ LOG_MAJOR_WARNING, /*< DAL subcomponent warning MSG*/ ++/*02*/ LOG_MAJOR_INTERFACE_TRACE,/*< DAL subcomponent interface tracing*/ ++/*03*/ LOG_MAJOR_HW_TRACE, /*< Log ASIC register read/write, ++ * ATOMBIOS exec table call and delays*/ ++ ++/*04*/ LOG_MAJOR_MST, /*< related to multi-stream*/ ++/*05*/ LOG_MAJOR_DCS, /*< related to Dcs*/ ++/*06*/ LOG_MAJOR_DCP, /*< related to Dcp grph and ovl,gamam and csc*/ ++/*07*/ LOG_MAJOR_BIOS, /*< related to BiosParser*/ ++/*08*/ LOG_MAJOR_REGISTER, /*< register access*/ ++/*09*/ LOG_MAJOR_INFO_PACKETS, /*< info packets*/ ++/*10*/ LOG_MAJOR_DSAT, /*< related ++ * to Display System Analysis Tool*/ ++/*11*/ LOG_MAJOR_EC, /*< External Components - MCIL Events/queries, ++ * PPLib notifications/queries*/ ++/*12*/ LOG_MAJOR_BWM, /*< related to Bandwidth Manager*/ ++/*13*/ LOG_MAJOR_MODE_ENUM, /*< related to mode enumeration*/ ++/*14*/ LOG_MAJOR_I2C_AUX, /*< i2c and aux channel log*/ ++/*15*/ LOG_MAJOR_LINE_BUFFER, /*< Line Buffer object logging activity*/ ++/*16*/ LOG_MAJOR_HWSS, /*< HWSS object logging activity*/ ++/*17*/ LOG_MAJOR_OPTIMIZATION, /*< Optimization code path*/ ++/*18*/ LOG_MAJOR_PERF_MEASURE, /*< Performance measurement dumps*/ ++/*19*/ LOG_MAJOR_SYNC, /*< related to HW and SW Synchronization*/ ++/*20*/ LOG_MAJOR_BACKLIGHT, /*< related to backlight */ ++/*21*/ LOG_MAJOR_INTERRUPTS, /*< logging for all interrupts */ ++ ++/*22*/ LOG_MAJOR_TM, /*< related to Topology Manager*/ ++/*23*/ LOG_MAJOR_DISPLAY_SERVICE, /*< related to Display Service*/ ++/*24*/ LOG_MAJOR_FEATURE_OVERRIDE, /*< related to features*/ ++/*25*/ LOG_MAJOR_DETECTION, /*< related to detection*/ ++ LOG_MAJOR_COUNT, /*< count of the Major categories*/ ++}; ++ ++/** ++* @brief defines minor switch for logging. each of these define sub category ++* of log message per LogMajor ++*/ ++ ++ ++enum log_minor { ++ ++ /* Special case for 'all' checkbox */ ++ LOG_MINOR_MASK_ALL = (uint32_t)-1, /* 0xFFFFFFFF */ ++/** ++* @brief defines minor category for ++* LOG_MAJOR_ERROR, ++* LOG_MAJOR_WARNING, ++* LOG_MAJOR_INTERFACE_TRACE ++* ++* @note each DAL subcomponent should have a corresponding enum ++*/ ++ LOG_MINOR_COMPONENT_LINK_SERVICE = 0, ++ LOG_MINOR_COMPONENT_DAL_INTERFACE, ++ LOG_MINOR_COMPONENT_HWSS, ++ LOG_MINOR_COMPONENT_ADAPTER_SERVICE, ++ LOG_MINOR_COMPONENT_DISPLAY_SERVICE, ++ LOG_MINOR_COMPONENT_TOPOLOGY_MANAGER, ++ LOG_MINOR_COMPONENT_ENCODER, ++ LOG_MINOR_COMPONENT_I2C_AUX, ++ LOG_MINOR_COMPONENT_AUDIO, ++ LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, ++ LOG_MINOR_COMPONENT_DMCU, ++ LOG_MINOR_COMPONENT_GPU, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ LOG_MINOR_COMPONENT_ISR, ++ LOG_MINOR_COMPONENT_BIOS, ++ LOG_MINOR_COMPONENT_DC, ++ LOG_MINOR_COMPONENT_IRQ_SERVICE, ++ ++/** ++* @brief define minor category for LogMajor_HardwareTrace ++* ++* @note defines functionality based HW programming trace ++*/ ++ LOG_MINOR_HW_TRACE_MST = 0, ++ LOG_MINOR_HW_TRACE_TRAVIS, ++ LOG_MINOR_HW_TRACE_HOTPLUG, ++ LOG_MINOR_HW_TRACE_LINK_TRAINING, ++ LOG_MINOR_HW_TRACE_SET_MODE, ++ LOG_MINOR_HW_TRACE_RESUME_S3, ++ LOG_MINOR_HW_TRACE_RESUME_S4, ++ LOG_MINOR_HW_TRACE_BOOTUP, ++ LOG_MINOR_HW_TRACE_AUDIO, ++ LOG_MINOR_HW_TRACE_HPD_IRQ, ++ LOG_MINOR_HW_TRACE_INTERRUPT, ++ LOG_MINOR_HW_TRACE_MPO, ++ ++/** ++* @brief defines minor category for LogMajor_Mst ++* ++* @note define sub functionality related to MST ++*/ ++ LOG_MINOR_MST_IRQ_HPD_RX = 0, ++ LOG_MINOR_MST_IRQ_TIMER, ++ LOG_MINOR_MST_NATIVE_AUX, ++ LOG_MINOR_MST_SIDEBAND_MSG, ++ LOG_MINOR_MST_MSG_TRANSACTION, ++ LOG_MINOR_MST_SIDEBAND_MSG_PARSED, ++ LOG_MINOR_MST_MSG_TRANSACTION_PARSED, ++ LOG_MINOR_MST_AUX_MSG_DPCD_ACCESS, ++ LOG_MINOR_MST_PROGRAMMING, ++ LOG_MINOR_MST_TOPOLOGY_DISCOVERY, ++ LOG_MINOR_MST_CONVERTER_CAPS, ++ ++/** ++* @brief defines minor category for LogMajor_DCS ++* ++* @note should define sub functionality related to Dcs ++*/ ++ LOG_MINOR_DCS_EDID_EMULATOR = 0, ++ LOG_MINOR_DCS_DONGLE_DETECTION, ++ ++/** ++* @brief defines minor category for DCP ++* ++* @note should define sub functionality related to Dcp ++*/ ++ LOG_MINOR_DCP_GAMMA_GRPH = 0, ++ LOG_MINOR_DCP_GAMMA_OVL, ++ LOG_MINOR_DCP_CSC_GRPH, ++ LOG_MINOR_DCP_CSC_OVL, ++ LOG_MINOR_DCP_SCALER, ++ LOG_MINOR_DCP_SCALER_TABLES, ++/** ++* @brief defines minor category for LogMajor_Bios ++* ++* @note define sub functionality related to BiosParser ++*/ ++ LOG_MINOR_BIOS_CMD_TABLE = 0, ++/** ++* @brief defines minor category for LogMajor_Bios ++* ++* @note define sub functionality related to BiosParser ++*/ ++ LOG_MINOR_REGISTER_INDEX = 0, ++/** ++* @brief defines minor category for info packets ++* ++*/ ++ LOG_MINOR_INFO_PACKETS_HDMI = 0, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_DSAT ++* ++* @note define sub functionality related to Display System Analysis Tool ++*/ ++ LOG_MINOR_DSAT_LOGGER = 0, ++ LOG_MINOR_DSAT_GET_EDID, ++ LOG_MINOR_DSAT_EDID_OVERRIDE, ++ LOG_MINOR_DSAT_SET_ADJUSTMENTS, ++ LOG_MINOR_DSAT_GET_ADJUSTMENTS, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_EC ++* ++* @note define sub functionality related to External components notifications ++*/ ++ LOG_MINOR_EC_PPLIB_NOTIFY = 0, ++ LOG_MINOR_EC_PPLIB_QUERY, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_BWM ++* ++* @note define sub functionality related to Bandwidth Manager ++*/ ++ LOG_MINOR_BWM_MODE_VALIDATION = 0, ++ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS, ++ ++/** ++* @brief define minor category for LogMajor_ModeEnum ++* ++* @note defines functionality mode enumeration trace ++*/ ++ LOG_MINOR_MODE_ENUM_BEST_VIEW_CANDIDATES = 0, ++ LOG_MINOR_MODE_ENUM_VIEW_SOLUTION, ++ LOG_MINOR_MODE_ENUM_TS_LIST_BUILD, ++ LOG_MINOR_MODE_ENUM_TS_LIST, ++ LOG_MINOR_MODE_ENUM_MASTER_VIEW_LIST, ++ LOG_MINOR_MODE_ENUM_MASTER_VIEW_LIST_UPDATE, ++ ++/** ++* @brief defines minor category for LogMajor_I2C_AUX ++* ++* @note define sub functionality related to I2c and Aux Channel Log ++*/ ++ LOG_MINOR_I2C_AUX_LOG = 0, ++ LOG_MINOR_I2C_AUX_AUX_TIMESTAMP, ++ LOG_MINOR_I2C_AUX_CFG, ++ ++/** ++* @brief defines minor category for LogMajor_LineBuffer ++* ++* @note define sub functionality related to LineBuffer ++*/ ++ LOG_MINOR_LINE_BUFFER_POWERGATING = 0, ++ ++/** ++* @brief defines minor category for LogMajor_HWSS ++* ++* @note define sub functionality related to HWSS ++*/ ++ LOG_MINOR_HWSS_TAPS_VALIDATION = 0, ++ ++/** ++* @brief defines minor category for LogMajor_Optimization ++* ++* @note define sub functionality related to Optimization ++*/ ++ LOG_MINOR_OPTMZ_GENERAL = 0, ++ LOG_MINOR_OPTMZ_DO_NOT_TURN_OFF_VCC_DURING_SET_MODE, ++ ++/** ++* @brief defines minor category for LogMajor_PerfMeasure ++* ++* @note define sub functionality related to Performance measurement dumps ++*/ ++ LOG_MINOR_PERF_MEASURE_GENERAL = 0, ++ LOG_MINOR_PERF_MEASURE_HEAP_MEMORY, ++ ++/** ++* @brief defines minor category for LogMajor_Sync ++* ++* @note define sub functionality related to HW and SW Synchronization ++*/ ++ LOG_MINOR_SYNC_HW_CLOCK_ADJUST = 0, ++ LOG_MINOR_SYNC_TIMING, ++ ++/** ++* @brief defines minor category for LogMajor_Backlight ++* ++* @note define sub functionality related to backlight (including VariBright) ++*/ ++ LOG_MINOR_BACKLIGHT_BRIGHTESS_CAPS = 0, ++ LOG_MINOR_BACKLIGHT_DMCU_DELTALUT, ++ LOG_MINOR_BACKLIGHT_DMCU_BUILD_DELTALUT, ++ LOG_MINOR_BACKLIGHT_INTERFACE, ++ LOG_MINOR_BACKLIGHT_LID, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_TM ++* ++* @note define sub functionality related to Topology Manager ++*/ ++ LOG_MINOR_TM_INFO = 0, ++ LOG_MINOR_TM_IFACE_TRACE, ++ LOG_MINOR_TM_RESOURCES, ++ LOG_MINOR_TM_ENCODER_CTL, ++ LOG_MINOR_TM_ENG_ASN, ++ LOG_MINOR_TM_CONTROLLER_ASN, ++ LOG_MINOR_TM_PWR_GATING, ++ LOG_MINOR_TM_BUILD_DSP_PATH, ++ LOG_MINOR_TM_DISPLAY_DETECT, ++ LOG_MINOR_TM_LINK_SRV, ++ LOG_MINOR_TM_NOT_IMPLEMENTED, ++ LOG_MINOR_TM_COFUNC_PATH, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_DISPLAY_SERVICE ++* ++* @note define sub functionality related to Display Service ++*/ ++ LOG_MINOR_DS_MODE_SETTING = 0, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_FEATURE_OVERRIDE ++* ++* @note define sub functionality related to features in adapter service ++*/ ++ LOG_MINOR_FEATURE_OVERRIDE = 0, ++ ++/** ++* @brief defines minor category for LOG_MAJOR_DETECTION ++* ++* @note define sub functionality related to detection ++*/ ++ LOG_MINOR_DETECTION_EDID_PARSER = 0, ++ LOG_MINOR_DETECTION_DP_CAPS, ++}; ++ ++union logger_flags { ++ struct { ++ uint32_t ENABLE_CONSOLE:1; /* Print to console */ ++ uint32_t ENABLE_BUFFER:1; /* Print to buffer */ ++ uint32_t RESERVED:30; ++ } bits; ++ uint32_t value; ++}; ++ ++struct log_entry { ++ ++ struct dal_logger *logger; ++ enum log_major major; ++ enum log_minor minor; ++ ++ char *buf; ++ uint32_t buf_offset; ++ uint32_t max_buf_bytes; ++}; ++ ++/** ++* Structure for enumerating LogMajors and LogMinors ++*/ ++ ++#define MAX_MAJOR_NAME_LEN 32 ++#define MAX_MINOR_NAME_LEN 32 ++ ++struct log_major_info { ++ enum log_major major; ++ char major_name[MAX_MAJOR_NAME_LEN]; ++}; ++ ++struct log_minor_info { ++ enum log_minor minor; ++ char minor_name[MAX_MINOR_NAME_LEN]; ++}; ++ ++#endif /* __DAL_LOGGER_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/mode_manager_types.h b/drivers/gpu/drm/amd/dal/include/mode_manager_types.h +new file mode 100644 +index 0000000..576b21f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/mode_manager_types.h +@@ -0,0 +1,71 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_MODE_MANAGER_TYPES_H__ ++#define __DAL_MODE_MANAGER_TYPES_H__ ++ ++#include "bit_set.h" ++#include "dc_types.h" ++ ++static inline void stereo_3d_view_reset(struct stereo_3d_view *stereo_3d_view) ++{ ++ stereo_3d_view->view_3d_format = VIEW_3D_FORMAT_NONE; ++ stereo_3d_view->flags.raw = 0; ++} ++ ++bool dal_refresh_rate_is_equal( ++ const struct refresh_rate *lhs, ++ const struct refresh_rate *rhs); ++ ++bool dal_refresh_rate_less_than( ++ const struct refresh_rate *lhs, ++ const struct refresh_rate *rhs); ++ ++void refresh_rate_from_mode_info( ++ struct refresh_rate *, ++ const struct dc_mode_info *); ++bool dal_solution_less_than(const void *lhs, const void *rhs); ++bool dal_view_is_equal(const struct view *lhs, const struct view *rhs); ++ ++struct pixel_format_list { ++ uint32_t set; ++ struct bit_set_iterator_32 iter; ++}; ++ ++void dal_pixel_format_list_reset_iterator(struct pixel_format_list *pfl); ++void dal_pixel_format_list_zero_iterator(struct pixel_format_list *pfl); ++ ++void dal_pixel_format_list_construct( ++ struct pixel_format_list *pfl, ++ uint32_t mask); ++ ++uint32_t dal_pixel_format_list_next(struct pixel_format_list *pfl); ++ ++uint32_t dal_pixel_format_list_get_count( ++ const struct pixel_format_list *pfl); ++enum pixel_format dal_pixel_format_list_get_pixel_format( ++ const struct pixel_format_list *pfl); ++ ++#endif /* __DAL_MODE_MANAGER_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/mode_query_interface.h b/drivers/gpu/drm/amd/dal/include/mode_query_interface.h +new file mode 100644 +index 0000000..1d20e73 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/mode_query_interface.h +@@ -0,0 +1,93 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_MODE_QUERY_INTERFACE_H__ ++#define __DAL_MODE_QUERY_INTERFACE_H__ ++ ++#include "include/set_mode_types.h" ++#include "include/mode_manager_types.h" ++ ++enum query_option { ++ QUERY_OPTION_ALLOW_PAN, ++ QUERY_OPTION_ALLOW_PAN_NO_VIEW_RESTRICTION, ++ QUERY_OPTION_PAN_ON_LIMITED_RESOLUTION_DISP_PATH, ++ QUERY_OPTION_NO_PAN, ++ QUERY_OPTION_NO_PAN_NO_DISPLAY_VIEW_RESTRICTION, ++ QUERY_OPTION_3D_LIMITED_CANDIDATES, ++ QUERY_OPTION_TILED_DISPLAY_PREFERRED, ++ QUERY_OPTION_MAX, ++}; ++ ++struct topology { ++ uint32_t disp_path_num; ++ uint32_t display_index[MAX_COFUNC_PATH]; ++}; ++ ++struct path_mode; ++struct mode_query; ++ ++bool dal_mode_query_pin_path_mode( ++ struct mode_query *mq, ++ const struct path_mode *path_mode); ++ ++const struct render_mode *dal_mode_query_get_current_render_mode( ++ const struct mode_query *mq); ++ ++const struct stereo_3d_view *dal_mode_query_get_current_3d_view( ++ const struct mode_query *mq); ++ ++const struct refresh_rate *dal_mode_query_get_current_refresh_rate( ++ const struct mode_query *mq); ++ ++const struct path_mode_set *dal_mode_query_get_current_path_mode_set( ++ const struct mode_query *mq); ++ ++bool dal_mode_query_select_first(struct mode_query *mq); ++bool dal_mode_query_select_next_render_mode(struct mode_query *mq); ++ ++bool dal_mode_query_select_render_mode(struct mode_query *mq, ++ const struct render_mode *render_mode); ++ ++bool dal_mode_query_select_next_view_3d_format(struct mode_query *mq); ++bool dal_mode_query_select_view_3d_format( ++ struct mode_query *mq, ++ enum view_3d_format format); ++ ++bool dal_mode_query_select_refresh_rate(struct mode_query *mq, ++ const struct refresh_rate *refresh_rate); ++ ++bool dal_mode_query_select_refresh_rate_ex(struct mode_query *mq, ++ uint32_t refresh_rate, ++ bool interlaced); ++ ++bool dal_mode_query_select_next_scaling(struct mode_query *mq); ++ ++bool dal_mode_query_select_next_refresh_rate(struct mode_query *mq); ++ ++bool dal_mode_query_base_select_next_scaling(struct mode_query *mq); ++ ++void dal_mode_query_destroy(struct mode_query **mq); ++ ++#endif /* __DAL_MODE_QUERY_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/mode_timing_list_interface.h b/drivers/gpu/drm/amd/dal/include/mode_timing_list_interface.h +new file mode 100644 +index 0000000..a558fec +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/mode_timing_list_interface.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_MODE_TIMING_LIST_INTERFACE_H__ ++#define __DAL_MODE_TIMING_LIST_INTERFACE_H__ ++ ++ ++struct mode_timing_filter; ++struct mode_timing_list; ++ ++struct mode_timing_list *dal_mode_timing_list_create( ++ struct dal_context *ctx, ++ uint32_t display_index, ++ const struct mode_timing_filter *mt_filter); ++ ++void dal_mode_timing_list_destroy(struct mode_timing_list **mtl); ++ ++ ++uint32_t dal_mode_timing_list_get_count( ++ const struct mode_timing_list *mode_timing_list); ++ ++const struct dc_mode_timing *dal_mode_timing_list_get_timing_at_index( ++ const struct mode_timing_list *mode_timing_list, ++ uint32_t index); ++ ++const struct dc_mode_timing *dal_mode_timing_list_get_single_selected_mode_timing( ++ const struct mode_timing_list *mode_timing_list); ++ ++#endif /*__DAL_MODE_TIMING_LIST_INTERFACE_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/include/overlay_interface.h b/drivers/gpu/drm/amd/dal/include/overlay_interface.h +new file mode 100644 +index 0000000..c33bd73 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/overlay_interface.h +@@ -0,0 +1,137 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_OVERLAY_INTERFACE_H__ ++#define __DAL_OVERLAY_INTERFACE_H__ ++ ++#include "include/overlay_types.h" ++#include "include/display_service_types.h" ++ ++struct ds_overlay; ++struct path_mode_set; ++struct path_mode; ++struct view; ++ ++bool dal_ds_overlay_is_active( ++ struct ds_overlay *ovl, ++ uint32_t display_index); ++ ++uint32_t dal_ds_overlay_get_controller_handle( ++ struct ds_overlay *ovl, ++ uint32_t display_index); ++ ++enum ds_return dal_ds_overlay_alloc( ++ struct ds_overlay *ovl, ++ struct path_mode_set *path_mode_set, ++ uint32_t display_index, ++ struct view *view, ++ struct overlay_data *data); ++ ++enum ds_return dal_ds_overlay_validate( ++ struct ds_overlay *ovl, ++ struct path_mode_set *path_mode_set, ++ uint32_t display_index, ++ struct view *view, ++ struct overlay_data *data); ++ ++enum ds_return dal_ds_overlay_free( ++ struct ds_overlay *ovl, ++ struct path_mode_set *path_mode_set, ++ uint32_t display_index); ++ ++enum ds_return dal_ds_overlay_get_info( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ enum overlay_color_space *color_space, ++ enum overlay_backend_bpp *backend_bpp, ++ enum overlay_alloc_option *alloc_option, ++ enum overlay_format *surface_format); ++ ++enum ds_return dal_ds_overlay_set_otm( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ const struct path_mode *current_path_mode); ++ ++enum ds_return dal_ds_overlay_reset_otm( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ struct path_mode **saved_path_mode); ++ ++/**is in overlay theater mode*/ ++bool dal_ds_overlay_is_in_otm( ++ struct ds_overlay *ovl, ++ uint32_t display_index); ++ ++void dal_ds_overlay_set_matrix( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ const struct overlay_color_matrix *matrix); ++ ++void dal_ds_overlay_reset_matrix( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ enum overlay_csc_matrix_type type); ++ ++const struct overlay_color_matrix *dal_ds_overlay_get_matrix( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ enum overlay_csc_matrix_type type); ++ ++bool dal_ds_overlay_set_color_space( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ enum overlay_color_space space); ++ ++bool dal_ds_overlay_get_display_pixel_encoding( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ enum display_pixel_encoding *pixel_encoding); ++ ++bool dal_ds_overlay_set_display_pixel_encoding( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ enum display_pixel_encoding pixel_encoding); ++ ++bool dal_ds_overlay_reset_display_pixel_encoding( ++ struct ds_overlay *ovl, ++ uint32_t display_index); ++ ++/*After Set Overlay Theatre Mode (OTM) on a display path, ++ * saving the passed setting of Gpu scaling option for later restore*/ ++enum ds_return dal_ds_overlay_save_gpu_scaling_before_otm( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ int32_t timing_sel_before_otm); ++ ++/* After reset Overlay Theatre Mode (OTM) on a display path, ++ * returning the previous Gpu scaling option by SetOverlayTheatreMode*/ ++enum ds_return dal_ds_overlay_get_gpu_scaling_before_otm( ++ struct ds_overlay *ovl, ++ uint32_t display_index, ++ int32_t *timing_sel_before_otm); ++ ++uint32_t dal_ds_overlay_get_num_of_allowed(struct ds_overlay *ovl); ++ ++#endif /* __DAL_OVERLAY_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/overlay_types.h b/drivers/gpu/drm/amd/dal/include/overlay_types.h +new file mode 100644 +index 0000000..c001edf +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/overlay_types.h +@@ -0,0 +1,164 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_OVERLAY_TYPES_H__ ++#define __DAL_OVERLAY_TYPES_H__ ++ ++enum overlay_color_space { ++ OVERLAY_COLOR_SPACE_UNINITIALIZED, ++ OVERLAY_COLOR_SPACE_RGB, /* the first*/ ++ OVERLAY_COLOR_SPACE_BT601, ++ OVERLAY_COLOR_SPACE_BT709, /* the last*/ ++ OVERLAY_COLOR_SPACE_INVALID, ++ ++ /* flag the first and last*/ ++ OVERLAY_COLOR_SPACE_BEGIN = OVERLAY_COLOR_SPACE_RGB, ++ OVERLAY_COLOR_SPACE_END = OVERLAY_COLOR_SPACE_BT709, ++}; ++ ++enum overlay_backend_bpp { ++ OVERLAY_BACKENDBPP_UNINITIALIZED, ++ ++ OVERLAY_BACKEND_BPP_32_FULL_BANDWIDTH,/* the first*/ ++ OVERLAY_BACKEND_BPP_16_FULL_BANDWIDTH, ++ OVERLAY_BACKEND_BPP_32_HALF_BANDWIDTH,/* the last*/ ++ ++ OVERLAY_BACKEND_BPP_INVALID, ++ ++ /* flag the first and last*/ ++ OVERLAY_BACKEND_BPP_BEGIN = OVERLAY_BACKEND_BPP_32_FULL_BANDWIDTH, ++ OVERLAY_BACKEND_BPP_END = OVERLAY_BACKEND_BPP_32_HALF_BANDWIDTH, ++}; ++ ++enum overlay_alloc_option { ++ OVERLAY_ALLOC_OPTION_UNINITIALIZED, ++ ++ OVERLAY_ALLOC_OPTION_APPLY_OVERLAY_CSC, /* the first*/ ++ OVERLAY_ALLOC_OPTION_APPLY_DESKTOP_CSC, /* the last*/ ++ ++ OVERLAY_ALLOC_OPTION_INVALID, ++ ++ /* flag the first and last*/ ++ OVERLAY_ALLOC_OPTION_BEGIN = OVERLAY_ALLOC_OPTION_APPLY_OVERLAY_CSC, ++ OVERLAY_ALLOC_OPTION_END = OVERLAY_ALLOC_OPTION_APPLY_DESKTOP_CSC, ++}; ++ ++enum overlay_format { ++ OVERLAY_FORMAT_UNINITIALIZED, ++ OVERLAY_FORMAT_YUY2, ++ OVERLAY_FORMAT_UYVY, ++ OVERLAY_FORMAT_RGB565, ++ OVERLAY_FORMAT_RGB555, ++ OVERLAY_FORMAT_RGB32, ++ OVERLAY_FORMAT_YUV444, ++ OVERLAY_FORMAT_RGB32_2101010, ++ ++ OVERLAY_FORMAT_INVALID, ++ ++ /* flag the first and last*/ ++ OVERLAY_FORMAT_BEGIN = OVERLAY_FORMAT_YUY2, ++ OVERLAY_FORMAT_END = OVERLAY_FORMAT_RGB32_2101010, ++}; ++ ++enum display_pixel_encoding { ++ DISPLAY_PIXEL_ENCODING_UNDEFINED = 0, ++ DISPLAY_PIXEL_ENCODING_RGB, ++ DISPLAY_PIXEL_ENCODING_YCBCR422, ++ DISPLAY_PIXEL_ENCODING_YCBCR444 ++}; ++ ++union overlay_data_status { ++ uint32_t u32all; ++ struct { ++ uint32_t COLOR_SPACE_SET:1; ++ uint32_t BACKEND_BPP:1; ++ uint32_t ALLOC_OPTION:1; ++ uint32_t SURFACE_FORMAT:1; ++ uint32_t PIXEL_ENCODING:1; ++ uint32_t reserved:27; ++ ++ } bits; ++}; ++ ++struct overlay_data { ++ enum overlay_color_space color_space; ++ enum overlay_backend_bpp backend_bpp; ++ enum overlay_alloc_option alloc_option; ++ enum overlay_format surface_format; ++}; ++ ++enum overlay_csc_matrix_type { ++ OVERLAY_CSC_MATRIX_NOTDEFINED = 0, ++ OVERLAY_CSC_MATRIX_BT709, ++ OVERLAY_CSC_MATRIX_BT601, ++ OVERLAY_CSC_MATRIX_SMPTE240, ++ OVERLAY_CSC_MATRIX_SRGB, ++}; ++ ++#define DEFAULT_APP_MATRIX_DIVIDER 10000 ++#define MAX_OVL_MATRIX_COUNTS 2 ++#define OVL_BT709 0 ++#define OVL_BT601 1 ++ ++#define OVL_MATRIX_ITEM 9 ++#define OVL_MATRIX_OFFSET_ITEM 3 ++ ++struct overlay_color_matrix { ++ enum overlay_csc_matrix_type csc_matrix; ++/*3*3 Gamut Matrix (value is the real value * M_GAMUT_PRECISION_MULTIPLIER)*/ ++ int32_t matrix_settings[OVL_MATRIX_ITEM]; ++ int32_t offsets[OVL_MATRIX_OFFSET_ITEM]; ++}; ++ ++enum setup_adjustment_ovl_value_type { ++ SETUP_ADJUSTMENT_MIN, ++ SETUP_ADJUSTMENT_MAX, ++ SETUP_ADJUSTMENT_DEF, ++ SETUP_ADJUSTMENT_CURRENT, ++ SETUP_ADJUSTMENT_BUNDLE_MIN, ++ SETUP_ADJUSTMENT_BUNDLE_MAX, ++ SETUP_ADJUSTMENT_BUNDLE_DEF, ++ SETUP_ADJUSTMENT_BUNDLE_CURRENT ++}; ++ ++struct overlay_parameter { ++ union { ++ uint32_t u32all; ++ struct { ++ uint32_t VALID_OVL_COLOR_SPACE:1; ++ uint32_t VALID_VALUE_TYPE:1; ++ uint32_t VALID_OVL_SURFACE_FORMAT:1; ++ uint32_t CONFIG_IS_CHANGED:1; ++ uint32_t reserved:28; ++ ++ } bits; ++ }; ++ /*currently colorSpace here packed, continue this list*/ ++ enum overlay_color_space color_space; ++ enum setup_adjustment_ovl_value_type value_type; ++ enum overlay_format surface_format; ++}; ++ ++#endif /* OVERLAY_TYPES_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/include/path_mode_set_interface.h b/drivers/gpu/drm/amd/dal/include/path_mode_set_interface.h +new file mode 100644 +index 0000000..a277010 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/path_mode_set_interface.h +@@ -0,0 +1,107 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_PATH_MODE_SET_INTERFACE_H__ ++#define __DAL_PATH_MODE_SET_INTERFACE_H__ ++ ++/* Set of path modes */ ++struct path_mode_set { ++ union control_flags { ++ struct { ++ uint32_t KEEP_DISPLAY_POWERED_OFF:1; ++ uint32_t UNBLANCK_SOURCE_AFTER_SETMODE:1; ++ uint32_t NODE_FAULT_UNDERSCAN:1; ++ } bits; ++ ++ uint32_t all; ++ } control_flags; ++ ++ struct path_mode path_mode_set[MAX_COFUNC_PATH]; ++ uint32_t count; ++}; ++ ++/* Create path mode set */ ++struct path_mode_set *dal_pms_create(void); ++ ++/* Deallocate path mode set */ ++void dal_pms_destroy( ++ struct path_mode_set **pms); ++ ++/* Create a copy of given path mode set */ ++struct path_mode_set *dal_pms_copy( ++ const struct path_mode_set *copy); ++ ++/* Constructor for path mode set */ ++bool dal_pms_construct( ++ struct path_mode_set *set); ++ ++/* Add a path mode into the set */ ++bool dal_pms_add_path_mode( ++ struct path_mode_set *set, ++ const struct path_mode *path_mode); ++ ++/* Get number of path modes in the set */ ++uint32_t dal_pms_get_path_mode_num( ++ const struct path_mode_set *set); ++ ++/* Return the path mode at the index */ ++const struct path_mode *dal_pms_get_path_mode_at_index( ++ const struct path_mode_set *set, ++ uint32_t index); ++ ++/* Return the path mode for the given display index */ ++const struct path_mode *dal_pms_get_path_mode_for_display_index( ++ const struct path_mode_set *set, ++ uint32_t index); ++ ++/* Remove the path mode at index */ ++bool dal_pms_remove_path_mode_at_index( ++ struct path_mode_set *set, ++ uint32_t index); ++ ++/* Remove the given path mode if it is found in the set */ ++bool dal_pms_remove_path_mode( ++ struct path_mode_set *set, ++ struct path_mode *mode); ++ ++/* Add control flag to keep display powered off */ ++void dal_pms_keep_display_powered_off( ++ struct path_mode_set *set, ++ bool keep); ++ ++/* Return control flag if display needs to be kept powered off */ ++bool dal_pms_is_display_power_off_required( ++ const struct path_mode_set *set); ++ ++/* Add control flag to not use default underscan*/ ++void dal_pms_fallback_remove_default_underscan( ++ struct path_mode_set *set, ++ bool lean); ++ ++/* Return control flag if default underscan is not used */ ++bool dal_pms_is_fallback_no_default_underscan_enabled( ++ struct path_mode_set *set); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/plane_types.h b/drivers/gpu/drm/amd/dal/include/plane_types.h +new file mode 100644 +index 0000000..a2a8939 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/plane_types.h +@@ -0,0 +1,309 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_PLANE_TYPES_H__ ++#define __DAL_PLANE_TYPES_H__ ++ ++#include "scaler_types.h" ++ ++enum display_flip_mode { ++ DISPLAY_FLIP_MODE_VERTICAL = 0, ++ DISPLAY_FLIP_MODE_HORIZONTAL ++}; ++ ++/*rect or view */ ++struct rect_position { ++ uint32_t x; ++ uint32_t y; ++}; ++ ++union plane_config_change_flags { ++ struct { ++ uint32_t MIRROR_FLAGS:1; ++ uint32_t BLEND_FLAGS:1; ++ uint32_t COLORIMETRY:1; ++ uint32_t SCALING_RECTS:1; ++ ++ uint32_t SCALING_QUALITY:1; ++ uint32_t VIDEO_SCAN_FORMAT:1; ++ uint32_t STEREO_FORMAT:1; ++ uint32_t PLANE_SIZE:1; ++ ++ uint32_t TITLING_INFO:1; ++ uint32_t FORMAT:1; ++ uint32_t ROTATION:1; ++ ++ uint32_t RESERVED:21; ++ } bits; ++ uint32_t value; ++}; ++ ++ ++enum array_mode { ++ ARRAY_MODE_LINEAR_GENERAL = 0x00000000, ++ ARRAY_MODE_LINEAR_ALIGNED = 0x00000001, ++ ARRAY_MODE_1D_TILED_THIN1 = 0x00000002, ++ ARRAY_MODE_1D_TILED_THICK = 0x00000003, ++ ARRAY_MODE_2D_TILED_THIN1 = 0x00000004, ++ ARRAY_MODE_PRT_TILED_THIN1 = 0x00000005, ++ ARRAY_MODE_PRT_2D_TILED_THIN1 = 0x00000006, ++ ARRAY_MODE_2D_TILED_THICK = 0x00000007, ++ ARRAY_MODE_2D_TILED_X_THICK = 0x00000008, ++ ARRAY_MODE_PRT_TILED_THICK = 0x00000009, ++ ARRAY_MODE_PRT_2D_TILED_THICK = 0x0000000a, ++ ARRAY_MODE_PRT_3D_TILED_THIN1 = 0x0000000b, ++ ARRAY_MODE_3D_TILED_THIN1 = 0x0000000c, ++ ARRAY_MODE_3D_TILED_THICK = 0x0000000d, ++ ARRAY_MODE_3D_TILED_X_THICK = 0x0000000e, ++ ARRAY_MODE_PRT_3D_TILED_THICK = 0x0000000f ++}; ++ ++/* single enum for grph and video (both luma and chroma) */ ++enum tile_split { ++ TILE_SPLIT_64B = 0x00000000, ++ TILE_SPLIT_128B = 0x00000001, ++ TILE_SPLIT_256B = 0x00000002, ++ TILE_SPLIT_512B = 0x00000003, ++ TILE_SPLIT_1KB = 0x00000004, ++ TILE_SPLIT_2KB = 0x00000005, ++ TILE_SPLIT_4KB = 0x00000006 ++}; ++ ++/* single enum for grph and video (both luma and chroma)*/ ++enum macro_tile_aspect { ++ MACRO_TILE_ASPECT_1 = 0x00000000, ++ MACRO_TILE_ASPECT_2 = 0x00000001, ++ MACRO_TILE_ASPECT_4 = 0x00000002, ++ MACRO_TILE_ASPECT_8 = 0x00000003 ++}; ++ ++enum video_array_mode { ++ VIDEO_ARRAY_MODE_LINEAR_GENERAL = 0x00000000, ++ VIDEO_ARRAY_MODE_LINEAR_ALIGNED = 0x00000001, ++ VIDEO_ARRAY_MODE_1D_TILED_THIN1 = 0x00000002, ++ VIDEO_ARRAY_MODE_1D_TILED_THICK = 0x00000003, ++ VIDEO_ARRAY_MODE_2D_TILED_THIN1 = 0x00000004, ++ VIDEO_ARRAY_MODE_2D_TILED_THICK = 0x00000007, ++ VIDEO_ARRAY_MODE_3D_TILED_THIN1 = 0x0000000c, ++ VIDEO_ARRAY_MODE_3D_TILED_THICK = 0x0000000d ++}; ++ ++/* single enum for grph and video (both luma and chroma)*/ ++enum micro_tile_mode { ++ MICRO_TILE_MODE_DISPLAY = 0x00000000, ++ MICRO_TILE_MODE_THIN = 0x00000001, ++ MICRO_TILE_MODE_DEPTH = 0x00000002, ++ MICRO_TILE_MODE_ROTATED = 0x00000003 ++}; ++ ++/* KK: taken from addrlib*/ ++enum addr_pipe_config { ++ ADDR_PIPE_CONFIG_INVALID = 0, ++ /* 2 pipes */ ++ ADDR_PIPE_CONFIG_P2 = 1, ++ /* 4 pipes */ ++ ADDR_PIPE_CONFIG_P4_8x16 = 5, ++ ADDR_PIPE_CONFIG_P4_16x16 = 6, ++ ADDR_PIPE_CONFIG_P4_16x32 = 7, ++ ADDR_PIPE_CONFIG_P4_32x32 = 8, ++ /* 8 pipes*/ ++ ADDR_PIPE_CONFIG_P8_16x16_8x16 = 9, ++ ADDR_PIPE_CONFIG_P8_16x32_8x16 = 10, ++ ADDR_PIPE_CONFIG_P8_32x32_8x16 = 11, ++ ADDR_PIPE_CONFIG_P8_16x32_16x16 = 12, ++ ADDR_PIPE_CONFIG_P8_32x32_16x16 = 13, ++ ADDR_PIPE_CONFIG_P8_32x32_16x32 = 14, ++ ADDR_PIPE_CONFIG_P8_32x64_32x32 = 15, ++ /* 16 pipes */ ++ ADDR_PIPE_CONFIG_P16_32x32_8x16 = 17, ++ ADDR_PIPE_CONFIG_P16_32x32_16x16 = 18, ++ ADDR_PIPE_CONFIG_MAX = 19 ++}; ++ ++struct plane_surface_config { ++ uint32_t layer_index; ++ /*used in set operation*/ ++ bool enabled; ++ ++ union plane_size plane_size; ++ union plane_tiling_info tiling_info; ++ /* surface pixel format from display manager or fb*/ ++ enum surface_pixel_format format; ++ /*pixel format for DAL internal hardware programming*/ ++ enum pixel_format dal_pixel_format; ++ enum dc_rotation_angle rotation; ++}; ++ ++/* For Caps, maximum taps for each axis is returned*/ ++/* For Set, the requested taps will be used*/ ++struct plane_src_scaling_quality { ++ /* INVALID_TAP_VALUE indicates DAL ++ * decides considering aspect ratio ++ * & bandwidth ++ */ ++ uint32_t h_taps; ++ /* INVALID_TAP_VALUE indicates DAL ++ * decides considering aspect ratio ++ * & bandwidth ++ */ ++ uint32_t v_taps; ++ uint32_t h_taps_c; ++ uint32_t v_taps_c; ++}; ++ ++struct plane_mirror_flags { ++ union { ++ struct { ++ uint32_t vertical_mirror:1; ++ uint32_t horizontal_mirror:1; ++ uint32_t reserved:30; ++ } bits; ++ uint32_t value; ++ }; ++}; ++ ++/* Note some combinations are mutually exclusive*/ ++struct plane_blend_flags { ++ union { ++ struct { ++ uint32_t PER_PIXEL_ALPHA_BLEND:1; ++ uint32_t GLOBAL_ALPHA_BLEND:1; ++ uint32_t RESERVED:30; ++ } bits; ++ uint32_t value; ++ }; ++}; ++ ++enum plane_vid_scan_fmt { ++ PLANE_VID_SCAN_FMT_PROGRESSIVE = 0, ++ PLANE_VID_SCAN_FMT_INTERLACED_TOP_FIRST = 1, ++ PLANE_VID_SCAN_FMT_INTERLACED_BOTTOM_FIRST = 2 ++}; ++ ++ ++struct plane_attributes { ++ /*mirror options */ ++ struct plane_mirror_flags mirror_flags; ++ /*blending options*/ ++ struct plane_blend_flags blend_flags; ++ /*color space */ ++ struct plane_colorimetry colorimetry; ++ ++ struct rect src_rect; ++ struct rect dst_rect; ++ struct rect clip_rect; ++ struct scaling_taps scaling_quality; ++ /*progressive, interlaced*/ ++ enum plane_vid_scan_fmt video_scan_format; ++ enum plane_stereo_format stereo_format; ++}; ++ ++union address_flags { ++ struct { ++ /* always 1 for primary surface, used in get operation*/ ++ uint32_t ENABLE:1; ++ /* set 1 if returned address is from cache*/ ++ uint32_t ADDR_IS_PENDING:1; ++ /* currentFrameIsRightEye for stereo only*/ ++ uint32_t CURRENT_FRAME_IS_RIGHT_EYE:1; ++ uint32_t RESERVED:29; ++ } bits; ++ ++ uint32_t value; ++}; ++ ++struct address_info { ++ /* primary surface will be DAL_LAYER_INDEX_PRIMARY*/ ++ int32_t layer_index; ++ /*the flags to describe the address info*/ ++ union address_flags flags; ++ struct dc_plane_address address; ++}; ++ ++union plane_valid_mask { ++ struct { ++ /* set 1 if config is valid in DalPlane*/ ++ uint32_t SURFACE_CONFIG_IS_VALID:1; ++ /* set 1 if plane_attributes is valid in plane*/ ++ uint32_t PLANE_ATTRIBUTE_IS_VALID:1; ++ uint32_t RESERVED:30; ++ } bits; ++ uint32_t value; ++}; ++ ++union flip_valid_mask { ++ struct { ++ /* set 1 if flip_immediate is ++ * valid in plane_addr_flip_info ++ */ ++ uint32_t FLIP_VALID:1; ++ /* set 1 if addressInfo is ++ * valid in plane_addr_flip_info ++ */ ++ uint32_t ADDRESS_VALID:1; ++ uint32_t RESERVED:30; ++ } bits; ++ uint32_t value; ++}; ++ ++struct plane_addr_flip_info { ++ uint32_t display_index; ++ struct address_info address_info; ++ /* flip on vsync if false . When ++ * flip_immediate is true then ++ * update_duration is unused ++ */ ++ bool flip_immediate; ++ /* 48 Hz support for single and ++ * multi plane cases ,set 0 when ++ * it is unused. ++ */ ++ uint32_t update_duration; ++ union flip_valid_mask mask; ++}; ++ ++struct plane_config { ++ union plane_valid_mask mask; ++ uint32_t display_index; ++ struct plane_surface_config config; ++ struct plane_attributes attributes; ++ struct mp_scaling_data mp_scaling_data; ++ union plane_config_change_flags plane_change_flags; ++}; ++ ++struct plane_validate_config { ++ uint32_t display_index; ++ bool flip_immediate; ++ struct plane_surface_config config; ++ struct plane_attributes attributes; ++}; ++ ++struct view_port { ++ uint32_t display_index; ++ struct rect view_port_rect; ++}; ++ ++#endif ++ +diff --git a/drivers/gpu/drm/amd/dal/include/scaler_types.h b/drivers/gpu/drm/amd/dal/include/scaler_types.h +new file mode 100644 +index 0000000..db52dbc +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/scaler_types.h +@@ -0,0 +1,196 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_SCALER_TYPES_H__ ++#define __DAL_SCALER_TYPES_H__ ++ ++#include "signal_types.h" ++#include "fixed31_32.h" ++#include "dc_types.h" ++ ++enum pixel_type { ++ PIXEL_TYPE_30BPP = 1, ++ PIXEL_TYPE_20BPP ++}; ++ ++/*overscan or window*/ ++struct overscan_info { ++ uint32_t left; ++ uint32_t right; ++ uint32_t top; ++ uint32_t bottom; ++}; ++ ++struct mp_scaling_data { ++ struct rect viewport; ++ struct view dst_res; ++ struct overscan_info overscan; ++ struct scaling_taps taps; ++ struct scaling_ratios ratios; ++}; ++ ++struct scaler_validation_params { ++ uint32_t INTERLACED:1; ++ uint32_t CHROMA_SUB_SAMPLING:1; ++ ++ uint32_t line_buffer_size; ++ uint32_t display_clock; /* in KHz */ ++ uint32_t actual_pixel_clock; /* in KHz */ ++ struct view source_view; ++ struct view dest_view; ++ enum signal_type signal_type; ++ ++ struct scaling_taps taps_requested; ++ enum pixel_format pixel_format; ++ enum dc_rotation_angle rotation; ++}; ++ ++struct adjustment_factor { ++ int32_t adjust; /* Actual adjustment value * lDivider */ ++ uint32_t divider; ++}; ++ ++struct sharpness_adjustment { ++ int32_t sharpness; ++ bool enable_sharpening; ++}; ++ ++enum scaling_options { ++ SCALING_BYPASS = 0, ++ SCALING_ENABLE ++}; ++ ++/* same as Hw register */ ++enum filter_type { ++ FILTER_TYPE_V_LOW_PASS = 0x0, ++ FILTER_TYPE_V_HIGH_PASS = 0x1, ++ FILTER_TYPE_H_LUMA = 0x2, ++ FILTER_TYPE_H_CHROMA = 0x3 ++}; ++ ++/* Validation Result enumeration */ ++enum scaler_validation_code { ++ SCALER_VALIDATION_OK = 0, ++ SCALER_VALIDATION_INVALID_INPUT_PARAMETERS, ++ SCALER_VALIDATION_SCALING_RATIO_NOT_SUPPORTED, ++ SCALER_VALIDATION_SOURCE_VIEW_WIDTH_EXCEEDING_LIMIT, ++ SCALER_VALIDATION_DISPLAY_CLOCK_BELOW_PIXEL_CLOCK, ++ SCALER_VALIDATION_FAILURE_PREDEFINED_TAPS_NUMBER ++}; ++ ++ ++#define FILTER_TYPE_MASK 0x0000000FL ++#define TWO_TAPS 2 ++ ++struct init_int_and_frac { ++ uint32_t integer; ++ uint32_t fraction; ++}; ++ ++struct scl_ratios_inits { ++ uint32_t bottom_enable; ++ uint32_t h_int_scale_ratio; ++ uint32_t v_int_scale_ratio; ++ struct init_int_and_frac h_init; ++ struct init_int_and_frac v_init; ++ struct init_int_and_frac v_init_bottom; ++}; ++ ++union scaler_flags { ++ uint32_t raw; ++ struct { ++ uint32_t INTERLACED:1; ++ uint32_t DOUBLE_SCAN_MODE:1; ++ /* this one is legacy flag only used in DCE80 */ ++ uint32_t RGB_COLOR_SPACE:1; ++ uint32_t PIPE_LOCK_REQ:1; ++ /* 4 */ ++ uint32_t WIDE_DISPLAY:1; ++ uint32_t OTHER_PIPE:1; ++ uint32_t SHOULD_PROGRAM_VIEWPORT:1; ++ uint32_t SHOULD_UNLOCK:1; ++ /* 8 */ ++ uint32_t SHOULD_PROGRAM_ALPHA:1; ++ uint32_t SHOW_COLOURED_BORDER:1; ++ ++ uint32_t RESERVED:22; ++ } bits; ++}; ++ ++struct scaler_data { ++ struct view src_res; ++ struct view dst_res; ++ struct overscan_info overscan; ++ struct scaling_taps taps; ++ struct adjustment_factor scale_ratio_hp_factor; ++ struct adjustment_factor scale_ratio_lp_factor; ++ enum pixel_type pixel_type; /*legacy*/ ++ struct sharpness_adjustment sharp_gain; ++ ++ union scaler_flags flags; ++ int32_t h_sharpness; ++ int32_t v_sharpness; ++ ++ struct view src_res_wide_display; ++ struct view dst_res_wide_display; ++ ++ /* it is here because of the HW bug in NI (UBTS #269539) ++ causes glitches in this VBI signal. It shouldn't change after ++ initialization, kind of a const */ ++ const struct hw_crtc_timing *hw_crtc_timing; ++ ++ struct rect viewport; ++ ++ enum pixel_format dal_pixel_format;/*plane concept*/ ++ /*stereoformat TODO*/ ++ /*hwtotation TODO*/ ++ ++ const struct scaling_ratios *ratios; ++}; ++ ++enum bypass_type { ++ /* 00 - 00 - Manual Centering, Manual Replication */ ++ BYPASS_TYPE_MANUAL = 0, ++ /* 01 - 01 - Auto-Centering, No Replication */ ++ BYPASS_TYPE_AUTO_CENTER = 1, ++ /* 02 - 10 - Auto-Centering, Auto-Replication */ ++ BYPASS_TYPE_AUTO_REPLICATION = 3 ++}; ++ ++struct replication_factor { ++ uint32_t h_manual; ++ uint32_t v_manual; ++}; ++ ++enum ram_filter_type { ++ FILTER_TYPE_RGB_Y_VERTICAL = 0, /* 0 - RGB/Y Vertical filter */ ++ FILTER_TYPE_CBCR_VERTICAL = 1, /* 1 - CbCr Vertical filter */ ++ FILTER_TYPE_RGB_Y_HORIZONTAL = 2, /* 1 - RGB/Y Horizontal filter */ ++ FILTER_TYPE_CBCR_HORIZONTAL = 3, /* 3 - CbCr Horizontal filter */ ++ FILTER_TYPE_ALPHA_VERTICAL = 4, /* 4 - Alpha Vertical filter. */ ++ FILTER_TYPE_ALPHA_HORIZONTAL = 5, /* 5 - Alpha Horizontal filter. */ ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/set_mode_params_interface.h b/drivers/gpu/drm/amd/dal/include/set_mode_params_interface.h +new file mode 100644 +index 0000000..e4f52c4 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/set_mode_params_interface.h +@@ -0,0 +1,101 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_SET_MODE_PARAMS_INTERFACE_H__ ++#define __DAL_SET_MODE_PARAMS_INTERFACE_H__ ++ ++struct set_mode_params; ++ ++struct set_mode_params_init_data { ++ struct hw_sequencer *hws; ++ struct dal_context *ctx; ++ struct topology_mgr *tm; ++}; ++ ++struct view_stereo_3d_support dal_set_mode_params_get_stereo_3d_support( ++ struct set_mode_params *smp, ++ uint32_t display_index, ++ enum dc_timing_3d_format); ++ ++bool dal_set_mode_params_update_view_on_path( ++ struct set_mode_params *smp, ++ uint32_t display_index, ++ const struct view *vw); ++ ++bool dal_set_mode_params_update_mode_timing_on_path( ++ struct set_mode_params *smp, ++ uint32_t display_index, ++ const struct dc_mode_timing *mode_timing, ++ enum view_3d_format format); ++ ++bool dal_set_mode_params_update_scaling_on_path( ++ struct set_mode_params *smp, ++ uint32_t display_index, ++ enum scaling_transformation st); ++ ++bool dal_set_mode_params_update_pixel_format_on_path( ++ struct set_mode_params *smp, ++ uint32_t display_index, ++ enum pixel_format pf); ++ ++bool dal_set_mode_params_update_tiling_mode_on_path( ++ struct set_mode_params *smp, ++ uint32_t display_index, ++ enum tiling_mode tm); ++ ++bool dal_set_mode_params_is_path_mode_set_supported( ++ struct set_mode_params *smp); ++ ++bool dal_set_mode_params_is_path_mode_set_guaranteed( ++ struct set_mode_params *smp); ++ ++bool dal_set_mode_params_report_single_selected_timing( ++ struct set_mode_params *smp, ++ uint32_t display_index); ++ ++bool dal_set_mode_params_report_ce_mode_only( ++ struct set_mode_params *smp, ++ uint32_t display_index); ++ ++struct set_mode_params *dal_set_mode_params_create( ++ struct set_mode_params_init_data *init_data); ++ ++bool dal_set_mode_params_init_with_topology( ++ struct set_mode_params *smp, ++ const uint32_t display_indicies[], ++ uint32_t idx_num); ++ ++bool dal_set_mode_params_is_multiple_pixel_encoding_supported( ++ struct set_mode_params *smp, ++ uint32_t display_index); ++ ++enum dc_pixel_encoding dal_set_mode_params_get_default_pixel_format_preference( ++ struct set_mode_params *smp, ++ unsigned int display_index); ++ ++void dal_set_mode_params_destroy( ++ struct set_mode_params **set_mode_params); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/set_mode_types.h b/drivers/gpu/drm/amd/dal/include/set_mode_types.h +new file mode 100644 +index 0000000..3647815 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/set_mode_types.h +@@ -0,0 +1,285 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_SET_MODE_TYPES_H__ ++#define __DAL_SET_MODE_TYPES_H__ ++ ++#include "adjustment_types.h" ++#include "hw_adjustment_types.h" ++#include "include/plane_types.h" ++#include "dc_types.h" ++ ++/* Forward declaration */ ++struct dc_mode_timing; ++struct display_path; ++ ++/* State of stereo 3D for workstation */ ++enum ws_stereo_state { ++ WS_STEREO_STATE_INACTIVE = 0, ++ WS_STEREO_STATE_ACTIVE, ++ WS_STEREO_STATE_ACTIVE_MASTER ++}; ++ ++/* GTC group number */ ++enum gtc_group { ++ GTC_GROUP_DISABLED, ++ GTC_GROUP_1, ++ GTC_GROUP_2, ++ GTC_GROUP_3, ++ GTC_GROUP_4, ++ GTC_GROUP_5, ++ GTC_GROUP_6, ++ GTC_GROUP_MAX ++}; ++ ++/* Adjustment action*/ ++enum adjustment_action { ++ ADJUSTMENT_ACTION_UNDEFINED = 0, ++ ADJUSTMENT_ACTION_VALIDATE, ++ ADJUSTMENT_ACTION_SET_ADJUSTMENT ++}; ++ ++/* Type of adjustment parameters*/ ++enum adjustment_par_type { ++ ADJUSTMENT_PAR_TYPE_NONE = 0, ++ ADJUSTMENT_PAR_TYPE_TIMING, ++ ADJUSTMENT_PAR_TYPE_MODE ++}; ++ ++/* Method of validation */ ++enum validation_method { ++ VALIDATION_METHOD_STATIC = 0, ++ VALIDATION_METHOD_DYNAMIC ++}; ++ ++/* Info frame packet status */ ++enum info_frame_flag { ++ INFO_PACKET_PACKET_INVALID = 0, ++ INFO_PACKET_PACKET_VALID = 1, ++ INFO_PACKET_PACKET_RESET = 2, ++ INFO_PACKET_PACKET_UPDATE_SCAN_TYPE = 8 ++}; ++ ++/* Info frame types */ ++enum info_frame_type { ++ INFO_FRAME_GAMUT = 0x0A, ++ INFO_FRAME_VENDOR_INFO = 0x81, ++ INFO_FRAME_AVI = 0x82 ++}; ++ ++/* Info frame versions */ ++enum info_frame_version { ++ INFO_FRAME_VERSION_1 = 1, ++ INFO_FRAME_VERSION_2 = 2, ++ INFO_FRAME_VERSION_3 = 3 ++}; ++ ++/* Info frame size */ ++enum info_frame_size { ++ INFO_FRAME_SIZE_AVI = 13, ++ INFO_FRAME_SIZE_VENDOR = 25, ++ INFO_FRAME_SIZE_AUDIO = 10 ++}; ++ ++/* Active format */ ++enum active_format_info { ++ ACTIVE_FORMAT_NO_DATA = 0, ++ ACTIVE_FORMAT_VALID = 1 ++}; ++/* Bar info */ ++enum bar_info { ++ BAR_INFO_NOT_VALID = 0, ++ BAR_INFO_VERTICAL_VALID = 1, ++ BAR_INFO_HORIZONTAL_VALID = 2, ++ BAR_INFO_BOTH_VALID = 3 ++}; ++ ++/* Picture scaling */ ++enum picture_scaling { ++ PICTURE_SCALING_UNIFORM = 0, ++ PICTURE_SCALING_HORIZONTAL = 1, ++ PICTURE_SCALING_VERTICAL = 2, ++ PICTURE_SCALING_BOTH = 3 ++}; ++ ++/* Colorimetry */ ++enum colorimetry { ++ COLORIMETRY_NO_DATA = 0, ++ COLORIMETRY_ITU601 = 1, ++ COLORIMETRY_ITU709 = 2, ++ COLORIMETRY_EXTENDED = 3 ++}; ++ ++/* ColorimetryEx */ ++enum colorimetry_ex { ++ COLORIMETRY_EX_XVYCC601 = 0, ++ COLORIMETRY_EX_XVYCC709 = 1, ++ COLORIMETRY_EX_SYCC601 = 2, ++ COLORIMETRY_EX_ADOBEYCC601 = 3, ++ COLORIMETRY_EX_ADOBERGB = 4, ++ COLORIMETRY_EX_RESERVED5 = 5, ++ COLORIMETRY_EX_RESERVED6 = 6, ++ COLORIMETRY_EX_RESERVED7 = 7 ++}; ++ ++/* Active format aspect ratio */ ++enum active_format_aspect_ratio { ++ ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE = 8, ++ ACTIVE_FORMAT_ASPECT_RATIO_4_3 = 9, ++ ACTIVE_FORMAT_ASPECT_RATIO_16_9 = 0XA, ++ ACTIVE_FORMAT_ASPECT_RATIO_14_9 = 0XB ++}; ++ ++/* RGB quantization range */ ++enum rgb_quantization_range { ++ RGB_QUANTIZATION_DEFAULT_RANGE = 0, ++ RGB_QUANTIZATION_LIMITED_RANGE = 1, ++ RGB_QUANTIZATION_FULL_RANGE = 2, ++ RGB_QUANTIZATION_RESERVED = 3 ++}; ++ ++/* YYC quantization range */ ++enum yyc_quantization_range { ++ YYC_QUANTIZATION_LIMITED_RANGE = 0, ++ YYC_QUANTIZATION_FULL_RANGE = 1, ++ YYC_QUANTIZATION_RESERVED2 = 2, ++ YYC_QUANTIZATION_RESERVED3 = 3 ++}; ++ ++/* Rotation capability */ ++struct rotation_capability { ++ bool ROTATION_ANGLE_0_CAP:1; ++ bool ROTATION_ANGLE_90_CAP:1; ++ bool ROTATION_ANGLE_180_CAP:1; ++ bool ROTATION_ANGLE_270_CAP:1; ++}; ++ ++/* Underscan position and size */ ++struct ds_underscan_desc { ++ uint32_t x; ++ uint32_t y; ++ uint32_t width; ++ uint32_t height; ++}; ++ ++/* View, timing and other mode related information */ ++struct path_mode { ++ struct view view; ++ struct rect_position view_position; ++ enum view_3d_format view_3d_format; ++ const struct dc_mode_timing *mode_timing; ++ enum scaling_transformation scaling; ++ enum pixel_format pixel_format; ++ uint32_t display_path_index; ++ enum tiling_mode tiling_mode; ++ enum dc_rotation_angle rotation_angle; ++ bool is_tiling_rotated; ++ struct rotation_capability rotation_capability; ++}; ++ ++struct hdmi_info_frame_header { ++ uint8_t info_frame_type; ++ uint8_t version; ++ uint8_t length; ++}; ++ ++#pragma pack(push) ++#pragma pack(1) ++struct info_packet_raw_data { ++ uint8_t hb0; ++ uint8_t hb1; ++ uint8_t hb2; ++ uint8_t sb[28]; /* sb0~sb27 */ ++}; ++ ++union hdmi_info_packet { ++ struct avi_info_frame { ++ struct hdmi_info_frame_header header; ++ ++ uint8_t CHECK_SUM:8; ++ ++ uint8_t S0_S1:2; ++ uint8_t B0_B1:2; ++ uint8_t A0:1; ++ uint8_t Y0_Y1_Y2:3; ++ ++ uint8_t R0_R3:4; ++ uint8_t M0_M1:2; ++ uint8_t C0_C1:2; ++ ++ uint8_t SC0_SC1:2; ++ uint8_t Q0_Q1:2; ++ uint8_t EC0_EC2:3; ++ uint8_t ITC:1; ++ ++ uint8_t VIC0_VIC7:8; ++ ++ uint8_t PR0_PR3:4; ++ uint8_t CN0_CN1:2; ++ uint8_t YQ0_YQ1:2; ++ ++ uint16_t bar_top; ++ uint16_t bar_bottom; ++ uint16_t bar_left; ++ uint16_t bar_right; ++ ++ uint8_t reserved[14]; ++ } bits; ++ ++ struct info_packet_raw_data packet_raw_data; ++}; ++ ++struct info_packet { ++ enum info_frame_flag flags; ++ union hdmi_info_packet info_packet_hdmi; ++}; ++ ++struct info_frame { ++ struct info_packet avi_info_packet; ++ struct info_packet gamut_packet; ++ struct info_packet vendor_info_packet; ++ struct info_packet spd_info_packet; ++}; ++ ++ ++/* Adjustment parameter */ ++struct adjustment_parameters { ++ enum adjustment_par_type type; ++ struct { ++ enum adjustment_id ajd_id; ++ enum hw_adjustment_id adj_id_hw; ++ } timings; ++}; ++ ++/* Parameters for adjustments*/ ++struct adjustment_params { ++ enum adjustment_action action; ++ struct adjustment_parameters params; ++ const struct display_path *affected_path; ++}; ++ ++#pragma pack(pop) ++ ++#endif /* __DAL_SET_MODE_TYPES_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/signal_types.h b/drivers/gpu/drm/amd/dal/include/signal_types.h +new file mode 100644 +index 0000000..e95e821 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/signal_types.h +@@ -0,0 +1,58 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_SIGNAL_TYPES_H__ ++#define __DC_SIGNAL_TYPES_H__ ++ ++enum signal_type { ++ SIGNAL_TYPE_NONE = 0L, /* no signal */ ++ SIGNAL_TYPE_DVI_SINGLE_LINK = (1 << 0), ++ SIGNAL_TYPE_DVI_DUAL_LINK = (1 << 1), ++ SIGNAL_TYPE_HDMI_TYPE_A = (1 << 2), ++ SIGNAL_TYPE_LVDS = (1 << 3), ++ SIGNAL_TYPE_RGB = (1 << 4), ++ SIGNAL_TYPE_DISPLAY_PORT = (1 << 5), ++ SIGNAL_TYPE_DISPLAY_PORT_MST = (1 << 6), ++ SIGNAL_TYPE_EDP = (1 << 7), ++ SIGNAL_TYPE_WIRELESS = (1 << 8), /* Wireless Display */ ++ ++ SIGNAL_TYPE_COUNT = 9, ++ SIGNAL_TYPE_ALL = (1 << SIGNAL_TYPE_COUNT) - 1 ++}; ++ ++/* help functions for signal types manipulation */ ++bool dc_is_hdmi_signal(enum signal_type signal); ++bool dc_is_dp_sst_signal(enum signal_type signal); ++bool dc_is_dp_signal(enum signal_type signal); ++bool dc_is_dp_external_signal(enum signal_type signal); ++bool dc_is_analog_signal(enum signal_type signal); ++bool dc_is_embedded_signal(enum signal_type signal); ++bool dc_is_dvi_signal(enum signal_type signal); ++bool dc_is_dvi_single_link_signal(enum signal_type signal); ++bool dc_is_dual_link_signal(enum signal_type signal); ++bool dc_is_audio_capable_signal(enum signal_type signal); ++bool dc_is_digital_encoder_compatible_signal(enum signal_type signal); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/stream_encoder_types.h b/drivers/gpu/drm/amd/dal/include/stream_encoder_types.h +new file mode 100644 +index 0000000..0d3e67c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/stream_encoder_types.h +@@ -0,0 +1,16 @@ ++/* ++ * stream_encoder_types.h ++ * ++ */ ++#include "encoder_interface.h" ++ ++#ifndef STREAM_ENCODER_TYPES_H_ ++#define STREAM_ENCODER_TYPES_H_ ++ ++struct stream_encoder { ++ enum engine_id id; ++ struct adapter_service *adapter_service; ++ struct dc_context *ctx; ++}; ++ ++#endif /* STREAM_ENCODER_TYPES_H_ */ +diff --git a/drivers/gpu/drm/amd/dal/include/timing_generator_types.h b/drivers/gpu/drm/amd/dal/include/timing_generator_types.h +new file mode 100644 +index 0000000..9c4d92d +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/timing_generator_types.h +@@ -0,0 +1,150 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_TIMING_GENERATOR_TYPES_H__ ++#define __DAL_TIMING_GENERATOR_TYPES_H__ ++ ++#include "include/grph_csc_types.h" ++ ++/** ++ * These parameters are required as input when doing blanking/Unblanking ++*/ ++struct crtc_black_color { ++ uint32_t black_color_r_cr; ++ uint32_t black_color_g_y; ++ uint32_t black_color_b_cb; ++}; ++ ++/* Contains CRTC vertical/horizontal pixel counters */ ++struct crtc_position { ++ uint32_t vertical_count; ++ uint32_t horizontal_count; ++ uint32_t nominal_vcount; ++}; ++ ++/* ++ * Parameters to enable/disable stereo 3D mode on CRTC ++ * - rightEyePolarity: if true, '0' means left eye image and '1' means right ++ * eye image. ++ * if false, '0' means right eye image and '1' means left eye image ++ * - framePacked: true when HDMI 1.4a FramePacking 3D format ++ * enabled/disabled ++ */ ++struct crtc_stereo_parameters { ++ uint8_t PROGRAM_STEREO:1; ++ uint8_t PROGRAM_POLARITY:1; ++ uint8_t RIGHT_EYE_POLARITY:1; ++ uint8_t FRAME_PACKED:1; ++}; ++ ++struct crtc_stereo_status { ++ uint8_t ENABLED:1; ++ uint8_t CURRENT_FRAME_IS_RIGHT_EYE:1; ++ uint8_t CURRENT_FRAME_IS_ODD_FIELD:1; ++ uint8_t FRAME_PACKED:1; ++ uint8_t PENDING_RESET:1; ++}; ++ ++enum dcp_gsl_purpose { ++ DCP_GSL_PURPOSE_SURFACE_FLIP = 0, ++ DCP_GSL_PURPOSE_STEREO3D_PHASE, ++ DCP_GSL_PURPOSE_UNDEFINED ++}; ++ ++struct dcp_gsl_params { ++ enum sync_source gsl_group; ++ enum dcp_gsl_purpose gsl_purpose; ++ bool timing_server; ++ bool overlay_present; ++ bool gsl_paused; ++}; ++ ++struct vbi_end_signal_setup { ++ uint32_t minimum_interval_in_us; /* microseconds */ ++ uint32_t pixel_clock; /* in KHz */ ++ bool scaler_enabled; ++ bool interlace; ++ uint32_t src_height; ++ uint32_t overscan_top; ++ uint32_t overscan_bottom; ++ uint32_t v_total; ++ uint32_t v_addressable; ++ uint32_t h_total; ++}; ++ ++#define LEFT_EYE_3D_PRIMARY_SURFACE 1 ++#define RIGHT_EYE_3D_PRIMARY_SURFACE 0 ++ ++enum test_pattern_dyn_range { ++ TEST_PATTERN_DYN_RANGE_VESA = 0, ++ TEST_PATTERN_DYN_RANGE_CEA ++}; ++ ++enum test_pattern_mode { ++ TEST_PATTERN_MODE_COLORSQUARES_RGB = 0, ++ TEST_PATTERN_MODE_COLORSQUARES_YCBCR601, ++ TEST_PATTERN_MODE_COLORSQUARES_YCBCR709, ++ TEST_PATTERN_MODE_VERTICALBARS, ++ TEST_PATTERN_MODE_HORIZONTALBARS, ++ TEST_PATTERN_MODE_SINGLERAMP_RGB, ++ TEST_PATTERN_MODE_DUALRAMP_RGB ++}; ++ ++enum test_pattern_color_format { ++ TEST_PATTERN_COLOR_FORMAT_BPC_6 = 0, ++ TEST_PATTERN_COLOR_FORMAT_BPC_8, ++ TEST_PATTERN_COLOR_FORMAT_BPC_10, ++ TEST_PATTERN_COLOR_FORMAT_BPC_12 ++}; ++ ++enum controller_dp_test_pattern { ++ CONTROLLER_DP_TEST_PATTERN_D102 = 0, ++ CONTROLLER_DP_TEST_PATTERN_SYMBOLERROR, ++ CONTROLLER_DP_TEST_PATTERN_PRBS7, ++ CONTROLLER_DP_TEST_PATTERN_COLORSQUARES, ++ CONTROLLER_DP_TEST_PATTERN_VERTICALBARS, ++ CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS, ++ CONTROLLER_DP_TEST_PATTERN_COLORRAMP, ++ CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, ++ CONTROLLER_DP_TEST_PATTERN_RESERVED_8, ++ CONTROLLER_DP_TEST_PATTERN_RESERVED_9, ++ CONTROLLER_DP_TEST_PATTERN_RESERVED_A, ++ CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ++}; ++ ++struct timing_generator { ++ uint32_t *regs; ++ struct bios_parser *bp; ++ enum controller_id controller_id; ++ struct dc_context *ctx; ++ uint32_t max_h_total; ++ uint32_t max_v_total; ++ ++ uint32_t min_h_blank; ++ uint32_t min_h_front_porch; ++ uint32_t min_h_back_porch; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/timing_list_query_interface.h b/drivers/gpu/drm/amd/dal/include/timing_list_query_interface.h +new file mode 100644 +index 0000000..16e3521 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/timing_list_query_interface.h +@@ -0,0 +1,69 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_TIMING_LIST_QUERY_INTERFACE_H__ ++#define __DAL_TIMING_LIST_QUERY_INTERFACE_H__ ++ ++/* External dependencies */ ++#include "include/dcs_interface.h" ++ ++/* Forward declarations */ ++struct dal; ++struct dal_timing_list_query; ++ ++enum timing_support_level { ++ TIMING_SUPPORT_LEVEL_UNDEFINED, ++ /* assumed to be guaranteed supported by display, ++ * usually one timing is marked as native */ ++ TIMING_SUPPORT_LEVEL_NATIVE, ++ /* user wants DAL to drive this timing as if Display supports it */ ++ TIMING_SUPPORT_LEVEL_GUARANTEED, ++ /* user wants DAL to drive this timing even if display ++ * may not support it */ ++ TIMING_SUPPORT_LEVEL_NOT_GUARANTEED ++}; ++ ++struct timing_list_query_init_data { ++ struct dal *dal; /* an instance of DAL */ ++ struct timing_service *timing_srv; ++ struct dcs *dcs; ++ uint32_t display_index; ++}; ++ ++struct dal_timing_list_query *dal_timing_list_query_create( ++ struct timing_list_query_init_data *init_data); ++ ++void dal_timing_list_query_destroy(struct dal_timing_list_query **tlsq); ++ ++/* Get count of mode timings in the list. */ ++uint32_t dal_timing_list_query_get_mode_timing_count( ++ const struct dal_timing_list_query *tlsq); ++ ++const struct dc_mode_timing *dal_timing_list_query_get_mode_timing_at_index( ++ const struct dal_timing_list_query *tlsq, ++ uint32_t index); ++ ++ ++#endif /* __DAL_TIMING_LIST_QUERY_INTERFACE_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/vector.h b/drivers/gpu/drm/amd/dal/include/vector.h +new file mode 100644 +index 0000000..8233b7c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/vector.h +@@ -0,0 +1,150 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_VECTOR_H__ ++#define __DAL_VECTOR_H__ ++ ++struct vector { ++ uint8_t *container; ++ uint32_t struct_size; ++ uint32_t count; ++ uint32_t capacity; ++ struct dc_context *ctx; ++}; ++ ++bool dal_vector_construct( ++ struct vector *vector, ++ struct dc_context *ctx, ++ uint32_t capacity, ++ uint32_t struct_size); ++ ++struct vector *dal_vector_create( ++ struct dc_context *ctx, ++ uint32_t capacity, ++ uint32_t struct_size); ++ ++/* 'initial_value' is optional. If initial_value not supplied, ++ * each "structure" in the vector will contain zeros by default. */ ++struct vector *dal_vector_presized_create( ++ struct dc_context *ctx, ++ uint32_t size, ++ void *initial_value, ++ uint32_t struct_size); ++ ++void dal_vector_destruct( ++ struct vector *vector); ++ ++void dal_vector_destroy( ++ struct vector **vector); ++ ++uint32_t dal_vector_get_count( ++ const struct vector *vector); ++ ++/* dal_vector_insert_at ++ * reallocate container if necessary ++ * then shell items at right and insert ++ * return if the container modified ++ * do not check that index belongs to container ++ * since the function is private and index is going to be calculated ++ * either with by function or as get_count+1 */ ++bool dal_vector_insert_at( ++ struct vector *vector, ++ const void *what, ++ uint32_t position); ++ ++bool dal_vector_append( ++ struct vector *vector, ++ const void *item); ++ ++/* operator[] */ ++void *dal_vector_at_index( ++ const struct vector *vector, ++ uint32_t index); ++ ++void dal_vector_set_at_index( ++ const struct vector *vector, ++ const void *what, ++ uint32_t index); ++ ++/* create a clone (copy) of a vector */ ++struct vector *dal_vector_clone( ++ const struct vector *vector_other); ++ ++/* dal_vector_remove_at_index ++ * Shifts elements on the right from remove position to the left, ++ * removing an element at position by overwrite means*/ ++bool dal_vector_remove_at_index( ++ struct vector *vector, ++ uint32_t index); ++ ++uint32_t dal_vector_capacity(const struct vector *vector); ++ ++bool dal_vector_reserve(struct vector *vector, uint32_t capacity); ++ ++void dal_vector_clear(struct vector *vector); ++ ++/*************************************************************************** ++ * Macro definitions of TYPE-SAFE versions of vector set/get functions. ++ ***************************************************************************/ ++ ++#define DAL_VECTOR_INSERT_AT(vector_type, type_t) \ ++ static bool vector_type##_vector_insert_at( \ ++ struct vector *vector, \ ++ type_t what, \ ++ uint32_t position) \ ++{ \ ++ return dal_vector_insert_at(vector, what, position); \ ++} ++ ++#define DAL_VECTOR_APPEND(vector_type, type_t) \ ++ static bool vector_type##_vector_append( \ ++ struct vector *vector, \ ++ type_t item) \ ++{ \ ++ return dal_vector_append(vector, item); \ ++} ++ ++/* Note: "type_t" is the ONLY token accepted by "checkpatch.pl" and by ++ * "checkcommit" as *return type*. ++ * For uniformity reasons "type_t" is used for all type-safe macro ++ * definitions here. */ ++#define DAL_VECTOR_AT_INDEX(vector_type, type_t) \ ++ static type_t vector_type##_vector_at_index( \ ++ const struct vector *vector, \ ++ uint32_t index) \ ++{ \ ++ return dal_vector_at_index(vector, index); \ ++} ++ ++#define DAL_VECTOR_SET_AT_INDEX(vector_type, type_t) \ ++ static void vector_type##_vector_set_at_index( \ ++ const struct vector *vector, \ ++ type_t what, \ ++ uint32_t index) \ ++{ \ ++ dal_vector_set_at_index(vector, what, index); \ ++} ++ ++#endif /* __DAL_VECTOR_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/include/video_csc_types.h b/drivers/gpu/drm/amd/dal/include/video_csc_types.h +new file mode 100644 +index 0000000..c229f5a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/video_csc_types.h +@@ -0,0 +1,135 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_VIDEO_CSC_TYPES_H__ ++#define __DAL_VIDEO_CSC_TYPES_H__ ++ ++#include "video_gamma_types.h" ++ ++enum ovl_alpha_blending_mode { ++ OVL_ALPHA_PER_PIXEL_GRPH_ALPHA_MODE = 0, ++ OVL_ALPHA_PER_PIXEL_OVL_ALPHA_MODE ++}; ++ ++enum ovl_color_space { ++ OVL_COLOR_SPACE_UNKNOWN = 0, ++ OVL_COLOR_SPACE_RGB, ++ OVL_COLOR_SPACE_YUV601, ++ OVL_COLOR_SPACE_YUV709 ++}; ++ ++enum ovl_surface_format { ++ OVL_SURFACE_FORMAT_UNKNOWN = 0, ++ OVL_SURFACE_FORMAT_YUY2, ++ OVL_SURFACE_FORMAT_UYVY, ++ OVL_SURFACE_FORMAT_RGB565, ++ OVL_SURFACE_FORMAT_RGB555, ++ OVL_SURFACE_FORMAT_RGB32, ++ OVL_SURFACE_FORMAT_YUV444, ++ OVL_SURFACE_FORMAT_RGB32_2101010 ++}; ++ ++struct ovl_color_adjust_option { ++ uint32_t ALLOW_OVL_RGB_ADJUST:1; ++ uint32_t ALLOW_OVL_TEMPERATURE:1; ++ uint32_t FULL_RANGE:1; /* 0 for limited range it'is default for YUV */ ++ uint32_t OVL_MATRIX:1; ++ uint32_t RESERVED:28; ++}; ++ ++struct overlay_adjust_item { ++ int32_t adjust; /* InInteger */ ++ int32_t adjust_divider; ++}; ++ ++enum overlay_csc_adjust_type { ++ OVERLAY_CSC_ADJUST_TYPE_BYPASS = 0, ++ OVERLAY_CSC_ADJUST_TYPE_HW, /* without adjustments */ ++ OVERLAY_CSC_ADJUST_TYPE_SW /* use adjustments */ ++}; ++ ++enum overlay_gamut_adjust_type { ++ OVERLAY_GAMUT_ADJUST_TYPE_BYPASS = 0, ++ OVERLAY_GAMUT_ADJUST_TYPE_SW /* use adjustments */ ++}; ++ ++#define TEMPERATURE_MATRIX_SIZE 9 ++#define MAXTRIX_SIZE TEMPERATURE_MAXTRIX_SIZE ++#define MAXTRIX_SIZE_WITH_OFFSET 12 ++ ++/* overlay adjustment input */ ++union ovl_csc_flag { ++ uint32_t u_all; ++ struct { ++ uint32_t CONFIG_IS_CHANGED:1; ++ uint32_t RESERVED:31; ++ } bits; ++}; ++ ++struct ovl_csc_adjustment { ++ enum ovl_color_space ovl_cs; ++ struct ovl_color_adjust_option ovl_option; ++ enum dc_color_depth display_color_depth; ++ uint32_t lb_color_depth; ++ enum pixel_format desktop_surface_pixel_format; ++ enum ovl_surface_format ovl_sf; ++ /* API adjustment */ ++ struct overlay_adjust_item overlay_brightness; ++ struct overlay_adjust_item overlay_gamma; ++ struct overlay_adjust_item overlay_contrast; ++ struct overlay_adjust_item overlay_saturation; ++ struct overlay_adjust_item overlay_hue; /* unit in degree from API. */ ++ int32_t f_temperature[TEMPERATURE_MATRIX_SIZE]; ++ uint32_t temperature_divider; ++ /* OEM/Application matrix related. */ ++ int32_t matrix[MAXTRIX_SIZE_WITH_OFFSET]; ++ uint32_t matrix_divider; ++ ++ /* DCE50 parameters */ ++ struct regamma_lut regamma; ++ enum overlay_gamma_adjust adjust_gamma_type; ++ enum overlay_csc_adjust_type adjust_csc_type; ++ enum overlay_gamut_adjust_type adjust_gamut_type; ++ union ovl_csc_flag flag; ++ ++}; ++ ++enum ovl_csc_adjust_item { ++ OVERLAY_BRIGHTNESS = 0, ++ OVERLAY_GAMMA, ++ OVERLAY_CONTRAST, ++ OVERLAY_SATURATION, ++ OVERLAY_HUE, ++ OVERLAY_ALPHA, ++ OVERLAY_ALPHA_PER_PIX, ++ OVERLAY_COLOR_TEMPERATURE ++}; ++ ++struct input_csc_matrix { ++ enum color_space color_space; ++ uint16_t regval[12]; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/include/video_gamma_types.h b/drivers/gpu/drm/amd/dal/include/video_gamma_types.h +new file mode 100644 +index 0000000..dc294b6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/include/video_gamma_types.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_VIDEO_GAMMA_TYPES_H__ ++#define __DAL_VIDEO_GAMMA_TYPES_H__ ++ ++#include "set_mode_types.h" ++ ++enum overlay_gamma_adjust { ++ OVERLAY_GAMMA_ADJUST_BYPASS, ++ OVERLAY_GAMMA_ADJUST_HW, /* without adjustments */ ++ OVERLAY_GAMMA_ADJUST_SW /* use adjustments */ ++ ++}; ++ ++union video_gamma_flag { ++ struct { ++ uint32_t CONFIG_IS_CHANGED:1; ++ uint32_t RESERVED:31; ++ } bits; ++ uint32_t u_all; ++}; ++ ++struct overlay_gamma_parameters { ++ union video_gamma_flag flag; ++ int32_t ovl_gamma_cont; ++ enum overlay_gamma_adjust adjust_type; ++ enum pixel_format desktop_surface; ++ struct regamma_lut regamma; ++ ++ /* here we grow with parameters if necessary */ ++}; ++ ++#endif +-- +2.7.4 + |