diff options
Diffstat (limited to 'common/recipes-kernel/linux/files/0956-drm-amd-dal-add-core-support-for-Polaris-family-v2.patch')
-rw-r--r-- | common/recipes-kernel/linux/files/0956-drm-amd-dal-add-core-support-for-Polaris-family-v2.patch | 9820 |
1 files changed, 9820 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0956-drm-amd-dal-add-core-support-for-Polaris-family-v2.patch b/common/recipes-kernel/linux/files/0956-drm-amd-dal-add-core-support-for-Polaris-family-v2.patch new file mode 100644 index 00000000..6143ca86 --- /dev/null +++ b/common/recipes-kernel/linux/files/0956-drm-amd-dal-add-core-support-for-Polaris-family-v2.patch @@ -0,0 +1,9820 @@ +From bc3e400b9b554d0e8448d89c7a721f887b612dfc Mon Sep 17 00:00:00 2001 +From: Alex Deucher <alexander.deucher@amd.com> +Date: Tue, 15 Mar 2016 10:53:48 -0400 +Subject: [PATCH 0956/1110] drm/amd/dal: add core support for Polaris family + (v2) + +This adds core dc support for polaris 10 and 11. + +v2: add missing files + +Signed-off-by: Alex Deucher <alexander.deucher@amd.com> +--- + drivers/gpu/drm/amd/dal/dc/Makefile | 4 + + drivers/gpu/drm/amd/dal/dc/adapter/Makefile | 4 + + .../gpu/drm/amd/dal/dc/adapter/adapter_service.c | 12 + + .../adapter/dce112/hw_ctx_adapter_service_dce112.c | 302 +++ + .../adapter/dce112/hw_ctx_adapter_service_dce112.h | 39 + + .../gpu/drm/amd/dal/dc/asic_capability/Makefile | 9 + + .../amd/dal/dc/asic_capability/asic_capability.c | 15 +- + .../dc/asic_capability/polaris10_asic_capability.c | 146 ++ + .../dc/asic_capability/polaris10_asic_capability.h | 36 + + drivers/gpu/drm/amd/dal/dc/audio/Makefile | 8 + + drivers/gpu/drm/amd/dal/dc/audio/audio_base.c | 9 + + .../gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.c | 451 +++++ + .../gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.h | 40 + + .../amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.c | 1923 ++++++++++++++++++++ + .../amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.h | 47 + + drivers/gpu/drm/amd/dal/dc/bios/Makefile | 9 + + .../gpu/drm/amd/dal/dc/bios/bios_parser_helper.c | 6 + + .../gpu/drm/amd/dal/dc/bios/bios_parser_helper.h | 4 + + drivers/gpu/drm/amd/dal/dc/bios/command_table.c | 78 +- + .../gpu/drm/amd/dal/dc/bios/command_table_helper.c | 6 + + .../gpu/drm/amd/dal/dc/bios/command_table_helper.h | 3 + + .../dal/dc/bios/dce112/bios_parser_helper_dce112.c | 480 +++++ + .../dal/dc/bios/dce112/bios_parser_helper_dce112.h | 34 + + .../dc/bios/dce112/command_table_helper_dce112.c | 417 +++++ + .../dc/bios/dce112/command_table_helper_dce112.h | 34 + + drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c | 206 +++ + drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c | 7 + + drivers/gpu/drm/amd/dal/dc/core/dc_resource.c | 22 +- + .../drm/amd/dal/dc/dce110/dce110_hw_sequencer.c | 1 + + .../amd/dal/dc/dce110/dce110_timing_generator.c | 2 +- + drivers/gpu/drm/amd/dal/dc/dce112/Makefile | 10 + + .../drm/amd/dal/dc/dce112/dce112_clock_source.c | 266 +++ + .../drm/amd/dal/dc/dce112/dce112_clock_source.h | 52 + + .../gpu/drm/amd/dal/dc/dce112/dce112_compressor.c | 883 +++++++++ + .../gpu/drm/amd/dal/dc/dce112/dce112_compressor.h | 84 + + .../drm/amd/dal/dc/dce112/dce112_hw_sequencer.c | 178 ++ + .../drm/amd/dal/dc/dce112/dce112_hw_sequencer.h | 36 + + .../drm/amd/dal/dc/dce112/dce112_link_encoder.c | 116 ++ + .../drm/amd/dal/dc/dce112/dce112_link_encoder.h | 41 + + .../gpu/drm/amd/dal/dc/dce112/dce112_mem_input.c | 455 +++++ + .../gpu/drm/amd/dal/dc/dce112/dce112_mem_input.h | 38 + + .../gpu/drm/amd/dal/dc/dce112/dce112_resource.c | 1404 ++++++++++++++ + .../gpu/drm/amd/dal/dc/dce112/dce112_resource.h | 42 + + drivers/gpu/drm/amd/dal/dc/dm_services_types.h | 5 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c | 3 + + drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c | 3 + + drivers/gpu/drm/amd/dal/dc/gpu/Makefile | 8 + + .../amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.c | 89 + + .../amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.h | 33 + + .../amd/dal/dc/gpu/dce112/display_clock_dce112.c | 964 ++++++++++ + .../amd/dal/dc/gpu/dce112/display_clock_dce112.h | 54 + + drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c | 5 +- + drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h | 4 +- + drivers/gpu/drm/amd/dal/dc/irq/irq_service.c | 4 + + drivers/gpu/drm/amd/dal/include/dal_asic_id.h | 14 + + drivers/gpu/drm/amd/dal/include/dal_types.h | 3 + + .../drm/amd/dal/include/display_clock_interface.h | 6 + + 57 files changed, 9146 insertions(+), 8 deletions(-) + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_asic_capability.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_asic_capability.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/Makefile + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.h + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.h + +diff --git a/drivers/gpu/drm/amd/dal/dc/Makefile b/drivers/gpu/drm/amd/dal/dc/Makefile +index 5112ec9..a718674 100644 +--- a/drivers/gpu/drm/amd/dal/dc/Makefile ++++ b/drivers/gpu/drm/amd/dal/dc/Makefile +@@ -5,6 +5,10 @@ + DC_LIBS = adapter asic_capability audio basics bios calcs \ + gpio gpu i2caux irq virtual + ++ifdef CONFIG_DRM_AMD_DAL_DCE11_2 ++DC_LIBS += dce112 ++endif ++ + ifdef CONFIG_DRM_AMD_DAL_DCE11_0 + DC_LIBS += dce110 + endif +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/Makefile b/drivers/gpu/drm/amd/dal/dc/adapter/Makefile +index db1f0e8..370323e 100644 +--- a/drivers/gpu/drm/amd/dal/dc/adapter/Makefile ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/Makefile +@@ -25,6 +25,10 @@ ifdef CONFIG_DRM_AMD_DAL_DCE11_0 + AMD_DAL_FILES += $(AMDDALPATH)/dc/adapter/dce110/hw_ctx_adapter_service_dce110.o + endif + ++ifdef CONFIG_DRM_AMD_DAL_DCE11_2 ++AMD_DAL_FILES += $(AMDDALPATH)/dc/adapter/dce112/hw_ctx_adapter_service_dce112.o ++endif ++ + ############################################################################### + # FPGA Diagnositcs + ############################################################################### +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c +index f7aea01..308d456 100644 +--- a/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c +@@ -49,6 +49,10 @@ + #include "dce110/hw_ctx_adapter_service_dce110.h" + #endif + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "dce112/hw_ctx_adapter_service_dce112.h" ++#endif ++ + #include "diagnostics/hw_ctx_adapter_service_diag.h" + + /* +@@ -664,6 +668,10 @@ static struct hw_ctx_adapter_service *create_hw_ctx( + case DCE_VERSION_11_0: + return dal_adapter_service_create_hw_ctx_dce110(ctx); + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ return dal_adapter_service_create_hw_ctx_dce112(ctx); ++#endif + default: + ASSERT_CRITICAL(false); + return NULL; +@@ -907,6 +915,10 @@ enum dce_version dal_adapter_service_get_dce_version( + case 0x110: + return DCE_VERSION_11_0; + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case 0x112: ++ return DCE_VERSION_11_2; ++#endif + default: + ASSERT_CRITICAL(false); + return DCE_VERSION_UNKNOWN; +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.c b/drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.c +new file mode 100644 +index 0000000..f438998 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.c +@@ -0,0 +1,302 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_services.h" ++ ++#include "../hw_ctx_adapter_service.h" ++ ++#include "hw_ctx_adapter_service_dce112.h" ++ ++#include "include/logger_interface.h" ++#include "include/grph_object_id.h" ++ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_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_dce112, 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_dce112 *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_dce112 *hw_ctx = ++ FROM_HW_CTX(ptr); ++ ++ destruct(hw_ctx); ++ ++ dm_free(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); ++ ++ dm_write_reg(ctx, audio_index_reg_offset[i], value); ++ ++ value = 0; ++ value = dm_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 = dm_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_dce112 *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 = dm_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 = dm_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 DCE112 */ ++} ++ ++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_dce112 *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_dce112( ++ struct dc_context *ctx) ++{ ++ struct hw_ctx_adapter_service_dce112 *hw_ctx = ++ dm_alloc(sizeof(struct hw_ctx_adapter_service_dce112)); ++ ++ if (!hw_ctx) { ++ ASSERT_CRITICAL(false); ++ return NULL; ++ } ++ ++ if (construct(hw_ctx, ctx)) ++ return &hw_ctx->base; ++ ++ ASSERT_CRITICAL(false); ++ ++ dm_free(hw_ctx); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.h b/drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.h +new file mode 100644 +index 0000000..bc60030 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/adapter/dce112/hw_ctx_adapter_service_dce112.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_HW_CTX_ADAPTER_SERVICE_DCE112_H__ ++#define __DAL_HW_CTX_ADAPTER_SERVICE_DCE112_H__ ++ ++struct hw_ctx_adapter_service_dce112 { ++ struct hw_ctx_adapter_service base; ++ uint32_t number_of_connected_audio_endpoints; ++}; ++ ++struct hw_ctx_adapter_service * ++ dal_adapter_service_create_hw_ctx_dce112( ++ struct dc_context *ctx); ++ ++#endif /* __DAL_HW_CTX_ADAPTER_SERVICE_DCE112_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile b/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile +index b243542..e80de2a 100644 +--- a/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile +@@ -46,3 +46,12 @@ AMD_DAL_ASIC_CAPABILITY_DCE11 = \ + + AMD_DAL_FILES += $(AMD_DAL_ASIC_CAPABILITY_DCE11) + endif ++ ++ifdef CONFIG_DRM_AMD_DAL_DCE11_2 ++ASIC_CAPABILITY_DCE112 = polaris10_asic_capability.o ++ ++AMD_DAL_ASIC_CAPABILITY_DCE112 = \ ++ $(addprefix $(AMDDALPATH)/dc/asic_capability/,$(ASIC_CAPABILITY_DCE112)) ++ ++AMD_DAL_FILES += $(AMD_DAL_ASIC_CAPABILITY_DCE112) ++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 +index 75e0e27..aeabfc6 100644 +--- a/drivers/gpu/drm/amd/dal/dc/asic_capability/asic_capability.c ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/asic_capability.c +@@ -44,6 +44,10 @@ + #include "carrizo_asic_capability.h" + #endif + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "polaris10_asic_capability.h" ++#endif ++ + /* + * Initializes asic_capability instance. + */ +@@ -108,7 +112,8 @@ static bool construct( + asic_supported = true; + #endif + break; +- case FAMILY_VI: ++ ++ case FAMILY_VI: + #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) + if (ASIC_REV_IS_TONGA_P(init->hw_internal_rev) || + ASIC_REV_IS_FIJI_P(init->hw_internal_rev)) { +@@ -117,7 +122,15 @@ static bool construct( + break; + } + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ if (ASIC_REV_IS_POLARIS10_P(init->hw_internal_rev) || ++ ASIC_REV_IS_POLARIS11_M(init->hw_internal_rev)) { ++ polaris10_asic_capability_create(cap, init); ++ asic_supported = true; ++ } ++#endif + break; ++ + default: + /* unsupported "chip_family" */ + break; +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_asic_capability.c b/drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_asic_capability.c +new file mode 100644 +index 0000000..9e4fdfa +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_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 "dm_services.h" ++ ++#include "include/asic_capability_interface.h" ++#include "include/asic_capability_types.h" ++ ++#include "polaris10_asic_capability.h" ++ ++#include "atom.h" ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_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 polaris10_asic_capability_create(struct asic_capability *cap, ++ struct hw_asic_id *init) ++{ ++ uint32_t e_fuse_setting; ++ /* ASIC data */ ++ if (ASIC_REV_IS_POLARIS11_M(init->hw_internal_rev)) { ++ cap->data[ASIC_DATA_CONTROLLERS_NUM] = 5; ++ cap->data[ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM] = 5; ++ cap->data[ASIC_DATA_LINEBUFFER_NUM] = 5; ++ cap->data[ASIC_DATA_DIGFE_NUM] = 5; ++ cap->data[ASIC_DATA_CLOCKSOURCES_NUM] = 7; ++ cap->data[ASIC_DATA_MAX_COFUNC_NONDP_DISPLAYS] = 5; ++ cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM] = 5; ++ } else { ++ cap->data[ASIC_DATA_CONTROLLERS_NUM] = 6; ++ cap->data[ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM] = 6; ++ cap->data[ASIC_DATA_LINEBUFFER_NUM] = 6; ++ cap->data[ASIC_DATA_DIGFE_NUM] = 6; ++ cap->data[ASIC_DATA_CLOCKSOURCES_NUM] = 8; ++ cap->data[ASIC_DATA_MAX_COFUNC_NONDP_DISPLAYS] = 6; ++ cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM] = 6; ++ } ++ ++ cap->data[ASIC_DATA_PATH_NUM_PER_DPMST_CONNECTOR] = 4; ++ cap->data[ASIC_DATA_DCE_VERSION] = 0x112; /* DCE 11 */ ++ cap->data[ASIC_DATA_LINEBUFFER_SIZE] = 5124 * 144; ++ cap->data[ASIC_DATA_DRAM_BANDWIDTH_EFFICIENCY] = 70; ++ ++ cap->data[ASIC_DATA_MC_LATENCY] = 3000; ++ cap->data[ASIC_DATA_STUTTERMODE] = 0x200A; ++ cap->data[ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY] = 2; ++ ++ cap->data[ASIC_DATA_MEMORYTYPE_MULTIPLIER] = 4; ++ cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ] = 100; ++ cap->data[ASIC_DATA_NUM_OF_VIDEO_PLANES] = 0; ++ ++ cap->data[ASIC_DATA_MIN_DISPCLK_FOR_UNDERSCAN] = 300000; ++ ++ /* 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 = dm_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; ++ } ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_asic_capability.h b/drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_asic_capability.h +new file mode 100644 +index 0000000..c8aebe1 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/polaris10_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_POLARIS10_ASIC_CAPABILITY_H__ ++#define __DAL_POLARIS10_ASIC_CAPABILITY_H__ ++ ++/* Forward declaration */ ++struct asic_capability; ++ ++/* Create and initialize Polaris10 data */ ++void polaris10_asic_capability_create(struct asic_capability *cap, ++ struct hw_asic_id *init); ++ ++#endif /* __DAL_POLARIS10_ASIC_CAPABILITY_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/Makefile b/drivers/gpu/drm/amd/dal/dc/audio/Makefile +index 2433d90..9a9a64c 100644 +--- a/drivers/gpu/drm/amd/dal/dc/audio/Makefile ++++ b/drivers/gpu/drm/amd/dal/dc/audio/Makefile +@@ -32,3 +32,11 @@ AMD_DAL_AUDIO_DCE11 = $(addprefix $(AMDDALPATH)/dc/audio/dce110/,$(AUDIO_DCE11)) + + AMD_DAL_FILES += $(AMD_DAL_AUDIO_DCE11) + endif ++ ++ifdef CONFIG_DRM_AMD_DAL_DCE11_2 ++AUDIO_DCE112 = audio_dce112.o hw_ctx_audio_dce112.o ++ ++AMD_DAL_AUDIO_DCE112 = $(addprefix $(AMDDALPATH)/dc/audio/dce112/,$(AUDIO_DCE112)) ++ ++AMD_DAL_FILES += $(AMD_DAL_AUDIO_DCE112) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c +index c297d95..a8137e0 100644 +--- a/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c ++++ b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c +@@ -40,6 +40,11 @@ + #include "dce110/hw_ctx_audio_dce110.h" + #endif + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "dce112/audio_dce112.h" ++#include "dce112/hw_ctx_audio_dce112.h" ++#endif ++ + /***** static function : only used within audio.c *****/ + + /* stub for hook functions */ +@@ -281,6 +286,10 @@ struct audio *dal_audio_create( + case DCE_VERSION_11_0: + return dal_audio_create_dce110(init_data); + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ return dal_audio_create_dce112(init_data); ++#endif + default: + BREAK_TO_DEBUGGER(); + return NULL; +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.c b/drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.c +new file mode 100644 +index 0000000..66c32b0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.c +@@ -0,0 +1,451 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_services.h" ++ ++#include "include/logger_interface.h" ++ ++#include "audio_dce112.h" ++ ++/***** static functions *****/ ++ ++static void destruct(struct audio_dce112 *audio) ++{ ++ /*release memory allocated for hw_ctx -- allocated is initiated ++ *by audio_dce112 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_dce112 *audio = NULL; ++ ++ audio = container_of(*ptr, struct audio_dce112, base); ++ ++ destruct(audio); ++ ++ /* release memory allocated for audio_dce112*/ ++ dm_free(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_dce112. ++ */ ++ ++/** ++* 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_dce112_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_dce112 *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_dce112( ++ const struct audio_init_data *init_data) ++{ ++ /*allocate memory for audio_dce112 */ ++ struct audio_dce112 *audio = dm_alloc(sizeof(*audio)); ++ ++ if (audio == NULL) { ++ ASSERT_CRITICAL(audio); ++ return NULL; ++ } ++ /*pointer to base_audio_block of audio_dce112 ==> 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 */ ++ dm_free(audio); ++ return NULL; ++} ++ ++/* Do not need expose construct_dce112 and destruct_dce112 becuase there is ++ *derived object after dce112 ++ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.h b/drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.h +new file mode 100644 +index 0000000..7c8d71c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce112/audio_dce112.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_AUDIO_DCE_112_H__ ++#define __DAL_AUDIO_DCE_112_H__ ++ ++#include "audio/audio.h" ++#include "audio/hw_ctx_audio.h" ++#include "audio/dce112/hw_ctx_audio_dce112.h" ++ ++struct audio_dce112 { ++ struct audio base; ++ /* dce-specific members are following */ ++ /* none */ ++}; ++ ++struct audio *dal_audio_create_dce112(const struct audio_init_data *init_data); ++ ++#endif /*__DAL_AUDIO_DCE_112_H__*/ +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.c b/drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.c +new file mode 100644 +index 0000000..95cb86f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.c +@@ -0,0 +1,1923 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_services.h" ++ ++#include "include/logger_interface.h" ++#include "../hw_ctx_audio.h" ++#include "hw_ctx_audio_dce112.h" ++ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_sh_mask.h" ++ ++#define FROM_BASE(ptr) \ ++ container_of((ptr), struct hw_ctx_audio_dce112, 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, ++ mmDIG4_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, ++ mmDIG5_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL ++}; ++ ++static void destruct( ++ struct hw_ctx_audio_dce112 *hw_ctx_dce112) ++{ ++ dal_audio_destruct_hw_ctx_audio(&hw_ctx_dce112->base); ++} ++ ++static void destroy( ++ struct hw_ctx_audio **ptr) ++{ ++ struct hw_ctx_audio_dce112 *hw_ctx_dce112; ++ ++ hw_ctx_dce112 = container_of( ++ *ptr, struct hw_ctx_audio_dce112, base); ++ ++ destruct(hw_ctx_dce112); ++ /* release memory allocated for struct hw_ctx_audio_dce112 */ ++ dm_free(hw_ctx_dce112); ++ ++ *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); ++ ++ dm_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); ++ dm_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); ++ ++ dm_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 = dm_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 = dm_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); ++ ++ dm_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO_SOURCE, value); ++ } ++ ++ /* module */ ++ { ++ value = dm_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); ++ dm_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO0_MODULE, value); ++ } ++ ++ /* phase */ ++ { ++ value = 0; ++ ++ value = dm_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); ++ ++ dm_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); ++ */ ++ ++ dm_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO_SOURCE, value); ++ } ++ ++ /* module */ ++ { ++ value = 0; ++ ++ value = dm_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); ++ ++ dm_write_reg(hw_ctx->ctx, ++ mmDCCG_AUDIO_DTO1_MODULE, value); ++ } ++ ++ /* phase */ ++ { ++ value = 0; ++ ++ value = dm_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); ++ ++ dm_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 = dm_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); ++ ++ dm_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_AUDIO_PACKET_CONTROL */ ++ { ++ addr = mmAFMT_AUDIO_PACKET_CONTROL + engine_offset[engine_id]; ++ ++ value = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 1, ++ AFMT_AUDIO_PACKET_CONTROL, ++ AFMT_60958_CS_UPDATE); ++ ++ dm_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_AUDIO_PACKET_CONTROL2 */ ++ { ++ addr = mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; ++ ++ value = dm_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); ++ ++ dm_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* HDMI_ACR_PACKET_CONTROL */ ++ { ++ addr = mmHDMI_ACR_PACKET_CONTROL + engine_offset[engine_id]; ++ ++ value = dm_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); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, audio_clock_info.cts_32khz, ++ HDMI_ACR_32_0, ++ HDMI_ACR_CTS_32); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.n_32khz, ++ HDMI_ACR_32_1, ++ HDMI_ACR_N_32); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.cts_44khz, ++ HDMI_ACR_44_0, ++ HDMI_ACR_CTS_44); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.n_44khz, ++ HDMI_ACR_44_1, ++ HDMI_ACR_N_44); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.cts_48khz, ++ HDMI_ACR_48_0, ++ HDMI_ACR_CTS_48); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, audio_clock_info.n_48khz, ++ HDMI_ACR_48_1, ++ HDMI_ACR_N_48); ++ ++ dm_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 = dm_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); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 2, ++ AFMT_60958_1, ++ AFMT_60958_CS_CHANNEL_NUMBER_R); ++ ++ dm_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 = dm_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); ++ ++ dm_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); ++ dm_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); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, ++ 1, ++ AFMT_AUDIO_PACKET_CONTROL, ++ AFMT_60958_CS_UPDATE); ++ ++ dm_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_AUDIO_PACKET_CONTROL2 */ ++ { ++ addr = ++ mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; ++ ++ value = 0; ++ ++ value = dm_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); ++ ++ dm_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* AFMT_INFOFRAME_CONTROL0 */ ++ { ++ addr = ++ mmAFMT_INFOFRAME_CONTROL0 + engine_offset[engine_id]; ++ ++ value = 0; ++ ++ value = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, ++ 1, ++ AFMT_INFOFRAME_CONTROL0, ++ AFMT_AUDIO_INFO_UPDATE); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, ++ 0, ++ AFMT_60958_0, ++ AFMT_60958_CS_CLOCK_ACCURACY); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, mmAFMT_CNTL + engine_offs); ++ ++ /*enable AFMT clock*/ ++ set_reg_field_value(value, enable, ++ AFMT_CNTL, AFMT_AUDIO_CLOCK_EN); ++ dm_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.*/ ++ udelay(1); ++ value = dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ set_reg_field_value(value, 1, ++ DP_SEC_CNTL, ++ DP_SEC_ASP_ENABLE); ++ ++ dm_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); ++ ++ dm_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); ++ ++ dm_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 = dm_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); ++ ++ dm_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) ++ dm_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); ++ ++ dm_write_reg(hw_ctx->ctx, addr, value); ++ } ++ ++ /* Channel allocation */ ++ { ++ const uint32_t addr = ++ mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; ++ uint32_t value = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, ++ channels, ++ AFMT_AUDIO_PACKET_CONTROL2, ++ AFMT_AUDIO_CHANNEL_ENABLE); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 1, ++ AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 0, ++ AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND); ++ ++ dm_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 = dm_read_reg(hw_ctx->ctx, addr); ++ ++ set_reg_field_value(value, 0x70, ++ AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, ++ AUDIO_RATE_CAPABILITIES); ++ ++ dm_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 = dm_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); ++ dm_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_dce112 *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 dce112 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_dce112 is derived from audio directly, not via dce80 */ ++struct hw_ctx_audio *dal_hw_ctx_audio_dce112_create( ++ struct dc_context *ctx, ++ uint32_t azalia_stream_id) ++{ ++ /* allocate memory for struc hw_ctx_audio_dce112 */ ++ struct hw_ctx_audio_dce112 *hw_ctx_dce112 = ++ dm_alloc(sizeof(struct hw_ctx_audio_dce112)); ++ ++ if (!hw_ctx_dce112) { ++ ASSERT_CRITICAL(hw_ctx_dce112); ++ return NULL; ++ } ++ ++ /*return pointer to hw_ctx_audio back to caller -- audio object */ ++ if (construct( ++ hw_ctx_dce112, azalia_stream_id, ctx)) ++ return &hw_ctx_dce112->base; ++ ++ dal_logger_write( ++ ctx->logger, ++ LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_AUDIO, ++ "Failed to create hw_ctx_audio for DCE11\n"); ++ ++ dm_free(hw_ctx_dce112); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.h b/drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.h +new file mode 100644 +index 0000000..af61aad +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/audio/dce112/hw_ctx_audio_dce112.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_DCE112_H__ ++#define __DAL_HW_CTX_AUDIO_DCE112_H__ ++ ++#include "audio/hw_ctx_audio.h" ++ ++struct hw_ctx_audio_dce112 { ++ 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_dce112_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/bios/Makefile b/drivers/gpu/drm/amd/dal/dc/bios/Makefile +index e5c8876..9c90230 100644 +--- a/drivers/gpu/drm/amd/dal/dc/bios/Makefile ++++ b/drivers/gpu/drm/amd/dal/dc/bios/Makefile +@@ -37,3 +37,12 @@ endif + + AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce110/command_table_helper_dce110.o + endif ++ ++ifdef CONFIG_DRM_AMD_DAL_DCE11_2 ++ccflags-y += -DLATEST_ATOM_BIOS_SUPPORT ++ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT ++AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce112/bios_parser_helper_dce112.o ++endif ++ ++AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce112/command_table_helper_dce112.o ++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 +index 4e2bc90..4204798 100644 +--- a/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.c ++++ b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.c +@@ -55,6 +55,12 @@ bool dal_bios_parser_init_bios_helper( + return true; + + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ bp->bios_helper = dal_bios_parser_helper_dce112_get_table(); ++ return true; ++ ++#endif + default: + BREAK_TO_DEBUGGER(); + return false; +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 +index c58b9bb..b93b046 100644 +--- a/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.h ++++ b/drivers/gpu/drm/amd/dal/dc/bios/bios_parser_helper.h +@@ -34,6 +34,10 @@ + #include "dce110/bios_parser_helper_dce110.h" + #endif + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "dce112/bios_parser_helper_dce112.h" ++#endif ++ + struct bios_parser; + + struct vbios_helper_data { +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/command_table.c b/drivers/gpu/drm/amd/dal/dc/bios/command_table.c +index ccd1c7e..22524b3 100644 +--- a/drivers/gpu/drm/amd/dal/dc/bios/command_table.c ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table.c +@@ -104,6 +104,13 @@ static enum bp_result encoder_control_digx_v3( + static enum bp_result encoder_control_digx_v4( + struct bios_parser *bp, + struct bp_encoder_control *cntl); ++ ++#ifdef LATEST_ATOM_BIOS_SUPPORT ++static enum bp_result encoder_control_digx_v5( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl); ++#endif ++ + static void init_encoder_control_dig_v1(struct bios_parser *bp); + + static void init_dig_encoder_control(struct bios_parser *bp) +@@ -112,12 +119,19 @@ static void init_dig_encoder_control(struct bios_parser *bp) + BIOS_CMD_TABLE_PARA_REVISION(DIGxEncoderControl); + + switch (version) { ++ case 2: ++ bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v3; ++ break; + 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; ++ ++#ifdef LATEST_ATOM_BIOS_SUPPORT ++ case 5: ++ bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v5; + break; ++#endif ++ + default: + init_encoder_control_dig_v1(bp); + break; +@@ -302,6 +316,66 @@ static enum bp_result encoder_control_digx_v4( + return result; + } + ++#ifdef LATEST_ATOM_BIOS_SUPPORT ++static enum bp_result encoder_control_digx_v5( ++ struct bios_parser *bp, ++ struct bp_encoder_control *cntl) ++{ ++ enum bp_result result = BP_RESULT_FAILURE; ++ ENCODER_STREAM_SETUP_PARAMETERS_V5 params = {0}; ++ ++ params.ucDigId = (uint8_t)(cntl->engine_id); ++ params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); ++ ++ params.ulPixelClock = cntl->pixel_clock / 10; ++ params.ucDigMode = ++ (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( ++ cntl->signal, ++ cntl->enable_dp_audio)); ++ params.ucLaneNum = (uint8_t)(cntl->lanes_number); ++ ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_888: ++ params.ucBitPerColor = PANEL_8BIT_PER_COLOR; ++ break; ++ case COLOR_DEPTH_101010: ++ params.ucBitPerColor = PANEL_10BIT_PER_COLOR; ++ break; ++ case COLOR_DEPTH_121212: ++ params.ucBitPerColor = PANEL_12BIT_PER_COLOR; ++ break; ++ case COLOR_DEPTH_161616: ++ params.ucBitPerColor = PANEL_16BIT_PER_COLOR; ++ break; ++ default: ++ break; ++ } ++ ++ if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) ++ switch (cntl->color_depth) { ++ case COLOR_DEPTH_101010: ++ params.ulPixelClock = ++ (params.ulPixelClock * 30) / 24; ++ break; ++ case COLOR_DEPTH_121212: ++ params.ulPixelClock = ++ (params.ulPixelClock * 36) / 24; ++ break; ++ case COLOR_DEPTH_161616: ++ params.ulPixelClock = ++ (params.ulPixelClock * 48) / 24; ++ break; ++ default: ++ break; ++ } ++ ++ if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) ++ result = BP_RESULT_OK; ++ ++ return result; ++} ++#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 +index 85a5924..a27db8c 100644 +--- a/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.c ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.c +@@ -55,6 +55,12 @@ bool dal_bios_parser_init_cmd_tbl_helper( + return true; + #endif + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ *h = dal_cmd_tbl_helper_dce112_get_table(); ++ return true; ++#endif ++ + default: + /* Unsupported DCE */ + BREAK_TO_DEBUGGER(); +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 +index a462917..e6a0d19 100644 +--- a/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.h ++++ b/drivers/gpu/drm/amd/dal/dc/bios/command_table_helper.h +@@ -32,6 +32,9 @@ + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) || defined(CONFIG_DRM_AMD_DAL_DCE10_0) + #include "dce110/command_table_helper_dce110.h" + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "dce112/command_table_helper_dce112.h" ++#endif + + struct command_table_helper { + bool (*controller_id_to_atom)(enum controller_id id, uint8_t *atom_id); +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.c b/drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.c +new file mode 100644 +index 0000000..1b0f816 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.c +@@ -0,0 +1,480 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and 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 "dm_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 = dm_read_reg(ctx, addr); ++ ++ value |= ATOM_S6_ACC_MODE; ++ ++ dm_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 = dm_read_reg(ctx, addr); ++ ++ value &= ~ATOM_S3_DEVICE_ACTIVE_MASK; ++ value |= (d->active & ATOM_S3_DEVICE_ACTIVE_MASK); ++ ++ dm_write_reg(ctx, addr, value); ++ ++ /* mmBIOS_SCRATCH_6 = mmBIOS_SCRATCH_0 + ATOM_ACC_CHANGE_INFO_DEF */ ++ addr = mmBIOS_SCRATCH_6; ++ ++ value = dm_read_reg(ctx, addr); ++ ++ value &= ~ATOM_S6_ACC_REQ_MASK; ++ value |= (d->requested & ATOM_S6_ACC_REQ_MASK); ++ ++ dm_write_reg(ctx, addr, value); ++ ++ /* mmBIOS_SCRATCH_5 = mmBIOS_SCRATCH_0 + ATOM_DOS_REQ_INFO_DEF */ ++ addr = mmBIOS_SCRATCH_5; ++ ++ value = dm_read_reg(ctx, addr); ++ ++ value &= ~ATOM_S5_DOS_REQ_DEVICEw0; ++ value |= (d->active & ATOM_S5_DOS_REQ_DEVICEw0); ++ ++ dm_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 = dm_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 = dm_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 = dm_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 = dm_read_reg(ctx, addr); ++ ++ if (connected) ++ value |= update; ++ else ++ value &= ~update; ++ ++ dm_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 = dm_read_reg(ctx, addr); ++ ++ if (state) ++ value |= ATOM_S6_CRITICAL_STATE; ++ else ++ value &= ~ATOM_S6_CRITICAL_STATE; ++ ++ dm_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 = ++ dm_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_dce112_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_dce112_get_table() ++{ ++ return &bios_parser_helper_funcs; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.h b/drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.h +new file mode 100644 +index 0000000..044327e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce112/bios_parser_helper_dce112.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_DCE112_H__ ++#define __DAL_BIOS_PARSER_HELPER_DCE112_H__ ++ ++struct bios_parser_helper; ++ ++/* Initialize BIOS helper functions */ ++const struct bios_parser_helper *dal_bios_parser_helper_dce112_get_table(void); ++ ++#endif /* __DAL_BIOS_PARSER_HELPER_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.c b/drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.c +new file mode 100644 +index 0000000..32ec228 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.c +@@ -0,0 +1,417 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_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_V6_DP; ++ ++ switch (s) { ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ case SIGNAL_TYPE_EDP: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; ++ break; ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_DVI_DUAL_LINK: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; ++ break; ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI; ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST; ++ break; ++ default: ++ atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_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_V6_HPD1_SEL; ++ break; ++ case HPD_SOURCEID2: ++ atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD2_SEL; ++ break; ++ case HPD_SOURCEID3: ++ atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD3_SEL; ++ break; ++ case HPD_SOURCEID4: ++ atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD4_SEL; ++ break; ++ case HPD_SOURCEID5: ++ atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD5_SEL; ++ break; ++ case HPD_SOURCEID6: ++ atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD6_SEL; ++ break; ++ case HPD_SOURCEID_UNKNOWN: ++ default: ++ atom_hpd_sel = 0; ++ break; ++ } ++ return atom_hpd_sel; ++} ++ ++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_V6__DIGA_SEL; ++ break; ++ case ENGINE_ID_DIGB: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGB_SEL; ++ break; ++ case ENGINE_ID_DIGC: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGC_SEL; ++ break; ++ case ENGINE_ID_DIGD: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGD_SEL; ++ break; ++ case ENGINE_ID_DIGE: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGE_SEL; ++ break; ++ case ENGINE_ID_DIGF: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGF_SEL; ++ break; ++ case ENGINE_ID_DIGG: ++ atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__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_V6__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_COMBO_PHY_PLL0: ++ *atom_pll_id = ATOM_COMBOPHY_PLL0; ++ break; ++ case CLOCK_SOURCE_COMBO_PHY_PLL1: ++ *atom_pll_id = ATOM_COMBOPHY_PLL1; ++ break; ++ case CLOCK_SOURCE_COMBO_PHY_PLL2: ++ *atom_pll_id = ATOM_COMBOPHY_PLL2; ++ break; ++ case CLOCK_SOURCE_COMBO_PHY_PLL3: ++ *atom_pll_id = ATOM_COMBOPHY_PLL3; ++ break; ++ case CLOCK_SOURCE_COMBO_PHY_PLL4: ++ *atom_pll_id = ATOM_COMBOPHY_PLL4; ++ break; ++ case CLOCK_SOURCE_COMBO_PHY_PLL5: ++ *atom_pll_id = ATOM_COMBOPHY_PLL5; ++ break; ++ case CLOCK_SOURCE_COMBO_DISPLAY_PLL0: ++ *atom_pll_id = ATOM_PPLL0; ++ break; ++ case CLOCK_SOURCE_ID_DFS: ++ *atom_pll_id = ATOM_GCK_DFS; ++ break; ++ case CLOCK_SOURCE_ID_VCE: ++ *atom_pll_id = ATOM_DP_DTO; ++ break; ++ case CLOCK_SOURCE_ID_DP_DTO: ++ *atom_pll_id = ATOM_DP_DTO; ++ 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_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_STREAM_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; ++} ++ ++bool dc_clock_type_to_atom(enum bp_dce_clock_type id, uint32_t *atom_clock_type) ++{ ++ bool retCode = true; ++ ++ if (atom_clock_type != NULL) { ++ switch (id) { ++ case DCECLOCK_TYPE_DISPLAY_CLOCK: ++ *atom_clock_type = DCE_CLOCK_TYPE_DISPCLK; ++ break; ++ ++ case DCECLOCK_TYPE_DPREFCLK: ++ *atom_clock_type = DCE_CLOCK_TYPE_DPREFCLK; ++ break; ++ ++ default: ++ ASSERT_CRITICAL(false); /* Unhandle action in driver! */ ++ break; ++ } ++ } ++ ++ return retCode; ++} ++ ++uint8_t transmitter_color_depth_to_atom(enum transmitter_color_depth id) ++{ ++ uint8_t atomColorDepth = 0; ++ ++ switch (id) { ++ case TRANSMITTER_COLOR_DEPTH_24: ++ atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS; ++ break; ++ case TRANSMITTER_COLOR_DEPTH_30: ++ atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4; ++ break; ++ case TRANSMITTER_COLOR_DEPTH_36: ++ atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2; ++ break; ++ case TRANSMITTER_COLOR_DEPTH_48: ++ atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1; ++ break; ++ default: ++ ASSERT_CRITICAL(false); /* Unhandle action in driver! */ ++ break; ++ } ++ ++ return atomColorDepth; ++} ++ ++/* 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, ++ .dc_clock_type_to_atom = dc_clock_type_to_atom, ++ .transmitter_color_depth_to_atom = transmitter_color_depth_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_dce112_get_table() ++{ ++ return &command_table_helper_funcs; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.h b/drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.h +new file mode 100644 +index 0000000..dc36609 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/bios/dce112/command_table_helper_dce112.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_DCE112_H__ ++#define __DAL_COMMAND_TABLE_HELPER_DCE112_H__ ++ ++struct command_table_helper; ++ ++/* Initialize command table helper functions */ ++const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table(void); ++ ++#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c b/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c +index f39499a..8a19139 100644 +--- a/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c ++++ b/drivers/gpu/drm/amd/dal/dc/calcs/bandwidth_calcs.c +@@ -3726,6 +3726,212 @@ void bw_calcs_init(struct bw_calcs_dceip *bw_dceip, + dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; + dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); /* todo: this is a bug*/ + break; ++ case BW_CALCS_VERSION_ELLESMERE: ++ vbios.number_of_dram_channels = 8; ++ vbios.dram_channel_width_in_bits = 32; ++ vbios.number_of_dram_banks = 8; ++ vbios.high_yclk = bw_int_to_fixed(6000); ++ vbios.mid_yclk = bw_int_to_fixed(3200); ++ vbios.low_yclk = bw_int_to_fixed(1000); ++ vbios.low_sclk = bw_int_to_fixed(300); ++ vbios.mid_sclk = bw_int_to_fixed(974); ++ vbios.high_sclk = bw_int_to_fixed(1154); ++ vbios.low_voltage_max_dispclk = bw_int_to_fixed(459); ++ vbios.mid_voltage_max_dispclk = bw_int_to_fixed(654); ++ vbios.high_voltage_max_dispclk = bw_int_to_fixed(1132); ++ vbios.data_return_bus_width = bw_int_to_fixed(32); ++ vbios.trc = bw_int_to_fixed(48); ++ vbios.dmifmc_urgent_latency = bw_int_to_fixed(3); ++ vbios.stutter_self_refresh_exit_latency = bw_int_to_fixed(5); ++ vbios.nbp_state_change_latency = bw_int_to_fixed(45); ++ vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); ++ vbios.scatter_gather_enable = true; ++ vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); ++ vbios.cursor_width = 32; ++ vbios.average_compression_rate = 4; ++ vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = ++ 256; ++ vbios.blackout_duration = bw_int_to_fixed(0); /* us */ ++ vbios.maximum_blackout_recovery_time = bw_int_to_fixed(0); ++ ++ dceip.dmif_request_buffer_size = bw_int_to_fixed(768); ++ dceip.de_tiling_buffer = bw_int_to_fixed(0); ++ dceip.dcfclk_request_generation = 0; ++ dceip.lines_interleaved_into_lb = 2; ++ dceip.chunk_width = 256; ++ dceip.number_of_graphics_pipes = 6; ++ dceip.number_of_underlay_pipes = 0; ++ dceip.display_write_back_supported = false; ++ dceip.argb_compression_support = false; ++ dceip.underlay_vscaler_efficiency6_bit_per_component = ++ bw_frc_to_fixed(35556, 10000); ++ dceip.underlay_vscaler_efficiency8_bit_per_component = ++ bw_frc_to_fixed(34286, 10000); ++ dceip.underlay_vscaler_efficiency10_bit_per_component = ++ bw_frc_to_fixed(32, 10); ++ dceip.underlay_vscaler_efficiency12_bit_per_component = ++ bw_int_to_fixed(3); ++ dceip.graphics_vscaler_efficiency6_bit_per_component = ++ bw_frc_to_fixed(35, 10); ++ dceip.graphics_vscaler_efficiency8_bit_per_component = ++ bw_frc_to_fixed(34286, 10000); ++ dceip.graphics_vscaler_efficiency10_bit_per_component = ++ bw_frc_to_fixed(32, 10); ++ dceip.graphics_vscaler_efficiency12_bit_per_component = ++ bw_int_to_fixed(3); ++ dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); ++ dceip.max_dmif_buffer_allocated = 4; ++ dceip.graphics_dmif_size = 12288; ++ dceip.underlay_luma_dmif_size = 19456; ++ dceip.underlay_chroma_dmif_size = 23552; ++ dceip.pre_downscaler_enabled = true; ++ dceip.underlay_downscale_prefetch_enabled = true; ++ dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); ++ dceip.lb_size_per_component444 = bw_int_to_fixed(245952); ++ dceip.graphics_lb_nodownscaling_multi_line_prefetching = true; ++ dceip.stutter_and_dram_clock_state_change_gated_before_cursor = ++ bw_int_to_fixed(1); ++ dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( ++ 82176); ++ dceip.underlay420_chroma_lb_size_per_component = ++ bw_int_to_fixed(164352); ++ dceip.underlay422_lb_size_per_component = bw_int_to_fixed( ++ 82176); ++ dceip.cursor_chunk_width = bw_int_to_fixed(64); ++ dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); ++ dceip.cursor_memory_interface_buffer_pixels = bw_int_to_fixed( ++ 64); ++ dceip.underlay_maximum_width_efficient_for_tiling = ++ bw_int_to_fixed(1920); ++ dceip.underlay_maximum_height_efficient_for_tiling = ++ bw_int_to_fixed(1080); ++ dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = ++ bw_frc_to_fixed(3, 10); ++ dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = ++ bw_int_to_fixed(25); ++ dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( ++ 2); ++ dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = ++ bw_int_to_fixed(128); ++ dceip.limit_excessive_outstanding_dmif_requests = true; ++ dceip.linear_mode_line_request_alternation_slice = ++ bw_int_to_fixed(64); ++ dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = ++ 32; ++ dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; ++ dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; ++ dceip.request_efficiency = bw_frc_to_fixed(8, 10); ++ dceip.dispclk_per_request = bw_int_to_fixed(2); ++ dceip.dispclk_ramping_factor = bw_frc_to_fixed(11, 10); ++ dceip.display_pipe_throughput_factor = bw_frc_to_fixed( ++ 105, ++ 100); ++ dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; ++ dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); ++ break; ++ case BW_CALCS_VERSION_BAFFIN: ++ vbios.number_of_dram_channels = 4; ++ vbios.dram_channel_width_in_bits = 32; ++ vbios.number_of_dram_banks = 8; ++ vbios.high_yclk = bw_int_to_fixed(6000); ++ vbios.mid_yclk = bw_int_to_fixed(3200); ++ vbios.low_yclk = bw_int_to_fixed(1000); ++ vbios.low_sclk = bw_int_to_fixed(300); ++ vbios.mid_sclk = bw_int_to_fixed(974); ++ vbios.high_sclk = bw_int_to_fixed(1154); ++ vbios.low_voltage_max_dispclk = bw_int_to_fixed(459); ++ vbios.mid_voltage_max_dispclk = bw_int_to_fixed(654); ++ vbios.high_voltage_max_dispclk = bw_int_to_fixed(1132); ++ vbios.data_return_bus_width = bw_int_to_fixed(32); ++ vbios.trc = bw_int_to_fixed(48); ++ vbios.dmifmc_urgent_latency = bw_int_to_fixed(3); ++ vbios.stutter_self_refresh_exit_latency = bw_int_to_fixed(5); ++ vbios.nbp_state_change_latency = bw_int_to_fixed(45); ++ vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); ++ vbios.scatter_gather_enable = true; ++ vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); ++ vbios.cursor_width = 32; ++ vbios.average_compression_rate = 4; ++ vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = ++ 256; ++ vbios.blackout_duration = bw_int_to_fixed(0); /* us */ ++ vbios.maximum_blackout_recovery_time = bw_int_to_fixed(0); ++ ++ dceip.dmif_request_buffer_size = bw_int_to_fixed(768); ++ dceip.de_tiling_buffer = bw_int_to_fixed(0); ++ dceip.dcfclk_request_generation = 0; ++ dceip.lines_interleaved_into_lb = 2; ++ dceip.chunk_width = 256; ++ dceip.number_of_graphics_pipes = 5; ++ dceip.number_of_underlay_pipes = 0; ++ dceip.display_write_back_supported = false; ++ dceip.argb_compression_support = false; ++ dceip.underlay_vscaler_efficiency6_bit_per_component = ++ bw_frc_to_fixed(35556, 10000); ++ dceip.underlay_vscaler_efficiency8_bit_per_component = ++ bw_frc_to_fixed(34286, 10000); ++ dceip.underlay_vscaler_efficiency10_bit_per_component = ++ bw_frc_to_fixed(32, 10); ++ dceip.underlay_vscaler_efficiency12_bit_per_component = ++ bw_int_to_fixed(3); ++ dceip.graphics_vscaler_efficiency6_bit_per_component = ++ bw_frc_to_fixed(35, 10); ++ dceip.graphics_vscaler_efficiency8_bit_per_component = ++ bw_frc_to_fixed(34286, 10000); ++ dceip.graphics_vscaler_efficiency10_bit_per_component = ++ bw_frc_to_fixed(32, 10); ++ dceip.graphics_vscaler_efficiency12_bit_per_component = ++ bw_int_to_fixed(3); ++ dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); ++ dceip.max_dmif_buffer_allocated = 4; ++ dceip.graphics_dmif_size = 12288; ++ dceip.underlay_luma_dmif_size = 19456; ++ dceip.underlay_chroma_dmif_size = 23552; ++ dceip.pre_downscaler_enabled = true; ++ dceip.underlay_downscale_prefetch_enabled = true; ++ dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); ++ dceip.lb_size_per_component444 = bw_int_to_fixed(245952); ++ dceip.graphics_lb_nodownscaling_multi_line_prefetching = true; ++ dceip.stutter_and_dram_clock_state_change_gated_before_cursor = ++ bw_int_to_fixed(1); ++ dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( ++ 82176); ++ dceip.underlay420_chroma_lb_size_per_component = ++ bw_int_to_fixed(164352); ++ dceip.underlay422_lb_size_per_component = bw_int_to_fixed( ++ 82176); ++ dceip.cursor_chunk_width = bw_int_to_fixed(64); ++ dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); ++ dceip.cursor_memory_interface_buffer_pixels = bw_int_to_fixed( ++ 64); ++ dceip.underlay_maximum_width_efficient_for_tiling = ++ bw_int_to_fixed(1920); ++ dceip.underlay_maximum_height_efficient_for_tiling = ++ bw_int_to_fixed(1080); ++ dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = ++ bw_frc_to_fixed(3, 10); ++ dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = ++ bw_int_to_fixed(25); ++ dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( ++ 2); ++ dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = ++ bw_int_to_fixed(128); ++ dceip.limit_excessive_outstanding_dmif_requests = true; ++ dceip.linear_mode_line_request_alternation_slice = ++ bw_int_to_fixed(64); ++ dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = ++ 32; ++ dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; ++ dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; ++ dceip.request_efficiency = bw_frc_to_fixed(8, 10); ++ dceip.dispclk_per_request = bw_int_to_fixed(2); ++ dceip.dispclk_ramping_factor = bw_frc_to_fixed(11, 10); ++ dceip.display_pipe_throughput_factor = bw_frc_to_fixed( ++ 105, ++ 100); ++ dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; ++ dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); ++ break; + default: + break; + } +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 +index 61bb67a..f9dd0d8 100644 +--- a/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c +@@ -34,6 +34,9 @@ + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + #include "dce110/dce110_hw_sequencer.h" + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "dce112/dce112_hw_sequencer.h" ++#endif + + bool dc_construct_hw_sequencer( + struct adapter_service *adapter_serv, +@@ -55,6 +58,10 @@ bool dc_construct_hw_sequencer( + case DCE_VERSION_11_0: + return dce110_hw_sequencer_construct(dc); + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ return dce112_hw_sequencer_construct(dc); ++#endif + default: + break; + } +diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c +index 5f3b702..087670d 100644 +--- a/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c ++++ b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c +@@ -41,11 +41,13 @@ + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + #include "dce110/dce110_resource.h" + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#include "dce112/dce112_resource.h" ++#endif + + enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) +- { ++{ + enum dce_version dc_version = DCE_VERSION_UNKNOWN; +- + switch (asic_id.chip_family) { + + #if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +@@ -68,6 +70,12 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) + break; + } + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ if (ASIC_REV_IS_POLARIS10_P(asic_id.hw_internal_rev) || ++ ASIC_REV_IS_POLARIS11_M(asic_id.hw_internal_rev)) { ++ dc_version = DCE_VERSION_11_2; ++ } ++#endif + break; + default: + dc_version = DCE_VERSION_UNKNOWN; +@@ -83,6 +91,11 @@ bool dc_construct_resource_pool(struct adapter_service *adapter_serv, + { + + switch (dc_version) { ++#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) ++ case DCE_VERSION_8_0: ++ return dce80_construct_resource_pool( ++ adapter_serv, num_virtual_links, dc, &dc->res_pool); ++#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) + case DCE_VERSION_10_0: + return dce100_construct_resource_pool( +@@ -93,6 +106,11 @@ bool dc_construct_resource_pool(struct adapter_service *adapter_serv, + return dce110_construct_resource_pool( + adapter_serv, num_virtual_links, dc, &dc->res_pool); + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ return dce112_construct_resource_pool( ++ adapter_serv, num_virtual_links, dc, &dc->res_pool); ++#endif + default: + break; + } +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 +index 3d4f8b7..a21fcbd 100644 +--- a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c +@@ -416,6 +416,7 @@ static void dce110_crtc_switch_to_clk_src( + uint32_t pixel_rate_cntl_value; + uint32_t addr; + ++ /* These addresses are the same across DCE8 - DCE11.2 */ + addr = mmCRTC0_PIXEL_RATE_CNTL + crtc_inst * + (mmCRTC1_PIXEL_RATE_CNTL - mmCRTC0_PIXEL_RATE_CNTL); + +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 +index de370ee..a9ef65d 100644 +--- a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c +@@ -59,7 +59,7 @@ enum black_color_format { + + /* Flowing register offsets are same in files of + * dce/dce_11_0_d.h +- * dce/vi_ellesmere_p/vi_ellesmere_d.h ++ * dce/vi_polaris10_p/vi_polaris10_d.h + * + * So we can create dce110 timing generator to use it. + */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/Makefile b/drivers/gpu/drm/amd/dal/dc/dce112/Makefile +new file mode 100644 +index 0000000..c7d61d9 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the 'controller' sub-component of DAL. ++# It provides the control and status of HW CRTC block. ++ ++DCE112 = dce112_link_encoder.o dce112_compressor.o dce112_hw_sequencer.o \ ++dce112_resource.o dce112_clock_source.o dce112_mem_input.o ++ ++AMD_DAL_DCE112 = $(addprefix $(AMDDALPATH)/dc/dce112/,$(DCE112)) ++ ++AMD_DAL_FILES += $(AMD_DAL_DCE112) +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.c b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.c +new file mode 100644 +index 0000000..7ec9508 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.c +@@ -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 ++ * ++ */ ++ ++#include "dm_services.h" ++ ++#include "dce112_clock_source.h" ++ ++/* include DCE11.2 register header files */ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_sh_mask.h" ++ ++#include "dc_types.h" ++#include "core_types.h" ++ ++#include "include/grph_object_id.h" ++#include "include/logger_interface.h" ++ ++/** ++ * 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 dce112_get_pix_clk_dividers( ++ struct clock_source *cs, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct dce112_clk_src *clk_src = TO_DCE112_CLK_SRC(cs); ++ uint32_t actualPixelClockInKHz; ++ ++ 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 0; ++ } ++ ++ memset(pll_settings, 0, sizeof(*pll_settings)); ++ ++ if (clk_src->base.id == CLOCK_SOURCE_ID_DP_DTO) { ++ pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz; ++ pll_settings->calculated_pix_clk = clk_src->ext_clk_khz; ++ pll_settings->actual_pix_clk = ++ pix_clk_params->requested_pix_clk; ++ return 0; ++ } ++ /* PLL only after this point */ ++ ++ actualPixelClockInKHz = pix_clk_params->requested_pix_clk; ++ ++ /* Calculate Dividers */ ++ if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { ++ switch (pix_clk_params->color_depth) { ++ case COLOR_DEPTH_101010: ++ actualPixelClockInKHz = (actualPixelClockInKHz * 5) >> 2; ++ break; ++ case COLOR_DEPTH_121212: ++ actualPixelClockInKHz = (actualPixelClockInKHz * 6) >> 2; ++ break; ++ case COLOR_DEPTH_161616: ++ actualPixelClockInKHz = actualPixelClockInKHz * 2; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ pll_settings->actual_pix_clk = actualPixelClockInKHz; ++ pll_settings->adjusted_pix_clk = actualPixelClockInKHz; ++ pll_settings->calculated_pix_clk = pix_clk_params->requested_pix_clk; ++ ++ return 0; ++} ++ ++static void program_pixel_clk_resync( ++ struct dce112_clk_src *clk_src, ++ enum signal_type signal_type, ++ enum dc_color_depth colordepth) ++{ ++ uint32_t value = 0; ++ ++ value = dm_read_reg(clk_src->base.ctx, ++ clk_src->offsets.pixclk_resync_cntl); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ PHYPLLA_PIXCLK_RESYNC_CNTL, ++ PHYPLLA_DCCG_DEEP_COLOR_CNTL); ++ ++ /* ++ 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, ++ PHYPLLA_PIXCLK_RESYNC_CNTL, ++ PHYPLLA_DCCG_DEEP_COLOR_CNTL); ++ break; ++ case COLOR_DEPTH_101010: ++ set_reg_field_value( ++ value, ++ 1, ++ PHYPLLA_PIXCLK_RESYNC_CNTL, ++ PHYPLLA_DCCG_DEEP_COLOR_CNTL); ++ break; ++ case COLOR_DEPTH_121212: ++ set_reg_field_value( ++ value, ++ 2, ++ PHYPLLA_PIXCLK_RESYNC_CNTL, ++ PHYPLLA_DCCG_DEEP_COLOR_CNTL); ++ break; ++ case COLOR_DEPTH_161616: ++ set_reg_field_value( ++ value, ++ 3, ++ PHYPLLA_PIXCLK_RESYNC_CNTL, ++ PHYPLLA_DCCG_DEEP_COLOR_CNTL); ++ break; ++ default: ++ break; ++ } ++ ++ dm_write_reg( ++ clk_src->base.ctx, ++ clk_src->offsets.pixclk_resync_cntl, ++ value); ++} ++ ++static bool dce112_program_pix_clk( ++ struct clock_source *clk_src, ++ struct pixel_clk_params *pix_clk_params, ++ struct pll_settings *pll_settings) ++{ ++ struct dce112_clk_src *dce112_clk_src = TO_DCE112_CLK_SRC(clk_src); ++ struct bp_pixel_clock_parameters bp_pc_params = {0}; ++ ++ /*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->id; ++ bp_pc_params.target_pixel_clock = pll_settings->actual_pix_clk; ++ bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; ++ bp_pc_params.signal_type = pix_clk_params->signal_type; ++ ++ if (clk_src->id != CLOCK_SOURCE_ID_DP_DTO) { ++ bp_pc_params.flags.SET_GENLOCK_REF_DIV_SRC = ++ pll_settings->use_external_clk; ++ bp_pc_params.flags.SET_XTALIN_REF_SRC = ++ !pll_settings->use_external_clk; ++ bp_pc_params.flags.SUPPORT_YUV_420 = 0; ++ } ++ ++ if (dce112_clk_src->bios->funcs->set_pixel_clock( ++ dce112_clk_src->bios, &bp_pc_params) != BP_RESULT_OK) ++ return false; ++ ++ /* TODO: support YCBCR420 */ ++ ++ /* Resync deep color DTO */ ++ if (clk_src->id != CLOCK_SOURCE_ID_DP_DTO) ++ program_pixel_clk_resync(dce112_clk_src, ++ pix_clk_params->signal_type, ++ pix_clk_params->color_depth); ++ ++ return true; ++} ++ ++static bool dce112_clock_source_power_down( ++ struct clock_source *clk_src) ++{ ++ struct dce112_clk_src *dce112_clk_src = TO_DCE112_CLK_SRC(clk_src); ++ enum bp_result bp_result; ++ struct bp_pixel_clock_parameters bp_pixel_clock_params = {0}; ++ ++ if (clk_src->id == CLOCK_SOURCE_ID_DP_DTO) ++ return true; ++ ++ /* If Pixel Clock is 0 it means Power Down Pll*/ ++ bp_pixel_clock_params.controller_id = CONTROLLER_ID_UNDEFINED; ++ bp_pixel_clock_params.pll_id = clk_src->id; ++ bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1; ++ ++ /*Call ASICControl to process ATOMBIOS Exec table*/ ++ bp_result = dce112_clk_src->bios->funcs->set_pixel_clock( ++ dce112_clk_src->bios, ++ &bp_pixel_clock_params); ++ ++ return bp_result == BP_RESULT_OK; ++} ++ ++/*****************************************/ ++/* Constructor */ ++/*****************************************/ ++static struct clock_source_funcs dce112_clk_src_funcs = { ++ .cs_power_down = dce112_clock_source_power_down, ++ .program_pix_clk = dce112_program_pix_clk, ++ .get_pix_clk_dividers = dce112_get_pix_clk_dividers ++}; ++ ++bool dce112_clk_src_construct( ++ struct dce112_clk_src *clk_src, ++ struct dc_context *ctx, ++ struct dc_bios *bios, ++ enum clock_source_id id, ++ const struct dce112_clk_src_reg_offsets *reg_offsets) ++{ ++ struct firmware_info fw_info = { { 0 } }; ++ ++ clk_src->base.ctx = ctx; ++ clk_src->bios = bios; ++ clk_src->base.id = id; ++ clk_src->base.funcs = &dce112_clk_src_funcs; ++ clk_src->offsets = *reg_offsets; ++ ++ if (clk_src->bios->funcs->get_firmware_info( ++ clk_src->bios, &fw_info) != BP_RESULT_OK) { ++ ASSERT_CRITICAL(false); ++ goto unexpected_failure; ++ } ++ ++ clk_src->ext_clk_khz = fw_info.external_clock_source_frequency_for_dp; ++ ++ return true; ++ ++unexpected_failure: ++ return false; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.h b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_clock_source.h +new file mode 100644 +index 0000000..40ecc58 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_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 __DC_CLOCK_SOURCE_DCE110_H__ ++#define __DC_CLOCK_SOURCE_DCE110_H__ ++ ++#include "clock_source.h" ++ ++#define TO_DCE112_CLK_SRC(clk_src)\ ++ container_of(clk_src, struct dce112_clk_src, base) ++ ++struct dce112_clk_src_reg_offsets { ++ uint32_t pixclk_resync_cntl; ++}; ++ ++struct dce112_clk_src { ++ struct clock_source base; ++ struct dce112_clk_src_reg_offsets offsets; ++ struct dc_bios *bios; ++ ++ uint32_t ext_clk_khz; ++}; ++ ++bool dce112_clk_src_construct( ++ struct dce112_clk_src *clk_src, ++ struct dc_context *ctx, ++ struct dc_bios *bios, ++ enum clock_source_id, ++ const struct dce112_clk_src_reg_offsets *reg_offsets); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.c b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.c +new file mode 100644 +index 0000000..9526ffd +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.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 "dm_services.h" ++ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_sh_mask.h" ++#include "gmc/gmc_8_1_sh_mask.h" ++#include "gmc/gmc_8_1_d.h" ++ ++#include "include/logger_interface.h" ++#include "include/adapter_service_interface.h" ++ ++#include "dce112_compressor.h" ++ ++#define DCP_REG(reg)\ ++ (reg + cp110->offsets.dcp_offset) ++#define DMIF_REG(reg)\ ++ (reg + cp110->offsets.dmif_offset) ++ ++static const struct dce112_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 dce112_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 dce112_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 dce112_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 dce112_compressor *cp110, ++ uint32_t pixels) ++{ ++ return 256 * ((pixels + 255) / 256); ++} ++ ++static void wait_for_fbc_state_changed( ++ struct dce112_compressor *cp110, ++ bool enabled) ++{ ++ uint8_t counter = 0; ++ uint32_t addr = mmFBC_STATUS; ++ uint32_t value; ++ ++ while (counter < 10) { ++ value = dm_read_reg(cp110->base.ctx, addr); ++ if (get_reg_field_value( ++ value, ++ FBC_STATUS, ++ FBC_ENABLE_STATUS) == enabled) ++ break; ++ udelay(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 dce112_compressor_power_up_fbc(struct compressor *compressor) ++{ ++ uint32_t value; ++ uint32_t addr; ++ ++ addr = mmFBC_CNTL; ++ value = dm_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); ++ } ++ dm_write_reg(compressor->ctx, addr, value); ++ ++ addr = mmFBC_COMP_MODE; ++ value = dm_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); ++ dm_write_reg(compressor->ctx, addr, value); ++ ++ addr = mmFBC_COMP_CNTL; ++ value = dm_read_reg(compressor->ctx, addr); ++ set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); ++ dm_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); ++ dm_write_reg(compressor->ctx, addr, value); ++ compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; ++ ++ value = 0; ++ dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); ++ ++ value = 0xFFFFFF; ++ dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); ++} ++ ++void dce112_compressor_enable_fbc( ++ struct compressor *compressor, ++ uint32_t paths_num, ++ struct compr_addr_and_pitch_params *params) ++{ ++ struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); ++ ++ if (compressor->options.bits.FBC_SUPPORT && ++ (compressor->options.bits.DUMMY_BACKEND == 0) && ++ (!dce112_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)) { ++ dce112_compressor_enable_lpt(compressor); ++ } ++ ++ addr = mmFBC_CNTL; ++ value = dm_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); ++ dm_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); ++ dm_write_reg(compressor->ctx, addr, value); ++ set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); ++ dm_write_reg(compressor->ctx, addr, value); ++ ++ wait_for_fbc_state_changed(cp110, true); ++ } ++} ++ ++void dce112_compressor_disable_fbc(struct compressor *compressor) ++{ ++ struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); ++ ++ if (compressor->options.bits.FBC_SUPPORT && ++ dce112_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { ++ uint32_t reg_data; ++ /* Turn off compression */ ++ reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); ++ set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); ++ dm_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) ++ dce112_compressor_disable_lpt(compressor); ++ ++ wait_for_fbc_state_changed(cp110, false); ++ } ++} ++ ++bool dce112_compressor_is_fbc_enabled_in_hw( ++ struct compressor *compressor, ++ uint32_t *inst) ++{ ++ /* Check the hardware register */ ++ uint32_t value; ++ ++ value = dm_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 = dm_read_reg(compressor->ctx, mmFBC_MISC); ++ if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { ++ value = dm_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 dce112_compressor_is_lpt_enabled_in_hw(struct compressor *compressor) ++{ ++ /* Check the hardware register */ ++ uint32_t value = dm_read_reg(compressor->ctx, ++ mmLOW_POWER_TILING_CONTROL); ++ ++ return get_reg_field_value( ++ value, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ENABLE); ++} ++ ++void dce112_compressor_program_compressed_surface_address_and_pitch( ++ struct compressor *compressor, ++ struct compr_addr_and_pitch_params *params) ++{ ++ struct dce112_compressor *cp110 = TO_DCE112_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. */ ++ dm_write_reg( ++ compressor->ctx, ++ DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), ++ 0); ++ dm_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. */ ++ dm_write_reg(compressor->ctx, ++ DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), ++ compressor->compr_surface_address.addr.high_part); ++ dm_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. */ ++ dm_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); ++ dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); ++ ++} ++ ++void dce112_compressor_disable_lpt(struct compressor *compressor) ++{ ++ struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); ++ uint32_t value; ++ uint32_t addr; ++ uint32_t inx; ++ ++ /* Disable all pipes LPT Stutter */ ++ for (inx = 0; inx < 3; inx++) { ++ value = ++ dm_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); ++ dm_write_reg( ++ compressor->ctx, ++ DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), ++ value); ++ } ++ /* Disable Underlay pipe LPT Stutter */ ++ addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; ++ value = dm_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 0, ++ DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, ++ STUTTER_ENABLE_NONLPTCH); ++ dm_write_reg(compressor->ctx, addr, value); ++ ++ /* Disable LPT */ ++ addr = mmLOW_POWER_TILING_CONTROL; ++ value = dm_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 0, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ENABLE); ++ dm_write_reg(compressor->ctx, addr, value); ++ ++ /* Clear selection of Channel(s) containing Compressed Surface */ ++ addr = mmGMCON_LPT_TARGET; ++ value = dm_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 0xFFFFFFFF, ++ GMCON_LPT_TARGET, ++ STCTRL_LPT_TARGET); ++ dm_write_reg(compressor->ctx, mmGMCON_LPT_TARGET, value); ++} ++ ++void dce112_compressor_enable_lpt(struct compressor *compressor) ++{ ++ struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); ++ uint32_t value; ++ uint32_t addr; ++ uint32_t value_control; ++ uint32_t channels; ++ ++ /* Enable LPT Stutter from Display pipe */ ++ value = dm_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); ++ dm_write_reg(compressor->ctx, ++ DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), value); ++ ++ /* Enable Underlay pipe LPT Stutter */ ++ addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; ++ value = dm_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, ++ STUTTER_ENABLE_NONLPTCH); ++ dm_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 = dm_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 = dm_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); ++ dm_write_reg(compressor->ctx, addr, value); ++ ++ /* Enable LPT */ ++ addr = mmLOW_POWER_TILING_CONTROL; ++ value = dm_read_reg(compressor->ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ LOW_POWER_TILING_CONTROL, ++ LOW_POWER_TILING_ENABLE); ++ dm_write_reg(compressor->ctx, addr, value); ++} ++ ++void dce112_compressor_program_lpt_control( ++ struct compressor *compressor, ++ struct compr_addr_and_pitch_params *params) ++{ ++ struct dce112_compressor *cp110 = TO_DCE112_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 = dm_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); ++ ++ dm_write_reg(compressor->ctx, ++ mmLOW_POWER_TILING_CONTROL, lpt_control); ++} ++ ++/* ++ * DCE 11 Frame Buffer Compression Implementation ++ */ ++ ++void dce112_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 = dm_read_reg(compressor->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ FBC_CLIENT_REGION_MASK, ++ FBC_MEMORY_REGION_MASK); ++ dm_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 = dm_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); ++ dm_write_reg(compressor->ctx, addr, value); ++} ++ ++bool dce112_compressor_construct(struct dce112_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 *dce112_compressor_create(struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ struct dce112_compressor *cp110 = ++ dm_alloc(sizeof(struct dce112_compressor)); ++ ++ if (!cp110) ++ return NULL; ++ ++ if (dce112_compressor_construct(cp110, ctx, as)) ++ return &cp110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(cp110); ++ return NULL; ++} ++ ++void dce112_compressor_destroy(struct compressor **compressor) ++{ ++ dm_free(TO_DCE112_COMPRESSOR(*compressor)); ++ *compressor = NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.h b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_compressor.h +new file mode 100644 +index 0000000..bcf4480 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_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_DCE112_H__ ++#define __DC_COMPRESSOR_DCE112_H__ ++ ++#include "../inc/compressor.h" ++ ++#define TO_DCE112_COMPRESSOR(compressor)\ ++ container_of(compressor, struct dce112_compressor, base) ++ ++struct dce112_compressor_reg_offsets { ++ uint32_t dcp_offset; ++ uint32_t dmif_offset; ++}; ++ ++struct dce112_compressor { ++ struct compressor base; ++ struct dce112_compressor_reg_offsets offsets; ++}; ++ ++struct compressor *dce112_compressor_create(struct dc_context *ctx, ++ struct adapter_service *as); ++ ++bool dce112_compressor_construct(struct dce112_compressor *cp110, ++ struct dc_context *ctx, struct adapter_service *as); ++ ++void dce112_compressor_destroy(struct compressor **cp); ++ ++/* FBC RELATED */ ++void dce112_compressor_power_up_fbc(struct compressor *cp); ++ ++void dce112_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, ++ struct compr_addr_and_pitch_params *params); ++ ++void dce112_compressor_disable_fbc(struct compressor *cp); ++ ++void dce112_compressor_set_fbc_invalidation_triggers(struct compressor *cp, ++ uint32_t fbc_trigger); ++ ++void dce112_compressor_program_compressed_surface_address_and_pitch( ++ struct compressor *cp, ++ struct compr_addr_and_pitch_params *params); ++ ++bool dce112_compressor_get_required_compressed_surface_size( ++ struct compressor *cp, ++ struct fbc_input_info *input_info, ++ struct fbc_requested_compressed_size *size); ++ ++bool dce112_compressor_is_fbc_enabled_in_hw(struct compressor *cp, ++ uint32_t *fbc_mapped_crtc_id); ++ ++/* LPT RELATED */ ++void dce112_compressor_enable_lpt(struct compressor *cp); ++ ++void dce112_compressor_disable_lpt(struct compressor *cp); ++ ++void dce112_compressor_program_lpt_control(struct compressor *cp, ++ struct compr_addr_and_pitch_params *params); ++ ++bool dce112_compressor_is_lpt_enabled_in_hw(struct compressor *cp); ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.c +new file mode 100644 +index 0000000..b94130f +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.c +@@ -0,0 +1,178 @@ ++/* ++ * 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 "dm_services.h" ++#include "dc.h" ++#include "core_dc.h" ++#include "core_types.h" ++#include "dce112_hw_sequencer.h" ++ ++#include "dce110/dce110_hw_sequencer.h" ++#include "gpu/dce112/dc_clock_gating_dce112.h" ++ ++/* include DCE11.2 register header files */ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_sh_mask.h" ++ ++static void dce112_crtc_switch_to_clk_src( ++ struct clock_source *clk_src, uint8_t crtc_inst) ++{ ++ uint32_t pixel_rate_cntl_value; ++ uint32_t addr; ++ ++ addr = mmCRTC0_PIXEL_RATE_CNTL + crtc_inst * ++ (mmCRTC1_PIXEL_RATE_CNTL - mmCRTC0_PIXEL_RATE_CNTL); ++ ++ pixel_rate_cntl_value = dm_read_reg(clk_src->ctx, addr); ++ ++ if (clk_src->id == CLOCK_SOURCE_ID_DP_DTO) ++ 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, ++ clk_src->id - 1, ++ CRTC0_PIXEL_RATE_CNTL, ++ CRTC0_PIXEL_RATE_SOURCE); ++ } ++ dm_write_reg(clk_src->ctx, addr, pixel_rate_cntl_value); ++} ++ ++static void dce112_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 = dm_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); ++ ++ dm_write_reg(ctx, addr, value); ++ ++ addr = mmDVMM_PTE_REQ; ++ value = dm_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); ++ ++ dm_write_reg(ctx, addr, value); ++ } ++} ++ ++static bool dce112_enable_display_power_gating( ++ struct dc_context *ctx, ++ uint8_t controller_id, ++ struct dc_bios *dcb, ++ enum pipe_gating_control power_gating) ++{ ++ enum bp_result bp_result = BP_RESULT_OK; ++ enum bp_pipe_control_action cntl; ++ ++ if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) ++ return true; ++ ++ 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 == 0) ++ bp_result = dcb->funcs->enable_disp_power_gating( ++ dcb, controller_id + 1, cntl); ++ ++ if (power_gating != PIPE_GATING_CONTROL_ENABLE) ++ dce112_init_pte(ctx); ++ ++ if (bp_result == BP_RESULT_OK) ++ return true; ++ else ++ return false; ++} ++ ++bool dce112_hw_sequencer_construct(struct core_dc *dc) ++{ ++ /* All registers used by dce11.2 match those in dce11 in offset and ++ * structure ++ */ ++ dce110_hw_sequencer_construct(dc); ++ dc->hwss.crtc_switch_to_clk_src = dce112_crtc_switch_to_clk_src; ++ dc->hwss.enable_display_power_gating = dce112_enable_display_power_gating; ++ dc->hwss.clock_gating_power_up = dal_dc_clock_gating_dce112_power_up; ++ ++ return true; ++} ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.h b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_hw_sequencer.h +new file mode 100644 +index 0000000..d96c582 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_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_DCE112_H__ ++#define __DC_HWSS_DCE112_H__ ++ ++#include "core_types.h" ++ ++struct core_dc; ++ ++bool dce112_hw_sequencer_construct(struct core_dc *dc); ++ ++#endif /* __DC_HWSS_DCE112_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.c +new file mode 100644 +index 0000000..23e127c +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.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 "dm_services.h" ++#include "core_types.h" ++#include "link_encoder.h" ++#include "stream_encoder.h" ++#include "dce112_link_encoder.h" ++#include "../dce110/dce110_link_encoder.h" ++#include "i2caux_interface.h" ++#include "dce/dce_11_2_sh_mask.h" ++ ++/* 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 ++ ++/* 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 ++}; ++ ++static void dce112_link_encoder_dp_set_phy_pattern( ++ struct link_encoder *enc, ++ const struct encoder_set_dp_phy_pattern_param *param) ++{ ++ switch (param->dp_phy_pattern) { ++ case DP_TEST_PATTERN_TRAINING_PATTERN4: ++ dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 3); ++ break; ++ default: ++ dce110_link_encoder_dp_set_phy_pattern(enc, param); ++ break; ++ } ++} ++ ++static struct link_encoder_funcs dce112_lnk_enc_funcs = { ++ .validate_output_with_stream = ++ dce110_link_encoder_validate_output_with_stream, ++ .hw_init = dce110_link_encoder_hw_init, ++ .setup = dce110_link_encoder_setup, ++ .enable_tmds_output = dce110_link_encoder_enable_tmds_output, ++ .enable_dp_output = dce110_link_encoder_enable_dp_output, ++ .enable_dp_mst_output = dce110_link_encoder_enable_dp_mst_output, ++ .disable_output = dce110_link_encoder_disable_output, ++ .dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings, ++ .dp_set_phy_pattern = dce112_link_encoder_dp_set_phy_pattern, ++ .update_mst_stream_allocation_table = ++ dce110_link_encoder_update_mst_stream_allocation_table, ++ .set_lcd_backlight_level = dce110_link_encoder_set_lcd_backlight_level, ++ .backlight_control = dce110_link_encoder_edp_backlight_control, ++ .power_control = dce110_link_encoder_edp_power_control, ++ .connect_dig_be_to_fe = dce110_link_encoder_connect_dig_be_to_fe, ++ .destroy = dce110_link_encoder_destroy ++}; ++ ++bool dce112_link_encoder_construct( ++ struct dce110_link_encoder *enc110, ++ const struct encoder_init_data *init_data, ++ const struct dce110_link_enc_registers *link_regs, ++ const struct dce110_link_enc_aux_registers *aux_regs, ++ const struct dce110_link_enc_bl_registers *bl_regs) ++{ ++ dce110_link_encoder_construct( ++ enc110, ++ init_data, ++ link_regs, ++ aux_regs, ++ bl_regs); ++ ++ enc110->base.funcs = &dce112_lnk_enc_funcs; ++ ++ enc110->base.features.flags.bits.IS_HBR3_CAPABLE = true; ++ ++ enc110->base.features.flags.bits.IS_TPS4_CAPABLE = true; ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.h +new file mode 100644 +index 0000000..cfc9cc5 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_link_encoder.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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__DCE112_H__ ++#define __DC_LINK_ENCODER__DCE112_H__ ++ ++#include "link_encoder.h" ++#include "../dce110/dce110_link_encoder.h" ++ ++bool dce112_link_encoder_construct( ++ struct dce110_link_encoder *enc110, ++ const struct encoder_init_data *init_data, ++ const struct dce110_link_enc_registers *link_regs, ++ const struct dce110_link_enc_aux_registers *aux_regs, ++ const struct dce110_link_enc_bl_registers *bl_regs); ++ ++/****************** HW programming ************************/ ++ ++#endif /* __DC_LINK_ENCODER__DCE112_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.c b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.c +new file mode 100644 +index 0000000..823849e +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.c +@@ -0,0 +1,455 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_services.h" ++#include "dce112_mem_input.h" ++ ++ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_sh_mask.h" ++ ++ ++#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 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 = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 0, ++ DPG_WATERMARK_MASK_CONTROL, ++ URGENCY_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ urgency_cntl = dm_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); ++ dm_write_reg(ctx, urgency_addr, urgency_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set B*/ ++ wm_mask_cntl = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 1, ++ DPG_WATERMARK_MASK_CONTROL, ++ URGENCY_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ urgency_cntl = dm_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); ++ dm_write_reg(ctx, urgency_addr, urgency_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set C*/ ++ wm_mask_cntl = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 2, ++ DPG_WATERMARK_MASK_CONTROL, ++ URGENCY_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ urgency_cntl = dm_read_reg(ctx, urgency_addr); ++ ++ set_reg_field_value(urgency_cntl, ++ marks_low.c_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); ++ dm_write_reg(ctx, urgency_addr, urgency_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set D*/ ++ wm_mask_cntl = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 3, ++ DPG_WATERMARK_MASK_CONTROL, ++ URGENCY_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ urgency_cntl = dm_read_reg(ctx, urgency_addr); ++ ++ set_reg_field_value(urgency_cntl, ++ marks_low.d_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); ++ dm_write_reg(ctx, urgency_addr, urgency_cntl); ++} ++ ++static void program_stutter_watermark( ++ const struct dc_context *ctx, ++ const uint32_t offset, ++ struct bw_watermarks marks) ++{ ++ /* 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 = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 0, ++ DPG_WATERMARK_MASK_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ stutter_cntl = dm_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); ++ dm_write_reg(ctx, stutter_addr, stutter_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set B*/ ++ wm_mask_cntl = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 1, ++ DPG_WATERMARK_MASK_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ stutter_cntl = dm_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); ++ dm_write_reg(ctx, stutter_addr, stutter_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set C*/ ++ wm_mask_cntl = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 2, ++ DPG_WATERMARK_MASK_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ stutter_cntl = dm_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 C*/ ++ set_reg_field_value(stutter_cntl, ++ marks.c_mark, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK); ++ dm_write_reg(ctx, stutter_addr, stutter_cntl); ++ ++ /*Write mask to enable reading/writing of watermark set D*/ ++ wm_mask_cntl = dm_read_reg(ctx, wm_addr); ++ set_reg_field_value(wm_mask_cntl, ++ 3, ++ DPG_WATERMARK_MASK_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); ++ dm_write_reg(ctx, wm_addr, wm_mask_cntl); ++ ++ stutter_cntl = dm_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 D*/ ++ set_reg_field_value(stutter_cntl, ++ marks.d_mark, ++ DPG_PIPE_STUTTER_CONTROL, ++ STUTTER_EXIT_SELF_REFRESH_WATERMARK); ++ dm_write_reg(ctx, stutter_addr, stutter_cntl); ++} ++ ++static void program_nbp_watermark( ++ const struct dc_context *ctx, ++ const uint32_t offset, ++ struct bw_watermarks marks) ++{ ++ uint32_t value; ++ uint32_t addr; ++ /* Write mask to enable reading/writing of watermark set A */ ++ addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 0, ++ DPG_WATERMARK_MASK_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK_MASK); ++ dm_write_reg(ctx, addr, value); ++ ++ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; ++ value = dm_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); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write watermark set A */ ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ marks.a_mark, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write mask to enable reading/writing of watermark set B */ ++ addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 1, ++ DPG_WATERMARK_MASK_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK_MASK); ++ dm_write_reg(ctx, addr, value); ++ ++ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; ++ value = dm_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); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write watermark set B */ ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ marks.b_mark, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write mask to enable reading/writing of watermark set C */ ++ addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 2, ++ DPG_WATERMARK_MASK_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK_MASK); ++ dm_write_reg(ctx, addr, value); ++ ++ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; ++ value = dm_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); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write watermark set C */ ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ marks.c_mark, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write mask to enable reading/writing of watermark set D */ ++ addr = offset + mmDPG_WATERMARK_MASK_CONTROL; ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ 3, ++ DPG_WATERMARK_MASK_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK_MASK); ++ dm_write_reg(ctx, addr, value); ++ ++ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; ++ value = dm_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); ++ dm_write_reg(ctx, addr, value); ++ ++ /* Write watermark set D */ ++ value = dm_read_reg(ctx, addr); ++ set_reg_field_value( ++ value, ++ marks.d_mark, ++ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, ++ NB_PSTATE_CHANGE_WATERMARK); ++ dm_write_reg(ctx, addr, value); ++} ++ ++static void dce112_mem_input_program_display_marks( ++ struct mem_input *mem_input, ++ struct bw_watermarks nbp, ++ struct bw_watermarks stutter, ++ struct bw_watermarks urgent, ++ uint32_t total_dest_line_time_ns) ++{ ++ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mem_input); ++ ++ program_urgency_watermark( ++ mem_input->ctx, ++ bm_dce110->offsets.dmif, ++ urgent, ++ total_dest_line_time_ns); ++ ++ program_nbp_watermark( ++ mem_input->ctx, ++ bm_dce110->offsets.dmif, ++ nbp); ++ ++ program_stutter_watermark( ++ mem_input->ctx, ++ bm_dce110->offsets.dmif, ++ stutter); ++} ++ ++/*****************************************/ ++/* Constructor, Destructor */ ++/*****************************************/ ++ ++bool dce112_mem_input_construct( ++ struct dce110_mem_input *mem_input110, ++ struct dc_context *ctx, ++ uint32_t inst, ++ const struct dce110_mem_input_reg_offsets *offsets) ++{ ++ if (!dce110_mem_input_construct(mem_input110, ctx, inst, offsets)) ++ return false; ++ ++ mem_input110->base.funcs->mem_input_program_display_marks = ++ dce112_mem_input_program_display_marks; ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.h b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.h +new file mode 100644 +index 0000000..de2aaf0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_mem_input.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 __DC_MEM_INPUT_DCE112_H__ ++#define __DC_MEM_INPUT_DCE112_H__ ++ ++#include "mem_input.h" ++#include "dce110/dce110_mem_input.h" ++ ++bool dce112_mem_input_construct( ++ struct dce110_mem_input *mem_input110, ++ struct dc_context *ctx, ++ uint32_t inst, ++ const struct dce110_mem_input_reg_offsets *offsets); ++ ++ ++#endif +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.c +new file mode 100644 +index 0000000..420b8ca +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.c +@@ -0,0 +1,1404 @@ ++/* ++* Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_services.h" ++ ++#include "link_encoder.h" ++#include "stream_encoder.h" ++ ++#include "resource.h" ++#include "include/irq_service_interface.h" ++#include "../virtual/virtual_stream_encoder.h" ++#include "dce110/dce110_resource.h" ++#include "dce110/dce110_timing_generator.h" ++#include "dce112/dce112_mem_input.h" ++#include "dce112/dce112_link_encoder.h" ++#include "dce110/dce110_link_encoder.h" ++#include "dce110/dce110_transform.h" ++#include "dce110/dce110_stream_encoder.h" ++#include "dce110/dce110_opp.h" ++#include "dce110/dce110_ipp.h" ++#include "dce112/dce112_clock_source.h" ++ ++#include "dce/dce_11_2_d.h" ++ ++#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 ++ ++enum dce112_clk_src_array_id { ++ DCE112_CLK_SRC_PLL0, ++ DCE112_CLK_SRC_PLL1, ++ DCE112_CLK_SRC_PLL2, ++ DCE112_CLK_SRC_PLL3, ++ DCE112_CLK_SRC_PLL4, ++ DCE112_CLK_SRC_PLL5, ++ ++ DCE112_CLK_SRC_TOTAL ++}; ++ ++static const struct dce110_transform_reg_offsets dce112_xfm_offsets[] = { ++{ ++ .scl_offset = (mmSCL0_SCL_CONTROL - mmSCL_CONTROL), ++ .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), ++ .lb_offset = (mmLB0_LB_DATA_FORMAT - mmLB_DATA_FORMAT), ++}, ++{ .scl_offset = (mmSCL1_SCL_CONTROL - mmSCL_CONTROL), ++ .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), ++ .lb_offset = (mmLB1_LB_DATA_FORMAT - mmLB_DATA_FORMAT), ++}, ++{ .scl_offset = (mmSCL2_SCL_CONTROL - mmSCL_CONTROL), ++ .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), ++ .lb_offset = (mmLB2_LB_DATA_FORMAT - mmLB_DATA_FORMAT), ++}, ++{ ++ .scl_offset = (mmSCL3_SCL_CONTROL - mmSCL_CONTROL), ++ .dcfe_offset = (mmDCFE3_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), ++ .lb_offset = (mmLB3_LB_DATA_FORMAT - mmLB_DATA_FORMAT), ++}, ++{ .scl_offset = (mmSCL4_SCL_CONTROL - mmSCL_CONTROL), ++ .dcfe_offset = (mmDCFE4_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), ++ .lb_offset = (mmLB4_LB_DATA_FORMAT - mmLB_DATA_FORMAT), ++}, ++{ .scl_offset = (mmSCL5_SCL_CONTROL - mmSCL_CONTROL), ++ .dcfe_offset = (mmDCFE5_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), ++ .lb_offset = (mmLB5_LB_DATA_FORMAT - mmLB_DATA_FORMAT), ++} ++}; ++ ++static const struct dce110_timing_generator_offsets dce112_tg_offsets[] = { ++ { ++ .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), ++ .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), ++ }, ++ { ++ .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), ++ .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), ++ }, ++ { ++ .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), ++ .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), ++ }, ++ { ++ .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), ++ .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), ++ }, ++ { ++ .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), ++ .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), ++ }, ++ { ++ .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), ++ .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), ++ } ++}; ++ ++static const struct dce110_mem_input_reg_offsets dce112_mi_reg_offsets[] = { ++ { ++ .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), ++ .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL ++ - mmDPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE0_DMIF_BUFFER_CONTROL ++ - mmPIPE0_DMIF_BUFFER_CONTROL), ++ }, ++ { ++ .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), ++ .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL ++ - mmDPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE1_DMIF_BUFFER_CONTROL ++ - mmPIPE0_DMIF_BUFFER_CONTROL), ++ }, ++ { ++ .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), ++ .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL ++ - mmDPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE2_DMIF_BUFFER_CONTROL ++ - mmPIPE0_DMIF_BUFFER_CONTROL), ++ }, ++ { ++ .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), ++ .dmif = (mmDMIF_PG3_DPG_WATERMARK_MASK_CONTROL ++ - mmDPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE3_DMIF_BUFFER_CONTROL ++ - mmPIPE0_DMIF_BUFFER_CONTROL), ++ }, ++ { ++ .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), ++ .dmif = (mmDMIF_PG4_DPG_WATERMARK_MASK_CONTROL ++ - mmDPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE4_DMIF_BUFFER_CONTROL ++ - mmPIPE0_DMIF_BUFFER_CONTROL), ++ }, ++ { ++ .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), ++ .dmif = (mmDMIF_PG5_DPG_WATERMARK_MASK_CONTROL ++ - mmDPG_WATERMARK_MASK_CONTROL), ++ .pipe = (mmPIPE5_DMIF_BUFFER_CONTROL ++ - mmPIPE0_DMIF_BUFFER_CONTROL), ++ } ++}; ++ ++static const struct dce110_ipp_reg_offsets ipp_reg_offsets[] = { ++{ ++ .dcp_offset = (mmDCP0_CUR_CONTROL - mmCUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP1_CUR_CONTROL - mmCUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP2_CUR_CONTROL - mmCUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP3_CUR_CONTROL - mmCUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP4_CUR_CONTROL - mmCUR_CONTROL), ++}, ++{ ++ .dcp_offset = (mmDCP5_CUR_CONTROL - mmCUR_CONTROL), ++} ++}; ++ ++static const struct dce110_link_enc_bl_registers link_enc_bl_regs = { ++ .BL_PWM_CNTL = mmBL_PWM_CNTL, ++ .BL_PWM_GRP1_REG_LOCK = mmBL_PWM_GRP1_REG_LOCK, ++ .BL_PWM_PERIOD_CNTL = mmBL_PWM_PERIOD_CNTL, ++ .LVTMA_PWRSEQ_CNTL = mmLVTMA_PWRSEQ_CNTL, ++ .LVTMA_PWRSEQ_STATE = mmLVTMA_PWRSEQ_STATE ++}; ++ ++#define aux_regs(id)\ ++[id] = {\ ++ .AUX_CONTROL = mmDP_AUX ## id ## _AUX_CONTROL,\ ++ .AUX_DPHY_RX_CONTROL0 = mmDP_AUX ## id ## _AUX_DPHY_RX_CONTROL0\ ++} ++ ++static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { ++ aux_regs(0), ++ aux_regs(1), ++ aux_regs(2), ++ aux_regs(3), ++ aux_regs(4), ++ aux_regs(5) ++}; ++ ++#define link_regs(id)\ ++[id] = {\ ++ .DIG_BE_CNTL = mmDIG ## id ## _DIG_BE_CNTL,\ ++ .DIG_BE_EN_CNTL = mmDIG ## id ## _DIG_BE_EN_CNTL,\ ++ .DP_CONFIG = mmDP ## id ## _DP_CONFIG,\ ++ .DP_DPHY_CNTL = mmDP ## id ## _DP_DPHY_CNTL,\ ++ .DP_DPHY_INTERNAL_CTRL = mmDP ## id ## _DP_DPHY_INTERNAL_CTRL,\ ++ .DP_DPHY_PRBS_CNTL = mmDP ## id ## _DP_DPHY_PRBS_CNTL,\ ++ .DP_DPHY_SYM0 = mmDP ## id ## _DP_DPHY_SYM0,\ ++ .DP_DPHY_SYM1 = mmDP ## id ## _DP_DPHY_SYM1,\ ++ .DP_DPHY_SYM2 = mmDP ## id ## _DP_DPHY_SYM2,\ ++ .DP_DPHY_TRAINING_PATTERN_SEL = mmDP ## id ## _DP_DPHY_TRAINING_PATTERN_SEL,\ ++ .DP_LINK_CNTL = mmDP ## id ## _DP_LINK_CNTL,\ ++ .DP_LINK_FRAMING_CNTL = mmDP ## id ## _DP_LINK_FRAMING_CNTL,\ ++ .DP_MSE_SAT0 = mmDP ## id ## _DP_MSE_SAT0,\ ++ .DP_MSE_SAT1 = mmDP ## id ## _DP_MSE_SAT1,\ ++ .DP_MSE_SAT2 = mmDP ## id ## _DP_MSE_SAT2,\ ++ .DP_MSE_SAT_UPDATE = mmDP ## id ## _DP_MSE_SAT_UPDATE,\ ++ .DP_SEC_CNTL = mmDP ## id ## _DP_SEC_CNTL,\ ++ .DP_VID_STREAM_CNTL = mmDP ## id ## _DP_VID_STREAM_CNTL\ ++} ++ ++static const struct dce110_link_enc_registers link_enc_regs[] = { ++ link_regs(0), ++ link_regs(1), ++ link_regs(2), ++ link_regs(3), ++ link_regs(4), ++ link_regs(5) ++}; ++ ++#define stream_enc_regs(id)\ ++[id] = {\ ++ .AFMT_AVI_INFO0 = mmDIG ## id ## _AFMT_AVI_INFO0,\ ++ .AFMT_AVI_INFO1 = mmDIG ## id ## _AFMT_AVI_INFO1,\ ++ .AFMT_AVI_INFO2 = mmDIG ## id ## _AFMT_AVI_INFO2,\ ++ .AFMT_AVI_INFO3 = mmDIG ## id ## _AFMT_AVI_INFO3,\ ++ .AFMT_GENERIC_0 = mmDIG ## id ## _AFMT_GENERIC_0,\ ++ .AFMT_GENERIC_7 = mmDIG ## id ## _AFMT_GENERIC_7,\ ++ .AFMT_GENERIC_HDR = mmDIG ## id ## _AFMT_GENERIC_HDR,\ ++ .AFMT_INFOFRAME_CONTROL0 = mmDIG ## id ## _AFMT_INFOFRAME_CONTROL0,\ ++ .AFMT_VBI_PACKET_CONTROL = mmDIG ## id ## _AFMT_VBI_PACKET_CONTROL,\ ++ .DIG_FE_CNTL = mmDIG ## id ## _DIG_FE_CNTL,\ ++ .DP_MSE_RATE_CNTL = mmDP ## id ## _DP_MSE_RATE_CNTL,\ ++ .DP_MSE_RATE_UPDATE = mmDP ## id ## _DP_MSE_RATE_UPDATE,\ ++ .DP_PIXEL_FORMAT = mmDP ## id ## _DP_PIXEL_FORMAT,\ ++ .DP_SEC_CNTL = mmDP ## id ## _DP_SEC_CNTL,\ ++ .DP_STEER_FIFO = mmDP ## id ## _DP_STEER_FIFO,\ ++ .DP_VID_M = mmDP ## id ## _DP_VID_M,\ ++ .DP_VID_N = mmDP ## id ## _DP_VID_N,\ ++ .DP_VID_STREAM_CNTL = mmDP ## id ## _DP_VID_STREAM_CNTL,\ ++ .DP_VID_TIMING = mmDP ## id ## _DP_VID_TIMING,\ ++ .HDMI_CONTROL = mmDIG ## id ## _HDMI_CONTROL,\ ++ .HDMI_GC = mmDIG ## id ## _HDMI_GC,\ ++ .HDMI_GENERIC_PACKET_CONTROL0 = mmDIG ## id ## _HDMI_GENERIC_PACKET_CONTROL0,\ ++ .HDMI_GENERIC_PACKET_CONTROL1 = mmDIG ## id ## _HDMI_GENERIC_PACKET_CONTROL1,\ ++ .HDMI_INFOFRAME_CONTROL0 = mmDIG ## id ## _HDMI_INFOFRAME_CONTROL0,\ ++ .HDMI_INFOFRAME_CONTROL1 = mmDIG ## id ## _HDMI_INFOFRAME_CONTROL1,\ ++ .HDMI_VBI_PACKET_CONTROL = mmDIG ## id ## _HDMI_VBI_PACKET_CONTROL,\ ++ .TMDS_CNTL = mmDIG ## id ## _TMDS_CNTL\ ++} ++ ++static const struct dce110_stream_enc_registers stream_enc_regs[] = { ++ stream_enc_regs(0), ++ stream_enc_regs(1), ++ stream_enc_regs(2), ++ stream_enc_regs(3), ++ stream_enc_regs(4), ++ stream_enc_regs(5) ++}; ++ ++static const struct dce110_opp_reg_offsets dce112_opp_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), ++}, ++{ ++ .fmt_offset = (mmFMT3_FMT_CONTROL - mmFMT0_FMT_CONTROL), ++ .dcfe_offset = (mmDCFE3_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++}, ++{ .fmt_offset = (mmFMT4_FMT_CONTROL - mmFMT0_FMT_CONTROL), ++ .dcfe_offset = (mmDCFE4_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++}, ++{ .fmt_offset = (mmFMT5_FMT_CONTROL - mmFMT0_FMT_CONTROL), ++ .dcfe_offset = (mmDCFE5_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), ++ .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), ++} ++}; ++ ++static const struct dce112_clk_src_reg_offsets dce112_clk_src_reg_offsets[] = { ++ { ++ .pixclk_resync_cntl = mmPHYPLLA_PIXCLK_RESYNC_CNTL ++ }, ++ { ++ .pixclk_resync_cntl = mmPHYPLLB_PIXCLK_RESYNC_CNTL ++ }, ++ { ++ .pixclk_resync_cntl = mmPHYPLLC_PIXCLK_RESYNC_CNTL ++ }, ++ { ++ .pixclk_resync_cntl = mmPHYPLLD_PIXCLK_RESYNC_CNTL ++ }, ++ { ++ .pixclk_resync_cntl = mmPHYPLLE_PIXCLK_RESYNC_CNTL ++ }, ++ { ++ .pixclk_resync_cntl = mmPHYPLLF_PIXCLK_RESYNC_CNTL ++ } ++}; ++ ++static struct timing_generator *dce112_timing_generator_create( ++ struct adapter_service *as, ++ struct dc_context *ctx, ++ uint32_t instance, ++ const struct dce110_timing_generator_offsets *offsets) ++{ ++ struct dce110_timing_generator *tg110 = ++ dm_alloc(sizeof(struct dce110_timing_generator)); ++ ++ if (!tg110) ++ return NULL; ++ ++ if (dce110_timing_generator_construct(tg110, as, ctx, instance, offsets)) ++ return &tg110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(tg110); ++ return NULL; ++} ++ ++static struct stream_encoder *dce112_stream_encoder_create( ++ enum engine_id eng_id, ++ struct dc_context *ctx, ++ struct dc_bios *bp, ++ const struct dce110_stream_enc_registers *regs) ++{ ++ struct dce110_stream_encoder *enc110 = ++ dm_alloc(sizeof(struct dce110_stream_encoder)); ++ ++ if (!enc110) ++ return NULL; ++ ++ if (dce110_stream_encoder_construct(enc110, ctx, bp, eng_id, regs)) ++ return &enc110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(enc110); ++ return NULL; ++} ++ ++static struct mem_input *dce112_mem_input_create( ++ struct dc_context *ctx, ++ uint32_t inst, ++ const struct dce110_mem_input_reg_offsets *offset) ++{ ++ struct dce110_mem_input *mem_input110 = ++ dm_alloc(sizeof(struct dce110_mem_input)); ++ ++ if (!mem_input110) ++ return NULL; ++ ++ if (dce112_mem_input_construct(mem_input110, ++ ctx, inst, offset)) ++ return &mem_input110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(mem_input110); ++ return NULL; ++} ++ ++static void dce112_transform_destroy(struct transform **xfm) ++{ ++ dm_free(TO_DCE110_TRANSFORM(*xfm)); ++ *xfm = NULL; ++} ++ ++static struct transform *dce112_transform_create( ++ struct dc_context *ctx, ++ uint32_t inst, ++ const struct dce110_transform_reg_offsets *offsets) ++{ ++ struct dce110_transform *transform = ++ dm_alloc(sizeof(struct dce110_transform)); ++ ++ if (!transform) ++ return NULL; ++ ++ if (dce110_transform_construct(transform, ctx, inst, offsets)) ++ return &transform->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(transform); ++ return NULL; ++} ++struct link_encoder *dce112_link_encoder_create( ++ const struct encoder_init_data *enc_init_data) ++{ ++ struct dce110_link_encoder *enc110 = ++ dm_alloc(sizeof(struct dce110_link_encoder)); ++ ++ if (!enc110) ++ return NULL; ++ ++ if (dce112_link_encoder_construct( ++ enc110, ++ enc_init_data, ++ &link_enc_regs[enc_init_data->transmitter], ++ &link_enc_aux_regs[enc_init_data->channel - 1], ++ &link_enc_bl_regs)) ++ return &enc110->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(enc110); ++ return NULL; ++} ++ ++struct input_pixel_processor *dce112_ipp_create( ++ struct dc_context *ctx, ++ uint32_t inst, ++ const struct dce110_ipp_reg_offsets *offset) ++{ ++ struct dce110_ipp *ipp = ++ dm_alloc(sizeof(struct dce110_ipp)); ++ ++ if (!ipp) ++ return NULL; ++ ++ if (dce110_ipp_construct(ipp, ctx, inst, offset)) ++ return &ipp->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(ipp); ++ return NULL; ++} ++ ++void dce112_ipp_destroy(struct input_pixel_processor **ipp) ++{ ++ dm_free(TO_DCE110_IPP(*ipp)); ++ *ipp = NULL; ++} ++ ++struct output_pixel_processor *dce112_opp_create( ++ struct dc_context *ctx, ++ uint32_t inst, ++ const struct dce110_opp_reg_offsets *offset) ++{ ++ struct dce110_opp *opp = ++ dm_alloc(sizeof(struct dce110_opp)); ++ ++ if (!opp) ++ return NULL; ++ ++ if (dce110_opp_construct(opp, ++ ctx, inst, offset)) ++ return &opp->base; ++ ++ BREAK_TO_DEBUGGER(); ++ dm_free(opp); ++ return NULL; ++} ++ ++void dce112_opp_destroy(struct output_pixel_processor **opp) ++{ ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.coeff128_dx); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.coeff128_oem); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.coeff128); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.axis_x_1025); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.axis_x_256); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.coordinates_x); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.rgb_regamma); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.rgb_resulted); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.rgb_oem); ++ dm_free(FROM_DCE11_OPP(*opp)->regamma.rgb_user); ++ dm_free(FROM_DCE11_OPP(*opp)); ++ *opp = NULL; ++} ++ ++struct clock_source *dce112_clock_source_create( ++ struct dc_context *ctx, ++ struct dc_bios *bios, ++ enum clock_source_id id, ++ const struct dce112_clk_src_reg_offsets *offsets) ++{ ++ struct dce112_clk_src *clk_src = ++ dm_alloc(sizeof(struct dce112_clk_src)); ++ ++ if (!clk_src) ++ return NULL; ++ ++ if (dce112_clk_src_construct(clk_src, ctx, bios, id, offsets)) ++ return &clk_src->base; ++ ++ BREAK_TO_DEBUGGER(); ++ return NULL; ++} ++ ++void dce112_clock_source_destroy(struct clock_source **clk_src) ++{ ++ dm_free(TO_DCE112_CLK_SRC(*clk_src)); ++ *clk_src = NULL; ++} ++ ++void dce112_destruct_resource_pool(struct resource_pool *pool) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < pool->pipe_count; i++) { ++ if (pool->opps[i] != NULL) ++ dce112_opp_destroy(&pool->opps[i]); ++ ++ if (pool->transforms[i] != NULL) ++ dce112_transform_destroy(&pool->transforms[i]); ++ ++ if (pool->ipps[i] != NULL) ++ dce112_ipp_destroy(&pool->ipps[i]); ++ ++ if (pool->mis[i] != NULL) { ++ dm_free(TO_DCE110_MEM_INPUT(pool->mis[i])); ++ pool->mis[i] = NULL; ++ } ++ ++ if (pool->timing_generators[i] != NULL) { ++ dm_free(DCE110TG_FROM_TG(pool->timing_generators[i])); ++ pool->timing_generators[i] = NULL; ++ } ++ } ++ ++ for (i = 0; i < pool->stream_enc_count; i++) { ++ if (pool->stream_enc[i] != NULL) ++ dm_free(DCE110STRENC_FROM_STRENC(pool->stream_enc[i])); ++ } ++ ++ for (i = 0; i < pool->clk_src_count; i++) { ++ if (pool->clock_sources[i] != NULL) { ++ dce112_clock_source_destroy(&pool->clock_sources[i]); ++ } ++ } ++ ++ if (pool->dp_clock_source != NULL) ++ dce112_clock_source_destroy(&pool->dp_clock_source); ++ ++ 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 struct clock_source *find_matching_pll(struct resource_context *res_ctx, ++ const struct core_stream *const stream) ++{ ++ switch (stream->sink->link->link_enc->transmitter) { ++ case TRANSMITTER_UNIPHY_A: ++ return res_ctx->pool.clock_sources[DCE112_CLK_SRC_PLL0]; ++ case TRANSMITTER_UNIPHY_B: ++ return res_ctx->pool.clock_sources[DCE112_CLK_SRC_PLL1]; ++ case TRANSMITTER_UNIPHY_C: ++ return res_ctx->pool.clock_sources[DCE112_CLK_SRC_PLL2]; ++ case TRANSMITTER_UNIPHY_D: ++ return res_ctx->pool.clock_sources[DCE112_CLK_SRC_PLL3]; ++ case TRANSMITTER_UNIPHY_E: ++ return res_ctx->pool.clock_sources[DCE112_CLK_SRC_PLL4]; ++ case TRANSMITTER_UNIPHY_F: ++ return res_ctx->pool.clock_sources[DCE112_CLK_SRC_PLL5]; ++ default: ++ return NULL; ++ }; ++ ++ return 0; ++} ++ ++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 pipe_ctx *pipe_ctx, ++ struct audio_output *audio_output) ++{ ++ const struct core_stream *stream = pipe_ctx->stream; ++ audio_output->engine_id = pipe_ctx->stream_enc->id; ++ ++ audio_output->signal = pipe_ctx->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 = ++ pipe_ctx->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 = ++ pipe_ctx->pix_clk_params.requested_pix_clk; ++ ++ if (pipe_ctx->signal == SIGNAL_TYPE_DISPLAY_PORT || ++ pipe_ctx->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { ++ audio_output->pll_info.dp_dto_source_clock_in_khz = ++ dal_display_clock_get_dp_ref_clk_frequency( ++ pipe_ctx->dis_clk); ++ } ++ ++ audio_output->pll_info.feed_back_divider = ++ pipe_ctx->pll_settings.feedback_divider; ++ ++ audio_output->pll_info.dto_source = ++ translate_to_dto_source( ++ pipe_ctx->pipe_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 = ++ pipe_ctx->pll_settings.ss_percentage; ++} ++ ++static void get_pixel_clock_parameters( ++ const struct pipe_ctx *pipe_ctx, ++ struct pixel_clk_params *pixel_clk_params) ++{ ++ const struct core_stream *stream = pipe_ctx->stream; ++ 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 = pipe_ctx->pipe_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_pipe_hw_param(struct pipe_ctx *pipe_ctx) ++{ ++ /*TODO: unhardcode*/ ++ pipe_ctx->max_tmds_clk_from_edid_in_mhz = 0; ++ pipe_ctx->max_hdmi_deep_color = COLOR_DEPTH_121212; ++ pipe_ctx->max_hdmi_pixel_clock = 600000; ++ ++ get_pixel_clock_parameters(pipe_ctx, &pipe_ctx->pix_clk_params); ++ pipe_ctx->clock_source->funcs->get_pix_clk_dividers( ++ pipe_ctx->clock_source, ++ &pipe_ctx->pix_clk_params, ++ &pipe_ctx->pll_settings); ++ ++ build_audio_output(pipe_ctx, &pipe_ctx->audio_output); ++ ++ return DC_OK; ++} ++ ++static enum dc_status validate_mapped_resource( ++ const struct core_dc *dc, ++ struct validate_context *context) ++{ ++ enum dc_status status = DC_OK; ++ 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->public.stream_count; j++) { ++ struct core_stream *stream = ++ DC_STREAM_TO_CORE(target->public.streams[j]); ++ struct core_link *link = stream->sink->link; ++ ++ for (k = 0; k < MAX_PIPES; k++) { ++ struct pipe_ctx *pipe_ctx = ++ &context->res_ctx.pipe_ctx[k]; ++ ++ if (context->res_ctx.pipe_ctx[k].stream != stream) ++ continue; ++ ++ if (!pipe_ctx->tg->funcs->validate_timing( ++ pipe_ctx->tg, &stream->public.timing)) ++ return DC_FAIL_CONTROLLER_VALIDATE; ++ ++ status = build_pipe_hw_param(pipe_ctx); ++ ++ if (status != DC_OK) ++ return status; ++ ++ if (!link->link_enc->funcs->validate_output_with_stream( ++ link->link_enc, ++ pipe_ctx)) ++ 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; ++ ++ resource_build_info_frame(pipe_ctx); ++ ++ /* do not need to validate non root pipes */ ++ break; ++ } ++ } ++ } ++ ++ return DC_OK; ++} ++ ++enum dc_status dce112_validate_bandwidth( ++ const struct core_dc *dc, ++ struct validate_context *context) ++{ ++ uint8_t i; ++ enum dc_status result = DC_ERROR_UNEXPECTED; ++ uint8_t number_of_displays = 0; ++ uint8_t max_htaps = 1; ++ uint8_t max_vtaps = 1; ++ bool all_displays_in_sync = true; ++ struct dc_crtc_timing prev_timing; ++ ++ memset(&context->bw_mode_data, 0, sizeof(context->bw_mode_data)); ++ ++ for (i = 0; i < MAX_PIPES; i++) { ++ struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; ++ struct bw_calcs_input_single_display *disp = &context-> ++ bw_mode_data.displays_data[number_of_displays]; ++ ++ if (pipe_ctx->stream == NULL) ++ continue; ++ ++ if (pipe_ctx->scl_data.ratios.vert.value == 0) { ++ disp->graphics_scale_ratio = bw_int_to_fixed(1); ++ disp->graphics_h_taps = 2; ++ disp->graphics_v_taps = 2; ++ ++ /* TODO: remove when bw formula accepts taps per ++ * display ++ */ ++ if (max_vtaps < 2) ++ max_vtaps = 2; ++ if (max_htaps < 2) ++ max_htaps = 2; ++ ++ } else { ++ disp->graphics_scale_ratio = ++ fixed31_32_to_bw_fixed( ++ pipe_ctx->scl_data.ratios.vert.value); ++ disp->graphics_h_taps = pipe_ctx->scl_data.taps.h_taps; ++ disp->graphics_v_taps = pipe_ctx->scl_data.taps.v_taps; ++ ++ /* TODO: remove when bw formula accepts taps per ++ * display ++ */ ++ if (max_vtaps < pipe_ctx->scl_data.taps.v_taps) ++ max_vtaps = pipe_ctx->scl_data.taps.v_taps; ++ if (max_htaps < pipe_ctx->scl_data.taps.h_taps) ++ max_htaps = pipe_ctx->scl_data.taps.h_taps; ++ } ++ ++ disp->graphics_src_width = ++ pipe_ctx->stream->public.timing.h_addressable; ++ disp->graphics_src_height = ++ pipe_ctx->stream->public.timing.v_addressable; ++ disp->h_total = pipe_ctx->stream->public.timing.h_total; ++ disp->pixel_rate = bw_frc_to_fixed( ++ pipe_ctx->stream->public.timing.pix_clk_khz, 1000); ++ ++ /*TODO: get from surface*/ ++ disp->graphics_bytes_per_pixel = 4; ++ disp->graphics_tiling_mode = bw_def_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 = bw_def_mono; ++ disp->underlay_mode = bw_def_none; ++ ++ /*All displays will be synchronized if timings are all ++ * the same ++ */ ++ if (number_of_displays != 0 && all_displays_in_sync) ++ if (memcmp(&prev_timing, ++ &pipe_ctx->stream->public.timing, ++ sizeof(struct dc_crtc_timing)) != 0) ++ all_displays_in_sync = false; ++ if (number_of_displays == 0) ++ prev_timing = pipe_ctx->stream->public.timing; ++ ++ number_of_displays++; ++ } ++ ++ /* TODO: remove when bw formula accepts taps per ++ * display ++ */ ++ context->bw_mode_data.displays_data[0].graphics_v_taps = max_vtaps; ++ context->bw_mode_data.displays_data[0].graphics_h_taps = max_htaps; ++ ++ context->bw_mode_data.number_of_displays = number_of_displays; ++ context->bw_mode_data.display_synchronization_enabled = ++ all_displays_in_sync; ++ ++ dal_logger_write( ++ dc->ctx->logger, ++ LOG_MAJOR_BWM, ++ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS, ++ "%s: start", ++ __func__); ++ ++ if (!bw_calcs( ++ dc->ctx, ++ &dc->bw_dceip, ++ &dc->bw_vbios, ++ &context->bw_mode_data, ++ &context->bw_results)) ++ result = DC_FAIL_BANDWIDTH_VALIDATE; ++ else ++ result = DC_OK; ++ ++ if (result == DC_FAIL_BANDWIDTH_VALIDATE) ++ dal_logger_write(dc->ctx->logger, ++ LOG_MAJOR_BWM, ++ LOG_MINOR_BWM_MODE_VALIDATION, ++ "%s: Bandwidth validation failed!", ++ __func__); ++ ++ if (memcmp(&dc->current_context.bw_results, ++ &context->bw_results, sizeof(context->bw_results))) { ++ struct log_entry log_entry; ++ dal_logger_open( ++ dc->ctx->logger, ++ &log_entry, ++ LOG_MAJOR_BWM, ++ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS); ++ dal_logger_append(&log_entry, "%s: finish, numDisplays: %d\n" ++ "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" ++ "stutMark_b: %d stutMark_a: %d\n", ++ __func__, number_of_displays, ++ context->bw_results.nbp_state_change_wm_ns[0].b_mark, ++ context->bw_results.nbp_state_change_wm_ns[0].a_mark, ++ context->bw_results.urgent_wm_ns[0].b_mark, ++ context->bw_results.urgent_wm_ns[0].a_mark, ++ context->bw_results.stutter_exit_wm_ns[0].b_mark, ++ context->bw_results.stutter_exit_wm_ns[0].a_mark); ++ dal_logger_append(&log_entry, ++ "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" ++ "stutMark_b: %d stutMark_a: %d\n", ++ context->bw_results.nbp_state_change_wm_ns[1].b_mark, ++ context->bw_results.nbp_state_change_wm_ns[1].a_mark, ++ context->bw_results.urgent_wm_ns[1].b_mark, ++ context->bw_results.urgent_wm_ns[1].a_mark, ++ context->bw_results.stutter_exit_wm_ns[1].b_mark, ++ context->bw_results.stutter_exit_wm_ns[1].a_mark); ++ dal_logger_append(&log_entry, ++ "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" ++ "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n", ++ context->bw_results.nbp_state_change_wm_ns[2].b_mark, ++ context->bw_results.nbp_state_change_wm_ns[2].a_mark, ++ context->bw_results.urgent_wm_ns[2].b_mark, ++ context->bw_results.urgent_wm_ns[2].a_mark, ++ context->bw_results.stutter_exit_wm_ns[2].b_mark, ++ context->bw_results.stutter_exit_wm_ns[2].a_mark, ++ context->bw_results.stutter_mode_enable); ++ dal_logger_append(&log_entry, ++ "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" ++ "sclk: %d sclk_sleep: %d yclk: %d blackout_duration: %d\n", ++ context->bw_results.cpuc_state_change_enable, ++ context->bw_results.cpup_state_change_enable, ++ context->bw_results.nbp_state_change_enable, ++ context->bw_results.all_displays_in_sync, ++ context->bw_results.dispclk_khz, ++ context->bw_results.required_sclk, ++ context->bw_results.required_sclk_deep_sleep, ++ context->bw_results.required_yclk, ++ context->bw_results.required_blackout_duration_us); ++ dal_logger_close(&log_entry); ++ } ++ return result; ++} ++ ++static void set_target_unchanged( ++ struct validate_context *context, ++ uint8_t target_idx) ++{ ++ uint8_t i, j; ++ struct core_target *target = context->targets[target_idx]; ++ context->target_flags[target_idx].unchanged = true; ++ for (i = 0; i < target->public.stream_count; i++) { ++ struct core_stream *stream = ++ DC_STREAM_TO_CORE(target->public.streams[i]); ++ for (j = 0; j < MAX_PIPES; j++) { ++ if (context->res_ctx.pipe_ctx[j].stream == stream) ++ context->res_ctx.pipe_ctx[j].flags.unchanged = ++ true; ++ } ++ } ++} ++ ++static enum dc_status map_clock_resources( ++ const struct core_dc *dc, ++ struct validate_context *context) ++{ ++ uint8_t i, j, k; ++ ++ /* 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->public.stream_count; j++) { ++ struct core_stream *stream = ++ DC_STREAM_TO_CORE(target->public.streams[j]); ++ ++ for (k = 0; k < MAX_PIPES; k++) { ++ struct pipe_ctx *pipe_ctx = ++ &context->res_ctx.pipe_ctx[k]; ++ ++ if (context->res_ctx.pipe_ctx[k].stream != stream) ++ continue; ++ ++ if (dc_is_dp_signal(pipe_ctx->signal) ++ || pipe_ctx->signal == SIGNAL_TYPE_VIRTUAL) ++ pipe_ctx->clock_source = ++ context->res_ctx.pool.dp_clock_source; ++ else ++ pipe_ctx->clock_source = ++ find_matching_pll(&context->res_ctx, ++ stream); ++ ++ if (pipe_ctx->clock_source == NULL) ++ return DC_NO_CLOCK_SOURCE_RESOURCE; ++ ++ resource_reference_clock_source( ++ &context->res_ctx, ++ pipe_ctx->clock_source); ++ ++ /* only one cs per stream regardless of mpo */ ++ break; ++ } ++ } ++ } ++ ++ return DC_OK; ++} ++ ++enum dc_status dce112_validate_with_context( ++ const struct core_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++) { ++ bool unchanged = false; ++ ++ context->targets[i] = DC_TARGET_TO_CORE(set[i].target); ++ dc_target_retain(&context->targets[i]->public); ++ context->target_count++; ++ ++ for (j = 0; j < dc->current_context.target_count; j++) ++ if (dc->current_context.targets[j] ++ == context->targets[i]) { ++ unchanged = true; ++ set_target_unchanged(context, i); ++ resource_attach_surfaces_to_context( ++ (struct dc_surface **)dc->current_context. ++ target_status[j].surfaces, ++ dc->current_context.target_status[j].surface_count, ++ &context->targets[i]->public, ++ context); ++ context->target_status[i] = ++ dc->current_context.target_status[j]; ++ } ++ if (!unchanged || set[i].surface_count != 0) ++ if (!resource_attach_surfaces_to_context( ++ (struct dc_surface **)set[i].surfaces, ++ set[i].surface_count, ++ &context->targets[i]->public, ++ context)) { ++ DC_ERROR("Failed to attach surface to target!\n"); ++ return DC_FAIL_ATTACH_SURFACES; ++ } ++ } ++ ++ context->res_ctx.pool = dc->res_pool; ++ ++ result = resource_map_pool_resources(dc, context); ++ ++ if (result == DC_OK) ++ result = map_clock_resources(dc, context); ++ ++ if (result == DC_OK) ++ result = validate_mapped_resource(dc, context); ++ ++ if (result == DC_OK) ++ resource_build_scaling_params_for_context(dc, context); ++ ++ if (result == DC_OK) ++ result = dce112_validate_bandwidth(dc, context); ++ ++ return result; ++} ++ ++static struct resource_funcs dce112_res_pool_funcs = { ++ .destruct = dce112_destruct_resource_pool, ++ .link_enc_create = dce112_link_encoder_create, ++ .validate_with_context = dce112_validate_with_context, ++ .validate_bandwidth = dce112_validate_bandwidth ++}; ++ ++static void bw_calcs_data_update_from_pplib(struct core_dc *dc) ++{ ++ struct dm_pp_clock_levels clks = {0}; ++ ++ /*do system clock*/ ++ dm_pp_get_clock_levels_by_type( ++ dc->ctx, ++ DM_PP_CLOCK_TYPE_ENGINE_CLK, ++ &clks); ++ /* convert all the clock fro kHz to fix point mHz */ ++ dc->bw_vbios.high_sclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[clks.num_levels-1], 1000); ++ dc->bw_vbios.mid_sclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[clks.num_levels>>1], 1000); ++ dc->bw_vbios.low_sclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[0], 1000); ++ ++ /*do display clock*/ ++ dm_pp_get_clock_levels_by_type( ++ dc->ctx, ++ DM_PP_CLOCK_TYPE_DISPLAY_CLK, ++ &clks); ++ ++ dc->bw_vbios.high_voltage_max_dispclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[clks.num_levels-1], 1000); ++ dc->bw_vbios.mid_voltage_max_dispclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[clks.num_levels>>1], 1000); ++ dc->bw_vbios.low_voltage_max_dispclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[0], 1000); ++ ++ /*do memory clock*/ ++ dm_pp_get_clock_levels_by_type( ++ dc->ctx, ++ DM_PP_CLOCK_TYPE_MEMORY_CLK, ++ &clks); ++ ++ dc->bw_vbios.low_yclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[0] * MEMORY_TYPE_MULTIPLIER, 1000); ++ dc->bw_vbios.mid_yclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[clks.num_levels>>1] * MEMORY_TYPE_MULTIPLIER, ++ 1000); ++ dc->bw_vbios.high_yclk = bw_frc_to_fixed( ++ clks.clocks_in_khz[clks.num_levels-1] * MEMORY_TYPE_MULTIPLIER, ++ 1000); ++} ++ ++ ++bool dce112_construct_resource_pool( ++ struct adapter_service *adapter_serv, ++ uint8_t num_virtual_links, ++ struct core_dc *dc, ++ struct resource_pool *pool) ++{ ++ unsigned int i; ++ struct audio_init_data audio_init_data = { 0 }; ++ struct dc_context *ctx = dc->ctx; ++ ++ pool->adapter_srv = adapter_serv; ++ pool->funcs = &dce112_res_pool_funcs; ++ ++ pool->stream_engines.engine.ENGINE_ID_DIGA = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGB = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGC = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGD = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGE = 1; ++ pool->stream_engines.engine.ENGINE_ID_DIGF = 1; ++ ++ pool->clock_sources[DCE112_CLK_SRC_PLL0] = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_COMBO_PHY_PLL0, &dce112_clk_src_reg_offsets[0]); ++ pool->clock_sources[DCE112_CLK_SRC_PLL1] = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_COMBO_PHY_PLL1, &dce112_clk_src_reg_offsets[1]); ++ pool->clock_sources[DCE112_CLK_SRC_PLL2] = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_COMBO_PHY_PLL2, &dce112_clk_src_reg_offsets[2]); ++ pool->clock_sources[DCE112_CLK_SRC_PLL3] = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_COMBO_PHY_PLL3, &dce112_clk_src_reg_offsets[3]); ++ pool->clock_sources[DCE112_CLK_SRC_PLL4] = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_COMBO_PHY_PLL4, &dce112_clk_src_reg_offsets[4]); ++ pool->clock_sources[DCE112_CLK_SRC_PLL5] = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_COMBO_PHY_PLL5, &dce112_clk_src_reg_offsets[5]); ++ pool->clk_src_count = DCE112_CLK_SRC_TOTAL; ++ ++ pool->dp_clock_source = dce112_clock_source_create( ++ ctx, dal_adapter_service_get_bios_parser(adapter_serv), ++ CLOCK_SOURCE_ID_DP_DTO, &dce112_clk_src_reg_offsets[0]); ++ ++ for (i = 0; i < pool->clk_src_count; i++) { ++ if (pool->clock_sources[i] == NULL) { ++ dm_error("DC: failed to create clock sources!\n"); ++ BREAK_TO_DEBUGGER(); ++ goto clk_src_create_fail; ++ } ++ } ++ ++ pool->display_clock = dal_display_clock_dce112_create(ctx, adapter_serv); ++ if (pool->display_clock == NULL) { ++ dm_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->pipe_count = ++ dal_adapter_service_get_func_controllers_num(adapter_serv); ++ pool->stream_enc_count = 6; ++ pool->scaler_filter = dal_scaler_filter_create(ctx); ++ if (pool->scaler_filter == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error("DC: failed to create filter!\n"); ++ goto filter_create_fail; ++ } ++ ++ for (i = 0; i < pool->pipe_count; i++) { ++ pool->timing_generators[i] = dce112_timing_generator_create( ++ adapter_serv, ++ ctx, ++ i, ++ &dce112_tg_offsets[i]); ++ if (pool->timing_generators[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error("DC: failed to create tg!\n"); ++ goto controller_create_fail; ++ } ++ ++ pool->mis[i] = dce112_mem_input_create( ++ ctx, ++ i, ++ &dce112_mi_reg_offsets[i]); ++ if (pool->mis[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC: failed to create memory input!\n"); ++ goto controller_create_fail; ++ } ++ ++ pool->ipps[i] = dce112_ipp_create( ++ ctx, ++ i, ++ &ipp_reg_offsets[i]); ++ if (pool->ipps[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC: failed to create input pixel processor!\n"); ++ goto controller_create_fail; ++ } ++ ++ pool->transforms[i] = dce112_transform_create( ++ ctx, ++ i, ++ &dce112_xfm_offsets[i]); ++ if (pool->transforms[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC: failed to create transform!\n"); ++ goto controller_create_fail; ++ } ++ pool->transforms[i]->funcs->transform_set_scaler_filter( ++ pool->transforms[i], ++ pool->scaler_filter); ++ ++ pool->opps[i] = dce112_opp_create( ++ ctx, ++ i, ++ &dce112_opp_reg_offsets[i]); ++ if (pool->opps[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_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->pipe_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(); ++ dm_error("DC: failed to create DPPs!\n"); ++ goto audio_create_fail; ++ } ++ pool->audio_count++; ++ } ++ ++ for (i = 0; i < pool->stream_enc_count; i++) { ++ /* TODO: rework fragile code*/ ++ if (pool->stream_engines.u_all & 1 << i) { ++ pool->stream_enc[i] = dce112_stream_encoder_create( ++ i, dc->ctx, ++ dal_adapter_service_get_bios_parser( ++ adapter_serv), ++ &stream_enc_regs[i]); ++ if (pool->stream_enc[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error("DC: failed to create stream_encoder!\n"); ++ goto stream_enc_create_fail; ++ } ++ } ++ } ++ ++ for (i = 0; i < num_virtual_links; i++) { ++ pool->stream_enc[pool->stream_enc_count] = ++ virtual_stream_encoder_create( ++ dc->ctx, dal_adapter_service_get_bios_parser( ++ adapter_serv)); ++ if (pool->stream_enc[pool->stream_enc_count] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error("DC: failed to create stream_encoder!\n"); ++ goto stream_enc_create_fail; ++ } ++ pool->stream_enc_count++; ++ } ++ ++ /* Create hardware sequencer */ ++ if (!dc_construct_hw_sequencer(adapter_serv, dc)) ++ goto stream_enc_create_fail; ++ ++ bw_calcs_init(&dc->bw_dceip, &dc->bw_vbios, BW_CALCS_VERSION_BAFFIN); ++ ++ bw_calcs_data_update_from_pplib(dc); ++ ++ return true; ++ ++stream_enc_create_fail: ++ for (i = 0; i < pool->stream_enc_count; i++) { ++ if (pool->stream_enc[i] != NULL) ++ dm_free(DCE110STRENC_FROM_STRENC(pool->stream_enc[i])); ++ } ++ ++audio_create_fail: ++ for (i = 0; i < pool->pipe_count; i++) { ++ if (pool->audios[i] != NULL) ++ dal_audio_destroy(&pool->audios[i]); ++ } ++ ++controller_create_fail: ++ for (i = 0; i < pool->pipe_count; i++) { ++ if (pool->opps[i] != NULL) ++ dce112_opp_destroy(&pool->opps[i]); ++ ++ if (pool->transforms[i] != NULL) ++ dce112_transform_destroy(&pool->transforms[i]); ++ ++ if (pool->ipps[i] != NULL) ++ dce112_ipp_destroy(&pool->ipps[i]); ++ ++ if (pool->mis[i] != NULL) { ++ dm_free(TO_DCE110_MEM_INPUT(pool->mis[i])); ++ pool->mis[i] = NULL; ++ } ++ ++ if (pool->timing_generators[i] != NULL) { ++ dm_free(DCE110TG_FROM_TG(pool->timing_generators[i])); ++ pool->timing_generators[i] = NULL; ++ } ++ } ++ ++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) ++ dce112_clock_source_destroy(&pool->clock_sources[i]); ++ } ++ ++ return false; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.h b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.h +new file mode 100644 +index 0000000..eed1faf +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce112/dce112_resource.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 __DC_RESOURCE_DCE112_H__ ++#define __DC_RESOURCE_DCE112_H__ ++ ++#include "core_types.h" ++ ++struct adapter_service; ++struct core_dc; ++struct resource_pool; ++ ++bool dce112_construct_resource_pool( ++ struct adapter_service *adapter_serv, ++ uint8_t num_virtual_links, ++ struct core_dc *dc, ++ struct resource_pool *pool); ++ ++#endif /* __DC_RESOURCE_DCE112_H__ */ ++ +diff --git a/drivers/gpu/drm/amd/dal/dc/dm_services_types.h b/drivers/gpu/drm/amd/dal/dc/dm_services_types.h +index 1e87624..982e968 100644 +--- a/drivers/gpu/drm/amd/dal/dc/dm_services_types.h ++++ b/drivers/gpu/drm/amd/dal/dc/dm_services_types.h +@@ -212,6 +212,11 @@ enum dm_pp_clock_type { + struct dm_pp_clock_levels { + uint32_t num_levels; + uint32_t clocks_in_khz[DM_PP_MAX_CLOCK_LEVELS]; ++ ++ /* TODO: add latency for polaris11 ++ * do we need to know invalid (unsustainable boost) level for watermark ++ * programming? if not we can just report less elements in array ++ */ + }; + + struct dm_pp_single_disp_config { +diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c +index 63d6b54..5037a2d 100644 +--- a/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c +@@ -78,6 +78,9 @@ bool dal_hw_factory_init( + #endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + case DCE_VERSION_11_0: ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++#endif + dal_hw_factory_dce110_init(factory); + return true; + #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 +index d3c6bc8..da56db7 100644 +--- a/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c ++++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c +@@ -75,6 +75,9 @@ bool dal_hw_translate_init( + case DCE_VERSION_10_0: + #endif + case DCE_VERSION_11_0: ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++#endif + dal_hw_translate_dce110_init(translate); + return true; + #endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/Makefile b/drivers/gpu/drm/amd/dal/dc/gpu/Makefile +index cb23508..3095006 100644 +--- a/drivers/gpu/drm/amd/dal/dc/gpu/Makefile ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/Makefile +@@ -32,3 +32,11 @@ AMD_DAL_GPU_DCE110 = $(addprefix $(AMDDALPATH)/dc/gpu/dce110/,$(GPU_DCE110)) + + AMD_DAL_FILES += $(AMD_DAL_GPU_DCE110) + endif ++ ++ifdef CONFIG_DRM_AMD_DAL_DCE11_2 ++GPU_DCE112 = display_clock_dce112.o dc_clock_gating_dce112.o ++ ++AMD_DAL_GPU_DCE112 = $(addprefix $(AMDDALPATH)/dc/gpu/dce112/,$(GPU_DCE112)) ++ ++AMD_DAL_FILES += $(AMD_DAL_GPU_DCE110) $(AMD_DAL_GPU_DCE112) ++endif +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.c +new file mode 100644 +index 0000000..bf24457 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.c +@@ -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 ++ * ++ */ ++ ++#include "dm_services.h" ++ ++#include "include/logger_interface.h" ++ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_sh_mask.h" ++#include "dc_clock_gating_dce112.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 = dm_read_reg(ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ DC_MEM_GLOBAL_PWR_REQ_CNTL, ++ DC_MEM_GLOBAL_PWR_REQ_DIS); ++ ++ dm_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_dce112_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/dce112/dc_clock_gating_dce112.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.h +new file mode 100644 +index 0000000..118da64 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/dc_clock_gating_dce112.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_DCE112_H__ ++#define __DAL_DC_CLOCK_GATING_DCE112_H__ ++ ++void dal_dc_clock_gating_dce112_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/dce112/display_clock_dce112.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.c +new file mode 100644 +index 0000000..e559f95 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.c +@@ -0,0 +1,964 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_services.h" ++ ++#include "dce/dce_11_2_d.h" ++#include "dce/dce_11_2_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_dce112.h" ++ ++#define FROM_DISPLAY_CLOCK(base) \ ++ container_of(base, struct display_clock_dce112, disp_clk_base) ++ ++static struct state_dependent_clocks max_clks_by_state[] = { ++/*ClocksStateInvalid - should not be used*/ ++{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, ++/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ ++{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, ++/*ClocksStateLow*/ ++{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, ++/*ClocksStateNominal*/ ++{ .display_clk_khz = 467000, .pixel_clk_khz = 400000 }, ++/*ClocksStatePerformance*/ ++{ .display_clk_khz = 643000, .pixel_clk_khz = 4000000 } }; ++ ++/* 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 dce112_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_dce112 *dc = DCLCK112_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_dce112 *dc = DCLCK112_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_dce112 *disp_clk = FROM_DISPLAY_CLOCK(dc); ++ ++ /* ASSERT DP Reference Clock source is from DFS*/ ++ dp_ref_clk_cntl_value = dm_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 = dm_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_dce112 *dc112; ++ ++ dc112 = DCLCK112_FROM_BASE(*base); ++ ++ dm_free(dc112); ++ ++ *base = NULL; ++} ++ ++static uint32_t get_validation_clock(struct display_clock *dc) ++{ ++ uint32_t clk = 0; ++ struct display_clock_dce112 *disp_clk = DCLCK112_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_dce112 *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_dce112 *disp_clk_110 = DCLCK112_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_dce112 *dc = DCLCK112_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_dce112 *disp_clk, ++ struct adapter_service *as) ++{ ++ struct integrated_info info; ++ uint32_t i; ++ struct display_clock *base = &disp_clk->disp_clk_base; ++ ++ memset(&info, 0, sizeof(struct integrated_info)); ++ ++ 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; ++ ++ disp_clk->crystal_freq_khz = 100000; ++ ++ 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_dce112 *disp_clk = DCLCK112_FROM_BASE(dc); ++ ++ /* Read the mmDENTIST_DISPCLK_CNTL to get the currently programmed ++ DID DENTIST_DISPCLK_WDIVIDER.*/ ++ value = dm_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_dce112 *disp_clk = DCLCK112_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_set_dce_clock_parameters dce_clk_params; ++ struct display_clock_dce112 *dc = DCLCK112_FROM_BASE(base); ++ struct dc_bios *bp = dal_adapter_service_get_bios_parser(base->as); ++ ++ /* Prepare to program display clock*/ ++ memset(&dce_clk_params, 0, sizeof(dce_clk_params)); ++ ++ dce_clk_params.target_clock_frequency = requested_clk_khz; ++ dce_clk_params.pll_id = dc->disp_clk_base.id; ++ dce_clk_params.clock_type = DCECLOCK_TYPE_DISPLAY_CLOCK; ++ ++ bp->funcs->set_dce_clock(bp, &dce_clk_params); ++ ++ /* 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; ++ ++ /*Program DP ref Clock*/ ++ /*VBIOS will determine DPREFCLK frequency, so we don't set it*/ ++ dce_clk_params.target_clock_frequency = 0; ++ dce_clk_params.clock_type = DCECLOCK_TYPE_DPREFCLK; ++ dce_clk_params.flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK = ++ (dce_clk_params.pll_id == CLOCK_SOURCE_COMBO_DISPLAY_PLL0); ++ ++ bp->funcs->set_dce_clock(bp, &dce_clk_params); ++} ++ ++static void set_clock_state( ++ struct display_clock *dc, ++ struct display_clock_state clk_state) ++{ ++ struct display_clock_dce112 *disp_clk = DCLCK112_FROM_BASE(dc); ++ ++ disp_clk->clock_state = clk_state; ++} ++ ++static struct display_clock_state get_clock_state( ++ struct display_clock *dc) ++{ ++ struct display_clock_dce112 *disp_clk = DCLCK112_FROM_BASE(dc); ++ ++ return disp_clk->clock_state; ++} ++ ++static uint32_t get_dfs_bypass_threshold(struct display_clock *dc) ++{ ++ return dce112_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_dce112_construct( ++ struct display_clock_dce112 *dc112, ++ struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ struct display_clock *dc_base = &dc112->disp_clk_base; ++ ++ if (NULL == as) ++ return false; ++ ++ if (!dal_display_clock_construct_base(dc_base, ctx, as)) ++ return false; ++ ++ dc_base->funcs = &funcs; ++ ++ dc112->dfs_bypass_disp_clk = 0; ++ ++ if (!display_clock_integrated_info_construct(dc112, as)) ++ dal_logger_write(dc_base->ctx->logger, ++ LOG_MAJOR_WARNING, ++ LOG_MINOR_COMPONENT_GPU, ++ "Cannot obtain VBIOS integrated info\n"); ++ ++ dc112->gpu_pll_ss_percentage = 0; ++ dc112->gpu_pll_ss_divider = 1000; ++ dc112->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_dce112::StoreMaxClocksState(). This call will come in ++ * on PPLIB init. This is from DCE5x. in case HW wants to use mixed method.*/ ++ dc112->max_clks_state = CLOCKS_STATE_NOMINAL; ++ ++ dc112->disp_clk_base.min_display_clk_threshold_khz = ++ dc112->crystal_freq_khz; ++ ++ if (dc112->disp_clk_base.min_display_clk_threshold_khz < ++ (dc112->dentist_vco_freq_khz / 62)) ++ dc112->disp_clk_base.min_display_clk_threshold_khz = ++ (dc112->dentist_vco_freq_khz / 62); ++ ++ 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; ++ ++ 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) { ++ dc112->ss_on_gpu_pll = true; ++ dc112->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 */ ++ dc112->gpu_pll_ss_percentage = ++ info.spread_spectrum_percentage; ++ } ++ } ++ ++ } ++ } ++ ++ dc112->use_max_disp_clk = true; ++ ++ return true; ++} ++ ++/***************************************************************************** ++ * public functions ++ *****************************************************************************/ ++ ++struct display_clock *dal_display_clock_dce112_create( ++ struct dc_context *ctx, ++ struct adapter_service *as) ++{ ++ struct display_clock_dce112 *dc112; ++ ++ dc112 = dm_alloc(sizeof(struct display_clock_dce112)); ++ ++ if (dc112 == NULL) ++ return NULL; ++ ++ if (dal_display_clock_dce112_construct(dc112, ctx, as)) ++ return &dc112->disp_clk_base; ++ ++ dm_free(dc112); ++ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.h +new file mode 100644 +index 0000000..02fc67a +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce112/display_clock_dce112.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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_DCE112_H__ ++#define __DAL_DISPLAY_CLOCK_DCE112_H__ ++ ++#include "gpu/display_clock.h" ++ ++struct display_clock_dce112 { ++ struct display_clock disp_clk_base; ++ /* Max display block clocks state*/ ++ enum clocks_state max_clks_state; ++ bool use_max_disp_clk; ++ uint32_t crystal_freq_khz; ++ 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 DCLCK112_FROM_BASE(dc_base) \ ++ container_of(dc_base, struct display_clock_dce112, disp_clk_base) ++ ++#endif /* __DAL_DISPLAY_CLOCK_DCE112_H__ */ +diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c +index 47e7922..2d394cf 100644 +--- a/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c ++++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c +@@ -88,10 +88,13 @@ struct i2caux *dal_i2caux_create( + return dal_i2caux_dce80_create(as, ctx); + #endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) ++ case DCE_VERSION_11_0: ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) + case DCE_VERSION_10_0: + #endif +- case DCE_VERSION_11_0: + return dal_i2caux_dce110_create(as, ctx); + #endif + default: +diff --git a/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h b/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h +index d6a599c..da00b2e 100644 +--- a/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h ++++ b/drivers/gpu/drm/amd/dal/dc/inc/bandwidth_calcs.h +@@ -505,7 +505,9 @@ struct bw_calcs_output { + + enum bw_calcs_version { + BW_CALCS_VERSION_INVALID, +- BW_CALCS_VERSION_CARRIZO ++ BW_CALCS_VERSION_CARRIZO, ++ BW_CALCS_VERSION_ELLESMERE, ++ BW_CALCS_VERSION_BAFFIN + }; + + /** +diff --git a/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c +index cde34ce..bfffa8e 100644 +--- a/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c ++++ b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c +@@ -65,6 +65,10 @@ struct irq_service *dal_irq_service_create( + case DCE_VERSION_10_0: + return dal_irq_service_dce110_create(init_data); + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ case DCE_VERSION_11_2: ++ return dal_irq_service_dce110_create(init_data); ++#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + case DCE_VERSION_11_0: + return dal_irq_service_dce110_create(init_data); +diff --git a/drivers/gpu/drm/amd/dal/include/dal_asic_id.h b/drivers/gpu/drm/amd/dal/include/dal_asic_id.h +index d8c4cd1..4cb6a9f 100644 +--- a/drivers/gpu/drm/amd/dal/include/dal_asic_id.h ++++ b/drivers/gpu/drm/amd/dal/include/dal_asic_id.h +@@ -83,11 +83,25 @@ + #define VI_TONGA_P_A1 21 + #define VI_FIJI_P_A0 60 + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++/* DCE112 */ ++#define VI_POLARIS10_P_A0 80 ++#define VI_POLARIS11_M_A0 90 ++#endif ++ ++#define VI_UNKNOWN 0xFF ++ + #define ASIC_REV_IS_TONGA_P(eChipRev) ((eChipRev >= VI_TONGA_P_A0) && \ + (eChipRev < 40)) + #define ASIC_REV_IS_FIJI_P(eChipRev) ((eChipRev >= VI_FIJI_P_A0) && \ + (eChipRev < 80)) + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++#define ASIC_REV_IS_POLARIS10_P(eChipRev) ((eChipRev >= VI_POLARIS10_P_A0) && \ ++ (eChipRev < VI_POLARIS11_M_A0)) ++#define ASIC_REV_IS_POLARIS11_M(eChipRev) (eChipRev >= VI_POLARIS11_M_A0) ++#endif ++ + /* DCE11 */ + #define CZ_CARRIZO_A0 0x01 + +diff --git a/drivers/gpu/drm/amd/dal/include/dal_types.h b/drivers/gpu/drm/amd/dal/include/dal_types.h +index bcf83e9..21ee669 100644 +--- a/drivers/gpu/drm/amd/dal/include/dal_types.h ++++ b/drivers/gpu/drm/amd/dal/include/dal_types.h +@@ -43,6 +43,9 @@ enum dce_version { + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + DCE_VERSION_11_0, + #endif ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++ DCE_VERSION_11_2, ++#endif + DCE_VERSION_MAX, + }; + +diff --git a/drivers/gpu/drm/amd/dal/include/display_clock_interface.h b/drivers/gpu/drm/amd/dal/include/display_clock_interface.h +index a625e24..317ce3b 100644 +--- a/drivers/gpu/drm/amd/dal/include/display_clock_interface.h ++++ b/drivers/gpu/drm/amd/dal/include/display_clock_interface.h +@@ -131,6 +131,12 @@ struct display_clock_state { + + struct display_clock; + ++#if defined(CONFIG_DRM_AMD_DAL_DCE11_2) ++struct display_clock *dal_display_clock_dce112_create( ++ struct dc_context *ctx, ++ struct adapter_service *as); ++#endif ++ + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + struct display_clock *dal_display_clock_dce110_create( + struct dc_context *ctx, +-- +2.7.4 + |