From 35eea4f1b20ded08fc0d65891163d03238e3adf6 Mon Sep 17 00:00:00 2001 From: Harry Wentland 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 Acked-by: Alex Deucher --- 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 +#include +#include + +#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 +#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( + LTSettings.laneSettings[lane].postCursor2); + dpcd_lane2[lane].bits.max_post_cursor2_reached = 0; + } + m_pDpcdAccessSrv->WriteDpcdData( + DpcdAddress_Lane0Set2, + reinterpret_cast(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 , 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 means sharing supported on all levels below and including + */ +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