From bc3e400b9b554d0e8448d89c7a721f887b612dfc Mon Sep 17 00:00:00 2001 From: Alex Deucher 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 --- 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