From f916c629a50d432a9b9b46d859ddbc93e2ca1973 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Wed, 17 Feb 2016 10:41:59 -0500 Subject: [PATCH 0803/1110] drm/amd/dal: Adding Hawaii and Bonaire support to DAL Signed-off-by: Harry Wentland Acked-by: Jordan Lazare --- drivers/gpu/drm/amd/dal/Kconfig | 17 +- drivers/gpu/drm/amd/dal/dc/Makefile | 4 + drivers/gpu/drm/amd/dal/dc/adapter/Makefile | 8 + .../gpu/drm/amd/dal/dc/adapter/adapter_service.c | 13 + .../adapter/dce80/hw_ctx_adapter_service_dce80.c | 322 ++++ .../adapter/dce80/hw_ctx_adapter_service_dce80.h | 40 + .../gpu/drm/amd/dal/dc/asic_capability/Makefile | 13 + .../amd/dal/dc/asic_capability/asic_capability.c | 8 + .../dc/asic_capability/hawaii_asic_capability.c | 151 ++ .../dc/asic_capability/hawaii_asic_capability.h | 37 + drivers/gpu/drm/amd/dal/dc/audio/Makefile | 12 + drivers/gpu/drm/amd/dal/dc/audio/audio_base.c | 9 + .../gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.c | 434 +++++ .../gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.h | 41 + .../amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.c | 1926 ++++++++++++++++++++ .../amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.h | 75 + drivers/gpu/drm/amd/dal/dc/bios/Makefile | 13 + .../gpu/drm/amd/dal/dc/bios/bios_parser_helper.c | 6 +- .../gpu/drm/amd/dal/dc/bios/bios_parser_helper.h | 4 + .../gpu/drm/amd/dal/dc/bios/command_table_helper.c | 5 + .../gpu/drm/amd/dal/dc/bios/command_table_helper.h | 3 + .../dal/dc/bios/dce80/bios_parser_helper_dce80.c | 773 ++++++++ .../dal/dc/bios/dce80/bios_parser_helper_dce80.h | 33 + .../dal/dc/bios/dce80/command_table_helper_dce80.c | 355 ++++ .../dal/dc/bios/dce80/command_table_helper_dce80.h | 33 + drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c | 7 + drivers/gpu/drm/amd/dal/dc/core/dc_resource.c | 3 + drivers/gpu/drm/amd/dal/dc/dce80/Makefile | 17 + .../gpu/drm/amd/dal/dc/dce80/dce80_compressor.c | 867 +++++++++ .../gpu/drm/amd/dal/dc/dce80/dce80_compressor.h | 84 + .../gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.c | 308 ++++ .../gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.h | 36 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.c | 64 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.h | 49 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp_gamma.c | 85 + .../gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.c | 329 ++++ .../gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.h | 39 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.c | 217 +++ drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.h | 41 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.c | 141 ++ drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.h | 130 ++ drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_csc.c | 905 +++++++++ .../gpu/drm/amd/dal/dc/dce80/dce80_opp_formatter.c | 577 ++++++ .../gpu/drm/amd/dal/dc/dce80/dce80_opp_regamma.c | 546 ++++++ drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.c | 1267 +++++++++++++ drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.h | 42 + .../drm/amd/dal/dc/dce80/dce80_stream_encoder.c | 1104 +++++++++++ .../drm/amd/dal/dc/dce80/dce80_stream_encoder.h | 85 + .../drm/amd/dal/dc/dce80/dce80_timing_generator.c | 241 +++ .../drm/amd/dal/dc/dce80/dce80_timing_generator.h | 49 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.c | 91 + drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.h | 87 + .../amd/dal/dc/dce80/dce80_transform_bit_depth.c | 841 +++++++++ .../amd/dal/dc/dce80/dce80_transform_bit_depth.h | 51 + .../drm/amd/dal/dc/dce80/dce80_transform_gamut.c | 297 +++ .../gpu/drm/amd/dal/dc/dce80/dce80_transform_scl.c | 814 +++++++++ drivers/gpu/drm/amd/dal/dc/gpio/Makefile | 12 + .../gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.c | 893 +++++++++ .../gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.h | 46 + .../drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.c | 78 + .../drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.h | 32 + .../gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.c | 378 ++++ .../gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.h | 44 + .../drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.c | 424 +++++ .../drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.h | 32 + drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c | 9 + drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c | 10 +- drivers/gpu/drm/amd/dal/dc/gpu/Makefile | 12 + .../amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.c | 52 + .../amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.h | 31 + .../drm/amd/dal/dc/gpu/dce80/display_clock_dce80.c | 925 ++++++++++ .../drm/amd/dal/dc/gpu/dce80/display_clock_dce80.h | 58 + drivers/gpu/drm/amd/dal/dc/i2caux/Makefile | 11 + .../drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.c | 740 ++++++++ .../drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.h | 54 + .../amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.c | 901 +++++++++ .../amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.h | 54 + .../amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.c | 187 ++ .../amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.h | 43 + .../gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.c | 264 +++ .../gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.h | 39 + drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c | 8 + drivers/gpu/drm/amd/dal/dc/irq/irq_service.c | 11 + drivers/gpu/drm/amd/dal/include/dal_types.h | 3 + .../drm/amd/dal/include/display_clock_interface.h | 6 + 85 files changed, 19100 insertions(+), 6 deletions(-) create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.c create mode 100644 drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.h create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/Makefile create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp_gamma.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_csc.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_formatter.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_regamma.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_gamut.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_scl.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.h create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.c create mode 100644 drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.h diff --git a/drivers/gpu/drm/amd/dal/Kconfig b/drivers/gpu/drm/amd/dal/Kconfig index 0dc6f86..b108756 100644 --- a/drivers/gpu/drm/amd/dal/Kconfig +++ b/drivers/gpu/drm/amd/dal/Kconfig @@ -19,14 +19,14 @@ config DRM_AMD_DAL_VBIOS_PRESENT x86 platforms and there is a VBIOS present in the system -config DRM_AMD_DAL_DCE11_0 - bool "Carrizo family" +config DRM_AMD_DAL_DCE8_0 + bool "CI family" depends on DRM_AMD_DAL help Choose this option if you want to have - CZ family - for display engine + CI family + for display engine. config DRM_AMD_DAL_DCE10_0 bool "VI family" @@ -37,6 +37,15 @@ config DRM_AMD_DAL_DCE10_0 VI family for display engine. +config DRM_AMD_DAL_DCE11_0 + bool "Carrizo family" + depends on DRM_AMD_DAL + help + Choose this option + if you want to have + CZ family + for display engine + config DEBUG_KERNEL_DAL bool "Enable kgdb break in DAL" depends on DRM_AMD_DAL diff --git a/drivers/gpu/drm/amd/dal/dc/Makefile b/drivers/gpu/drm/amd/dal/dc/Makefile index aed26ee..5112ec9 100644 --- a/drivers/gpu/drm/amd/dal/dc/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/Makefile @@ -13,6 +13,10 @@ ifdef CONFIG_DRM_AMD_DAL_DCE10_0 DC_LIBS += dce100 endif +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +DC_LIBS += dce80 +endif + AMD_DC = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/dc/,$(DC_LIBS))) include $(AMD_DC) diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/Makefile b/drivers/gpu/drm/amd/dal/dc/adapter/Makefile index 2c6ca7a..db1f0e8 100644 --- a/drivers/gpu/drm/amd/dal/dc/adapter/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/adapter/Makefile @@ -8,6 +8,14 @@ AMD_DAL_ADAPTER = $(addprefix $(AMDDALPATH)/dc/adapter/,$(ADAPTER)) AMD_DAL_FILES += $(AMD_DAL_ADAPTER) +############################################################################### +# DCE 8x +############################################################################### + +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +AMD_DAL_FILES += $(AMDDALPATH)/dc/adapter/dce80/hw_ctx_adapter_service_dce80.o +endif + ############################################################################### # DCE 11x 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 dd2f931..f914a8c 100644 --- a/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c +++ b/drivers/gpu/drm/amd/dal/dc/adapter/adapter_service.c @@ -41,6 +41,10 @@ #include "atom.h" +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/hw_ctx_adapter_service_dce80.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) #include "dce110/hw_ctx_adapter_service_dce110.h" #endif @@ -665,6 +669,10 @@ static struct hw_ctx_adapter_service *create_hw_ctx( return dal_adapter_service_create_hw_ctx_diag(ctx); switch (dce_version) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + return dal_adapter_service_create_hw_ctx_dce80(ctx); +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: return dal_adapter_service_create_hw_ctx_dce110(ctx); @@ -903,6 +911,11 @@ enum dce_version dal_adapter_service_get_dce_version( uint32_t version = as->asic_cap->data[ASIC_DATA_DCE_VERSION]; switch (version) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case 0x80: + /* CI Bonaire */ + return DCE_VERSION_8_0; +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case 0x100: return DCE_VERSION_10_0; diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.c b/drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.c new file mode 100644 index 0000000..9d6505c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.c @@ -0,0 +1,322 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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/adapter_service_types.h" +#include "include/grph_object_id.h" +#include "../hw_ctx_adapter_service.h" + +#include "hw_ctx_adapter_service_dce80.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x1918 +#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 + +enum { + MAX_NUMBER_OF_AUDIO_PINS = 7 +}; + +static const uint32_t audio_index_reg_offset[] = { + mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, + mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_INDEX, + mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_INDEX, + mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_INDEX, + mmAZF0ENDPOINT4_AZALIA_F0_CODEC_ENDPOINT_INDEX, + mmAZF0ENDPOINT5_AZALIA_F0_CODEC_ENDPOINT_INDEX, + /* TR, BN has 7 audio endpoints but 6 DIGs */ + mmAZF0ENDPOINT6_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, + mmAZF0ENDPOINT4_AZALIA_F0_CODEC_ENDPOINT_DATA, + mmAZF0ENDPOINT5_AZALIA_F0_CODEC_ENDPOINT_DATA, + mmAZF0ENDPOINT6_AZALIA_F0_CODEC_ENDPOINT_DATA +}; + +static const struct graphics_object_id invalid_go = { + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN +}; + +#define FROM_HW_CTX(ptr) \ + container_of((ptr), struct hw_ctx_adapter_service_dce80, base) + +static void destruct( + struct hw_ctx_adapter_service_dce80 *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_dce80 *hw_ctx = + FROM_HW_CTX(ptr); + + destruct(hw_ctx); + + dm_free(ptr->ctx, hw_ctx); +} + +static uint32_t get_number_of_connected_audio_endpoints_multistream( + struct hw_ctx_adapter_service *hw_ctx) +{ + struct dc_context *ctx = hw_ctx->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; +} + +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); + + /* unexpected value */ + ASSERT_CRITICAL(false); + return field; +} + +static bool power_up( + struct hw_ctx_adapter_service *hw_ctx) +{ + 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); + } + + /* get the number of connected audio endpoints */ + FROM_HW_CTX(hw_ctx)->number_of_connected_audio_endpoints = + get_number_of_connected_audio_endpoints(hw_ctx); + + return true; +} + +static struct graphics_object_id enum_fake_path_resource( + const struct hw_ctx_adapter_service *hw_ctx, + uint32_t index) +{ + if (index == 0) + return dal_graphics_object_id_init( + CONNECTOR_ID_VGA, + ENUM_ID_1, + OBJECT_TYPE_CONNECTOR); + else if (index == 1) + return dal_graphics_object_id_init( + ENCODER_ID_INTERNAL_KLDSCP_DAC1, + ENUM_ID_1, + OBJECT_TYPE_ENCODER); + else + return invalid_go; +} + +static struct graphics_object_id enum_stereo_sync_object( + const struct hw_ctx_adapter_service *hw_ctx, + uint32_t index) +{ + return invalid_go; +} + +static struct graphics_object_id enum_sync_output_object( + const struct hw_ctx_adapter_service *hw_ctx, + uint32_t index) +{ + return invalid_go; +} + +static struct graphics_object_id enum_audio_object( + const struct hw_ctx_adapter_service *hw_ctx, + uint32_t index) +{ + uint32_t number_of_connected_audio_endpoints = + FROM_HW_CTX(hw_ctx)->number_of_connected_audio_endpoints; + + if (index >= number_of_connected_audio_endpoints) + return invalid_go; + else + return dal_graphics_object_id_init( + AUDIO_ID_INTERNAL_AZALIA, + (enum object_enum_id)(index + 1), + OBJECT_TYPE_AUDIO); +} + +static void update_audio_connectivity( + struct hw_ctx_adapter_service *hw_ctx, + uint32_t number_of_audio_capable_display_path, + uint32_t number_of_controllers) +{ + uint32_t number_of_connected_audio_endpoints = + FROM_HW_CTX(hw_ctx)->number_of_connected_audio_endpoints; + + uint32_t co_func_audio_endpoint = number_of_connected_audio_endpoints; + struct dc_context *ctx = hw_ctx->ctx; + + if (co_func_audio_endpoint > number_of_audio_capable_display_path) + co_func_audio_endpoint = number_of_audio_capable_display_path; + + if (co_func_audio_endpoint > number_of_controllers) + co_func_audio_endpoint = number_of_controllers; + + if (co_func_audio_endpoint < number_of_connected_audio_endpoints) { + const uint32_t addr = mmCC_RCU_DC_AUDIO_PORT_CONNECTIVITY; + + uint32_t value; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, + 7 - co_func_audio_endpoint, + CC_RCU_DC_AUDIO_PORT_CONNECTIVITY, + PORT_CONNECTIVITY); + set_reg_field_value(value, + 1, + CC_RCU_DC_AUDIO_PORT_CONNECTIVITY, + PORT_CONNECTIVITY_OVERRIDE_ENABLE); + + dm_write_reg(ctx, addr, value); + } +} + +static const struct hw_ctx_adapter_service_funcs funcs = { + destroy, + power_up, + enum_fake_path_resource, + enum_stereo_sync_object, + enum_sync_output_object, + enum_audio_object, + update_audio_connectivity +}; + +static bool construct( + struct hw_ctx_adapter_service_dce80 *hw_ctx, + struct dc_context *ctx) +{ + if (!dal_adapter_service_construct_hw_ctx(&hw_ctx->base, ctx)) { + BREAK_TO_DEBUGGER(); + 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_dce80(struct dc_context *ctx) +{ + struct hw_ctx_adapter_service_dce80 *hw_ctx = + dm_alloc(ctx, sizeof(struct hw_ctx_adapter_service_dce80)); + + if (!hw_ctx) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(hw_ctx, ctx)) + return &hw_ctx->base; + + BREAK_TO_DEBUGGER(); + + dm_free(ctx, hw_ctx); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.h b/drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.h new file mode 100644 index 0000000..a735eaf --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/adapter/dce80/hw_ctx_adapter_service_dce80.h @@ -0,0 +1,40 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_CTX_ADAPTER_SERVICE_DCE80_H__ +#define __DAL_HW_CTX_ADAPTER_SERVICE_DCE80_H__ + +struct hw_ctx_adapter_service_dce80 { + struct hw_ctx_adapter_service base; + uint32_t number_of_connected_audio_endpoints; +}; + +struct hw_ctx_adapter_service * + dal_adapter_service_create_hw_ctx_dce80( + struct dc_context *ctx); + +#endif /* __DAL_HW_CTX_ADAPTER_SERVICE_DCE80_H__ */ + + diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile b/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile index 8491b38..b243542 100644 --- a/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/Makefile @@ -10,6 +10,19 @@ AMD_DAL_ASIC_CAPABILITY = \ AMD_DAL_FILES += $(AMD_DAL_ASIC_CAPABILITY) ############################################################################### +# DCE 8x +############################################################################### +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +ASIC_CAPABILITY_DCE80 = hawaii_asic_capability.o + +AMD_DAL_ASIC_CAPABILITY_DCE80 = \ + $(addprefix $(AMDDALPATH)/dc/asic_capability/,$(ASIC_CAPABILITY_DCE80)) + +AMD_DAL_FILES += $(AMD_DAL_ASIC_CAPABILITY_DCE80) +endif + + +############################################################################### # DCE 10x ############################################################################### ifdef CONFIG_DRM_AMD_DAL_DCE10_0 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 7a905f5..69909dd 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 @@ -32,6 +32,10 @@ #include "include/dal_types.h" #include "include/dal_asic_id.h" +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "hawaii_asic_capability.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) #include "tonga_asic_capability.h" #endif @@ -85,6 +89,10 @@ static bool construct( switch (init->chip_family) { case FAMILY_CI: +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + dal_hawaii_asic_capability_create(cap, init); + asic_supported = true; +#endif break; case FAMILY_KV: diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.c b/drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.c new file mode 100644 index 0000000..2745ac1 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.c @@ -0,0 +1,151 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Includes + */ + +#include "dm_services.h" + +#include "include/asic_capability_interface.h" +#include "include/asic_capability_types.h" +#include "include/dal_types.h" +#include "include/dal_asic_id.h" +#include "include/logger_interface.h" +#include "hawaii_asic_capability.h" + +#include "atom.h" + +#include "dce/dce_8_0_d.h" +#include "gmc/gmc_7_1_d.h" + + +/* + * Sea Islands (CI) ASIC capability. + * + * dal_hawaii_asic_capability_create + * + * Create and initiate hawaii capability. + */ +void dal_hawaii_asic_capability_create(struct asic_capability *cap, + struct hw_asic_id *init) +{ + uint32_t mc_seq_misc0; + + /* ASIC data */ + cap->data[ASIC_DATA_CONTROLLERS_NUM] = 6; + cap->data[ASIC_DATA_FUNCTIONAL_CONTROLLERS_NUM] = 6; + cap->data[ASIC_DATA_DIGFE_NUM] = 6; + cap->data[ASIC_DATA_LINEBUFFER_NUM] = 6; + cap->data[ASIC_DATA_MAX_COFUNC_NONDP_DISPLAYS] = 2; + cap->data[ASIC_DATA_MIN_DISPCLK_FOR_UNDERSCAN] = 300000; + + cap->data[ASIC_DATA_DCE_VERSION] = 0x80; /* DCE 8.0 */ + + /* Pixel RAM is 1712 entries of 144 bits each or + * in other words 246528 bits. */ + cap->data[ASIC_DATA_LINEBUFFER_SIZE] = 1712 * 144; + cap->data[ASIC_DATA_DRAM_BANDWIDTH_EFFICIENCY] = 70; + cap->data[ASIC_DATA_CLOCKSOURCES_NUM] = 3; + cap->data[ASIC_DATA_MC_LATENCY] = 5000; /* units of ns */ + + /* StutterModeEnhanced; Quad DMIF Buffer */ + cap->data[ASIC_DATA_STUTTERMODE] = 0x2002; + cap->data[ASIC_DATA_PATH_NUM_PER_DPMST_CONNECTOR] = 4; + cap->data[ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY] = 2; + + /* 3 HDMI support by default */ + cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM] = 3; + + cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ] = 40; + + mc_seq_misc0 = dm_read_reg(cap->ctx, mmMC_SEQ_MISC0); + + switch (mc_seq_misc0 & MC_MISC0__MEMORY_TYPE_MASK) { + case MC_MISC0__MEMORY_TYPE__GDDR1: + case MC_MISC0__MEMORY_TYPE__DDR2: + case MC_MISC0__MEMORY_TYPE__DDR3: + case MC_MISC0__MEMORY_TYPE__GDDR3: + case MC_MISC0__MEMORY_TYPE__GDDR4: + cap->data[ASIC_DATA_MEMORYTYPE_MULTIPLIER] = 2; + break; + case MC_MISC0__MEMORY_TYPE__GDDR5: + cap->data[ASIC_DATA_MEMORYTYPE_MULTIPLIER] = 4; + break; + default: + dal_logger_write(cap->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_MASK_ALL, + "%s:Unrecognized memory type!", __func__); + cap->data[ASIC_DATA_MEMORYTYPE_MULTIPLIER] = 2; + break; + } + + /* ASIC stereo 3D capability */ + cap->stereo_3d_caps.INTERLEAVE = 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.DISPLAY_BASED_ON_WS = true; + + /* ASIC basic capability */ + cap->caps.DP_MST_SUPPORTED = true; + cap->caps.PANEL_SELF_REFRESH_SUPPORTED = true; + + cap->caps.MIRABILIS_SUPPORTED = true; + cap->caps.MIRABILIS_ENABLED_BY_DEFAULT = true; + + /* Remap device tag IDs when patching VBIOS. */ + cap->caps.DEVICE_TAG_REMAP_SUPPORTED = true; + + /* Report headless if no OPM attached (with MXM connectors present). */ + cap->caps.HEADLESS_NO_OPM_SUPPORTED = true; + + + cap->caps.HPD_CHECK_FOR_EDID = true; + cap->caps.NO_VCC_OFF_HPD_POLLING = true; + + /* true will hang the system! */ + cap->caps.DFSBYPASS_DYNAMIC_SUPPORT = false; + + /* Do w/a on CI A0 by default */ + if (init->hw_internal_rev == CI_BONAIRE_M_A0) + cap->bugs.LB_WA_IS_SUPPORTED = true; + + /* Apply MC Tuning for Hawaii */ + if (ASIC_REV_IS_HAWAII_P(init->hw_internal_rev)) + cap->caps.NEED_MC_TUNING = true; + + /* DCE6.0 and DCE8.0 has a HW issue when accessing registers + * from ROM block. When there is a W access following R or W access + * right after (no more than couple of cycles) the first W access + * sometimes is not executed (in rate of about once per 100K tries). + * It creates problems in different scenarios of FL setup. */ + cap->bugs.ROM_REGISTER_ACCESS = true; + + /* VCE is supported */ + cap->caps.VCE_SUPPORTED = true; +} diff --git a/drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.h b/drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.h new file mode 100644 index 0000000..191d9b2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/asic_capability/hawaii_asic_capability.h @@ -0,0 +1,37 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BONAIRE_CAPABILITY_H__ +#define __DAL_BONAIRE_CAPABILITY_H__ + +/* Forward declaration */ +struct asic_capability; +struct hw_asic_id; + +/* Create and initialise Bonaire data */ +void dal_hawaii_asic_capability_create(struct asic_capability *cap, + struct hw_asic_id *init); + +#endif /* __DAL_BONAIRE_CAPABILITY_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/audio/Makefile b/drivers/gpu/drm/amd/dal/dc/audio/Makefile index 0999372..2433d90 100644 --- a/drivers/gpu/drm/amd/dal/dc/audio/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/audio/Makefile @@ -11,6 +11,18 @@ AMD_DAL_FILES += $(AMD_DAL_AUDIO) ############################################################################### +# DCE 8x +############################################################################### +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +AUDIO_DCE80 = audio_dce80.o hw_ctx_audio_dce80.o + +AMD_DAL_AUDIO_DCE80 = $(addprefix $(AMDDALPATH)/dc/audio/dce80/,$(AUDIO_DCE80)) + +AMD_DAL_FILES += $(AMD_DAL_AUDIO_DCE80) +endif + + +############################################################################### # DCE 11x ############################################################################### ifdef CONFIG_DRM_AMD_DAL_DCE11_0 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 bfd6725..269c75d 100644 --- a/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c +++ b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c @@ -30,6 +30,11 @@ #include "audio.h" #include "hw_ctx_audio.h" +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/audio_dce80.h" +#include "dce80/hw_ctx_audio_dce80.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) #include "dce110/audio_dce110.h" #include "dce110/hw_ctx_audio_dce110.h" @@ -264,6 +269,10 @@ struct audio *dal_audio_create( as = init_data->as; switch (dal_adapter_service_get_dce_version(as)) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + return dal_audio_create_dce80(init_data); +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: return dal_audio_create_dce110(init_data); diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.c b/drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.c new file mode 100644 index 0000000..d6f437c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.c @@ -0,0 +1,434 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_dce80.h" + +/***** static functions *****/ + +static void destruct(struct audio_dce80 *audio) +{ + /*release memory allocated for hw_ctx -- allocated is initiated + *by audio_dce80 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_dce80 *audio = NULL; + + audio = container_of(*ptr, struct audio_dce80, base); + + destruct(audio); + + /* release memory allocated for audio_dce80*/ + dm_free(audio->base.ctx, audio); + *ptr = NULL; +} + + +/* The inital call of hook function comes from audio object level. + *The passing object handle "struct audio *audio" point to base object + *already.There is not need to get base object from audio_dce80. + */ + +/** +* Setup +* +* @brief +* setup Audio HW block, to be called by dal_audio_setup +* +* @param +* engine_id - HDMI engine id +* pTiming - CRTC timing +* actualPixelClock - actual programmed pixel clock +*/ +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: + case SIGNAL_TYPE_WIRELESS: + /* setup HDMI audio engine */ + audio->hw_ctx->funcs->setup_hdmi_audio( + audio->hw_ctx, output->engine_id, &output->crtc_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 */ + break; + default: + return AUDIO_RESULT_ERROR; + } + + /* setup Azalia block */ + audio->hw_ctx->funcs->setup_azalia( + audio->hw_ctx, + output->engine_id, + output->signal, + &output->crtc_info, + &output->pll_info, + info); + + return AUDIO_RESULT_OK; +} + +/** +* enable_output +* +* @brief +* enable Audio HW block, to be called by dal_audio_enable_output +* +* @param +* engine_id - HDMI engine id +*/ +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: { + /* 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 +* +* @param +* engine_id - HDMI engine id +*/ +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); + 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); + } + break; + default: + return AUDIO_RESULT_ERROR; + } + + return AUDIO_RESULT_OK; +} + +/** +* unmute +* +* @brief +* unmute audio, to be called by dal_audio_unmute +* +* @param +* engine_id - engine id +*/ +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_WIRELESS: + 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; + default: + return AUDIO_RESULT_ERROR; + } + return AUDIO_RESULT_OK; +} + +/** +* mute +* +* @brief +* mute audio, to be called by dal_audio_nmute +* +* @param +* engine_id - engine id +*/ +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_WIRELESS: + 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; + 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. +* +* @param +* NONE +*/ +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_audio_create_hw_ctx_audio_dce80( + 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); + + if (dal_adapter_service_is_feature_supported(FEATURE_LIGHT_SLEEP)) + audio->hw_ctx->funcs->disable_az_clock_gating(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; + } +} + +/** +* SetUnsolicitedResponsePayload +* +* @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); +} + +/** +* SetupAudioDTO +* +* @brief +* Update audio source clock from hardware context. +* +* @param +* determines if we have a HDMI link active +* known pixel rate for HDMI +* known DCPLL frequency +*/ +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); +} + +/** +* GetSupportedFeatures +* +* @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 +* +* @param +* NONE +*/ +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.ENGINE_DIGD = 1; + afs.ENGINE_DIGE = 1; + afs.ENGINE_DIGF = 1; + afs.ENGINE_DIGG = 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_dce80 *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_dce80( + const struct audio_init_data *init_data) +{ + /*allocate memory for audio_dce80 */ + struct audio_dce80 *audio = dm_alloc(init_data->ctx, sizeof(struct audio_dce80)); + + if (audio == NULL) + return NULL; + + /*pointer to base_audio_block of audio_dce80 ==> audio base object */ + if (construct(audio, init_data)) + return &audio->base; + + /*release memory allocated if fail */ + dm_free(init_data->ctx, audio); + return NULL; +} + +/* Do not need expose construct_dce80 and destruct_dce80 becuase there is + *derived object after dce80 + */ + diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.h b/drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.h new file mode 100644 index 0000000..4fef455 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce80/audio_dce80.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 __DAL_AUDIO_80__ +#define __DAL_AUDIO_80__ + +#include "audio/audio.h" +#include "audio/hw_ctx_audio.h" +#include "audio/dce80/hw_ctx_audio_dce80.h" + + +struct audio_dce80 { + struct audio base; + /* dce-specific members are following */ + /* none */ +}; + +struct audio *dal_audio_create_dce80(const struct audio_init_data *init_data); + +#endif /* __DAL_AUDIO_80__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.c b/drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.c new file mode 100644 index 0000000..521ad07 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.c @@ -0,0 +1,1926 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +#include "hw_ctx_audio_dce80.h" + +#define FROM_BASE(ptr) \ + container_of((ptr), struct hw_ctx_audio_dce80, 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 + +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, + mmDIG6_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL +}; +/* --- static functions --- */ + +/* static void dal_audio_destruct_hw_ctx_audio_dce80( + struct hw_ctx_audio_dce80 *ctx);*/ + + +static void destroy( + struct hw_ctx_audio **ptr) +{ + struct hw_ctx_audio_dce80 *hw_ctx_dce80; + + hw_ctx_dce80 = container_of( + *ptr, struct hw_ctx_audio_dce80, base); + + dal_audio_destruct_hw_ctx_audio_dce80(hw_ctx_dce80); + /* release memory allocated for struct hw_ctx_audio_dce80 */ + dm_free((*ptr)->ctx, hw_ctx_dce80); + + *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) +{ + if (hbr_channel_count > 7) + return; + + { + union AZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL value; + + value.u32All = dal_read_reg( + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL); + value.bits.HBR_CHANNEL_COUNT = hbr_channel_count; + dal_write_reg( + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, value.u32All); + } +}*/ + +/* set compressed audio channel cound */ +/*static void set_compressed_audio_channel_count( + const struct hw_ctx_audio *hw_ctx, + uint32_t compressed_audio_ch_count) +{ + if (compressed_audio_ch_count > 7) + return; + + { + union AZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL value; + + value.u32All = dal_read_reg( + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL); + value.bits.COMPRESSED_CHANNEL_COUNT = + compressed_audio_ch_count; + dal_write_reg( + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, + value.u32All); + } +}*/ + +/* 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.u32All = dal_read_reg(mmAZALIA_CYCLIC_BUFFER_SYNC); + value.bits.CYCLIC_BUFFER_SYNC_ENABLE = 1; + dal_write_reg(mmAZALIA_CYCLIC_BUFFER_SYNC, value.u32All); +}*/ + + + +/* disable HW/SW Sync */ +/*static void disable_hw_sw_sync( + const struct hw_ctx_audio *hw_ctx) +{ + union AZALIA_CYCLIC_BUFFER_SYNC value; + + value.u32All = dal_read_reg( + mmAZALIA_CYCLIC_BUFFER_SYNC); + value.bits.CYCLIC_BUFFER_SYNC_ENABLE = 0; + dal_write_reg( + mmAZALIA_CYCLIC_BUFFER_SYNC, value.u32All); +}*/ + + +/* 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.u32All = 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.u32All); +}*/ + + +/* 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) +{ + /*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);*/ +} + +/* 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); + + 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); + + /* 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); +} + + /* Disable Azalia Clock Gating Feature */ +static void disable_az_clock_gating( + const struct hw_ctx_audio *hw_ctx) +{ + uint32_t value; + + value = dm_read_reg(hw_ctx->ctx, + mmAZALIA_CONTROLLER_CLOCK_GATING); + set_reg_field_value(value, 0, AZALIA_CONTROLLER_CLOCK_GATING, ENABLE_CLOCK_GATING); + dm_write_reg(hw_ctx->ctx, + mmAZALIA_CONTROLLER_CLOCK_GATING, 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 = + disable_az_clock_gating, + .unmute_azalia_audio = + unmute_azalia_audio, + .mute_azalia_audio = + mute_azalia_audio, + .setup_channel_splitting_mapping = + setup_channel_splitting_mapping, + .get_channel_splitting_mapping = + get_channel_splitting_mapping, + .set_unsolicited_response_payload = + set_unsolicited_response_payload, + .hw_initialize = + hw_initialize, + .enable_gtc_embedding_with_group = + enable_gtc_embedding_with_group, + .disable_gtc_embedding = + disable_gtc_embedding, + .get_azalia_clock_info_hdmi = + get_azalia_clock_info_hdmi, + .get_azalia_clock_info_dp = + get_azalia_clock_info_dp, +}; + +bool dal_audio_construct_hw_ctx_audio_dce80( + struct hw_ctx_audio_dce80 *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 dce80 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; + case 5: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT4_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT4_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + case 6: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT5_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT5_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + case 7: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT6_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT6_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + default: + /*DALASSERT_MSG(false,("Invalid Azalia stream ID!"));*/ + BREAK_TO_DEBUGGER(); + break; + } + + return true; +} + + +/* audio_dce80 is derived from audio directly, not via dce80 */ + +void dal_audio_destruct_hw_ctx_audio_dce80( + struct hw_ctx_audio_dce80 *hw_ctx_dce80) +{ + dal_audio_destruct_hw_ctx_audio(&hw_ctx_dce80->base); +} + +struct hw_ctx_audio *dal_audio_create_hw_ctx_audio_dce80( + struct dc_context *ctx, + uint32_t azalia_stream_id) +{ + /* allocate memory for struc hw_ctx_audio_dce80 */ + struct hw_ctx_audio_dce80 *hw_ctx_dce80 = + dm_alloc(ctx, sizeof(struct hw_ctx_audio_dce80)); + + if (!hw_ctx_dce80) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + /*return pointer to hw_ctx_audio back to caller -- audio object */ + if (dal_audio_construct_hw_ctx_audio_dce80( + hw_ctx_dce80, azalia_stream_id, ctx)) + return &hw_ctx_dce80->base; + + BREAK_TO_DEBUGGER(); + + dm_free(ctx, hw_ctx_dce80); + + return NULL; +} + + + diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.h b/drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.h new file mode 100644 index 0000000..1d0e00d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce80/hw_ctx_audio_dce80.h @@ -0,0 +1,75 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_CTX_AUDIO_DCE80_H__ +#define __DAL_HW_CTX_AUDIO_DCE80_H__ + +#include "audio/hw_ctx_audio.h" + +struct hw_ctx_audio_dce80 { + 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 */ +}; + + +/* --- helpers --- all static functions*/ +/*set_high_bit_rate_capable +set_hbr_channel_count +set_compressed_audio_channel_count +set_video_latency +set_audio_latency +enable_hw_sw_sync +disable_hw_sw_sync +update_sw_write_ptr +update_av_association +write_indirect_azalia_reg +read_indirect_azalia_reg +*/ + +/* in case dce83 may derived from dce80, expose dce80 constructor +*and destroy for derived */ +bool dal_audio_construct_hw_ctx_audio_dce80( + struct hw_ctx_audio_dce80 *hw_ctx, + uint8_t azalia_stream_id, + struct dc_context *ctx); + +void dal_audio_destruct_hw_ctx_audio_dce80( + struct hw_ctx_audio_dce80 *hw_ctx); + +struct hw_ctx_audio *dal_audio_create_hw_ctx_audio_dce80( + struct dc_context *ctx, + uint32_t azalia_stream_id); + +#endif /* __DAL_HW_CTX_AUDIO_DCE80_H__ */ + + + diff --git a/drivers/gpu/drm/amd/dal/dc/bios/Makefile b/drivers/gpu/drm/amd/dal/dc/bios/Makefile index ddfe457..e5c8876 100644 --- a/drivers/gpu/drm/amd/dal/dc/bios/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/bios/Makefile @@ -12,6 +12,19 @@ ifndef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT AMD_DAL_FILES := $(filter-out $(AMDDALPATH)/dc/bios/bios_parser_helper.o,$(AMD_DAL_FILES)) endif +############################################################################### +# DCE 8x +############################################################################### +# All DCE8.x are derived from DCE8.0, so 8.0 MUST be defined if ANY of +# DCE8.x is compiled. +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 + +ifdef CONFIG_DRM_AMD_DAL_VBIOS_PRESENT +AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce80/bios_parser_helper_dce80.o +endif + +AMD_DAL_FILES += $(AMDDALPATH)/dc/bios/dce80/command_table_helper_dce80.o +endif ############################################################################### # DCE 11x 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 0aa227a..4e2bc90 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 @@ -38,7 +38,11 @@ bool dal_bios_parser_init_bios_helper( enum dce_version version) { switch (version) { - +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + bp->bios_helper = dal_bios_parser_helper_dce80_get_table(); + return true; +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: bp->bios_helper = dal_bios_parser_helper_dce110_get_table(); 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 1ad7455..c58b9bb 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 @@ -26,6 +26,10 @@ #ifndef __DAL_BIOS_PARSER_HELPER_H__ #define __DAL_BIOS_PARSER_HELPER_H__ +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/bios_parser_helper_dce80.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) || defined(CONFIG_DRM_AMD_DAL_DCE10_0) #include "dce110/bios_parser_helper_dce110.h" #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 566604e..85a5924 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 @@ -37,6 +37,11 @@ bool dal_bios_parser_init_cmd_tbl_helper( enum dce_version dce) { switch (dce) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + *h = dal_cmd_tbl_helper_dce80_get_table(); + return true; +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: 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 4646cab..a462917 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 @@ -26,6 +26,9 @@ #ifndef __DAL_COMMAND_TABLE_HELPER_H__ #define __DAL_COMMAND_TABLE_HELPER_H__ +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/command_table_helper_dce80.h" +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) || defined(CONFIG_DRM_AMD_DAL_DCE10_0) #include "dce110/command_table_helper_dce110.h" #endif diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.c b/drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.c new file mode 100644 index 0000000..541a8c4 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.c @@ -0,0 +1,773 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and 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 "dce/dce_8_0_d.h" +#include "bif/bif_4_1_d.h" + +#include "include/grph_object_id.h" +#include "include/grph_object_defs.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/bios_parser_types.h" +#include "include/adapter_service_types.h" + +#include "../bios_parser_helper.h" + + +static const uint8_t bios_scratch0_dacb_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) +{ + enum signal_type sink = SIGNAL_TYPE_NONE; + /* VBIOS does not provide bitfield definitions */ + uint32_t reg; + /* DCE 8.0 does not support DAC2 */ + if (encoder.id == ENCODER_ID_INTERNAL_DAC2 + || encoder.id == ENCODER_ID_INTERNAL_KLDSCP_DAC2) { + BREAK_TO_DEBUGGER(); + /* TODO: DALASSERT_MSG(false, ("%s: DCE 8.0 Does not support + * DAC2!", __FUNCTION__)); */ + return SIGNAL_TYPE_NONE; + } + + reg = 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) { + reg <<= bios_scratch0_dacb_shift; + } + + switch (signal) { + case SIGNAL_TYPE_RGB: + if (reg & ATOM_S0_CRT2_MASK) + sink = SIGNAL_TYPE_RGB; + break; + case SIGNAL_TYPE_LVDS: + if (reg & ATOM_S0_LCD1) + sink = SIGNAL_TYPE_LVDS; + break; + case SIGNAL_TYPE_EDP: + if (reg & ATOM_S0_LCD1) + sink = SIGNAL_TYPE_EDP; + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + return sink; +} + +static bool is_lid_open(struct dc_context *ctx) +{ + bool result = false; + + /* VBIOS does not provide bitfield definitions */ + uint32_t reg; + + reg = dm_read_reg(ctx, + mmBIOS_SCRATCH_0 + ATOM_ACC_CHANGE_INFO_DEF); + + /* lid is open if the bit is not set */ + result = !(reg & ATOM_S6_LID_STATE); + + return result; +} + +static bool is_lid_status_changed( + struct dc_context *ctx) +{ + bool result = false; + + /* VBIOS does not provide bitfield definitions */ + uint32_t reg; + + reg = dm_read_reg(ctx, + mmBIOS_SCRATCH_6); + + /* lid is open if the bit is not set */ + if (reg & ATOM_S6_LID_CHANGE) { + reg &= ~ATOM_S6_LID_CHANGE; + dm_write_reg(ctx, + mmBIOS_SCRATCH_6, reg); + + result = true; + } + + return result; +} + +static bool is_display_config_changed( + struct dc_context *ctx) +{ + bool result = false; + + /* VBIOS does not provide bitfield definitions */ + uint32_t reg; + + reg = dm_read_reg(ctx, + mmBIOS_SCRATCH_6); + + /* lid is open if the bit is not set */ + if (reg & ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK) { + reg &= ~ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK; + dm_write_reg(ctx, + mmBIOS_SCRATCH_6, reg); + + result = true; + } + + return result; +} + +/** + * 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 + * NONE + */ +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); +} + +/** + * is_accelerated_mode + * + * @brief + * set Accelerated Mode in VBIOS scratch register, VBIOS will clean it when + * VGA/non-Accelerated mode is set + * + * @param + * NONE + */ +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; +} + +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); +} + +/** + * 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: + /* TODO: DALASSERT_MSG(false, ("%s: DCE 8.0 Does + * not support DAC2!", __FUNCTION__)); + */ + default: + break; + } + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +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; +} + +/** + * 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: + switch (device_tag->dev_id.enum_id) { + case 1: + update |= ATOM_S0_CRT1_COLOR; + break; + case 2: + update |= ATOM_S0_CRT2_COLOR; + break; + default: + break; + } + 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_lcd_scale( + struct dc_context *ctx, + enum lcd_scale lcd_scale_request) +{ + uint32_t reg; + + reg = dm_read_reg(ctx, mmBIOS_SCRATCH_6); + + reg &= ~ATOM_S6_REQ_LCD_EXPANSION_FULL; + reg &= ~ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO; + + switch (lcd_scale_request) { + case LCD_SCALE_FULLPANEL: + /* set Lcd Scale to Full Panel Mode */ + reg |= ATOM_S6_REQ_LCD_EXPANSION_FULL; + break; + case LCD_SCALE_ASPECTRATIO: + /* set Lcd Scale to Aspect-Ratio Mode */ + reg |= ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO; + break; + case LCD_SCALE_NONE: + default: + break; + } + + dm_write_reg(ctx, mmBIOS_SCRATCH_6, reg); +} + +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; +} + +static uint32_t fmt_control( + struct dc_context *ctx, + enum controller_id id, + uint32_t *value) +{ + uint32_t result = 0; + uint32_t reg; + + switch (id) { + case CONTROLLER_ID_D0: + reg = mmFMT0_FMT_CONTROL; + break; + case CONTROLLER_ID_D1: + reg = mmFMT1_FMT_CONTROL; + break; + case CONTROLLER_ID_D2: + reg = mmFMT2_FMT_CONTROL; + break; + case CONTROLLER_ID_D3: + reg = mmFMT3_FMT_CONTROL; + break; + case CONTROLLER_ID_D4: + reg = mmFMT4_FMT_CONTROL; + break; + case CONTROLLER_ID_D5: + reg = mmFMT5_FMT_CONTROL; + break; + default: + return result; + } + + if (value != NULL) + dm_write_reg(ctx, reg, *value); + else + result = dm_read_reg(ctx, reg); + + return result; +} + +static uint32_t fmt_bit_depth_control( + struct dc_context *ctx, + enum controller_id id, + uint32_t *value) +{ + uint32_t addr; + + switch (id) { + case CONTROLLER_ID_D0: + addr = mmFMT0_FMT_BIT_DEPTH_CONTROL; + break; + case CONTROLLER_ID_D1: + addr = mmFMT1_FMT_BIT_DEPTH_CONTROL; + break; + case CONTROLLER_ID_D2: + addr = mmFMT2_FMT_BIT_DEPTH_CONTROL; + break; + case CONTROLLER_ID_D3: + addr = mmFMT3_FMT_BIT_DEPTH_CONTROL; + break; + case CONTROLLER_ID_D4: + addr = mmFMT4_FMT_BIT_DEPTH_CONTROL; + break; + case CONTROLLER_ID_D5: + addr = mmFMT5_FMT_BIT_DEPTH_CONTROL; + break; + default: + BREAK_TO_DEBUGGER(); + return 0; + } + + if (value != NULL) { + dm_write_reg(ctx, addr, *value); + return 0; + } else { + return dm_read_reg(ctx, addr); + } +} + +/** + * Read various BIOS Scratch registers and put the resulting information into a + * PowerPlay internal structure (which is not dependent on register bit layout). + */ +static void get_bios_event_info( + struct dc_context *ctx, + struct bios_event_info *info) +{ + uint32_t s2, s6; + uint32_t clear_mask; + + dm_memset(info, 0, sizeof(struct bios_event_info)); + + /* Handle backlight event ONLY. PPLib still handling other events */ + s6 = dm_read_reg(ctx, mmBIOS_SCRATCH_6); + + clear_mask = s6 & (ATOM_S6_VRI_BRIGHTNESS_CHANGE); + + dm_write_reg(ctx, + mmBIOS_SCRATCH_6, s6 & ~clear_mask); + + s2 = dm_read_reg(ctx, mmBIOS_SCRATCH_2); + + info->backlight_level = (s2 & ATOM_S2_CURRENT_BL_LEVEL_MASK) + >> ATOM_S2_CURRENT_BL_LEVEL_SHIFT; + info->backlight_changed = (0 != (s6 & ATOM_S6_VRI_BRIGHTNESS_CHANGE)); +} + +static void take_backlight_control( + struct dc_context *ctx, + bool control) +{ + const uint32_t addr = mmBIOS_SCRATCH_2; + + uint32_t s2; + + s2 = dm_read_reg(ctx, addr); + + if (control) + s2 |= ATOM_S2_VRI_BRIGHT_ENABLE; + else + s2 &= ~ATOM_S2_VRI_BRIGHT_ENABLE; + + dm_write_reg(ctx, addr, s2); +} + +static uint32_t get_requested_backlight_level( + struct dc_context *ctx) +{ + uint32_t s2; + + s2 = dm_read_reg(ctx, mmBIOS_SCRATCH_2); + + return (s2 & ATOM_S2_CURRENT_BL_LEVEL_MASK) + >> ATOM_S2_CURRENT_BL_LEVEL_SHIFT; +} + +static void update_requested_backlight_level( + struct dc_context *ctx, + uint32_t backlight_8bit) +{ + const uint32_t addr = mmBIOS_SCRATCH_2; + + uint32_t s2; + + s2 = dm_read_reg(ctx, addr); + + s2 &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK; + backlight_8bit &= (ATOM_S2_CURRENT_BL_LEVEL_MASK + >> ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + s2 |= (backlight_8bit << ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + + dm_write_reg(ctx, addr, s2); +} + +static bool is_active_display( + struct dc_context *ctx, + enum signal_type signal, + const struct connector_device_tag_info *dev_tag) +{ + uint32_t active = 0; + + uint32_t reg; + + switch (signal) { + 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: + active = ATOM_S3_DFP1_ACTIVE; + break; + case 2: + active = ATOM_S3_DFP2_ACTIVE; + break; + case 3: + active = ATOM_S3_DFP3_ACTIVE; + break; + case 4: + active = ATOM_S3_DFP4_ACTIVE; + break; + case 5: + active = ATOM_S3_DFP5_ACTIVE; + break; + + case 6: + active = ATOM_S3_DFP6_ACTIVE; + break; + default: + break; + } + } + break; + case SIGNAL_TYPE_LVDS: + case SIGNAL_TYPE_EDP: + active = ATOM_S3_LCD1_ACTIVE; + break; + case SIGNAL_TYPE_RGB: + if (dev_tag->dev_id.device_type == DEVICE_TYPE_CRT) + active = ATOM_S3_CRT1_ACTIVE; + break; + default: + break; + } + + reg = dm_read_reg(ctx, mmBIOS_SCRATCH_3); + reg &= ATOM_S3_DEVICE_ACTIVE_MASK; + + return 0 != (active & reg); +} + +static enum controller_id get_embedded_display_controller_id( + struct dc_context *ctx) +{ + uint32_t reg; + + reg = dm_read_reg(ctx, mmBIOS_SCRATCH_3); + + if (ATOM_S3_LCD1_ACTIVE & reg) + return (reg & ATOM_S3_LCD1_CRTC_ACTIVE) ? + CONTROLLER_ID_D1 : CONTROLLER_ID_D0; + + return CONTROLLER_ID_UNDEFINED; +} + +static uint32_t get_embedded_display_refresh_rate( + struct dc_context *ctx) +{ + uint32_t result = 0; + + uint32_t reg_3; + + reg_3 = dm_read_reg(ctx, mmBIOS_SCRATCH_3); + + if (ATOM_S3_LCD1_ACTIVE & reg_3) { + uint32_t reg_4; + + reg_4 = dm_read_reg(ctx, + mmBIOS_SCRATCH_4); + + result = (reg_4 & ATOM_S4_LCD1_REFRESH_MASK) + >> ATOM_S4_LCD1_REFRESH_SHIFT; + } + + return result; +} + +static const struct bios_parser_helper bios_parser_helper_funcs = { + .detect_sink = detect_sink, + .fmt_bit_depth_control = fmt_bit_depth_control, + .fmt_control = fmt_control, + .get_bios_event_info = get_bios_event_info, + .get_embedded_display_controller_id = + get_embedded_display_controller_id, + .get_embedded_display_refresh_rate = + get_embedded_display_refresh_rate, + .get_requested_backlight_level = get_requested_backlight_level, + .get_scratch_lcd_scale = get_scratch_lcd_scale, + .is_accelerated_mode = is_accelerated_mode, + .is_active_display = is_active_display, + .is_display_config_changed = is_display_config_changed, + .is_lid_open = is_lid_open, + .is_lid_status_changed = is_lid_status_changed, + .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 = take_backlight_control, + .update_requested_backlight_level = update_requested_backlight_level, +}; + +const struct bios_parser_helper *dal_bios_parser_helper_dce80_get_table() +{ + return &bios_parser_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.h b/drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.h new file mode 100644 index 0000000..db671be --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/bios/dce80/bios_parser_helper_dce80.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_BIOS_PARSER_HELPER_DCE80_H__ +#define __DAL_BIOS_PARSER_HELPER_DCE80_H__ + +struct bios_parser_helper; + +const struct bios_parser_helper *dal_bios_parser_helper_dce80_get_table(void); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.c b/drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.c new file mode 100644 index 0000000..d725c4c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.c @@ -0,0 +1,355 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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/grph_object_id.h" +#include "include/grph_object_defs.h" +#include "include/bios_parser_types.h" +#include "include/adapter_service_types.h" + +#include "../command_table_helper.h" + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static 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 bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_PLL1: + *atom_pll_id = ATOM_PPLL1; + break; + case CLOCK_SOURCE_ID_PLL2: + *atom_pll_id = ATOM_PPLL2; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_EXT_PLL1; + break; + case CLOCK_SOURCE_ID_VCE: + /* for VCE encoding, + * we need to pass in ATOM_PPLL_INVALID + */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DP_DTO: + /* When programming DP DTO PLL ID should be invalid */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + BREAK_TO_DEBUGGER(); /* check when this will happen! */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +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 signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + break; + case SIGNAL_TYPE_LVDS: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel >> 4; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGG_SEL; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + } + + return atom_dig_encoder_sel; +} + +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 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: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +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 = + dal_cmd_table_helper_assign_control_parameter, + .clock_source_id_to_ref_clk_src = + dal_cmd_table_helper_clock_source_id_to_ref_clk_src, + .transmitter_bp_to_atom = dal_cmd_table_helper_transmitter_bp_to_atom, + .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, +}; + +const struct command_table_helper *dal_cmd_tbl_helper_dce80_get_table() +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.h b/drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.h new file mode 100644 index 0000000..e675c35 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/bios/dce80/command_table_helper_dce80.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_COMMAND_TABLE_HELPER_DCE80_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE80_H__ + +struct command_table_helper; + +const struct command_table_helper *dal_cmd_tbl_helper_dce80_get_table(void); + +#endif 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 db4f131..133b174 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 @@ -25,6 +25,9 @@ #include "dm_services.h" #include "core_types.h" +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/dce80_hw_sequencer.h" +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) #include "dce100/dce100_hw_sequencer.h" #endif @@ -40,6 +43,10 @@ bool dc_construct_hw_sequencer( switch (dce_ver) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + return dce80_hw_sequencer_construct(dc); +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: return dce100_hw_sequencer_construct(dc); 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 70bf935..80fb823 100644 --- a/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c @@ -31,6 +31,9 @@ #include "opp.h" #include "transform.h" +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/dce80_resource.h" +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) #include "dce100/dce100_resource.h" #endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/Makefile b/drivers/gpu/drm/amd/dal/dc/dce80/Makefile new file mode 100644 index 0000000..efe001f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE80 = dce80_ipp.o dce80_ipp_gamma.o dce80_link_encoder.o dce80_opp.o \ + dce80_opp_formatter.o dce80_opp_regamma.o dce80_stream_encoder.o \ + dce80_timing_generator.o dce80_transform.o dce80_transform_gamut.o \ + dce80_transform_scl.o dce80_opp_csc.o\ + dce80_compressor.o dce80_mem_input.o dce80_hw_sequencer.o \ + dce80_transform_bit_depth.o dce80_resource.o + +AMD_DAL_DCE80 = $(addprefix $(AMDDALPATH)/dc/dce80/,$(DCE80)) + +AMD_DAL_FILES += $(AMD_DAL_DCE80) + + + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.c new file mode 100644 index 0000000..a3b767e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.c @@ -0,0 +1,867 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +#include "gmc/gmc_7_1_sh_mask.h" +#include "gmc/gmc_7_1_d.h" + +#include "include/logger_interface.h" +#include "include/adapter_service_interface.h" + +#include "dce80_compressor.h" + +#define DCP_REG(reg)\ + (reg + cp80->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp80->offsets.dmif_offset) + +static const struct dce80_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), +}, +{ + .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG3_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG4_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG5_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static const uint32_t dce8_one_lpt_channel_max_resolution = 2048 * 1200; + +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 dce80_compressor *cp80) +{ + /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ + return cp80->base.raw_size * cp80->base.banks_num * + cp80->base.dram_channels_num; +} + +static uint32_t lpt_memory_control_config(struct dce80_compressor *cp80, + uint32_t lpt_control) +{ + /*LPT MC Config */ + if (cp80->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 (cp80->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( + cp80->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 (cp80->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( + cp80->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 (cp80->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( + cp80->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 (cp80->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( + cp80->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Invalid LPT ROW_SIZE!!!", + __func__); + break; + } + } else { + dal_logger_write( + cp80->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 dce80_compressor *cp80, + uint32_t source_view_width, + uint32_t source_view_height) +{ + if (cp80->base.embedded_panel_h_size != 0 && + cp80->base.embedded_panel_v_size != 0 && + ((source_view_width * source_view_height) > + (cp80->base.embedded_panel_h_size * + cp80->base.embedded_panel_v_size))) + return true; + + return false; +} + +static uint32_t align_to_chunks_number_per_line( + struct dce80_compressor *cp80, + uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void wait_for_fbc_state_changed( + struct dce80_compressor *cp80, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dm_read_reg(cp80->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + dm_delay_in_microseconds(cp80->base.ctx, 10); + counter++; + } + + if (counter == 10) { + dal_logger_write( + cp80->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } +} + +void dce80_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); + 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 dce80_compressor_enable_fbc( + struct compressor *compressor, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (compressor->options.bits.DUMMY_BACKEND == 0) && + (!dce80_compressor_is_fbc_enabled_in_hw(compressor, NULL)) && + (!is_source_bigger_than_epanel_size( + cp80, + 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 <= + dce8_one_lpt_channel_max_resolution)) { + dce80_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; + cp80->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(cp80, true); + } +} + +void dce80_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + dce80_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) + dce80_compressor_disable_lpt(compressor); + + wait_for_fbc_state_changed(cp80, false); + } +} + +bool dce80_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_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 dce80_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 dce80_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce80_compressor *cp80 = TO_DCE80_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(cp80); + + 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( + cp80, + 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 DCE8 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 dce80_compressor_disable_lpt(struct compressor *compressor) +{ + struct dce80_compressor *cp80 = TO_DCE80_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 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 dce80_compressor_enable_lpt(struct compressor *compressor) +{ + struct dce80_compressor *cp80 = TO_DCE80_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); + + /* 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 dce80_compressor_program_lpt_control( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce80_compressor *cp80 = TO_DCE80_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(cp80, 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(cp80); + source_view_width = + align_to_chunks_number_per_line( + cp80, + 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 dce80_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 DCE8: + * - 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, DCE8.1 also needs to set new DCE8.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 dce80_compressor_construct(struct dce80_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 *dce80_compressor_create(struct dc_context *ctx, + struct adapter_service *as) +{ + struct dce80_compressor *cp80 = + dm_alloc(ctx, sizeof(struct dce80_compressor)); + + if (!cp80) + return NULL; + + if (dce80_compressor_construct(cp80, ctx, as)) + return &cp80->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, cp80); + return NULL; +} + +void dce80_compressor_destroy(struct compressor **compressor) +{ + dm_free((*compressor)->ctx, TO_DCE80_COMPRESSOR(*compressor)); + *compressor = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_compressor.h new file mode 100644 index 0000000..8254118 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_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_DCE80_H__ +#define __DC_COMPRESSOR_DCE80_H__ + +#include "../inc/compressor.h" + +#define TO_DCE80_COMPRESSOR(compressor)\ + container_of(compressor, struct dce80_compressor, base) + +struct dce80_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce80_compressor { + struct compressor base; + struct dce80_compressor_reg_offsets offsets; +}; + +struct compressor *dce80_compressor_create(struct dc_context *ctx, + struct adapter_service *as); + +bool dce80_compressor_construct(struct dce80_compressor *cp80, + struct dc_context *ctx, struct adapter_service *as); + +void dce80_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce80_compressor_power_up_fbc(struct compressor *cp); + +void dce80_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + +void dce80_compressor_disable_fbc(struct compressor *cp); + +void dce80_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce80_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce80_compressor_get_required_compressed_surface_size( + struct compressor *cp, + struct fbc_input_info *input_info, + struct fbc_requested_compressed_size *size); + +bool dce80_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce80_compressor_enable_lpt(struct compressor *cp); + +void dce80_compressor_disable_lpt(struct compressor *cp); + +void dce80_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce80_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.c new file mode 100644 index 0000000..9f3201f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.c @@ -0,0 +1,308 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" +#include "core_dc.h" +#include "core_types.h" +#include "dce80_hw_sequencer.h" + +#include "dce110/dce110_hw_sequencer.h" + +#include "gpu/dce80/dc_clock_gating_dce80.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +struct dce80_hw_seq_reg_offsets { + uint32_t blnd; + uint32_t crtc; +}; + +enum pipe_lock_control { + PIPE_LOCK_CONTROL_GRAPHICS = 1 << 0, + PIPE_LOCK_CONTROL_BLENDER = 1 << 1, + PIPE_LOCK_CONTROL_SCL = 1 << 2, + PIPE_LOCK_CONTROL_SURFACE = 1 << 3, + PIPE_LOCK_CONTROL_MODE = 1 << 4 +}; + +enum blender_mode { + BLENDER_MODE_CURRENT_PIPE = 0,/* Data from current pipe only */ + BLENDER_MODE_OTHER_PIPE, /* Data from other pipe only */ + BLENDER_MODE_BLENDING,/* Alpha blending - blend 'current' and 'other' */ + BLENDER_MODE_STEREO +}; + +static const struct dce80_hw_seq_reg_offsets reg_offsets[] = { +{ + .blnd = (mmBLND0_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .blnd = (mmBLND1_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .blnd = (mmBLND2_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .blnd = (mmBLND3_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .blnd = (mmBLND4_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .blnd = (mmBLND5_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_BLND(reg, id)\ + (reg + reg_offsets[id].blnd) + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + + + +/******************************************************************************* + * Private definitions + ******************************************************************************/ + +/***************************PIPE_CONTROL***********************************/ +static void dce80_enable_fe_clock( + struct dc_context *ctx, uint8_t controller_id, bool enable) +{ + uint32_t value = 0; + uint32_t addr; + + addr = HW_REG_CRTC(mmCRTC_DCFE_CLOCK_CONTROL, controller_id); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + enable, + CRTC_DCFE_CLOCK_CONTROL, + CRTC_DCFE_CLOCK_ENABLE); + + dm_write_reg(ctx, addr, value); +} + +static bool dce80_pipe_control_lock( + struct dc_context *ctx, + uint8_t controller_idx, + uint32_t control_mask, + bool lock) +{ + uint32_t addr = HW_REG_BLND(mmBLND_V_UPDATE_LOCK, controller_idx); + uint32_t value = dm_read_reg(ctx, addr); + bool need_to_wait = false; + + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_DCP_GRPH_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SCL) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_SCL_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_DCP_GRPH_SURF_V_UPDATE_LOCK); + + dm_write_reg(ctx, addr, value); + + if (!lock && need_to_wait) { + uint8_t counter = 0; + const uint8_t counter_limit = 100; + const uint16_t delay_us = 1000; + + uint8_t pipe_pending; + + addr = HW_REG_BLND(mmBLND_REG_UPDATE_STATUS, + controller_idx); + + while (counter < counter_limit) { + value = dm_read_reg(ctx, addr); + + pipe_pending = 0; + + if (control_mask & PIPE_LOCK_CONTROL_SCL) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + SCL_BLNDc_UPDATE_PENDING); + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + SCL_BLNDo_UPDATE_PENDING); + } + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDc_GRPH_UPDATE_PENDING); + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDo_GRPH_UPDATE_PENDING); + } + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) { + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDc_GRPH_SURF_UPDATE_PENDING); + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDo_GRPH_SURF_UPDATE_PENDING); + } + + if (pipe_pending == 0) + break; + + counter++; + dm_delay_in_microseconds(ctx, delay_us); + } + + if (counter == counter_limit) { + dal_logger_write( + ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: wait for update exceeded (wait %d us)\n", + __func__, + counter * delay_us); + dal_logger_write( + ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: control %d, remain value %x\n", + __func__, + control_mask, + value); + } else { + /* OK. */ + } + } + + return true; +} + +static void dce80_set_blender_mode( + struct dc_context *ctx, + uint8_t controller_id, + uint32_t mode) +{ + uint32_t value; + uint32_t addr = HW_REG_BLND(mmBLND_CONTROL, controller_id); + uint32_t blnd_mode; + uint32_t feedthrough = 0; + + switch (mode) { + case BLENDER_MODE_OTHER_PIPE: + feedthrough = 0; + blnd_mode = 1; + break; + case BLENDER_MODE_BLENDING: + feedthrough = 0; + blnd_mode = 2; + break; + case BLENDER_MODE_CURRENT_PIPE: + default: + feedthrough = 1; + blnd_mode = 0; + break; + } + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + blnd_mode, + BLND_CONTROL, + BLND_MODE); + + dm_write_reg(ctx, addr, value); +} + +static bool dce80_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 (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 (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +bool dce80_hw_sequencer_construct(struct dc *dc) +{ + dce110_hw_sequencer_construct(dc); + + dc->hwss.clock_gating_power_up = dal_dc_clock_gating_dce80_power_up; + dc->hwss.enable_fe_clock = dce80_enable_fe_clock; + dc->hwss.enable_display_power_gating = dce80_enable_display_power_gating; + dc->hwss.pipe_control_lock = dce80_pipe_control_lock; + dc->hwss.set_blender_mode = dce80_set_blender_mode; + + return true; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_hw_sequencer.h new file mode 100644 index 0000000..9d6dd05 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_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_DCE80_H__ +#define __DC_HWSS_DCE80_H__ + +#include "core_types.h" + +struct dc; + +bool dce80_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE80_H__ */ + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.c new file mode 100644 index 0000000..6dde1eb --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.c @@ -0,0 +1,64 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/logger_interface.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_ipp.h" + +#include "dce110/dce110_ipp.h" + +static struct ipp_funcs funcs = { + .ipp_cursor_set_attributes = dce110_ipp_cursor_set_attributes, + .ipp_cursor_set_position = dce110_ipp_cursor_set_position, + .ipp_program_prescale = dce110_ipp_program_prescale, + .ipp_set_degamma = dce110_ipp_set_degamma, +}; + +bool dce80_ipp_construct( + struct dce110_ipp *ipp, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offset) +{ + ipp->base.ctx = ctx; + + ipp->base.inst = inst; + + ipp->offsets = *offset; + + ipp->base.funcs = &funcs; + + return true; +} + +void dce80_ipp_destroy(struct input_pixel_processor **ipp) +{ + dm_free((*ipp)->ctx, TO_DCE80_IPP(*ipp)); + *ipp = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.h new file mode 100644 index 0000000..bd81693 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp.h @@ -0,0 +1,49 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_IPP_DCE80_H__ +#define __DC_IPP_DCE80_H__ + +#include "inc/ipp.h" + +#define TO_DCE80_IPP(input_pixel_processor)\ + container_of(input_pixel_processor, struct dce110_ipp, base) + +struct dce110_ipp; +struct dce110_ipp_reg_offsets; +struct gamma_parameters; +struct dev_c_lut; + + +bool dce80_ipp_construct( + struct dce110_ipp *ipp, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offset); + +void dce80_ipp_destroy(struct input_pixel_processor **ipp); + + +#endif /*__DC_IPP_DCE80_H__*/ diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp_gamma.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp_gamma.c new file mode 100644 index 0000000..fdffb8c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_ipp_gamma.c @@ -0,0 +1,85 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/logger_interface.h" +#include "include/fixed31_32.h" +#include "basics/conversion.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_ipp.h" +#include "dce110/dce110_ipp.h" +#include "gamma_types.h" + +#define DCP_REG(reg)\ + (reg + ipp80->offsets.dcp_offset) + +enum { + MAX_INPUT_LUT_ENTRY = 256 +}; + + +/*PROTOTYPE DECLARATIONS*/ + + +static void set_legacy_input_gamma_mode( + struct dce110_ipp *ipp80, + bool is_legacy); + + + +void dce80_ipp_set_legacy_input_gamma_mode( + struct input_pixel_processor *ipp, + bool is_legacy) +{ + struct dce110_ipp *ipp80 = TO_DCE80_IPP(ipp); + + set_legacy_input_gamma_mode(ipp80, is_legacy); +} + + +static void set_legacy_input_gamma_mode( + struct dce110_ipp *ipp80, + bool is_legacy) +{ + const uint32_t addr = DCP_REG(mmINPUT_GAMMA_CONTROL); + uint32_t value = dm_read_reg(ipp80->base.ctx, addr); + + set_reg_field_value( + value, + !is_legacy, + INPUT_GAMMA_CONTROL, + GRPH_INPUT_GAMMA_MODE); + + dm_write_reg(ipp80->base.ctx, addr, value); +} + + + + + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.c new file mode 100644 index 0000000..5288436 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.c @@ -0,0 +1,329 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dce80_link_encoder.h" +#include "stream_encoder.h" +#include "../dce110/dce110_link_encoder.h" +#include "i2caux_interface.h" + +/* TODO: change to dce80 header file */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dce/dce_11_0_enum.h" + +#define LINK_REG(reg)\ + (enc110->link_regs->reg) + +#define DCE8_UNIPHY_MAX_PIXEL_CLK_IN_KHZ 297000 + +#define DEFAULT_AUX_MAX_DATA_SIZE 16 +#define AUX_MAX_DEFER_WRITE_RETRY 20 +/* + * @brief + * Trigger Source Select + * ASIC-dependent, actual values for register programming + */ +#define DCE80_DIG_FE_SOURCE_SELECT_INVALID 0x0 +#define DCE80_DIG_FE_SOURCE_SELECT_DIGA 0x01 +#define DCE80_DIG_FE_SOURCE_SELECT_DIGB 0x02 +#define DCE80_DIG_FE_SOURCE_SELECT_DIGC 0x04 +#define DCE80_DIG_FE_SOURCE_SELECT_DIGD 0x08 +#define DCE80_DIG_FE_SOURCE_SELECT_DIGE 0x10 +#define DCE80_DIG_FE_SOURCE_SELECT_DIGF 0x20 + +/* 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 + +enum { + DP_MST_UPDATE_MAX_RETRY = 50 +}; + +static enum bp_result link_transmitter_control( + struct dce110_link_encoder *enc110, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + struct dc_bios *bp = dal_adapter_service_get_bios_parser( + enc110->base.adapter_service); + + result = bp->funcs->transmitter_control(bp, cntl); + + return result; +} + +static void dce80_link_encoder_enable_tmds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + if (hdmi) { + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.lanes_number = 4; + } else if (dual_link) { + cntl.signal = SIGNAL_TYPE_DVI_DUAL_LINK; + cntl.lanes_number = 8; + } else { + cntl.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + cntl.lanes_number = 4; + } + cntl.hpd_sel = enc110->base.hpd_source; + + cntl.pixel_clock = pixel_clock; + cntl.color_depth = color_depth; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dal_logger_write(ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_ENCODER, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +static void configure_encoder( + struct dce110_link_encoder *enc110, + const struct dc_link_settings *link_settings) +{ + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr; + uint32_t value; + + /* set number of lanes */ + addr = LINK_REG(DP_CONFIG); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, link_settings->lane_count - LANE_COUNT_ONE, + DP_CONFIG, DP_UDI_LANES); + dm_write_reg(ctx, addr, value); + +} + +/* enables DP PHY output */ +static void dce80_link_encoder_enable_dp_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + /* number_of_lanes is used for pixel clock adjust, + * but it's not passed to asic_control. + * We need to set number of lanes manually. + */ + configure_encoder(enc110, link_settings); + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_DISPLAY_PORT; + cntl.lanes_number = link_settings->lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_rate + * LINK_RATE_REF_FREQ_IN_KHZ; + /* TODO: check if undefined works */ + cntl.color_depth = COLOR_DEPTH_UNDEFINED; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dal_logger_write(ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_ENCODER, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +static struct link_encoder_funcs dce80_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 = dce80_link_encoder_enable_tmds_output, + .enable_dp_output = dce80_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 = dce110_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 +}; + + +bool dce80_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) +{ + struct graphics_object_encoder_cap_info enc_cap_info = {0}; + + enc110->base.funcs = &dce80_lnk_enc_funcs; + enc110->base.ctx = init_data->ctx; + enc110->base.id = init_data->encoder; + + enc110->base.hpd_source = init_data->hpd_source; + enc110->base.connector = init_data->connector; + enc110->base.input_signals = SIGNAL_TYPE_ALL; + + enc110->base.adapter_service = init_data->adapter_service; + + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + + enc110->base.features.flags.raw = 0; + + enc110->base.transmitter = init_data->transmitter; + + enc110->base.features.flags.bits.IS_AUDIO_CAPABLE = true; + + enc110->base.features.max_pixel_clock = DCE8_UNIPHY_MAX_PIXEL_CLK_IN_KHZ; + + /* set the flag to indicate whether driver poll the I2C data pin + * while doing the DP sink detect + */ + + if (dal_adapter_service_is_feature_supported( + FEATURE_DP_SINK_DETECT_POLL_DATA_PIN)) + enc110->base.features.flags.bits. + DP_SINK_DETECT_POLL_DATA_PIN = true; + + enc110->base.output_signals = + SIGNAL_TYPE_DVI_SINGLE_LINK | + SIGNAL_TYPE_DVI_DUAL_LINK | + SIGNAL_TYPE_LVDS | + SIGNAL_TYPE_DISPLAY_PORT | + SIGNAL_TYPE_DISPLAY_PORT_MST | + SIGNAL_TYPE_EDP | + SIGNAL_TYPE_HDMI_TYPE_A; + + /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. + * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. + * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer + * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS. + * Prefer DIG assignment is decided by board design. + * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design + * and VBIOS will filter out 7 UNIPHY for DCE 8.0. + * By this, adding DIGG should not hurt DCE 8.0. + * This will let DCE 8.1 share DCE 8.0 as much as possible + */ + + enc110->link_regs = link_regs; + enc110->aux_regs = aux_regs; + enc110->bl_regs = bl_regs; + + switch (enc110->base.transmitter) { + case TRANSMITTER_UNIPHY_A: + enc110->base.preferred_engine = ENGINE_ID_DIGA; + break; + case TRANSMITTER_UNIPHY_B: + enc110->base.preferred_engine = ENGINE_ID_DIGB; + + break; + case TRANSMITTER_UNIPHY_C: + enc110->base.preferred_engine = ENGINE_ID_DIGC; + break; + case TRANSMITTER_UNIPHY_D: + enc110->base.preferred_engine = ENGINE_ID_DIGD; + break; + case TRANSMITTER_UNIPHY_E: + enc110->base.preferred_engine = ENGINE_ID_DIGE; + break; + case TRANSMITTER_UNIPHY_F: + enc110->base.preferred_engine = ENGINE_ID_DIGF; + break; + default: + ASSERT_CRITICAL(false); + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + break; + } + + dal_logger_write(init_data->ctx->logger, + LOG_MAJOR_I2C_AUX, + LOG_MINOR_I2C_AUX_CFG, + "Using channel: %s [%d]\n", + DECODE_CHANNEL_ID(init_data->channel), + init_data->channel); + + /* Override features with DCE-specific values */ + if (dal_adapter_service_get_encoder_cap_info( + enc110->base.adapter_service, + enc110->base.id, &enc_cap_info)) + enc110->base.features.flags.bits.IS_HBR2_CAPABLE = + enc_cap_info.dp_hbr2_cap; + + /* test pattern 3 support */ + enc110->base.features.flags.bits.IS_TPS3_CAPABLE = true; + enc110->base.features.max_deep_color = COLOR_DEPTH_121212; + + enc110->base.features.flags.bits.IS_Y_ONLY_CAPABLE = + dal_adapter_service_is_feature_supported( + FEATURE_SUPPORT_DP_Y_ONLY); + + enc110->base.features.flags.bits.IS_YCBCR_CAPABLE = + dal_adapter_service_is_feature_supported( + FEATURE_SUPPORT_DP_YUV); + + return true; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.h new file mode 100644 index 0000000..b894643 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_link_encoder.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 __DC_LINK_ENCODER__DCE80_H__ +#define __DC_LINK_ENCODER__DCE80_H__ + +#include "inc/link_encoder.h" +#include "../dce110/dce110_link_encoder.h" + +bool dce80_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); + +#endif /* __DC_LINK_ENCODER__DCE80_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.c new file mode 100644 index 0000000..a8e9961 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.c @@ -0,0 +1,217 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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_DCE80_H__ + +#define __DC_MEM_INPUT_DCE80_H__ + +#include "dm_services.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +/* TODO: this needs to be looked at, used by Stella's workaround*/ +#include "gmc/gmc_7_1_d.h" +#include "gmc/gmc_7_1_sh_mask.h" + +#include "include/logger_interface.h" +#include "adapter_service_interface.h" +#include "inc/bandwidth_calcs.h" + +#include "../dce110/dce110_mem_input.h" +#include "dce80_mem_input.h" + + +#define MAX_WATERMARK 0xFFFF +#define SAFE_NBP_MARK 0x7FFF + +#define DCP_REG(reg) (reg + mem_input80->offsets.dcp) +#define DMIF_REG(reg) (reg + mem_input80->offsets.dmif) +#define PIPE_REG(reg) (reg + mem_input80->offsets.pipe) + +static uint32_t get_dmif_switch_time_us( + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz) +{ + uint32_t frame_time; + uint32_t pixels_per_second; + uint32_t pixels_per_frame; + uint32_t refresh_rate; + const uint32_t us_in_sec = 1000000; + const uint32_t min_single_frame_time_us = 30000; + /*return double of frame time*/ + const uint32_t single_frame_time_multiplier = 2; + + if (!h_total || v_total || !pix_clk_khz) + return single_frame_time_multiplier * min_single_frame_time_us; + + /*TODO: should we use pixel format normalized pixel clock here?*/ + pixels_per_second = pix_clk_khz * 1000; + pixels_per_frame = h_total * v_total; + + if (!pixels_per_second || !pixels_per_frame) { + /* avoid division by zero */ + ASSERT(pixels_per_frame); + ASSERT(pixels_per_second); + return single_frame_time_multiplier * min_single_frame_time_us; + } + + refresh_rate = pixels_per_second / pixels_per_frame; + + if (!refresh_rate) { + /* avoid division by zero*/ + ASSERT(refresh_rate); + return single_frame_time_multiplier * min_single_frame_time_us; + } + + frame_time = us_in_sec / refresh_rate; + + if (frame_time < min_single_frame_time_us) + frame_time = min_single_frame_time_us; + + frame_time *= single_frame_time_multiplier; + + return frame_time; +} + +static void allocate_mem_input( + struct mem_input *mi, + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz, + uint32_t total_targets_num) +{ + const uint32_t retry_delay = 10; + uint32_t retry_count = get_dmif_switch_time_us( + h_total, + v_total, + pix_clk_khz) / retry_delay; + + struct dce110_mem_input *bm80 = TO_DCE110_MEM_INPUT(mi); + uint32_t addr = bm80->offsets.pipe + mmPIPE0_DMIF_BUFFER_CONTROL; + uint32_t value; + uint32_t field; + + if (bm80->supported_stutter_mode + & STUTTER_MODE_NO_DMIF_BUFFER_ALLOCATION) + goto register_underflow_int; + + /*Allocate DMIF buffer*/ + value = dm_read_reg(mi->ctx, addr); + field = get_reg_field_value( + value, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED); + if (field == 2) + goto register_underflow_int; + + set_reg_field_value( + value, + 2, + PIPE0_DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATED); + + dm_write_reg(mi->ctx, addr, value); + + do { + value = dm_read_reg(mi->ctx, addr); + field = get_reg_field_value( + value, + PIPE0_DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATION_COMPLETED); + + if (field) + break; + + dm_delay_in_microseconds(mi->ctx, retry_delay); + retry_count--; + + } while (retry_count > 0); + + if (field == 0) + dal_logger_write(mi->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "%s: DMIF allocation failed", + __func__); + + /* + * Stella Wong proposed the following change + * + * Value of mcHubRdReqDmifLimit.ENABLE: + * 00 - disable DMIF rdreq limit + * 01 - enable DMIF rdreq limit, disabled by DMIF stall = 1 || urg != 0 + * 02 - enable DMIF rdreq limit, disable by DMIF stall = 1 + * 03 - force enable DMIF rdreq limit, ignore DMIF stall / urgent + */ + addr = mmMC_HUB_RDREQ_DMIF_LIMIT; + value = dm_read_reg(mi->ctx, addr); + if (total_targets_num > 1) + set_reg_field_value(value, 0, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE); + else + set_reg_field_value(value, 3, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE); + dm_write_reg(mi->ctx, addr, value); + +register_underflow_int: + /*todo*/; + /*register_interrupt(bm80, irq_source, ctrl_id);*/ +} + +static struct mem_input_funcs dce80_mem_input_funcs = { + .mem_input_program_display_marks = + dce110_mem_input_program_display_marks, + .allocate_mem_input = allocate_mem_input, + .free_mem_input = + dce110_free_mem_input, + .mem_input_program_surface_flip_and_addr = + dce110_mem_input_program_surface_flip_and_addr, + .mem_input_program_surface_config = + dce110_mem_input_program_surface_config, +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce80_mem_input_construct( + struct dce110_mem_input *mem_input80, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets) +{ + + mem_input80->base.funcs = &dce80_mem_input_funcs; + mem_input80->base.ctx = ctx; + + mem_input80->base.inst = inst; + + mem_input80->offsets = *offsets; + + mem_input80->supported_stutter_mode = 0; + dal_adapter_service_get_feature_value(FEATURE_STUTTER_MODE, + &(mem_input80->supported_stutter_mode), + sizeof(mem_input80->supported_stutter_mode)); + + return true; +} + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.h new file mode 100644 index 0000000..1d299da --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_mem_input.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_MEM_INPUT_DCE80_H__ +#define __DC_MEM_INPUT_DCE80_H__ + +#include "inc/mem_input.h" + +bool dce80_mem_input_construct( + struct dce110_mem_input *mem_input80, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets); + + +enum dc_status dce_base_validate_mapped_resource( + const struct dc *dc, + struct validate_context *context); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.c new file mode 100644 index 0000000..82c98a1 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.c @@ -0,0 +1,141 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_opp.h" + +#define FROM_OPP(opp)\ + container_of(opp, struct dce80_opp, base) + +enum { + MAX_LUT_ENTRY = 256, + MAX_NUMBER_OF_ENTRIES = 256 +}; + +static const struct dce80_opp_reg_offsets reg_offsets[] = { +{ + .fmt_offset = (mmFMT0_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .crtc_offset = (mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT1_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .crtc_offset = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT2_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .crtc_offset = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .fmt_offset = (mmFMT3_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .crtc_offset = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .fmt_offset = (mmFMT4_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .crtc_offset = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .fmt_offset = (mmFMT5_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .crtc_offset = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + + + +static struct opp_funcs funcs = { + .opp_power_on_regamma_lut = dce80_opp_power_on_regamma_lut, + .opp_program_bit_depth_reduction = + dce80_opp_program_bit_depth_reduction, + .opp_program_clamping_and_pixel_encoding = + dce80_opp_program_clamping_and_pixel_encoding, + .opp_set_csc_adjustment = dce80_opp_set_csc_adjustment, + .opp_set_csc_default = dce80_opp_set_csc_default, + .opp_set_dyn_expansion = dce80_opp_set_dyn_expansion, + .opp_program_regamma_pwl = dce80_opp_program_regamma_pwl, + .opp_set_regamma_mode = dce80_opp_set_regamma_mode, + .opp_destroy = dce80_opp_destroy, +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce80_opp_construct(struct dce80_opp *opp80, + struct dc_context *ctx, + uint32_t inst) +{ + if (inst >= ARRAY_SIZE(reg_offsets)) + return false; + + opp80->base.funcs = &funcs; + + opp80->base.ctx = ctx; + + opp80->base.inst = inst; + + opp80->offsets = reg_offsets[inst]; + + return true; +} + +void dce80_opp_destroy(struct output_pixel_processor **opp) +{ + dm_free((*opp)->ctx, FROM_OPP(*opp)); + *opp = NULL; +} + +struct output_pixel_processor *dce80_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce80_opp *opp = + dm_alloc(ctx, sizeof(struct dce80_opp)); + + if (!opp) + return NULL; + + if (dce80_opp_construct(opp, + ctx, inst)) + return &opp->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, opp); + return NULL; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.h new file mode 100644 index 0000000..d414f50 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp.h @@ -0,0 +1,130 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCE80_H__ +#define __DC_OPP_DCE80_H__ + +#include "dc_types.h" +#include "inc/opp.h" +#include "gamma_types.h" + +struct gamma_parameters; + +struct dce80_regamma { + struct gamma_curve arr_curve_points[16]; + struct curve_points arr_points[3]; + uint32_t hw_points_num; + struct hw_x_point *coordinates_x; + struct pwl_result_data *rgb_resulted; + + /* re-gamma curve */ + struct pwl_float_data_ex *rgb_regamma; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 256 */ + struct pixel_gamma_point *coeff128; + struct pixel_gamma_point *coeff128_oem; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 1025 */ + struct pixel_gamma_point *coeff128_dx; + /* evenly distributed points, gamma 256 software points 0-255 */ + struct gamma_pixel *axis_x_256; + /* evenly distributed points, gamma 1025 software points 0-1025 */ + struct gamma_pixel *axis_x_1025; + /* OEM supplied gamma for regamma LUT */ + struct pwl_float_data *rgb_oem; + /* user supplied gamma */ + struct pwl_float_data *rgb_user; + uint32_t extra_points; + bool use_half_points; + struct fixed31_32 x_max1; + struct fixed31_32 x_max2; + struct fixed31_32 x_min; + struct fixed31_32 divider1; + struct fixed31_32 divider2; + struct fixed31_32 divider3; +}; + +/* OPP RELATED */ +#define TO_DCE80_OPP(opp)\ + container_of(opp, struct dce80_opp, base) + +struct dce80_opp_reg_offsets { + uint32_t fmt_offset; + uint32_t dcp_offset; + uint32_t crtc_offset; +}; + +struct dce80_opp { + struct output_pixel_processor base; + struct dce80_opp_reg_offsets offsets; + struct dce80_regamma regamma; +}; + +bool dce80_opp_construct(struct dce80_opp *opp80, + struct dc_context *ctx, + uint32_t inst); + +void dce80_opp_destroy(struct output_pixel_processor **opp); + +struct output_pixel_processor *dce80_opp_create( + struct dc_context *ctx, + uint32_t inst); + +/* REGAMMA RELATED */ +void dce80_opp_power_on_regamma_lut( + struct output_pixel_processor *opp, + bool power_on); + +bool dce80_opp_program_regamma_pwl( + struct output_pixel_processor *opp, + const struct regamma_params *pamras); + +void dce80_opp_set_regamma_mode(struct output_pixel_processor *opp, + enum opp_regamma mode); + +void dce80_opp_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct grph_csc_adjustment *adjust); + +void dce80_opp_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust); + +/* FORMATTER RELATED */ +void dce80_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params); + +void dce80_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params); + + +void dce80_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_csc.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_csc.c new file mode 100644 index 0000000..90662ae --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_csc.c @@ -0,0 +1,905 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dce80_opp.h" +#include "basics/conversion.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#define DCP_REG(reg)\ + (reg + opp80->offsets.dcp_offset) + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + +struct out_csc_color_matrix { + enum color_space color_space; + uint16_t regval[OUTPUT_CSC_MATRIX_SIZE]; +}; + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB_FULL_RANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED_RANGE, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* YOnly same as YCbCr709 but Y in Full range -To do. */ +{ COLOR_SPACE_YCBCR601_YONLY, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_YONLY, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +static void program_color_matrix( + struct dce80_opp *opp80, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + struct dc_context *ctx = opp80->base.ctx; + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C11_C12); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C11); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C12); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C13_C14); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C13); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C14); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C21_C22); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C21); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C22); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C23_C24); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C23); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C24); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C31_C32); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C31); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C32); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C33_C34); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C33); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C34); + + dm_write_reg(ctx, addr, value); + } +} + +/* + * initialize_color_float_adj_reference_values + * This initialize display color adjust input from API to HW range for later + * calculation use. This is shared by all the display color adjustment. + * @param : + * @return None + */ +static void initialize_color_float_adj_reference_values( + const struct grph_csc_adjustment *adjust, + struct fixed31_32 *grph_cont, + struct fixed31_32 *grph_sat, + struct fixed31_32 *grph_bright, + struct fixed31_32 *sin_grph_hue, + struct fixed31_32 *cos_grph_hue) +{ + /* Hue adjustment could be negative. -45 ~ +45 */ + struct fixed31_32 hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction(adjust->grph_hue, 180), + dal_fixed31_32_pi); + + *sin_grph_hue = dal_fixed31_32_sin(hue); + *cos_grph_hue = dal_fixed31_32_cos(hue); + + if (adjust->adjust_divider) { + *grph_cont = + dal_fixed31_32_from_fraction( + adjust->grph_cont, + adjust->adjust_divider); + *grph_sat = + dal_fixed31_32_from_fraction( + adjust->grph_sat, + adjust->adjust_divider); + *grph_bright = + dal_fixed31_32_from_fraction( + adjust->grph_bright, + adjust->adjust_divider); + } else { + *grph_cont = dal_fixed31_32_from_int(adjust->grph_cont); + *grph_sat = dal_fixed31_32_from_int(adjust->grph_sat); + *grph_bright = dal_fixed31_32_from_int(adjust->grph_bright); + } +} + +static inline struct fixed31_32 fixed31_32_clamp( + struct fixed31_32 value, + int32_t min_numerator, + int32_t max_numerator, + int32_t denominator) +{ + return dal_fixed31_32_clamp( + value, + dal_fixed31_32_from_fraction( + min_numerator, + denominator), + dal_fixed31_32_from_fraction( + max_numerator, + denominator)); +} + +static void setup_reg_format( + struct fixed31_32 *coefficients, + uint16_t *reg_values) +{ + enum { + LENGTH = 12, + DENOMINATOR = 10000 + }; + + static const int32_t min_numerator[] = { + -3 * DENOMINATOR, + -DENOMINATOR + }; + + static const int32_t max_numerator[] = { + DENOMINATOR, + DENOMINATOR + }; + + static const uint8_t integer_bits[] = { 2, 0 }; + + uint32_t i = 0; + + do { + const uint32_t index = (i % 4) == 3; + + reg_values[i] = fixed_point_to_int_frac( + fixed31_32_clamp(coefficients[(i + 8) % LENGTH], + min_numerator[index], + max_numerator[index], + DENOMINATOR), + integer_bits[index], 13); + + ++i; + } while (i != LENGTH); +} + +/** + ***************************************************************************** + * Function: setup_adjustments + * @note prepare to setup the values + * + * @see + * + ***************************************************************************** + */ +static void setup_adjustments(const struct grph_csc_adjustment *adjust, + struct dc_csc_adjustments *adjustments) +{ + if (adjust->adjust_divider != 0) { + adjustments->brightness = + dal_fixed31_32_from_fraction(adjust->grph_bright, + adjust->adjust_divider); + adjustments->contrast = + dal_fixed31_32_from_fraction(adjust->grph_cont, + adjust->adjust_divider); + adjustments->saturation = + dal_fixed31_32_from_fraction(adjust->grph_sat, + adjust->adjust_divider); + } else { + adjustments->brightness = + dal_fixed31_32_from_fraction(adjust->grph_bright, 1); + adjustments->contrast = + dal_fixed31_32_from_fraction(adjust->grph_cont, 1); + adjustments->saturation = + dal_fixed31_32_from_fraction(adjust->grph_sat, 1); + } + + /* convert degrees into radians */ + adjustments->hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction(adjust->grph_hue, 180), + dal_fixed31_32_pi); +} + +static void prepare_tv_rgb_ideal( + struct fixed31_32 *matrix) +{ + static const int32_t matrix_[] = { + 85546875, 0, 0, 6250000, + 0, 85546875, 0, 6250000, + 0, 0, 85546875, 6250000 + }; + + uint32_t i = 0; + + do { + matrix[i] = dal_fixed31_32_from_fraction( + matrix_[i], + 100000000); + ++i; + } while (i != ARRAY_SIZE(matrix_)); +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_rgb_adjustment_legacy + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB color space + * + * @see + * + ***************************************************************************** + */ +static void set_rgb_adjustment_legacy( + struct dce80_opp *opp80, + const struct grph_csc_adjustment *adjust) +{ + const struct fixed31_32 k1 = + dal_fixed31_32_from_fraction(701000, 1000000); + const struct fixed31_32 k2 = + dal_fixed31_32_from_fraction(236568, 1000000); + const struct fixed31_32 k3 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k4 = + dal_fixed31_32_from_fraction(464432, 1000000); + const struct fixed31_32 k5 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k6 = + dal_fixed31_32_from_fraction(-701000, 1000000); + const struct fixed31_32 k7 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k8 = + dal_fixed31_32_from_fraction(-292569, 1000000); + const struct fixed31_32 k9 = + dal_fixed31_32_from_fraction(413000, 1000000); + const struct fixed31_32 k10 = + dal_fixed31_32_from_fraction(-92482, 1000000); + const struct fixed31_32 k11 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k12 = + dal_fixed31_32_from_fraction(385051, 1000000); + const struct fixed31_32 k13 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k14 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k15 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k16 = + dal_fixed31_32_from_fraction(-741914, 1000000); + const struct fixed31_32 k17 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k18 = + dal_fixed31_32_from_fraction(-144086, 1000000); + + const struct fixed31_32 luma_r = + dal_fixed31_32_from_fraction(299, 1000); + const struct fixed31_32 luma_g = + dal_fixed31_32_from_fraction(587, 1000); + const struct fixed31_32 luma_b = + dal_fixed31_32_from_fraction(114, 1000); + + struct out_csc_color_matrix tbl_entry; + struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; + + struct fixed31_32 grph_cont; + struct fixed31_32 grph_sat; + struct fixed31_32 grph_bright; + struct fixed31_32 sin_grph_hue; + struct fixed31_32 cos_grph_hue; + + initialize_color_float_adj_reference_values( + adjust, &grph_cont, &grph_sat, + &grph_bright, &sin_grph_hue, &cos_grph_hue); + + /* COEF_1_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + + * Sin(GrphHue) * K2)) */ + /* (Cos(GrphHue) * K1 + Sin(GrphHue) * K2) */ + matrix[0] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k1), + dal_fixed31_32_mul(sin_grph_hue, k2)); + /* GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2 */ + matrix[0] = dal_fixed31_32_mul(grph_sat, matrix[0]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2)) */ + matrix[0] = dal_fixed31_32_add(luma_r, matrix[0]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * + * K2)) */ + matrix[0] = dal_fixed31_32_mul(grph_cont, matrix[0]); + + /* COEF_1_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + + * Sin(GrphHue) * K4)) */ + /* (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ + matrix[1] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k3), + dal_fixed31_32_mul(sin_grph_hue, k4)); + /* GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ + matrix[1] = dal_fixed31_32_mul(grph_sat, matrix[1]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)) */ + matrix[1] = dal_fixed31_32_add(luma_g, matrix[1]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * + * K4)) */ + matrix[1] = dal_fixed31_32_mul(grph_cont, matrix[1]); + + /* COEF_1_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + + * Sin(GrphHue) * K6)) */ + /* (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k5), + dal_fixed31_32_mul(sin_grph_hue, k6)); + /* GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = dal_fixed31_32_mul(grph_sat, matrix[2]); + /* LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = dal_fixed31_32_add(luma_b, matrix[2]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * + * K6)) */ + matrix[2] = dal_fixed31_32_mul(grph_cont, matrix[2]); + + /* COEF_1_4 = GrphBright */ + matrix[3] = grph_bright; + + /* COEF_2_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + + * Sin(GrphHue) * K8)) */ + /* (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ + matrix[4] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k7), + dal_fixed31_32_mul(sin_grph_hue, k8)); + /* GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ + matrix[4] = dal_fixed31_32_mul(grph_sat, matrix[4]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)) */ + matrix[4] = dal_fixed31_32_add(luma_r, matrix[4]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * + * K8)) */ + matrix[4] = dal_fixed31_32_mul(grph_cont, matrix[4]); + + /* COEF_2_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + + * Sin(GrphHue) * K10)) */ + /* (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k9), + dal_fixed31_32_mul(sin_grph_hue, k10)); + /* GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = dal_fixed31_32_mul(grph_sat, matrix[5]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = dal_fixed31_32_add(luma_g, matrix[5]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * + * K10)) */ + matrix[5] = dal_fixed31_32_mul(grph_cont, matrix[5]); + + /* COEF_2_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + + * Sin(GrphHue) * K12)) */ + /* (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k11), + dal_fixed31_32_mul(sin_grph_hue, k12)); + /* GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = dal_fixed31_32_mul(grph_sat, matrix[6]); + /* (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = dal_fixed31_32_add(luma_b, matrix[6]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * + * K12)) */ + matrix[6] = dal_fixed31_32_mul(grph_cont, matrix[6]); + + /* COEF_2_4 = GrphBright */ + matrix[7] = grph_bright; + + /* COEF_3_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + + * Sin(GrphHue) * K14)) */ + /* (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k13), + dal_fixed31_32_mul(sin_grph_hue, k14)); + /* GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = dal_fixed31_32_mul(grph_sat, matrix[8]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = dal_fixed31_32_add(luma_r, matrix[8]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * + * K14)) */ + matrix[8] = dal_fixed31_32_mul(grph_cont, matrix[8]); + + /* COEF_3_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + + * Sin(GrphHue) * K16)) */ + /* GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16) */ + matrix[9] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k15), + dal_fixed31_32_mul(sin_grph_hue, k16)); + /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ + matrix[9] = dal_fixed31_32_mul(grph_sat, matrix[9]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ + matrix[9] = dal_fixed31_32_add(luma_g, matrix[9]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * + * K16)) */ + matrix[9] = dal_fixed31_32_mul(grph_cont, matrix[9]); + + /* COEF_3_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + + * Sin(GrphHue) * K18)) */ + /* (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k17), + dal_fixed31_32_mul(sin_grph_hue, k18)); + /* GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = dal_fixed31_32_mul(grph_sat, matrix[10]); + /* (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = dal_fixed31_32_add(luma_b, matrix[10]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * + * K18)) */ + matrix[10] = dal_fixed31_32_mul(grph_cont, matrix[10]); + + /* COEF_3_4 = GrphBright */ + matrix[11] = grph_bright; + + tbl_entry.color_space = adjust->c_space; + + convert_float_matrix(tbl_entry.regval, matrix, OUTPUT_CSC_MATRIX_SIZE); + + program_color_matrix( + opp80, &tbl_entry, adjust->color_adjust_option); +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_rgb_limited_range_adjustment + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB limited color space + * + * @see + * + ***************************************************************************** + */ +static void set_rgb_limited_range_adjustment( + struct dce80_opp *opp80, + const struct grph_csc_adjustment *adjust) +{ + struct out_csc_color_matrix reg_matrix; + struct fixed31_32 change_matrix[OUTPUT_CSC_MATRIX_SIZE]; + struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; + struct dc_csc_adjustments adjustments; + struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE]; + + prepare_tv_rgb_ideal(ideals); + + setup_adjustments(adjust, &adjustments); + + calculate_adjustments(ideals, &adjustments, matrix); + + dm_memmove(change_matrix, matrix, sizeof(matrix)); + + /* from 1 -> 3 */ + matrix[8] = change_matrix[0]; + matrix[9] = change_matrix[1]; + matrix[10] = change_matrix[2]; + matrix[11] = change_matrix[3]; + + /* from 2 -> 1 */ + matrix[0] = change_matrix[4]; + matrix[1] = change_matrix[5]; + matrix[2] = change_matrix[6]; + matrix[3] = change_matrix[7]; + + /* from 3 -> 2 */ + matrix[4] = change_matrix[8]; + matrix[5] = change_matrix[9]; + matrix[6] = change_matrix[10]; + matrix[7] = change_matrix[11]; + + dm_memset(®_matrix, 0, sizeof(struct out_csc_color_matrix)); + + setup_reg_format(matrix, reg_matrix.regval); + + program_color_matrix(opp80, ®_matrix, GRPH_COLOR_MATRIX_SW); +} + +static void prepare_yuv_ideal( + bool b601, + struct fixed31_32 *matrix) +{ + static const int32_t matrix_1[] = { + 25578516, 50216016, 9752344, 6250000, + -14764391, -28985609, 43750000, 50000000, + 43750000, -36635164, -7114836, 50000000 + }; + + static const int32_t matrix_2[] = { + 18187266, 61183125, 6176484, 6250000, + -10025059, -33724941, 43750000, 50000000, + 43750000, -39738379, -4011621, 50000000 + }; + + const int32_t *matrix_x = b601 ? matrix_1 : matrix_2; + + uint32_t i = 0; + + do { + matrix[i] = dal_fixed31_32_from_fraction( + matrix_x[i], + 100000000); + ++i; + } while (i != ARRAY_SIZE(matrix_1)); +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_yuv_adjustment + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for YUV color spaces + * + * @see + * + ***************************************************************************** + */ +static void set_yuv_adjustment( + struct dce80_opp *opp80, + const struct grph_csc_adjustment *adjust) +{ + bool b601 = (adjust->c_space == COLOR_SPACE_YPBPR601) || + (adjust->c_space == COLOR_SPACE_YCBCR601) || + (adjust->c_space == COLOR_SPACE_YCBCR601_YONLY); + struct out_csc_color_matrix reg_matrix; + struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; + struct dc_csc_adjustments adjustments; + struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE]; + + prepare_yuv_ideal(b601, ideals); + + setup_adjustments(adjust, &adjustments); + + if ((adjust->c_space == COLOR_SPACE_YCBCR601_YONLY) || + (adjust->c_space == COLOR_SPACE_YCBCR709_YONLY)) + calculate_adjustments_y_only( + ideals, &adjustments, matrix); + else + calculate_adjustments( + ideals, &adjustments, matrix); + + dm_memset(®_matrix, 0, sizeof(struct out_csc_color_matrix)); + + setup_reg_format(matrix, reg_matrix.regval); + + program_color_matrix(opp80, ®_matrix, GRPH_COLOR_MATRIX_SW); +} + +static bool configure_graphics_mode( + struct dce80_opp *opp80, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum color_space color_space) +{ + struct dc_context *ctx = opp80->base.ctx; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_CONTROL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { + set_reg_field_value( + value, + 4, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + } else { + + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + /* TV RGB */ + set_reg_field_value( + value, + 1, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_YONLY: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_YONLY: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + default: + return false; + } + } + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + /* TV RGB */ + set_reg_field_value( + value, + 1, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_YONLY: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_YONLY: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + default: + return false; + } + + } else + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + + addr = DCP_REG(mmOUTPUT_CSC_CONTROL); + dm_write_reg(ctx, addr, value); + + return true; +} + +void dce80_opp_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct grph_csc_adjustment *adjust) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + /* Apply color adjustments: brightness, saturation, hue, contrast and + * CSC. No need for different color space routine, color space defines + * the ideal values only, but keep original design to allow quick switch + * to the old legacy routines */ + switch (adjust->c_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + set_rgb_adjustment_legacy(opp80, adjust); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + set_rgb_limited_range_adjustment( + opp80, adjust); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR601_YONLY: + case COLOR_SPACE_YCBCR709_YONLY: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YPBPR709: + set_yuv_adjustment(opp80, adjust); + break; + default: + set_rgb_adjustment_legacy(opp80, adjust); + break; + } + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode(opp80, config, adjust->csc_adjust_type, + adjust->c_space); +} + +void dce80_opp_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->color_space) + continue; + /* program the matrix with default values from this + * file */ + program_color_matrix(opp80, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix */ + + configure_graphics_mode(opp80, config, + default_adjust->csc_adjust_type, + default_adjust->color_space); +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_formatter.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_formatter.c new file mode 100644 index 0000000..9d0a214 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_formatter.c @@ -0,0 +1,577 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_opp.h" + +#define FMT_REG(reg)\ + (reg + opp80->offsets.fmt_offset) + +/** + * set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE8 power saving reason. + */ +static void set_truncation( + struct dce80_opp *opp80, + const struct bit_depth_reduction_params *params) +{ + uint32_t value = 0; + uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + + /*Disable truncation*/ + value = dm_read_reg(opp80->base.ctx, addr); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); + + dm_write_reg(opp80->base.ctx, addr, value); + + /* no 10bpc trunc on DCE8*/ + if (params->flags.TRUNCATE_ENABLED == 0 || + params->flags.TRUNCATE_DEPTH == 2) + return; + + /*Set truncation depth and Enable truncation*/ + set_reg_field_value(value, 1, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); + set_reg_field_value(value, params->flags.TRUNCATE_MODE, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); + set_reg_field_value(value, params->flags.TRUNCATE_DEPTH, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); + + dm_write_reg(opp80->base.ctx, addr, value); + +} + +/** + * set_spatial_dither + * 1) set spatial dithering mode: pattern of seed + * 2) set spatical dithering depth: 0 for 18bpp or 1 for 24bpp + * 3) set random seed + * 4) set random mode + * lfsr is reset every frame or not reset + * RGB dithering method + * 0: RGB data are all dithered with x^28+x^3+1 + * 1: R data is dithered with x^28+x^3+1 + * G data is dithered with x^28+X^9+1 + * B data is dithered with x^28+x^13+1 + * enable high pass filter or not + * 5) enable spatical dithering + */ +static void set_spatial_dither( + struct dce80_opp *opp80, + const struct bit_depth_reduction_params *params) +{ + uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + uint32_t depth_cntl_value = 0; + uint32_t dither_r_value = 0; + uint32_t dither_g_value = 0; + uint32_t dither_b_value = 0; + + /*Disable spatial (random) dithering*/ + depth_cntl_value = dm_read_reg(opp80->base.ctx, addr); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE); + + dm_write_reg(opp80->base.ctx, addr, depth_cntl_value); + + /* no 10bpc on DCE8*/ + if (params->flags.SPATIAL_DITHER_ENABLED == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 2) + return; + + /*Set seed for random values for + * spatial dithering for R,G,B channels*/ + addr = FMT_REG(mmFMT_DITHER_RAND_R_SEED); + set_reg_field_value(dither_r_value, params->r_seed_value, + FMT_DITHER_RAND_R_SEED, + FMT_RAND_R_SEED); + dm_write_reg(opp80->base.ctx, addr, dither_r_value); + + addr = FMT_REG(mmFMT_DITHER_RAND_G_SEED); + set_reg_field_value(dither_g_value, + params->g_seed_value, + FMT_DITHER_RAND_G_SEED, + FMT_RAND_G_SEED); + dm_write_reg(opp80->base.ctx, addr, dither_g_value); + + addr = FMT_REG(mmFMT_DITHER_RAND_B_SEED); + set_reg_field_value(dither_b_value, params->b_seed_value, + FMT_DITHER_RAND_B_SEED, + FMT_RAND_B_SEED); + dm_write_reg(opp80->base.ctx, addr, dither_b_value); + + /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero + * offset for the R/Cr channel, lower 4LSB + * is forced to zeros. Typically set to 0 + * RGB and 0x80000 YCbCr. + */ + /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero + * offset for the G/Y channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB + * and 0x80000 YCbCr. + */ + /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero + * offset for the B/Cb channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB and + * 0x80000 YCbCr. + */ + + /*Set spatial dithering bit depth*/ + set_reg_field_value(depth_cntl_value, + params->flags.SPATIAL_DITHER_DEPTH, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_DEPTH); + + /* Set spatial dithering mode + * (default is Seed patterrn AAAA...) + */ + set_reg_field_value(depth_cntl_value, + params->flags.SPATIAL_DITHER_MODE, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_MODE); + + /*Reset only at startup*/ + set_reg_field_value(depth_cntl_value, + params->flags.FRAME_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_RGB_RANDOM_ENABLE); + + /*Set RGB data dithered with x^28+x^3+1*/ + set_reg_field_value(depth_cntl_value, + params->flags.RGB_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_RGB_RANDOM_ENABLE); + + /*Disable High pass filter*/ + set_reg_field_value(depth_cntl_value, + params->flags.HIGHPASS_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE); + + /*Enable spatial dithering*/ + set_reg_field_value(depth_cntl_value, + 1, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_EN); + + addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + dm_write_reg(opp80->base.ctx, addr, depth_cntl_value); + +} + +/** + * SetTemporalDither (Frame Modulation) + * 1) set temporal dither depth + * 2) select pattern: from hard-coded pattern or programmable pattern + * 3) select optimized strips for BGR or RGB LCD sub-pixel + * 4) set s matrix + * 5) set t matrix + * 6) set grey level for 0.25, 0.5, 0.75 + * 7) enable temporal dithering + */ +static void set_temporal_dither( + struct dce80_opp *opp80, + const struct bit_depth_reduction_params *params) +{ + uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + uint32_t value; + + /*Disable temporal (frame modulation) dithering first*/ + value = dm_read_reg(opp80->base.ctx, addr); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_RESET); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_OFFSET); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_50FRC_SEL); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_75FRC_SEL); + + dm_write_reg(opp80->base.ctx, addr, value); + + /* no 10bpc dither on DCE8*/ + if (params->flags.FRAME_MODULATION_ENABLED == 0 || + params->flags.FRAME_MODULATION_DEPTH == 2) + return; + + /* Set temporal dithering depth*/ + set_reg_field_value(value, + params->flags.FRAME_MODULATION_DEPTH, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_RESET); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_OFFSET); + + /*Select legacy pattern based on FRC and Temporal level*/ + addr = FMT_REG(mmFMT_TEMPORAL_DITHER_PATTERN_CONTROL); + dm_write_reg(opp80->base.ctx, addr, 0); + /*Set s matrix*/ + addr = FMT_REG( + mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX); + dm_write_reg(opp80->base.ctx, addr, 0); + /*Set t matrix*/ + addr = FMT_REG( + mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX); + dm_write_reg(opp80->base.ctx, addr, 0); + + /*Select patterns for 0.25, 0.5 and 0.75 grey level*/ + set_reg_field_value(value, + params->flags.TEMPORAL_LEVEL, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL); + + set_reg_field_value(value, + params->flags.FRC25, + FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL); + + set_reg_field_value(value, + params->flags.FRC50, + FMT_BIT_DEPTH_CONTROL, + FMT_50FRC_SEL); + + set_reg_field_value(value, + params->flags.FRC75, + FMT_BIT_DEPTH_CONTROL, + FMT_75FRC_SEL); + + /*Enable bit reduction by temporal (frame modulation) dithering*/ + set_reg_field_value(value, + 1, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN); + + addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + dm_write_reg(opp80->base.ctx, addr, value); + +} + +/** + * Set Clamping + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +static void set_clamping( + struct dce80_opp *opp80, + const struct clamping_and_pixel_encoding_params *params) +{ + uint32_t clamp_cntl_value = 0; + uint32_t red_clamp_value = 0; + uint32_t green_clamp_value = 0; + uint32_t blue_clamp_value = 0; + uint32_t addr = FMT_REG(mmFMT_CLAMP_CNTL); + + clamp_cntl_value = dm_read_reg(opp80->base.ctx, addr); + + set_reg_field_value(clamp_cntl_value, + 0, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 0, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + break; + + case CLAMPING_LIMITED_RANGE_8BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + + case CLAMPING_LIMITED_RANGE_10BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 2, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + case CLAMPING_LIMITED_RANGE_12BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 3, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 7, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + /*set the defaults*/ + set_reg_field_value(red_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_R, + FMT_CLAMP_LOWER_R); + + set_reg_field_value(red_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_R, + FMT_CLAMP_UPPER_R); + + addr = FMT_REG(mmFMT_CLAMP_COMPONENT_R); + dm_write_reg(opp80->base.ctx, addr, red_clamp_value); + + set_reg_field_value(green_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_G, + FMT_CLAMP_LOWER_G); + + set_reg_field_value(green_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_G, + FMT_CLAMP_UPPER_G); + + addr = FMT_REG(mmFMT_CLAMP_COMPONENT_G); + dm_write_reg(opp80->base.ctx, addr, green_clamp_value); + + set_reg_field_value(blue_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_B, + FMT_CLAMP_LOWER_B); + + set_reg_field_value(blue_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_B, + FMT_CLAMP_UPPER_B); + + addr = FMT_REG(mmFMT_CLAMP_COMPONENT_B); + dm_write_reg(opp80->base.ctx, addr, blue_clamp_value); + + break; + + default: + break; + } + + addr = FMT_REG(mmFMT_CLAMP_CNTL); + /*Set clamp control*/ + dm_write_reg(opp80->base.ctx, addr, clamp_cntl_value); + +} + +/** + * set_pixel_encoding + * + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ +static void set_pixel_encoding( + struct dce80_opp *opp80, + const struct clamping_and_pixel_encoding_params *params) +{ + uint32_t fmt_cntl_value; + uint32_t addr = FMT_REG(mmFMT_CONTROL); + + /*RGB 4:4:4 or YCbCr 4:4:4 - 0; YCbCr 4:2:2 -1.*/ + fmt_cntl_value = dm_read_reg(opp80->base.ctx, addr); + + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_PIXEL_ENCODING); + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + set_reg_field_value(fmt_cntl_value, + 1, + FMT_CONTROL, + FMT_PIXEL_ENCODING); + + /*00 - Pixels drop mode ,01 - Pixels average mode*/ + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_SUBSAMPLING_MODE); + + /*00 - Cb before Cr ,01 - Cr before Cb*/ + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_SUBSAMPLING_ORDER); + } + dm_write_reg(opp80->base.ctx, addr, fmt_cntl_value); + +} + +void dce80_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + + set_truncation(opp80, params); + set_spatial_dither(opp80, params); + set_temporal_dither(opp80, params); +} + +void dce80_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + + set_clamping(opp80, params); + set_pixel_encoding(opp80, params); +} + +void dce80_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + uint32_t value; + bool enable_dyn_exp = false; + uint32_t addr = FMT_REG(mmFMT_DYNAMIC_EXP_CNTL); + + value = dm_read_reg(opp->ctx, addr); + + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + + /* From HW programming guide: + FMT_DYNAMIC_EXP_EN = 0 for limited RGB or YCbCr output + FMT_DYNAMIC_EXP_EN = 1 for RGB full range only*/ + if (color_sp == COLOR_SPACE_SRGB_FULL_RANGE) + enable_dyn_exp = true; + + /*00 - 10-bit -> 12-bit dynamic expansion*/ + /*01 - 8-bit -> 12-bit dynamic expansion*/ + if (signal == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (color_dpth) { + case COLOR_DEPTH_888: + set_reg_field_value(value, enable_dyn_exp ? 1:0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 1, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + break; + case COLOR_DEPTH_101010: + set_reg_field_value(value, enable_dyn_exp ? 1:0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + break; + case COLOR_DEPTH_121212: + break; + default: + break; + } + } + + dm_write_reg(opp->ctx, addr, value); +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_regamma.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_regamma.c new file mode 100644 index 0000000..ef95e98 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_opp_regamma.c @@ -0,0 +1,546 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_opp.h" +#include "gamma_types.h" + +#define DCP_REG(reg)\ + (reg + opp80->offsets.dcp_offset) + +#define DCFE_REG(reg)\ + (reg + opp80->offsets.crtc_offset) + +enum { + MAX_PWL_ENTRY = 128, + MAX_REGIONS_NUMBER = 16 + +}; + +struct curve_config { + uint32_t offset; + int8_t segments[MAX_REGIONS_NUMBER]; + int8_t begin; +}; + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static void regamma_config_regions_and_segments( + struct dce80_opp *opp80, const struct regamma_params *params) +{ + const struct gamma_curve *curve; + uint32_t value = 0; + + { + set_reg_field_value( + value, + params->arr_points[0].custom_float_x, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START); + + set_reg_field_value( + value, + 0, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START_SEGMENT); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_START_CNTL), + value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[0].custom_float_slope, + REGAMMA_CNTLA_SLOPE_CNTL, + REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_SLOPE_CNTL), value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[1].custom_float_x, + REGAMMA_CNTLA_END_CNTL1, + REGAMMA_CNTLA_EXP_REGION_END); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_END_CNTL1), value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[2].custom_float_slope, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_BASE); + + set_reg_field_value( + value, + params->arr_points[1].custom_float_y, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_SLOPE); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_END_CNTL2), value); + } + + curve = params->arr_curve_points; + + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS); + + dm_write_reg( + opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_0_1), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_2_3), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_4_5), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_6_7), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_8_9), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_10_11), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_12_13), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_NUM_SEGMENTS); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_14_15), + value); + } +} + +static void program_pwl( + struct dce80_opp *opp80, + const struct regamma_params *params) +{ + uint32_t value; + + { + uint8_t max_tries = 10; + uint8_t counter = 0; + + /* Power on LUT memory */ + value = dm_read_reg(opp80->base.ctx, + DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL)); + + set_reg_field_value( + value, + 1, + DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS); + + dm_write_reg(opp80->base.ctx, + DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL), value); + + while (counter < max_tries) { + value = + dm_read_reg( + opp80->base.ctx, + DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL)); + + if (get_reg_field_value( + value, + DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_MEM_PWR_STATE) == 0) + break; + + ++counter; + } + + if (counter == max_tries) { + dal_logger_write(opp80->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: regamma lut was not powered on " + "in a timely manner," + " programming still proceeds\n", + __func__); + } + } + + value = 0; + + set_reg_field_value( + value, + 7, + REGAMMA_LUT_WRITE_EN_MASK, + REGAMMA_LUT_WRITE_EN_MASK); + + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_LUT_WRITE_EN_MASK), value); + dm_write_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_LUT_INDEX), 0); + + /* Program REGAMMA_LUT_DATA */ + { + const uint32_t addr = DCP_REG(mmREGAMMA_LUT_DATA); + + uint32_t i = 0; + + const struct pwl_result_data *rgb = + params->rgb_resulted; + + while (i != params->hw_points_num) { + dm_write_reg(opp80->base.ctx, addr, rgb->red_reg); + dm_write_reg(opp80->base.ctx, addr, rgb->green_reg); + dm_write_reg(opp80->base.ctx, addr, rgb->blue_reg); + + dm_write_reg(opp80->base.ctx, addr, + rgb->delta_red_reg); + dm_write_reg(opp80->base.ctx, addr, + rgb->delta_green_reg); + dm_write_reg(opp80->base.ctx, addr, + rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + value = dm_read_reg(opp80->base.ctx, + DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL)); + + set_reg_field_value( + value, + 0, + DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS); + + dm_write_reg(opp80->base.ctx, DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL), + value); +} + + +void dce80_opp_power_on_regamma_lut( + struct output_pixel_processor *opp, + bool power_on) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + + uint32_t value = + dm_read_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL)); + + set_reg_field_value( + value, + power_on, + DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS); + + set_reg_field_value( + value, + power_on, + DCFE_MEM_LIGHT_SLEEP_CNTL, + DCP_LUT_LIGHT_SLEEP_DIS); + + dm_write_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL), value); +} + +bool dce80_opp_program_regamma_pwl( + struct output_pixel_processor *opp, + const struct regamma_params *params) +{ + + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + + regamma_config_regions_and_segments(opp80, params); + + program_pwl(opp80, params); + + return true; +} + +void dce80_opp_set_regamma_mode(struct output_pixel_processor *opp, + enum opp_regamma mode) +{ + struct dce80_opp *opp80 = TO_DCE80_OPP(opp); + uint32_t value = dm_read_reg(opp80->base.ctx, + DCP_REG(mmREGAMMA_CONTROL)); + + set_reg_field_value( + value, + mode, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + + dm_write_reg(opp80->base.ctx, DCP_REG(mmREGAMMA_CONTROL), value); +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.c new file mode 100644 index 0000000..1eeb469 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.c @@ -0,0 +1,1267 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_timing_generator.h" +#include "dce110/dce110_mem_input.h" +#include "dce110/dce110_resource.h" +#include "dce80/dce80_timing_generator.h" +#include "dce80/dce80_link_encoder.h" +#include "dce110/dce110_link_encoder.h" +#include "dce80/dce80_mem_input.h" +#include "dce80/dce80_ipp.h" +#include "dce80/dce80_transform.h" +#include "dce110/dce110_stream_encoder.h" +#include "dce80/dce80_stream_encoder.h" +#include "dce80/dce80_opp.h" +#include "dce110/dce110_ipp.h" +#include "dce110/dce110_clock_source.h" + +#include "dce/dce_8_0_d.h" + +/* TODO remove this include */ + +#ifndef mmDP_DPHY_INTERNAL_CTRL +#define mmDP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP0_DP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP1_DP_DPHY_INTERNAL_CTRL 0x1FDE +#define mmDP2_DP_DPHY_INTERNAL_CTRL 0x42DE +#define mmDP3_DP_DPHY_INTERNAL_CTRL 0x45DE +#define mmDP4_DP_DPHY_INTERNAL_CTRL 0x48DE +#define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4BDE +#define mmDP6_DP_DPHY_INTERNAL_CTRL 0x4EDE +#endif + +enum dce80_clk_src_array_id { + DCE80_CLK_SRC_PLL0 = 0, + DCE80_CLK_SRC_PLL1, + DCE80_CLK_SRC_PLL2, + DCE80_CLK_SRC_EXT, + + DCE80_CLK_SRC_TOTAL +}; + +#define DCE11_DIG_FE_CNTL 0x4a00 +#define DCE11_DIG_BE_CNTL 0x4a47 +#define DCE11_DP_SEC 0x4ac3 + +static const struct dce110_timing_generator_offsets dce80_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmGRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG3_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG4_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG5_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + } +}; + +static const struct dce110_mem_input_reg_offsets dce80_mi_reg_offsets[] = { + { + .dcp = (mmGRPH_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 dce80_transform_reg_offsets dce80_xfm_offsets[] = { +{ + .scl_offset = (mmSCL_CONTROL - mmSCL_CONTROL), + .crtc_offset = (mmDCFE_MEM_LIGHT_SLEEP_CNTL - + mmDCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmGRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ .scl_offset = (mmSCL1_SCL_CONTROL - mmSCL_CONTROL), + .crtc_offset = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmDCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB1_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ .scl_offset = (mmSCL2_SCL_CONTROL - mmSCL_CONTROL), + .crtc_offset = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmDCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB2_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ + .scl_offset = (mmSCL3_SCL_CONTROL - mmSCL_CONTROL), + .crtc_offset = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmDCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB3_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ + .scl_offset = (mmSCL4_SCL_CONTROL - mmSCL_CONTROL), + .crtc_offset = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmDCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB4_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ + .scl_offset = (mmSCL5_SCL_CONTROL - mmSCL_CONTROL), + .crtc_offset = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - + mmDCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp_offset = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB5_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +} +}; + +static const struct dce110_ipp_reg_offsets ipp_reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_CUR_CONTROL - mmDCP0_CUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_CUR_CONTROL - mmDCP0_CUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_CUR_CONTROL - mmDCP0_CUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP3_CUR_CONTROL - mmDCP0_CUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP4_CUR_CONTROL - mmDCP0_CUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP5_CUR_CONTROL - mmDCP0_CUR_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_clk_src_reg_offsets dce80_clk_src_reg_offsets[] = { + { + .pll_cntl = mmDCCG_PLL0_PLL_CNTL, + .pixclk_resync_cntl = mmPIXCLK0_RESYNC_CNTL + }, + { + .pll_cntl = mmDCCG_PLL1_PLL_CNTL, + .pixclk_resync_cntl = mmPIXCLK1_RESYNC_CNTL + }, + { + .pll_cntl = mmDCCG_PLL2_PLL_CNTL, + .pixclk_resync_cntl = mmPIXCLK2_RESYNC_CNTL + } +}; + +static struct timing_generator *dce80_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(ctx, sizeof(struct dce110_timing_generator)); + + if (!tg110) + return NULL; + + if (dce80_timing_generator_construct(tg110, as, ctx, instance, offsets)) + return &tg110->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, tg110); + return NULL; +} + +static struct stream_encoder *dce80_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx, + struct dc_bios *dcb, + const struct dce110_stream_enc_registers *regs) +{ + struct dce110_stream_encoder *enc110 = + dm_alloc(ctx, sizeof(struct dce110_stream_encoder)); + + if (!enc110) + return NULL; + + if (dce80_stream_encoder_construct(enc110, ctx, dcb, eng_id, regs)) + return &enc110->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, enc110); + return NULL; +} + + +static struct mem_input *dce80_mem_input_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets) +{ + struct dce110_mem_input *mem_input80 = + dm_alloc(ctx, sizeof(struct dce110_mem_input)); + + if (!mem_input80) + return NULL; + + if (dce80_mem_input_construct(mem_input80, + ctx, inst, offsets)) + return &mem_input80->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, mem_input80); + return NULL; +} + +static void dce80_transform_destroy(struct transform **xfm) +{ + dm_free((*xfm)->ctx, TO_DCE80_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce80_transform_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce80_transform_reg_offsets *offsets) +{ + struct dce80_transform *transform = + dm_alloc(ctx, sizeof(struct dce80_transform)); + + if (!transform) + return NULL; + + if (dce80_transform_construct(transform, ctx, inst, offsets)) + return &transform->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, transform); + return NULL; +} + +static struct input_pixel_processor *dce80_ipp_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offset) +{ + struct dce110_ipp *ipp = + dm_alloc(ctx, sizeof(struct dce110_ipp)); + + if (!ipp) + return NULL; + + if (dce80_ipp_construct(ipp, ctx, inst, offset)) + return &ipp->base; + + + BREAK_TO_DEBUGGER(); + dm_free(ctx, ipp); + return NULL; +} + +struct link_encoder *dce80_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + dm_alloc( + enc_init_data->ctx, + sizeof(struct dce110_link_encoder)); + + if (!enc110) + return NULL; + + if (dce80_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(enc_init_data->ctx, enc110); + return NULL; +} + +struct clock_source *dce80_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_reg_offsets *offsets) +{ + struct dce110_clk_src *clk_src = + dm_alloc(ctx, sizeof(struct dce110_clk_src)); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, offsets)) + return &clk_src->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce80_clock_source_destroy(struct clock_source **clk_src) +{ + dm_free((*clk_src)->ctx, TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +void dce80_destruct_resource_pool(struct resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->pipe_count; i++) { + if (pool->opps[i] != NULL) + dce80_opp_destroy(&pool->opps[i]); + + if (pool->transforms[i] != NULL) + dce80_transform_destroy(&pool->transforms[i]); + + if (pool->ipps[i] != NULL) + dce80_ipp_destroy(&pool->ipps[i]); + + if (pool->mis[i] != NULL) { + dm_free(pool->mis[i]->ctx, + TO_DCE110_MEM_INPUT(pool->mis[i])); + pool->mis[i] = NULL; + } + + if (pool->timing_generators[i] != NULL) { + dm_free(pool->timing_generators[i]->ctx, 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(pool->stream_enc[i]->ctx, + DCE110STRENC_FROM_STRENC(pool->stream_enc[i])); + } + + for (i = 0; i < pool->clk_src_count; i++) { + if (pool->clock_sources[i] != NULL) { + dce80_clock_source_destroy(&pool->clock_sources[i]); + } + } + + for (i = 0; i < pool->audio_count; i++) { + if (pool->audios[i] != NULL) { + dal_audio_destroy(&pool->audios[i]); + } + } + + if (pool->display_clock != NULL) { + dal_display_clock_destroy(&pool->display_clock); + } + + if (pool->scaler_filter != NULL) { + dal_scaler_filter_destroy(&pool->scaler_filter); + } + if (pool->irqs != NULL) { + dal_irq_service_destroy(&pool->irqs); + } + + if (pool->adapter_srv != NULL) { + dal_adapter_service_destroy(&pool->adapter_srv); + } +} + +static struct clock_source *find_first_free_pll( + struct resource_context *res_ctx) +{ + if (res_ctx->clock_source_ref_count[DCE80_CLK_SRC_PLL0] == 0) { + return res_ctx->pool.clock_sources[DCE80_CLK_SRC_PLL0]; + } + if (res_ctx->clock_source_ref_count[DCE80_CLK_SRC_PLL1] == 0) { + return res_ctx->pool.clock_sources[DCE80_CLK_SRC_PLL1]; + } + if (res_ctx->clock_source_ref_count[DCE80_CLK_SRC_PLL2] == 0) { + return res_ctx->pool.clock_sources[DCE80_CLK_SRC_PLL2]; + } + + 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 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; + + build_info_frame(pipe_ctx); + + /* do not need to validate non root pipes */ + break; + } + } + } + + return DC_OK; +} + +enum dc_status dce80_validate_bandwidth( + const struct 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->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->ratios.vert.value); + disp->graphics_h_taps = pipe_ctx->taps.h_taps; + disp->graphics_v_taps = pipe_ctx->taps.v_taps; + + /* TODO: remove when bw formula accepts taps per + * display + */ + if (max_vtaps < pipe_ctx->taps.v_taps) + max_vtaps = pipe_ctx->taps.v_taps; + if (max_htaps < pipe_ctx->taps.h_taps) + max_htaps = pipe_ctx->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 (dm_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 (dm_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 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.clock_sources[DCE80_CLK_SRC_EXT]; + else + pipe_ctx->clock_source = + find_used_clk_src_for_sharing( + &context->res_ctx, pipe_ctx); + if (pipe_ctx->clock_source == NULL) + pipe_ctx->clock_source = + find_first_free_pll(&context->res_ctx); + + if (pipe_ctx->clock_source == NULL) + return DC_NO_CLOCK_SOURCE_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 dce80_validate_with_context( + const struct dc *dc, + const struct dc_validation_set set[], + uint8_t set_count, + struct validate_context *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + uint8_t i, j; + struct dc_context *dc_ctx = dc->ctx; + + for (i = 0; i < set_count; i++) { + bool unchanged = false; + + context->targets[i] = DC_TARGET_TO_CORE(set[i].target); + 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); + context->target_status[i] = + dc->current_context.target_status[j]; + } + if (!unchanged) + if (!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 = map_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) + build_scaling_params_for_context(dc, context); + + if (result == DC_OK) + result = dce80_validate_bandwidth(dc, context); + + return result; +} + +static struct resource_funcs dce80_res_pool_funcs = { + .destruct = dce80_destruct_resource_pool, + .link_enc_create = dce80_link_encoder_create, + .link_enc_destroy = dce110_link_encoder_destroy, + .validate_with_context = dce80_validate_with_context, + .validate_bandwidth = dce80_validate_bandwidth +}; + +bool dce80_construct_resource_pool( + struct adapter_service *adapter_serv, + uint8_t num_virtual_links, + struct 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 = &dce80_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[DCE80_CLK_SRC_PLL0] = dce80_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_PLL0, &dce80_clk_src_reg_offsets[0]); + pool->clock_sources[DCE80_CLK_SRC_PLL1] = dce80_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_PLL1, &dce80_clk_src_reg_offsets[1]); + pool->clock_sources[DCE80_CLK_SRC_PLL2] = dce80_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_PLL2, &dce80_clk_src_reg_offsets[2]); + pool->clock_sources[DCE80_CLK_SRC_EXT] = dce80_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_EXTERNAL, &dce80_clk_src_reg_offsets[0]); + pool->clk_src_count = DCE80_CLK_SRC_TOTAL; + + 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_dce80_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 = + dal_adapter_service_get_stream_engines_num(adapter_serv); + 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] = dce80_timing_generator_create( + adapter_serv, ctx, i, &dce80_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] = dce80_mem_input_create(ctx, i, + &dce80_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] = dce80_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] = dce80_transform_create( + ctx, i, &dce80_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] = dce80_opp_create(ctx, 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++) { + if (pool->stream_engines.u_all & 1 << i) { + pool->stream_enc[i] = dce80_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++; + } + + return true; + +stream_enc_create_fail: + for (i = 0; i < pool->stream_enc_count; i++) { + if (pool->stream_enc[i] != NULL) + dm_free(pool->stream_enc[i]->ctx, + 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) + dce80_opp_destroy(&pool->opps[i]); + + if (pool->transforms[i] != NULL) + dce80_transform_destroy(&pool->transforms[i]); + + if (pool->ipps[i] != NULL) + dce80_ipp_destroy(&pool->ipps[i]); + + if (pool->mis[i] != NULL) { + dm_free(pool->mis[i]->ctx, + TO_DCE110_MEM_INPUT(pool->mis[i])); + pool->mis[i] = NULL; + } + if (pool->timing_generators[i] != NULL) { + dm_free(pool->timing_generators[i]->ctx, + 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) + dce80_clock_source_destroy(&pool->clock_sources[i]); + } + + return false; +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_resource.h new file mode 100644 index 0000000..3d0f8fe --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_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_DCE80_H__ +#define __DC_RESOURCE_DCE80_H__ + +#include "core_types.h" + +struct adapter_service; +struct dc; +struct resource_pool; + +bool dce80_construct_resource_pool( + struct adapter_service *adapter_serv, + uint8_t num_virtual_links, + struct dc *dc, + struct resource_pool *pool); + +#endif /* __DC_RESOURCE_DCE80_H__ */ + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.c new file mode 100644 index 0000000..d45a1e4 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.c @@ -0,0 +1,1104 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "bios_parser_types.h" +#include "dc_bios_types.h" +#include "../dce110/dce110_stream_encoder.h" +#include "dce80_stream_encoder.h" +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#define LINK_REG(reg)\ + (enc110->regs->reg) + +#define VBI_LINE_0 0 +#define DP_BLANK_MAX_RETRY 20 + +enum dp_pixel_encoding { + DP_PIXEL_ENCODING_RGB444 = 0, + DP_PIXEL_ENCODING_YCBCR422, + DP_PIXEL_ENCODING_YCBCR444, + DP_PIXEL_ENCODING_RGB_WIDE_GAMUT, + DP_PIXEL_ENCODING_Y_ONLY +}; + +enum dp_component_depth { + DP_COMPONENT_DEPTH_6BPC = 0, + DP_COMPONENT_DEPTH_8BPC, + DP_COMPONENT_DEPTH_10BPC, + DP_COMPONENT_DEPTH_12BPC +}; + +enum { + DP_MST_UPDATE_MAX_RETRY = 50 +}; + +static struct stream_encoder_funcs dce80_str_enc_funcs = { + .dp_set_stream_attribute = + dce80_stream_encoder_dp_set_stream_attribute, + .hdmi_set_stream_attribute = + dce80_stream_encoder_hdmi_set_stream_attribute, + .dvi_set_stream_attribute = + dce80_stream_encoder_dvi_set_stream_attribute, + .set_mst_bandwidth = + dce80_stream_encoder_set_mst_bandwidth, + .update_hdmi_info_packets = + dce80_stream_encoder_update_hdmi_info_packets, + .stop_hdmi_info_packets = + dce80_stream_encoder_stop_hdmi_info_packets, + .update_dp_info_packets = + dce80_stream_encoder_update_dp_info_packets, + .stop_dp_info_packets = + dce80_stream_encoder_stop_dp_info_packets, + .dp_blank = + dce80_stream_encoder_dp_blank, + .dp_unblank = + dce80_stream_encoder_dp_unblank, +}; + +static void dce80_update_generic_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct encoder_info_packet *info_packet) +{ + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr; + uint32_t regval; + /* choose which generic packet to use */ + { + addr = LINK_REG(AFMT_VBI_PACKET_CONTROL); + + regval = dm_read_reg(ctx, addr); + + set_reg_field_value( + regval, + packet_index, + AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC_INDEX); + + dm_write_reg(ctx, addr, regval); + } + + /* write generic packet header + * (4th byte is for GENERIC0 only) + */ + { + addr = LINK_REG(AFMT_GENERIC_HDR); + + regval = 0; + + set_reg_field_value( + regval, + info_packet->hb0, + AFMT_GENERIC_HDR, + AFMT_GENERIC_HB0); + + set_reg_field_value( + regval, + info_packet->hb1, + AFMT_GENERIC_HDR, + AFMT_GENERIC_HB1); + + set_reg_field_value( + regval, + info_packet->hb2, + AFMT_GENERIC_HDR, + AFMT_GENERIC_HB2); + + set_reg_field_value( + regval, + info_packet->hb3, + AFMT_GENERIC_HDR, + AFMT_GENERIC_HB3); + + dm_write_reg(ctx, addr, regval); + } + + /* write generic packet contents + * (we never use last 4 bytes) + * there are 8 (0-7) mmDIG0_AFMT_GENERIC0_x registers + */ + { + const uint32_t *content = + (const uint32_t *) &info_packet->sb[0]; + + uint32_t counter = 0; + + addr = LINK_REG(AFMT_GENERIC_0); + + do { + dm_write_reg(ctx, addr++, *content++); + + ++counter; + } while (counter < 7); + } + + addr = LINK_REG(AFMT_GENERIC_7); + + dm_write_reg( + ctx, + addr, + 0); + + /* force double-buffered packet update */ + { + addr = LINK_REG(AFMT_VBI_PACKET_CONTROL); + + regval = dm_read_reg(ctx, addr); + + set_reg_field_value( + regval, + (packet_index == 0), + AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC0_UPDATE); + + set_reg_field_value( + regval, + (packet_index == 2), + AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC2_UPDATE); + + dm_write_reg(ctx, addr, regval); + } +} + +static void dce80_update_hdmi_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct encoder_info_packet *info_packet) +{ + struct dc_context *ctx = enc110->base.ctx; + uint32_t cont, send, line; + uint32_t addr = 0; + uint32_t regval; + + if (info_packet->valid) { + dce80_update_generic_info_packet( + enc110, + packet_index, + info_packet); + + /* enable transmission of packet(s) - + * packet transmission begins on the next frame + */ + cont = 1; + /* send packet(s) every frame */ + send = 1; + /* select line number to send packets on */ + line = 2; + } else { + cont = 0; + send = 0; + line = 0; + } + + /* choose which generic packet control to use */ + + switch (packet_index) { + case 0: + case 1: + addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL0); + break; + case 2: + case 3: + addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL1); + break; + default: + /* invalid HW packet index */ + dal_logger_write( + ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_ENCODER, + "Invalid HW packet index: %s()\n", + __func__); + break; + } + + regval = dm_read_reg(ctx, addr); + + switch (packet_index) { + case 0: + case 2: + set_reg_field_value( + regval, + cont, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_CONT); + set_reg_field_value( + regval, + send, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_SEND); + set_reg_field_value( + regval, + line, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_LINE); + break; + case 1: + case 3: + set_reg_field_value( + regval, + cont, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_CONT); + set_reg_field_value( + regval, + send, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_SEND); + set_reg_field_value( + regval, + line, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_LINE); + break; + default: + /* invalid HW packet index */ + dal_logger_write( + ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_ENCODER, + "Invalid HW packet index: %s()\n", + __func__); + break; + } + + dm_write_reg(ctx, addr, regval); +} + +bool dce80_stream_encoder_construct( + struct dce110_stream_encoder *enc110, + struct dc_context *ctx, + struct dc_bios *dcb, + enum engine_id eng_id, + const struct dce110_stream_enc_registers *regs) +{ + if (!enc110) + return false; + if (!dcb) + return false; + + enc110->base.funcs = &dce80_str_enc_funcs; + enc110->base.ctx = ctx; + enc110->base.id = eng_id; + enc110->base.bp = dcb; + enc110->regs = regs; + + return true; +} + +/* setup stream encoder in dp mode */ +void dce80_stream_encoder_dp_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = LINK_REG(DP_PIXEL_FORMAT); + uint32_t value = dm_read_reg(ctx, addr); + + /* set pixel encoding */ + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + set_reg_field_value( + value, + DP_PIXEL_ENCODING_YCBCR422, + DP_PIXEL_FORMAT, + DP_PIXEL_ENCODING); + break; + case PIXEL_ENCODING_YCBCR444: + set_reg_field_value( + value, + DP_PIXEL_ENCODING_YCBCR444, + DP_PIXEL_FORMAT, + DP_PIXEL_ENCODING); + + if (crtc_timing->flags.Y_ONLY) + if (crtc_timing->display_color_depth != COLOR_DEPTH_666) + /* HW testing only, no use case yet. + * Color depth of Y-only could be + * 8, 10, 12, 16 bits + */ + set_reg_field_value( + value, + DP_PIXEL_ENCODING_Y_ONLY, + DP_PIXEL_FORMAT, + DP_PIXEL_ENCODING); + /* Note: DP_MSA_MISC1 bit 7 is the indicator + * of Y-only mode. + * This bit is set in HW if register + * DP_PIXEL_ENCODING is programmed to 0x4 + */ + break; + default: + set_reg_field_value( + value, + DP_PIXEL_ENCODING_RGB444, + DP_PIXEL_FORMAT, + DP_PIXEL_ENCODING); + break; + } + + /* set color depth */ + + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + set_reg_field_value( + value, + DP_COMPONENT_DEPTH_8BPC, + DP_PIXEL_FORMAT, + DP_COMPONENT_DEPTH); + break; + case COLOR_DEPTH_101010: + set_reg_field_value( + value, + DP_COMPONENT_DEPTH_10BPC, + DP_PIXEL_FORMAT, + DP_COMPONENT_DEPTH); + break; + case COLOR_DEPTH_121212: + set_reg_field_value( + value, + DP_COMPONENT_DEPTH_12BPC, + DP_PIXEL_FORMAT, + DP_COMPONENT_DEPTH); + break; + default: + set_reg_field_value( + value, + DP_COMPONENT_DEPTH_6BPC, + DP_PIXEL_FORMAT, + DP_COMPONENT_DEPTH); + break; + } + + /* set dynamic range and YCbCr range */ + set_reg_field_value(value, 0, DP_PIXEL_FORMAT, DP_DYN_RANGE); + set_reg_field_value(value, 0, DP_PIXEL_FORMAT, DP_YCBCR_RANGE); + + dm_write_reg(ctx, addr, value); + +} + +/* setup stream encoder in hdmi mode */ +void dce80_stream_encoder_hdmi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool enable_audio) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = LINK_REG(TMDS_CNTL); + uint32_t value = dm_read_reg(ctx, addr); + uint32_t output_pixel_clock = crtc_timing->pix_clk_khz; + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.enable_dp_audio = enable_audio; + cntl.pixel_clock = crtc_timing->pix_clk_khz; + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.color_depth = crtc_timing->display_color_depth; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + set_reg_field_value(value, 1, TMDS_CNTL, TMDS_PIXEL_ENCODING); + break; + default: + set_reg_field_value(value, 0, TMDS_CNTL, TMDS_PIXEL_ENCODING); + break; + } + + set_reg_field_value(value, 0, TMDS_CNTL, TMDS_COLOR_FORMAT); + dm_write_reg(ctx, addr, value); + + /* setup HDMI engine */ + addr = LINK_REG(HDMI_CONTROL); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, 1, HDMI_CONTROL, HDMI_PACKET_GEN_VERSION); + set_reg_field_value(value, 1, HDMI_CONTROL, HDMI_KEEPOUT_MODE); + set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE); + + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + set_reg_field_value( + value, + 0, + HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH); + break; + case COLOR_DEPTH_101010: + set_reg_field_value( + value, + 1, + HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH); + set_reg_field_value( + value, + 1, + HDMI_CONTROL, + HDMI_DEEP_COLOR_ENABLE); + output_pixel_clock = (crtc_timing->pix_clk_khz * 30) / 24; + break; + case COLOR_DEPTH_121212: + set_reg_field_value( + value, + 2, + HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH); + set_reg_field_value( + value, + 1, + HDMI_CONTROL, + HDMI_DEEP_COLOR_ENABLE); + output_pixel_clock = (crtc_timing->pix_clk_khz * 36) / 24; + break; + case COLOR_DEPTH_161616: + set_reg_field_value( + value, + 3, + HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH); + set_reg_field_value( + value, + 1, + HDMI_CONTROL, + HDMI_DEEP_COLOR_ENABLE); + output_pixel_clock = (crtc_timing->pix_clk_khz * 48) / 24; + break; + default: + break; + } + + dm_write_reg(ctx, addr, value); + + addr = LINK_REG(HDMI_VBI_PACKET_CONTROL); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT); + set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND); + set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND); + + dm_write_reg(ctx, addr, value); + + /* following belongs to audio */ + addr = LINK_REG(HDMI_INFOFRAME_CONTROL0); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + 1, + HDMI_INFOFRAME_CONTROL0, + HDMI_AUDIO_INFO_SEND); + dm_write_reg(ctx, addr, value); + + addr = LINK_REG(AFMT_INFOFRAME_CONTROL0); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + 1, + AFMT_INFOFRAME_CONTROL0, + AFMT_AUDIO_INFO_UPDATE); + dm_write_reg(ctx, addr, value); + + addr = LINK_REG(HDMI_INFOFRAME_CONTROL1); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + VBI_LINE_0 + 2, + HDMI_INFOFRAME_CONTROL1, + HDMI_AUDIO_INFO_LINE); + dm_write_reg(ctx, addr, value); + + addr = LINK_REG(HDMI_GC); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, 0, HDMI_GC, HDMI_GC_AVMUTE); + dm_write_reg(ctx, addr, value); +} + +/* setup stream encoder in dvi mode */ +void dce80_stream_encoder_dvi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool is_dual_link) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = LINK_REG(TMDS_CNTL); + uint32_t value = dm_read_reg(ctx, addr); + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = is_dual_link ? + SIGNAL_TYPE_DVI_DUAL_LINK : + SIGNAL_TYPE_DVI_SINGLE_LINK; + cntl.enable_dp_audio = false; + cntl.pixel_clock = crtc_timing->pix_clk_khz; + cntl.lanes_number = (is_dual_link) ? + LANE_COUNT_EIGHT : LANE_COUNT_FOUR; + cntl.color_depth = crtc_timing->display_color_depth; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + set_reg_field_value(value, 1, TMDS_CNTL, TMDS_PIXEL_ENCODING); + break; + default: + set_reg_field_value(value, 0, TMDS_CNTL, TMDS_PIXEL_ENCODING); + break; + } + + switch (crtc_timing->pixel_encoding) { + case COLOR_DEPTH_101010: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) + set_reg_field_value( + value, + 2, + TMDS_CNTL, + TMDS_COLOR_FORMAT); + else + set_reg_field_value( + value, + 0, + TMDS_CNTL, + TMDS_COLOR_FORMAT); + break; + default: + set_reg_field_value(value, 0, TMDS_CNTL, TMDS_COLOR_FORMAT); + break; + } + dm_write_reg(ctx, addr, value); +} + +void dce80_stream_encoder_set_mst_bandwidth( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr; + uint32_t field; + uint32_t value; + uint32_t retries = 0; + uint32_t x = dal_fixed31_32_floor( + avg_time_slots_per_mtp); + uint32_t y = dal_fixed31_32_ceil( + dal_fixed31_32_shl( + dal_fixed31_32_sub_int( + avg_time_slots_per_mtp, + x), + 26)); + + { + addr = LINK_REG(DP_MSE_RATE_CNTL); + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + x, + DP_MSE_RATE_CNTL, + DP_MSE_RATE_X); + + set_reg_field_value( + value, + y, + DP_MSE_RATE_CNTL, + DP_MSE_RATE_Y); + + dm_write_reg(ctx, addr, value); + } + + /* wait for update to be completed on the link */ + /* i.e. DP_MSE_RATE_UPDATE_PENDING field (read only) */ + /* is reset to 0 (not pending) */ + { + addr = LINK_REG(DP_MSE_RATE_UPDATE); + + do { + value = dm_read_reg(ctx, addr); + + field = get_reg_field_value( + value, + DP_MSE_RATE_UPDATE, + DP_MSE_RATE_UPDATE_PENDING); + + if (!(field & + DP_MSE_RATE_UPDATE__DP_MSE_RATE_UPDATE_PENDING_MASK)) + break; + + dm_delay_in_microseconds(ctx, 10); + + ++retries; + } while (retries < DP_MST_UPDATE_MAX_RETRY); + } +} + +void dce80_stream_encoder_update_hdmi_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr; + uint32_t regval; + uint32_t control0val; + uint32_t control1val; + + if (info_frame->avi.valid) { + const uint32_t *content = + (const uint32_t *) &info_frame->avi.sb[0]; + + addr = LINK_REG(AFMT_AVI_INFO0); + regval = content[0]; + + dm_write_reg( + ctx, + addr, + regval); + + addr = LINK_REG(AFMT_AVI_INFO1); + regval = content[1]; + + dm_write_reg( + ctx, + addr, + regval); + + addr = LINK_REG(AFMT_AVI_INFO2); + regval = content[2]; + + dm_write_reg( + ctx, + addr, + regval); + + addr = LINK_REG(AFMT_AVI_INFO3); + regval = content[3]; + + /* move version to AVI_INFO3 */ + set_reg_field_value( + regval, + info_frame->avi.hb1, + AFMT_AVI_INFO3, + AFMT_AVI_INFO_VERSION); + + dm_write_reg( + ctx, + addr, + regval); + + addr = LINK_REG(HDMI_INFOFRAME_CONTROL0); + control0val = dm_read_reg(ctx, addr); + set_reg_field_value( + control0val, + 1, + HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND); + + set_reg_field_value( + control0val, + 1, + HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_CONT); + + dm_write_reg(ctx, addr, control0val); + + addr = LINK_REG(HDMI_INFOFRAME_CONTROL1); + + control1val = dm_read_reg(ctx, addr); + + set_reg_field_value( + control1val, + VBI_LINE_0 + 2, + HDMI_INFOFRAME_CONTROL1, + HDMI_AVI_INFO_LINE); + + dm_write_reg(ctx, addr, control1val); + } else { + addr = LINK_REG(HDMI_INFOFRAME_CONTROL0); + + regval = dm_read_reg(ctx, addr); + + set_reg_field_value( + regval, + 0, + HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND); + + set_reg_field_value( + regval, + 0, + HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_CONT); + + dm_write_reg(ctx, addr, regval); + } + + dce80_update_hdmi_info_packet(enc110, 0, &info_frame->vendor); + dce80_update_hdmi_info_packet(enc110, 1, &info_frame->gamut); + dce80_update_hdmi_info_packet(enc110, 2, &info_frame->spd); +} + +void dce80_stream_encoder_stop_hdmi_info_packets( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = 0; + uint32_t value = 0; + + /* stop generic packets 0 & 1 on HDMI */ + addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL0); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_CONT); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_LINE); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_SEND); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_CONT); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_LINE); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_SEND); + + dm_write_reg(ctx, addr, value); + + /* stop generic packets 2 & 3 on HDMI */ + addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL1); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC2_CONT); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC2_LINE); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC2_SEND); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC3_CONT); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC3_LINE); + set_reg_field_value( + value, + 0, + HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC3_SEND); + + dm_write_reg(ctx, addr, value); + + /* stop AVI packet on HDMI */ + addr = LINK_REG(HDMI_INFOFRAME_CONTROL0); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND); + set_reg_field_value( + value, + 0, + HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_CONT); + + dm_write_reg(ctx, addr, value); +} +void dce80_stream_encoder_update_dp_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = LINK_REG(DP_SEC_CNTL); + uint32_t value; + + if (info_frame->vsc.valid) + dce80_update_generic_info_packet( + enc110, + 0, + &info_frame->vsc); + + /* enable/disable transmission of packet(s). + * If enabled, packet transmission begins on the next frame + */ + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + info_frame->vsc.valid, + DP_SEC_CNTL, + DP_SEC_GSP0_ENABLE); + + /* This bit is the master enable bit. + * When enabling secondary stream engine, + * this master bit must also be set. + * This register shared with audio info frame. + * Therefore we need to enable master bit + * if at least on of the fields is not 0 + */ + if (value) + set_reg_field_value( + value, + 1, + DP_SEC_CNTL, + DP_SEC_STREAM_ENABLE); + + dm_write_reg(ctx, addr, value); +} + +void dce80_stream_encoder_stop_dp_info_packets( + struct stream_encoder *enc) +{ + /* stop generic packets on DP */ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = LINK_REG(DP_SEC_CNTL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP0_ENABLE); + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP1_ENABLE); + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP2_ENABLE); + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP3_ENABLE); + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_AVI_ENABLE); + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_MPG_ENABLE); + set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_STREAM_ENABLE); + + /* this register shared with audio info frame. + * therefore we need to keep master enabled + * if at least one of the fields is not 0 + */ + + if (value) + set_reg_field_value( + value, + 1, + DP_SEC_CNTL, + DP_SEC_STREAM_ENABLE); + + dm_write_reg(ctx, addr, value); +} + +void dce80_stream_encoder_dp_blank( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = LINK_REG(DP_VID_STREAM_CNTL); + uint32_t value = dm_read_reg(ctx, addr); + uint32_t retries = 0; + uint32_t max_retries = DP_BLANK_MAX_RETRY * 10; + + /* Note: For CZ, we are changing driver default to disable + * stream deferred to next VBLANK. If results are positive, we + * will make the same change to all DCE versions. There are a + * handful of panels that cannot handle disable stream at + * HBLANK and will result in a white line flash across the + * screen on stream disable. + */ + + /* Specify the video stream disable point + * (2 = start of the next vertical blank) + */ + set_reg_field_value( + value, + 2, + DP_VID_STREAM_CNTL, + DP_VID_STREAM_DIS_DEFER); + /* Larger delay to wait until VBLANK - use max retry of + * 10us*3000=30ms. This covers 16.6ms of typical 60 Hz mode + + * a little more because we may not trust delay accuracy. + */ + max_retries = DP_BLANK_MAX_RETRY * 150; + + /* disable DP stream */ + set_reg_field_value(value, 0, DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE); + dm_write_reg(ctx, addr, value); + + /* the encoder stops sending the video stream + * at the start of the vertical blanking. + * Poll for DP_VID_STREAM_STATUS == 0 + */ + + do { + value = dm_read_reg(ctx, addr); + + if (!get_reg_field_value( + value, + DP_VID_STREAM_CNTL, + DP_VID_STREAM_STATUS)) + break; + + dm_delay_in_microseconds(ctx, 10); + + ++retries; + } while (retries < max_retries); + + ASSERT(retries <= max_retries); + + /* Tell the DP encoder to ignore timing from CRTC, must be done after + * the polling. If we set DP_STEER_FIFO_RESET before DP stream blank is + * complete, stream status will be stuck in video stream enabled state, + * i.e. DP_VID_STREAM_STATUS stuck at 1. + */ + addr = LINK_REG(DP_STEER_FIFO); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, true, DP_STEER_FIFO, DP_STEER_FIFO_RESET); + dm_write_reg(ctx, addr, value); +} + +/* output video stream to link encoder */ +void dce80_stream_encoder_dp_unblank( + struct stream_encoder *enc, + const struct encoder_unblank_param *param) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr; + uint32_t value; + + if (param->link_settings.link_rate != LINK_RATE_UNKNOWN) { + uint32_t n_vid = 0x8000; + uint32_t m_vid; + + /* M / N = Fstream / Flink + * m_vid / n_vid = pixel rate / link rate + */ + + uint64_t m_vid_l = n_vid; + + m_vid_l *= param->crtc_timing.pixel_clock; + m_vid_l = div_u64(m_vid_l, + param->link_settings.link_rate + * LINK_RATE_REF_FREQ_IN_KHZ); + + m_vid = (uint32_t) m_vid_l; + + /* enable auto measurement */ + addr = LINK_REG(DP_VID_TIMING); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, 0, DP_VID_TIMING, DP_VID_M_N_GEN_EN); + dm_write_reg(ctx, addr, value); + + /* auto measurement need 1 full 0x8000 symbol cycle to kick in, + * therefore program initial value for Mvid and Nvid + */ + addr = LINK_REG(DP_VID_N); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, n_vid, DP_VID_N, DP_VID_N); + dm_write_reg(ctx, addr, value); + + addr = LINK_REG(DP_VID_M); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, m_vid, DP_VID_M, DP_VID_M); + dm_write_reg(ctx, addr, value); + + addr = LINK_REG(DP_VID_TIMING); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, 1, DP_VID_TIMING, DP_VID_M_N_GEN_EN); + dm_write_reg(ctx, addr, value); + } + + /* set DIG_START to 0x1 to resync FIFO */ + addr = LINK_REG(DIG_FE_CNTL); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, 1, DIG_FE_CNTL, DIG_START); + dm_write_reg(ctx, addr, value); + + /* switch DP encoder to CRTC data */ + addr = LINK_REG(DP_STEER_FIFO); + value = dm_read_reg(ctx, addr); + set_reg_field_value(value, false, DP_STEER_FIFO, DP_STEER_FIFO_RESET); + dm_write_reg(ctx, addr, value); + + /* wait 100us for DIG/DP logic to prime + * (i.e. a few video lines) + */ + dm_delay_in_microseconds(ctx, 100); + + /* the hardware would start sending video at the start of the next DP + * frame (i.e. rising edge of the vblank). + * NOTE: We used to program DP_VID_STREAM_DIS_DEFER = 2 here, but this + * register has no effect on enable transition! HW always guarantees + * VID_STREAM enable at start of next frame, and this is not + * programmable + */ + addr = LINK_REG(DP_VID_STREAM_CNTL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + true, + DP_VID_STREAM_CNTL, + DP_VID_STREAM_ENABLE); + dm_write_reg(ctx, addr, value); +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.h new file mode 100644 index 0000000..f4645a8 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_stream_encoder.h @@ -0,0 +1,85 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "inc/stream_encoder.h" + +#ifndef __DC_STREAM_ENCODER_DCE80_H__ +#define __DC_STREAM_ENCODER_DCE80_H__ + +bool dce80_stream_encoder_construct( + struct dce110_stream_encoder *enc110, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + const struct dce110_stream_enc_registers *regs); + +/***** HW programming ***********/ +/* setup stream encoder in dp mode */ +void dce80_stream_encoder_dp_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing); + +/* setup stream encoder in hdmi mode */ +void dce80_stream_encoder_hdmi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool enable_audio); + +/* setup stream encoder in dvi mode */ +void dce80_stream_encoder_dvi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool is_dual_link); + +/* set throttling for DP MST */ +void dce80_stream_encoder_set_mst_bandwidth( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp); + +void dce80_stream_encoder_update_hdmi_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame); + +void dce80_stream_encoder_stop_hdmi_info_packets( + struct stream_encoder *enc); + +void dce80_stream_encoder_update_dp_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame); + +void dce80_stream_encoder_stop_dp_info_packets( + struct stream_encoder *enc); + +/* output blank/idle stream to link encoder */ +void dce80_stream_encoder_dp_blank( + struct stream_encoder *enc); + +/* output video stream to link encoder */ +void dce80_stream_encoder_dp_unblank( + struct stream_encoder *enc, + const struct encoder_unblank_param *param); + + +#endif /* __DC_STREAM_ENCODER_DCE80_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.c new file mode 100644 index 0000000..80391c2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.c @@ -0,0 +1,241 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dc_types.h" + +#include "include/grph_object_id.h" +#include "include/adapter_service_interface.h" +#include "include/logger_interface.h" +#include "../dce110/dce110_timing_generator.h" +#include "dce80_timing_generator.h" + +#include "../inc/timing_generator.h" + +enum black_color_format { + BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ + BLACK_COLOR_FORMAT_RGB_LIMITED, + BLACK_COLOR_FORMAT_YUV_TV, + BLACK_COLOR_FORMAT_YUV_CV, + BLACK_COLOR_FORMAT_YUV_SUPER_AA, + + BLACK_COLOR_FORMAT_COUNT +}; + +static const struct dce110_timing_generator_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) +#define DMIF_REG(reg) (reg + tg110->offsets.dmif) + +void program_pix_dur(struct timing_generator *tg, uint32_t pix_clk_khz) +{ + uint64_t pix_dur; + uint32_t addr = mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL1 + + DCE110TG_FROM_TG(tg)->offsets.dmif; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (pix_clk_khz == 0) + return; + + pix_dur = 1000000000 / pix_clk_khz; + + set_reg_field_value( + value, + pix_dur, + DPG_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + + dm_write_reg(tg->ctx, addr, value); +} + +static void program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (!use_vbios) + program_pix_dur(tg, timing->pix_clk_khz); + + dce110_tg_program_timing(tg, timing, use_vbios); +} + +static struct timing_generator_funcs dce80_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_crtc_positions, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + + /* DCE8.0 overrides */ + .enable_advanced_request = + dce80_timing_generator_enable_advanced_request +}; + +bool dce80_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct adapter_service *as, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + if (!tg110) + return false; + + if (!as) + return false; + + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->offsets = *offsets; + tg110->derived_offsets = reg_offsets[instance]; + + tg110->base.funcs = &dce80_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = dal_adapter_service_get_bios_parser(as); + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; + + return true; +} + +void dce80_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable && !DCE110TG_FROM_TG(tg)->disable_advanced_request) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.h new file mode 100644 index 0000000..0b88686 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_timing_generator.h @@ -0,0 +1,49 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TIMING_GENERATOR_DCE80_H__ +#define __DC_TIMING_GENERATOR_DCE80_H__ + + +#include "../inc/timing_generator.h" +#include "../include/grph_object_id.h" + +/* DCE8.0 implementation inherits from DCE11.0 */ +bool dce80_timing_generator_construct( + struct dce110_timing_generator *tg, + struct adapter_service *as, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +/******** HW programming ************/ +void dce80_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing); + + + +#endif /* __DC_TIMING_GENERATOR_DCE80_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.c new file mode 100644 index 0000000..5654738 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.c @@ -0,0 +1,91 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dc_types.h" +#include "core_types.h" + +#include "include/grph_object_id.h" +#include "include/fixed31_32.h" +#include "include/logger_interface.h" + +#include "dce80_transform.h" + +#include "dce80_transform_bit_depth.h" + +static struct transform_funcs dce80_transform_funcs = { + .transform_power_up = + dce80_transform_power_up, + .transform_set_scaler = + dce80_transform_set_scaler, + .transform_set_scaler_bypass = + dce80_transform_set_scaler_bypass, + .transform_update_viewport = + dce80_transform_update_viewport, + .transform_set_scaler_filter = + dce80_transform_set_scaler_filter, + .transform_set_gamut_remap = + dce80_transform_set_gamut_remap, + .transform_set_pixel_storage_depth = + dce80_transform_set_pixel_storage_depth, + .transform_get_current_pixel_storage_depth = + dce80_transform_get_current_pixel_storage_depth +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce80_transform_construct( + struct dce80_transform *xfm80, + struct dc_context *ctx, + uint32_t inst, + const struct dce80_transform_reg_offsets *reg_offsets) +{ + xfm80->base.ctx = ctx; + + xfm80->base.inst = inst; + xfm80->base.funcs = &dce80_transform_funcs; + + xfm80->offsets = *reg_offsets; + + xfm80->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + return true; +} + +bool dce80_transform_power_up(struct transform *xfm) +{ + return dce80_transform_power_up_line_buffer(xfm); +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.h new file mode 100644 index 0000000..adcc54b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform.h @@ -0,0 +1,87 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_TRANSFORM_DCE80_H__ +#define __DAL_TRANSFORM_DCE80_H__ + +#include "inc/transform.h" +#include "include/grph_csc_types.h" + +#define TO_DCE80_TRANSFORM(transform)\ + container_of(transform, struct dce80_transform, base) + +struct dce80_transform_reg_offsets { + uint32_t scl_offset; + uint32_t crtc_offset; + uint32_t dcp_offset; + uint32_t lb_offset; +}; + +struct dce80_transform { + struct transform base; + struct dce80_transform_reg_offsets offsets; + + uint32_t lb_pixel_depth_supported; +}; + +bool dce80_transform_construct(struct dce80_transform *xfm80, + struct dc_context *ctx, + uint32_t inst, + const struct dce80_transform_reg_offsets *offsets); + +bool dce80_transform_power_up(struct transform *xfm); + +/* SCALER RELATED */ +bool dce80_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data); + +void dce80_transform_set_scaler_bypass(struct transform *xfm); + +bool dce80_transform_update_viewport( + struct transform *xfm, + const struct rect *view_port, + bool is_fbc_attached); + +void dce80_transform_set_scaler_filter( + struct transform *xfm, + struct scaler_filter *filter); + +/* GAMUT RELATED */ +void dce80_transform_set_gamut_remap( + struct transform *xfm, + const struct grph_csc_adjustment *adjust); + +/* BIT DEPTH RELATED */ +bool dce80_transform_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params); + +bool dce80_transform_get_current_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth *depth); + + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.c new file mode 100644 index 0000000..1dc0dbc --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.c @@ -0,0 +1,841 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_transform.h" + +#include "include/logger_interface.h" +#include "include/fixed32_32.h" + +#define DCP_REG(reg)\ + (reg + xfm80->offsets.dcp_offset) + +#define LB_REG(reg)\ + (reg + xfm80->offsets.lb_offset) + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +enum dcp_out_trunc_round_mode { + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_MODE_ROUND +}; + +enum dcp_out_trunc_round_depth { + DCP_OUT_TRUNC_ROUND_DEPTH_14BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_13BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_11BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_9BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_8BIT +}; + +/* defines the various methods of bit reduction available for use */ +enum dcp_bit_depth_reduction_mode { + DCP_BIT_DEPTH_REDUCTION_MODE_DITHER, + DCP_BIT_DEPTH_REDUCTION_MODE_ROUND, + DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE, + DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED, + DCP_BIT_DEPTH_REDUCTION_MODE_INVALID +}; + +enum dcp_spatial_dither_mode { + DCP_SPATIAL_DITHER_MODE_AAAA, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_MODE_AABBAABB, + DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC, + DCP_SPATIAL_DITHER_MODE_INVALID +}; + +enum dcp_spatial_dither_depth { + DCP_SPATIAL_DITHER_DEPTH_30BPP, + DCP_SPATIAL_DITHER_DEPTH_24BPP +}; + +static bool set_clamp( + struct dce80_transform *xfm80, + enum dc_color_depth depth); + +static bool set_round( + struct dce80_transform *xfm80, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth); + +static bool set_dither( + struct dce80_transform *xfm80, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable); + +/** + ******************************************************************************* + * dce80_transform_bit_depth_reduction_program + * + * @brief + * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, + * Dither) for dce80 + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @return + * true if succeeds. + ******************************************************************************* + */ +static bool program_bit_depth_reduction( + struct dce80_transform *xfm80, + enum dc_color_depth depth) +{ + enum dcp_bit_depth_reduction_mode depth_reduction_mode; + enum dcp_spatial_dither_mode spatial_dither_mode; + bool frame_random_enable; + bool rgb_random_enable; + bool highpass_random_enable; + + if (depth > COLOR_DEPTH_121212) { + ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ + return false; + } + + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DITHER; + + spatial_dither_mode = DCP_SPATIAL_DITHER_MODE_A_AA_A; + + frame_random_enable = true; + rgb_random_enable = true; + highpass_random_enable = true; + + if (!set_clamp(xfm80, depth)) { + /* Failure in set_clamp() */ + ASSERT_CRITICAL(false); + return false; + } + switch (depth_reduction_mode) { + case DCP_BIT_DEPTH_REDUCTION_MODE_DITHER: + /* Spatial Dither: Set round/truncate to bypass (12bit), + * enable Dither (30bpp) */ + set_round(xfm80, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm80, true, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_ROUND: + /* Round: Enable round (10bit), disable Dither */ + set_round(xfm80, + DCP_OUT_TRUNC_ROUND_MODE_ROUND, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm80, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE: /* Truncate */ + /* Truncate: Enable truncate (10bit), disable Dither */ + set_round(xfm80, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm80, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + + case DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED: /* Disabled */ + /* Truncate: Set round/truncate to bypass (12bit), + * disable Dither */ + set_round(xfm80, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm80, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + default: + /* Invalid DCP Depth reduction mode */ + ASSERT_CRITICAL(false); + break; + } + + return true; +} + +/** + ******************************************************************************* + * set_clamp + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @brief + * Programs clamp according to panel bit depth. + * + * @return + * true if succeeds + * + ******************************************************************************* + */ +static bool set_clamp( + struct dce80_transform *xfm80, + enum dc_color_depth depth) +{ + uint32_t clamp_max = 0; + + /* At the clamp block the data will be MSB aligned, so we set the max + * clamp accordingly. + * For example, the max value for 6 bits MSB aligned (14 bit bus) would + * be "11 1111 0000 0000" in binary, so 0x3F00. + */ + switch (depth) { + case COLOR_DEPTH_666: + /* 6bit MSB aligned on 14 bit bus '11 1111 0000 0000' */ + clamp_max = 0x3F00; + break; + case COLOR_DEPTH_888: + /* 8bit MSB aligned on 14 bit bus '11 1111 800 0000' */ + clamp_max = 0x3FC0; + break; + case COLOR_DEPTH_101010: + /* 10bit MSB aligned on 14 bit bus '11 1111 1111 800' */ + clamp_max = 0x3FFC; + break; + case COLOR_DEPTH_121212: + /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1111' */ + clamp_max = 0x3FFF; + break; + default: + ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ + return false; + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_B_CB, + OUT_CLAMP_MIN_B_CB); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_B_CB, + OUT_CLAMP_MAX_B_CB); + + dm_write_reg(xfm80->base.ctx, + DCP_REG(mmOUT_CLAMP_CONTROL_B_CB), + value); + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_G_Y, + OUT_CLAMP_MIN_G_Y); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_G_Y, + OUT_CLAMP_MAX_G_Y); + + dm_write_reg(xfm80->base.ctx, + DCP_REG(mmOUT_CLAMP_CONTROL_G_Y), + value); + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MIN_R_CR); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + + dm_write_reg(xfm80->base.ctx, + DCP_REG(mmOUT_CLAMP_CONTROL_R_CR), + value); + } + + return true; +} + +/** + ******************************************************************************* + * set_round + * + * @brief + * Programs Round/Truncate + * + * @param [in] mode :round or truncate + * @param [in] depth :bit depth to round/truncate to + OUT_ROUND_TRUNC_MODE 3:0 0xA Output data round or truncate mode + POSSIBLE VALUES: + 00 - truncate to u0.12 + 01 - truncate to u0.11 + 02 - truncate to u0.10 + 03 - truncate to u0.9 + 04 - truncate to u0.8 + 05 - reserved + 06 - truncate to u0.14 + 07 - truncate to u0.13 set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + 08 - round to u0.12 + 09 - round to u0.11 + 10 - round to u0.10 + 11 - round to u0.9 + 12 - round to u0.8 + 13 - reserved + 14 - round to u0.14 + 15 - round to u0.13 + + * @return + * true if succeeds. + ******************************************************************************* + */ +static bool set_round( + struct dce80_transform *xfm80, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth) +{ + uint32_t depth_bits = 0; + uint32_t mode_bit = 0; + /* zero out all bits */ + uint32_t value = 0; + + /* set up bit depth */ + switch (depth) { + case DCP_OUT_TRUNC_ROUND_DEPTH_14BIT: + depth_bits = 6; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_13BIT: + depth_bits = 7; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_12BIT: + depth_bits = 0; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_11BIT: + depth_bits = 1; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_10BIT: + depth_bits = 2; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_9BIT: + depth_bits = 3; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_8BIT: + depth_bits = 4; + break; + default: + /* Invalid dcp_out_trunc_round_depth */ + ASSERT_CRITICAL(false); + return false; + } + + set_reg_field_value( + value, + depth_bits, + OUT_ROUND_CONTROL, + OUT_ROUND_TRUNC_MODE); + + /* set up round or truncate */ + switch (mode) { + case DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE: + mode_bit = 0; + break; + case DCP_OUT_TRUNC_ROUND_MODE_ROUND: + mode_bit = 1; + break; + default: + /* Invalid dcp_out_trunc_round_mode */ + ASSERT_CRITICAL(false); + return false; + } + + depth_bits |= mode_bit << 3; + + set_reg_field_value( + value, + depth_bits, + OUT_ROUND_CONTROL, + OUT_ROUND_TRUNC_MODE); + + /* write the register */ + dm_write_reg(xfm80->base.ctx, + DCP_REG(mmOUT_ROUND_CONTROL), + value); + + return true; +} + +/** + ******************************************************************************* + * set_dither + * + * @brief + * Programs Dither + * + * @param [in] dither_enable : enable dither + * @param [in] dither_mode : dither mode to set + * @param [in] dither_depth : bit depth to dither to + * @param [in] frame_random_enable : enable frame random + * @param [in] rgb_random_enable : enable rgb random + * @param [in] highpass_random_enable : enable highpass random + * + * @return + * true if succeeds. + ******************************************************************************* + */ + +static bool set_dither( + struct dce80_transform *xfm80, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable) +{ + uint32_t dither_depth_bits = 0; + uint32_t dither_mode_bits = 0; + /* zero out all bits */ + uint32_t value = 0; + + /* set up the fields */ + if (dither_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_EN); + + switch (dither_mode) { + case DCP_SPATIAL_DITHER_MODE_AAAA: + dither_mode_bits = 0; + break; + case DCP_SPATIAL_DITHER_MODE_A_AA_A: + dither_mode_bits = 1; + break; + case DCP_SPATIAL_DITHER_MODE_AABBAABB: + dither_mode_bits = 2; + break; + case DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC: + dither_mode_bits = 3; + break; + default: + /* Invalid dcp_spatial_dither_mode */ + ASSERT_CRITICAL(false); + return false; + + } + set_reg_field_value( + value, + dither_mode_bits, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_MODE); + + switch (dither_depth) { + case DCP_SPATIAL_DITHER_DEPTH_30BPP: + dither_depth_bits = 0; + break; + case DCP_SPATIAL_DITHER_DEPTH_24BPP: + dither_depth_bits = 1; + break; + default: + /* Invalid dcp_spatial_dither_depth */ + ASSERT_CRITICAL(false); + return false; + } + + set_reg_field_value( + value, + dither_depth_bits, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_DEPTH); + + if (frame_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_FRAME_RANDOM_ENABLE); + + if (rgb_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_RGB_RANDOM_ENABLE); + + if (highpass_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_HIGHPASS_RANDOM_ENABLE); + + /* write the register */ + dm_write_reg(xfm80->base.ctx, + DCP_REG(mmDCP_SPATIAL_DITHER_CNTL), + value); + + return true; +} + +bool dce80_transform_get_max_num_of_supported_lines( + struct dce80_transform *xfm80, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines) +{ + uint32_t pixels_per_entries = 0; + uint32_t max_pixels_supports = 0; + + if (pixel_width == 0) + return false; + + /* Find number of pixels that can fit into a single LB entry and + * take floor of the value since we cannot store a single pixel + * across multiple entries. */ + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 18; + break; + + case LB_PIXEL_DEPTH_24BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 24; + break; + + case LB_PIXEL_DEPTH_30BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 30; + break; + + case LB_PIXEL_DEPTH_36BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 36; + break; + + default: + dal_logger_write(xfm80->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid LB pixel depth", + __func__); + break; + } + + if (pixels_per_entries == 0) + return false; + + max_pixels_supports = pixels_per_entries * LB_TOTAL_NUMBER_OF_ENTRIES; + + *lines = max_pixels_supports / pixel_width; + return true; +} + +void dce80_transform_enable_alpha( + struct dce80_transform *xfm80, + bool enable) +{ + struct dc_context *ctx = xfm80->base.ctx; + uint32_t value; + uint32_t addr = LB_REG(mmLB_DATA_FORMAT); + + value = dm_read_reg(ctx, addr); + + if (enable == 1) + set_reg_field_value( + value, + 1, + LB_DATA_FORMAT, + ALPHA_EN); + else + set_reg_field_value( + value, + 0, + LB_DATA_FORMAT, + ALPHA_EN); + + dm_write_reg(ctx, addr, value); +} + +static enum lb_pixel_depth translate_display_bpp_to_lb_depth( + uint32_t display_bpp) +{ + switch (display_bpp) { + case 18: + return LB_PIXEL_DEPTH_18BPP; + case 24: + return LB_PIXEL_DEPTH_24BPP; + case 36: + case 42: + case 48: + return LB_PIXEL_DEPTH_36BPP; + case 30: + default: + return LB_PIXEL_DEPTH_30BPP; + } +} + +bool dce80_transform_get_next_lower_pixel_storage_depth( + struct dce80_transform *xfm80, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth) +{ + enum lb_pixel_depth depth_req_by_display = + translate_display_bpp_to_lb_depth(display_bpp); + uint32_t current_required_depth = depth_req_by_display; + uint32_t current_depth = depth; + + /* if required display depth < current we could go down, for example + * from LB_PIXEL_DEPTH_30BPP to LB_PIXEL_DEPTH_24BPP + */ + if (current_required_depth < current_depth) { + current_depth = current_depth >> 1; + if (xfm80->lb_pixel_depth_supported & current_depth) { + *lower_depth = current_depth; + return true; + } + } + return false; +} + +bool dce80_transform_is_prefetch_enabled( + struct dce80_transform *xfm80) +{ + uint32_t value = dm_read_reg( + xfm80->base.ctx, LB_REG(mmLB_DATA_FORMAT)); + + if (get_reg_field_value(value, LB_DATA_FORMAT, PREFETCH) == 1) + return true; + + return false; +} + +bool dce80_transform_get_current_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth *depth) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + uint32_t value = 0; + + if (depth == NULL) + return false; + + value = dm_read_reg( + xfm->ctx, + LB_REG(mmLB_DATA_FORMAT)); + + switch (get_reg_field_value(value, LB_DATA_FORMAT, PIXEL_DEPTH)) { + case 0: + *depth = LB_PIXEL_DEPTH_30BPP; + break; + case 1: + *depth = LB_PIXEL_DEPTH_24BPP; + break; + case 2: + *depth = LB_PIXEL_DEPTH_18BPP; + break; + case 3: + *depth = LB_PIXEL_DEPTH_36BPP; + break; + default: + dal_logger_write(xfm->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid LB pixel depth", + __func__); + *depth = LB_PIXEL_DEPTH_30BPP; + break; + } + return true; + +} + +static void set_denormalization( + struct dce80_transform *xfm80, + enum dc_color_depth depth) +{ + uint32_t value = dm_read_reg(xfm80->base.ctx, + DCP_REG(mmDENORM_CONTROL)); + + switch (depth) { + case COLOR_DEPTH_666: + /* 63/64 for 6 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_888: + /* Unity for 8 bit output color depth + * because prescale is disabled by default */ + set_reg_field_value( + value, + 0, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 5, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_141414: + case COLOR_DEPTH_161616: + default: + /* not valid used case! */ + break; + } + + dm_write_reg(xfm80->base.ctx, + DCP_REG(mmDENORM_CONTROL), + value); + +} + +bool dce80_transform_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + bool ret = true; + uint32_t value; + enum dc_color_depth color_depth; + + value = dm_read_reg( + xfm->ctx, + LB_REG(mmLB_DATA_FORMAT)); + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + color_depth = COLOR_DEPTH_666; + set_reg_field_value(value, 2, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_24BPP: + color_depth = COLOR_DEPTH_888; + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_30BPP: + color_depth = COLOR_DEPTH_101010; + set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_36BPP: + color_depth = COLOR_DEPTH_121212; + set_reg_field_value(value, 3, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + default: + ret = false; + break; + } + + if (ret == true) { + set_denormalization(xfm80, color_depth); + ret = program_bit_depth_reduction(xfm80, color_depth); + + set_reg_field_value(value, 0, LB_DATA_FORMAT, ALPHA_EN); + dm_write_reg( + xfm->ctx, LB_REG(mmLB_DATA_FORMAT), value); + if (!(xfm80->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dal_logger_write(xfm->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Capability not supported", + __func__); + } + } + + return ret; +} + +/* LB_MEMORY_CONFIG + * 00 - Use all three pieces of memory + * 01 - Use only one piece of memory of total 720x144 bits + * 10 - Use two pieces of memory of total 960x144 bits + * 11 - reserved + * + * LB_MEMORY_SIZE + * Total entries of LB memory. + * This number should be larger than 960. The default value is 1712(0x6B0) */ +bool dce80_transform_power_up_line_buffer(struct transform *xfm) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + uint32_t value; + + value = dm_read_reg(xfm80->base.ctx, LB_REG(mmLB_MEMORY_CTRL)); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LB_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE8 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value(value, LB_TOTAL_NUMBER_OF_ENTRIES, LB_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dm_write_reg(xfm80->base.ctx, LB_REG(mmLB_MEMORY_CTRL), value); + + return true; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.h b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.h new file mode 100644 index 0000000..af831f8 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_bit_depth.h @@ -0,0 +1,51 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TRANSFORM_BIT_DEPTH_DCE80_H__ +#define __DC_TRANSFORM_BIT_DEPTH_DCE80_H__ + +#include "dce80_transform.h" + +bool dce80_transform_power_up_line_buffer(struct transform *xfm); + +bool dce80_transform_get_max_num_of_supported_lines( + struct dce80_transform *xfm80, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines); + +void dce80_transform_enable_alpha( + struct dce80_transform *xfm80, + bool enable); + +bool dce80_transform_get_next_lower_pixel_storage_depth( + struct dce80_transform *xfm80, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth); + +bool dce80_transform_is_prefetch_enabled( + struct dce80_transform *xfm80); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_gamut.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_gamut.c new file mode 100644 index 0000000..df5db67 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_gamut.c @@ -0,0 +1,297 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "dce80_transform.h" +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +#include "include/fixed31_32.h" +#include "basics/conversion.h" +#include "include/grph_object_id.h" + +enum { + GAMUT_MATRIX_SIZE = 12 +}; + +#define DCP_REG(reg)\ + (reg + xfm80->offsets.dcp_offset) + +#define DISP_BRIGHTNESS_DEFAULT_HW 0 +#define DISP_BRIGHTNESS_MIN_HW -25 +#define DISP_BRIGHTNESS_MAX_HW 25 +#define DISP_BRIGHTNESS_STEP_HW 1 +#define DISP_BRIGHTNESS_HW_DIVIDER 100 + +#define DISP_HUE_DEFAULT_HW 0 +#define DISP_HUE_MIN_HW -30 +#define DISP_HUE_MAX_HW 30 +#define DISP_HUE_STEP_HW 1 +#define DISP_HUE_HW_DIVIDER 1 + +#define DISP_CONTRAST_DEFAULT_HW 100 +#define DISP_CONTRAST_MIN_HW 50 +#define DISP_CONTRAST_MAX_HW 150 +#define DISP_CONTRAST_STEP_HW 1 +#define DISP_CONTRAST_HW_DIVIDER 100 + +#define DISP_SATURATION_DEFAULT_HW 100 +#define DISP_SATURATION_MIN_HW 0 +#define DISP_SATURATION_MAX_HW 200 +#define DISP_SATURATION_STEP_HW 1 +#define DISP_SATURATION_HW_DIVIDER 100 + +#define DISP_KELVIN_DEGRES_DEFAULT 6500 +#define DISP_KELVIN_DEGRES_MIN 4000 +#define DISP_KELVIN_DEGRES_MAX 10000 +#define DISP_KELVIN_DEGRES_STEP 100 +#define DISP_KELVIN_HW_DIVIDER 10000 + +static void program_gamut_remap( + struct dce80_transform *xfm80, + const uint16_t *reg_val) +{ + struct dc_context *ctx = xfm80->base.ctx; + uint32_t value = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_CONTROL); + + /* the register controls ovl also */ + value = dm_read_reg(ctx, addr); + + if (reg_val) { + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C11_C12); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[0], + GAMUT_REMAP_C11_C12, + GAMUT_REMAP_C11); + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[1], + GAMUT_REMAP_C11_C12, + GAMUT_REMAP_C12); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C13_C14); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[2], + GAMUT_REMAP_C13_C14, + GAMUT_REMAP_C13); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[3], + GAMUT_REMAP_C13_C14, + GAMUT_REMAP_C14); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C21_C22); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[4], + GAMUT_REMAP_C21_C22, + GAMUT_REMAP_C21); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[5], + GAMUT_REMAP_C21_C22, + GAMUT_REMAP_C22); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C23_C24); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[6], + GAMUT_REMAP_C23_C24, + GAMUT_REMAP_C23); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[7], + GAMUT_REMAP_C23_C24, + GAMUT_REMAP_C24); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C31_C32); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[8], + GAMUT_REMAP_C31_C32, + GAMUT_REMAP_C31); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[9], + GAMUT_REMAP_C31_C32, + GAMUT_REMAP_C32); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C33_C34); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[10], + GAMUT_REMAP_C33_C34, + GAMUT_REMAP_C33); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[11], + GAMUT_REMAP_C33_C34, + GAMUT_REMAP_C34); + + dm_write_reg(ctx, addr, reg_data); + } + + set_reg_field_value( + value, + 1, + GAMUT_REMAP_CONTROL, + GRPH_GAMUT_REMAP_MODE); + + } else + set_reg_field_value( + value, + 0, + GAMUT_REMAP_CONTROL, + GRPH_GAMUT_REMAP_MODE); + + addr = DCP_REG(mmGAMUT_REMAP_CONTROL); + dm_write_reg(ctx, addr, value); + +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_gamut_remap + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and apply color temperature adjustment to in Rgb color space + * + * @see + * + ***************************************************************************** + */ +void dce80_transform_set_gamut_remap( + struct transform *xfm, + const struct grph_csc_adjustment *adjust) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + + if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW || + adjust->temperature_divider == 0) + program_gamut_remap(xfm80, NULL); + else { + struct fixed31_32 arr_matrix[GAMUT_MATRIX_SIZE]; + uint16_t arr_reg_val[GAMUT_MATRIX_SIZE]; + + arr_matrix[0] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[0], + adjust->temperature_divider); + arr_matrix[1] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[1], + adjust->temperature_divider); + arr_matrix[2] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[2], + adjust->temperature_divider); + arr_matrix[3] = dal_fixed31_32_zero; + + arr_matrix[4] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[3], + adjust->temperature_divider); + arr_matrix[5] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[4], + adjust->temperature_divider); + arr_matrix[6] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[5], + adjust->temperature_divider); + arr_matrix[7] = dal_fixed31_32_zero; + + arr_matrix[8] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[6], + adjust->temperature_divider); + arr_matrix[9] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[7], + adjust->temperature_divider); + arr_matrix[10] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[8], + adjust->temperature_divider); + arr_matrix[11] = dal_fixed31_32_zero; + + convert_float_matrix( + arr_reg_val, arr_matrix, GAMUT_MATRIX_SIZE); + + program_gamut_remap(xfm80, arr_reg_val); + } +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_scl.c b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_scl.c new file mode 100644 index 0000000..62a3a04 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce80/dce80_transform_scl.c @@ -0,0 +1,814 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dce80_transform.h" + +#define UP_SCALER_RATIO_MAX 16000 +#define DOWN_SCALER_RATIO_MAX 250 +#define SCALER_RATIO_DIVIDER 1000 + +#define SCL_REG(reg)\ + (reg + xfm80->offsets.scl_offset) + +#define DCFE_REG(reg)\ + (reg + xfm80->offsets.crtc_offset) + +static void disable_enhanced_sharpness(struct dce80_transform *xfm80) +{ + uint32_t value; + + value = dm_read_reg(xfm80->base.ctx, + SCL_REG(mmSCL_F_SHARP_CONTROL)); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_HF_SHARP_EN); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_VF_SHARP_EN); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_HF_SHARP_SCALE_FACTOR); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_VF_SHARP_SCALE_FACTOR); + + dm_write_reg(xfm80->base.ctx, + SCL_REG(mmSCL_F_SHARP_CONTROL), value); +} + +/** +* Function: +* void setup_scaling_configuration +* +* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps +* Input: data +* +* Output: + void +*/ +static bool setup_scaling_configuration( + struct dce80_transform *xfm80, + const struct scaler_data *data) +{ + struct dc_context *ctx = xfm80->base.ctx; + uint32_t addr; + uint32_t value; + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + dce80_transform_set_scaler_bypass(&xfm80->base); + return false; + } + + { + addr = SCL_REG(mmSCL_MODE); + value = dm_read_reg(ctx, addr); + + if (data->dal_pixel_format <= PIXEL_FORMAT_GRPH_END) + set_reg_field_value(value, 1, SCL_MODE, SCL_MODE); + else + set_reg_field_value(value, 2, SCL_MODE, SCL_MODE); + + dm_write_reg(ctx, addr, value); + } + { + addr = SCL_REG(mmSCL_TAP_CONTROL); + value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, data->taps.h_taps - 1, + SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.v_taps - 1, + SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + + dm_write_reg(ctx, addr, value); + } + { + addr = SCL_REG(mmSCL_CONTROL); + value = dm_read_reg(ctx, addr); + /* 1 - Replaced out of bound pixels with edge */ + set_reg_field_value(value, 1, SCL_CONTROL, SCL_BOUNDARY_MODE); + + /* 1 - Replaced out of bound pixels with the edge pixel. */ + dm_write_reg(ctx, addr, value); + } + + return true; +} + +/** +* Function: +* void program_overscan +* +* Purpose: Programs overscan border +* Input: overscan +* +* Output: + void +*/ +static void program_overscan( + struct dce80_transform *xfm80, + const struct overscan_info *overscan) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + set_reg_field_value(overscan_left_right, overscan->left, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan->right, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, overscan->top, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan->bottom, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dm_write_reg(xfm80->base.ctx, + SCL_REG(mmEXT_OVERSCAN_LEFT_RIGHT), + overscan_left_right); + + dm_write_reg(xfm80->base.ctx, + SCL_REG(mmEXT_OVERSCAN_TOP_BOTTOM), + overscan_top_bottom); +} + +static void program_two_taps_filter( + struct dce80_transform *xfm80, + bool enable, + bool vertical) +{ + uint32_t addr; + uint32_t value; + /* 1: Hard coded 2 tap filter + * 0: Programmable 2 tap filter from coefficient RAM + */ + if (vertical) { + addr = SCL_REG(mmSCL_VERT_FILTER_CONTROL); + value = dm_read_reg(xfm80->base.ctx, addr); + set_reg_field_value( + value, + enable ? 1 : 0, + SCL_VERT_FILTER_CONTROL, + SCL_V_2TAP_HARDCODE_COEF_EN); + + } else { + addr = SCL_REG(mmSCL_HORZ_FILTER_CONTROL); + value = dm_read_reg(xfm80->base.ctx, addr); + set_reg_field_value( + value, + enable ? 1 : 0, + SCL_HORZ_FILTER_CONTROL, + SCL_H_2TAP_HARDCODE_COEF_EN); + } + + dm_write_reg(xfm80->base.ctx, addr, value); +} + +static void set_coeff_update_complete(struct dce80_transform *xfm80) +{ + uint32_t value; + uint32_t addr = SCL_REG(mmSCL_UPDATE); + + value = dm_read_reg(xfm80->base.ctx, addr); + set_reg_field_value(value, 1, + SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dm_write_reg(xfm80->base.ctx, addr, value); +} + +static void program_filter( + struct dce80_transform *xfm80, + enum ram_filter_type filter_type, + struct scaler_filter_params *scl_filter_params, + uint32_t *coeffs, + uint32_t coeffs_num) +{ + uint32_t phase = 0; + uint32_t array_idx = 0; + uint32_t pair = 0; + + uint32_t taps_pairs = (scl_filter_params->taps + 1) / 2; + uint32_t phases_to_program = scl_filter_params->phases / 2 + 1; + + uint32_t i; + uint32_t addr; + uint32_t select_addr; + uint32_t select; + uint32_t data; + /* We need to disable power gating on coeff memory to do programming */ + + uint32_t pwr_ctrl_orig; + uint32_t pwr_ctrl_off; + + addr = DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL); + pwr_ctrl_orig = dm_read_reg(xfm80->base.ctx, addr); + pwr_ctrl_off = pwr_ctrl_orig; + set_reg_field_value( + pwr_ctrl_off, + 1, + DCFE_MEM_LIGHT_SLEEP_CNTL, + SCL_LIGHT_SLEEP_DIS); + dm_write_reg(xfm80->base.ctx, addr, pwr_ctrl_off); + + /* Wait to disable gating: */ + for (i = 0; + i < 10 && + get_reg_field_value( + dm_read_reg(xfm80->base.ctx, addr), + DCFE_MEM_LIGHT_SLEEP_CNTL, + SCL_MEM_PWR_STATE); + i++) + dm_delay_in_microseconds(xfm80->base.ctx, 1); + + ASSERT(i < 10); + + select_addr = SCL_REG(mmSCL_COEF_RAM_SELECT); + select = dm_read_reg(xfm80->base.ctx, select_addr); + + set_reg_field_value( + select, + filter_type, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_FILTER_TYPE); + set_reg_field_value( + select, + 0, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_TAP_PAIR_IDX); + set_reg_field_value( + select, + 0, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_PHASE); + + data = 0; + + for (phase = 0; phase < phases_to_program; phase++) { + /* we always program N/2 + 1 phases, total phases N, but N/2-1 + * are just mirror phase 0 is unique and phase N/2 is unique + * if N is even + */ + + set_reg_field_value( + select, + phase, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_PHASE); + + for (pair = 0; pair < taps_pairs; pair++) { + set_reg_field_value( + select, + pair, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_TAP_PAIR_IDX); + dm_write_reg(xfm80->base.ctx, select_addr, select); + + /* even tap write enable */ + set_reg_field_value( + data, + 1, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF_EN); + /* even tap data */ + set_reg_field_value( + data, + coeffs[array_idx], + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF); + + /* if we have odd number of taps and the last pair is + * here then we do not need to program + */ + if (scl_filter_params->taps % 2 && + pair == taps_pairs - 1) { + /* odd tap write disable */ + set_reg_field_value( + data, + 0, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + set_reg_field_value( + data, + 0, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + array_idx += 1; + } else { + /* odd tap write enable */ + set_reg_field_value( + data, + 1, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + /* dbg_val: 0x1000 / sclFilterParams->taps; */ + set_reg_field_value( + data, + coeffs[array_idx + 1], + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + + array_idx += 2; + } + + dm_write_reg( + xfm80->base.ctx, + SCL_REG(mmSCL_COEF_RAM_TAP_DATA), + data); + } + } + + ASSERT(coeffs_num == array_idx); + + /* reset the power gating register */ + dm_write_reg( + xfm80->base.ctx, + DCFE_REG(mmDCFE_MEM_LIGHT_SLEEP_CNTL), + pwr_ctrl_orig); + + set_coeff_update_complete(xfm80); +} + +/* + * + * Populates an array with filter coefficients in 1.1.12 fixed point form +*/ +static bool get_filter_coefficients( + struct dce80_transform *xfm80, + uint32_t taps, + uint32_t **data_tab, + uint32_t *data_size) +{ + uint32_t num = 0; + uint32_t i; + const struct fixed31_32 *filter = + dal_scaler_filter_get( + xfm80->base.filter, + data_tab, + &num); + uint32_t *data_row; + + if (!filter) { + BREAK_TO_DEBUGGER(); + return false; + } + data_row = *data_tab; + + for (i = 0; i < num; ++i) { + /* req. format sign fixed 1.1.12, the values are always between + * [-1; 1] + * + * Each phase is mirrored as follows : + * 0 : Phase 0 + * 1 : Phase 1 or Phase 64 - 1 / 128 - 1 + * N : Phase N or Phase 64 - N / 128 - N + * + * Convert from Fixed31_32 to 1.1.12 by using floor on value + * shifted by number of required fractional bits(12) + */ + struct fixed31_32 value = filter[i]; + + data_row[i] = + dal_fixed31_32_floor(dal_fixed31_32_shl(value, 12)) & + 0x3FFC; + } + *data_size = num; + + return true; +} + +static bool program_multi_taps_filter( + struct dce80_transform *xfm80, + const struct scaler_data *data, + bool horizontal) +{ + struct scaler_filter_params filter_params; + enum ram_filter_type filter_type; + uint32_t src_size; + uint32_t dst_size; + + uint32_t *filter_data = NULL; + uint32_t filter_data_size = 0; + + /* 16 phases total for DCE8 */ + filter_params.phases = 16; + + if (horizontal) { + filter_params.taps = data->taps.h_taps; + filter_params.sharpness = data->h_sharpness; + filter_params.flags.bits.HORIZONTAL = 1; + + src_size = data->viewport.width; + dst_size = + dal_fixed31_32_floor( + dal_fixed31_32_div( + dal_fixed31_32_from_int( + data->viewport.width), + data->ratios->horz)); + + filter_type = FILTER_TYPE_RGB_Y_HORIZONTAL; + } else { + filter_params.taps = data->taps.v_taps; + filter_params.sharpness = data->v_sharpness; + filter_params.flags.bits.HORIZONTAL = 0; + + src_size = data->viewport.height; + dst_size = + dal_fixed31_32_floor( + dal_fixed31_32_div( + dal_fixed31_32_from_int( + data->viewport.height), + data->ratios->vert)); + + filter_type = FILTER_TYPE_RGB_Y_VERTICAL; + } + + /* 1. Generate the coefficients */ + if (!dal_scaler_filter_generate( + xfm80->base.filter, + &filter_params, + src_size, + dst_size)) + return false; + + /* 2. Convert coefficients to fixed point format 1.12 (note coeff. + * could be negative(!) and range is [ from -1 to 1 ]) */ + if (!get_filter_coefficients( + xfm80, + filter_params.taps, + &filter_data, + &filter_data_size)) + return false; + + /* 3. Program the filter */ + program_filter( + xfm80, + filter_type, + &filter_params, + filter_data, + filter_data_size); + + /* 4. Program the alpha if necessary */ + if (data->flags.bits.SHOULD_PROGRAM_ALPHA) { + if (horizontal) + filter_type = FILTER_TYPE_ALPHA_HORIZONTAL; + else + filter_type = FILTER_TYPE_ALPHA_VERTICAL; + + program_filter( + xfm80, + filter_type, + &filter_params, + filter_data, + filter_data_size); + } + + return true; +} + +static void program_viewport( + struct dce80_transform *xfm80, + const struct rect *view_port) +{ + struct dc_context *ctx = xfm80->base.ctx; + uint32_t value = 0; + uint32_t addr = 0; + + addr = SCL_REG(mmVIEWPORT_START); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + view_port->x, + VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + view_port->y, + VIEWPORT_START, + VIEWPORT_Y_START); + dm_write_reg(ctx, addr, value); + + addr = SCL_REG(mmVIEWPORT_SIZE); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + view_port->height, + VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + view_port->width, + VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dm_write_reg(ctx, addr, value); + + /* TODO: add stereo support */ +} + +static void calculate_inits( + struct dce80_transform *xfm80, + const struct scaler_data *data, + struct scl_ratios_inits *inits) +{ + struct fixed31_32 h_init; + struct fixed31_32 v_init; + struct fixed31_32 v_init_bot; + + inits->bottom_enable = 0; + inits->h_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios->horz) << 5; + inits->v_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios->vert) << 5; + + h_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->horz, + dal_fixed31_32_from_int(data->taps.h_taps + 1)), + 2); + inits->h_init.integer = dal_fixed31_32_floor(h_init); + inits->h_init.fraction = dal_fixed31_32_u0d19(h_init) << 5; + + v_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->vert, + dal_fixed31_32_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dal_fixed31_32_floor(v_init); + inits->v_init.fraction = dal_fixed31_32_u0d19(v_init) << 5; + + if (data->flags.bits.INTERLACED) { + v_init_bot = + dal_fixed31_32_add( + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->vert, + dal_fixed31_32_from_int( + data->taps.v_taps + 1)), + 2), + data->ratios->vert); + inits->v_init_bottom.integer = dal_fixed31_32_floor(v_init_bot); + inits->v_init_bottom.fraction = + dal_fixed31_32_u0d19(v_init_bot) << 5; + + inits->bottom_enable = 1; + } +} + +static void program_scl_ratios_inits( + struct dce80_transform *xfm80, + struct scl_ratios_inits *inits) +{ + uint32_t addr = SCL_REG(mmSCL_HORZ_FILTER_SCALE_RATIO); + uint32_t value = 0; + + set_reg_field_value( + value, + inits->h_int_scale_ratio, + SCL_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dm_write_reg(xfm80->base.ctx, addr, value); + + addr = SCL_REG(mmSCL_VERT_FILTER_SCALE_RATIO); + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio, + SCL_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dm_write_reg(xfm80->base.ctx, addr, value); + + addr = SCL_REG(mmSCL_HORZ_FILTER_INIT); + value = 0; + set_reg_field_value( + value, + inits->h_init.integer, + SCL_HORZ_FILTER_INIT, + SCL_H_INIT_INT); + set_reg_field_value( + value, + inits->h_init.fraction, + SCL_HORZ_FILTER_INIT, + SCL_H_INIT_FRAC); + dm_write_reg(xfm80->base.ctx, addr, value); + + addr = SCL_REG(mmSCL_VERT_FILTER_INIT); + value = 0; + set_reg_field_value( + value, + inits->v_init.integer, + SCL_VERT_FILTER_INIT, + SCL_V_INIT_INT); + set_reg_field_value( + value, + inits->v_init.fraction, + SCL_VERT_FILTER_INIT, + SCL_V_INIT_FRAC); + dm_write_reg(xfm80->base.ctx, addr, value); + + if (inits->bottom_enable) { + addr = SCL_REG(mmSCL_VERT_FILTER_INIT_BOT); + value = 0; + set_reg_field_value( + value, + inits->v_init_bottom.integer, + SCL_VERT_FILTER_INIT_BOT, + SCL_V_INIT_INT_BOT); + set_reg_field_value( + value, + inits->v_init_bottom.fraction, + SCL_VERT_FILTER_INIT_BOT, + SCL_V_INIT_FRAC_BOT); + dm_write_reg(xfm80->base.ctx, addr, value); + } + + addr = SCL_REG(mmSCL_AUTOMATIC_MODE_CONTROL); + value = 0; + set_reg_field_value( + value, + 0, + SCL_AUTOMATIC_MODE_CONTROL, + SCL_V_CALC_AUTO_RATIO_EN); + set_reg_field_value( + value, + 0, + SCL_AUTOMATIC_MODE_CONTROL, + SCL_H_CALC_AUTO_RATIO_EN); + dm_write_reg(xfm80->base.ctx, addr, value); +} + +static void get_viewport( + struct dce80_transform *xfm80, + struct rect *current_view_port) +{ + uint32_t value_start; + uint32_t value_size; + + if (current_view_port == NULL) + return; + + value_start = dm_read_reg(xfm80->base.ctx, SCL_REG(mmVIEWPORT_START)); + value_size = dm_read_reg(xfm80->base.ctx, SCL_REG(mmVIEWPORT_SIZE)); + + current_view_port->x = get_reg_field_value( + value_start, + VIEWPORT_START, + VIEWPORT_X_START); + current_view_port->y = get_reg_field_value( + value_start, + VIEWPORT_START, + VIEWPORT_Y_START); + current_view_port->height = get_reg_field_value( + value_size, + VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + current_view_port->width = get_reg_field_value( + value_size, + VIEWPORT_SIZE, + VIEWPORT_WIDTH); +} + + +bool dce80_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + bool is_scaling_required; + struct dc_context *ctx = xfm->ctx; + + { + uint32_t addr = SCL_REG(mmSCL_BYPASS_CONTROL); + uint32_t value = dm_read_reg(xfm->ctx, addr); + + set_reg_field_value( + value, + 0, + SCL_BYPASS_CONTROL, + SCL_BYPASS_MODE); + dm_write_reg(xfm->ctx, addr, value); + } + + disable_enhanced_sharpness(xfm80); + + /* 3. Program overscan */ + program_overscan(xfm80, &data->overscan); + + /* 4. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm80, data); + if (is_scaling_required) { + /* 5. Calculate and program ratio, filter initialization */ + struct scl_ratios_inits inits = { 0 }; + + calculate_inits(xfm80, data, &inits); + + program_scl_ratios_inits(xfm80, &inits); + + /* 6. Program vertical filters */ + if (data->taps.v_taps > 2) { + program_two_taps_filter(xfm80, false, true); + + if (!program_multi_taps_filter(xfm80, data, false)) { + dal_logger_write(ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed vertical taps programming\n"); + return false; + } + } else + program_two_taps_filter(xfm80, true, true); + + /* 7. Program horizontal filters */ + if (data->taps.h_taps > 2) { + program_two_taps_filter(xfm80, false, false); + + if (!program_multi_taps_filter(xfm80, data, true)) { + dal_logger_write(ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed horizontal taps programming\n"); + return false; + } + } else + program_two_taps_filter(xfm80, true, false); + } + + return true; +} + +void dce80_transform_set_scaler_bypass(struct transform *xfm) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + uint32_t sclv_mode; + + disable_enhanced_sharpness(xfm80); + + sclv_mode = dm_read_reg(xfm->ctx, SCL_REG(mmSCL_MODE)); + set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_MODE); + dm_write_reg(xfm->ctx, SCL_REG(mmSCL_MODE), sclv_mode); +} + +bool dce80_transform_update_viewport( + struct transform *xfm, + const struct rect *view_port, + bool is_fbc_attached) +{ + struct dce80_transform *xfm80 = TO_DCE80_TRANSFORM(xfm); + bool program_req = false; + struct rect current_view_port; + + if (view_port == NULL) + return program_req; + + get_viewport(xfm80, ¤t_view_port); + + if (current_view_port.x != view_port->x || + current_view_port.y != view_port->y || + current_view_port.height != view_port->height || + current_view_port.width != view_port->width) + program_req = true; + + if (program_req) { + /*underlay viewport is programmed with scaler + *program_viewport function pointer is not exposed*/ + program_viewport(xfm80, view_port); + } + + return program_req; +} + +void dce80_transform_set_scaler_filter( + struct transform *xfm, + struct scaler_filter *filter) +{ + xfm->filter = filter; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/Makefile b/drivers/gpu/drm/amd/dal/dc/gpio/Makefile index 2507bb5..a0d165c 100644 --- a/drivers/gpu/drm/amd/dal/dc/gpio/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/gpio/Makefile @@ -9,6 +9,18 @@ AMD_DAL_GPIO = $(addprefix $(AMDDALPATH)/dc/gpio/,$(GPIO)) AMD_DAL_FILES += $(AMD_DAL_GPIO) +############################################################################### +# DCE 8x +############################################################################### +# all DCE8.x are derived from DCE8.0 +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +GPIO_DCE80 = hw_translate_dce80.o hw_factory_dce80.o \ + hw_ddc_dce80.o hw_hpd_dce80.o + +AMD_DAL_GPIO_DCE80 = $(addprefix $(AMDDALPATH)/dc/gpio/dce80/,$(GPIO_DCE80)) + +AMD_DAL_FILES += $(AMD_DAL_GPIO_DCE80) +endif ############################################################################### # DCE 11x diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.c new file mode 100644 index 0000000..850caeb --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.c @@ -0,0 +1,893 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/gpio_types.h" +#include "../hw_gpio_pin.h" +#include "../hw_gpio.h" +#include "../hw_ddc.h" + +/* + * Header of this unit + */ + +#include "hw_ddc_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +/* + * This unit + */ + +#define FROM_HW_DDC(ptr) \ + container_of((ptr), struct hw_ddc_dce80, base) + +#define FROM_HW_GPIO(ptr) \ + FROM_HW_DDC(container_of((ptr), struct hw_ddc, base)) + +#define FROM_HW_GPIO_PIN(ptr) \ + FROM_HW_GPIO(container_of((ptr), struct hw_gpio, base)) + +static void destruct( + struct hw_ddc_dce80 *pin) +{ + dal_hw_ddc_destruct(&pin->base); +} + +static void destroy( + struct hw_gpio_pin **ptr) +{ + struct hw_ddc_dce80 *pin = FROM_HW_GPIO_PIN(*ptr); + + destruct(pin); + + dm_free((*ptr)->ctx, pin); + + *ptr = NULL; +} + +static void setup_i2c_polling( + struct dc_context *ctx, + const uint32_t addr, + bool enable_detect, + bool detect_mode) +{ + uint32_t value; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + enable_detect, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_ENABLE); + + set_reg_field_value( + value, + enable_detect, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_EDID_DETECT_ENABLE); + + if (enable_detect) + set_reg_field_value( + value, + detect_mode, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_EDID_DETECT_MODE); + + dm_write_reg(ctx, addr, value); +} + +static enum gpio_result set_config( + struct hw_gpio_pin *ptr, + const struct gpio_config_data *config_data) +{ + struct hw_ddc_dce80 *pin = FROM_HW_GPIO_PIN(ptr); + struct hw_gpio *hw_gpio = NULL; + uint32_t addr; + uint32_t regval; + uint32_t ddc_data_pd_en = 0; + uint32_t ddc_clk_pd_en = 0; + uint32_t aux_pad_mode = 0; + + hw_gpio = &pin->base.base; + + if (hw_gpio == NULL) { + ASSERT_CRITICAL(false); + return GPIO_RESULT_NULL_HANDLE; + } + + /* switch dual mode GPIO to I2C/AUX mode */ + + addr = hw_gpio->pin_reg.DC_GPIO_DATA_MASK.addr; + + regval = dm_read_reg(ptr->ctx, addr); + + ddc_data_pd_en = get_reg_field_value( + regval, + DC_GPIO_DDC1_MASK, + DC_GPIO_DDC1DATA_PD_EN); + + ddc_clk_pd_en = get_reg_field_value( + regval, + DC_GPIO_DDC1_MASK, + DC_GPIO_DDC1CLK_PD_EN); + + aux_pad_mode = get_reg_field_value( + regval, + DC_GPIO_DDC1_MASK, + AUX_PAD1_MODE); + + switch (config_data->config.ddc.type) { + case GPIO_DDC_CONFIG_TYPE_MODE_I2C: + /* On plug-in, there is a transient level on the pad + * which must be discharged through the internal pull-down. + * Enable internal pull-down, 2.5msec discharge time + * is required for detection of AUX mode */ + if (hw_gpio->base.en != GPIO_DDC_LINE_VIP_PAD) { + if (!ddc_data_pd_en || !ddc_clk_pd_en) { + set_reg_field_value( + regval, + 1, + DC_GPIO_DDC1_MASK, + DC_GPIO_DDC1DATA_PD_EN); + + set_reg_field_value( + regval, + 1, + DC_GPIO_DDC1_MASK, + DC_GPIO_DDC1CLK_PD_EN); + + dm_write_reg(ptr->ctx, addr, regval); + + if (config_data->type == + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) + /* should not affect normal I2C R/W */ + /* [anaumov] in DAL2, there was + * dc_service_delay_in_microseconds(2500); */ + dm_sleep_in_milliseconds(ptr->ctx, 3); + } + } else { + uint32_t reg2 = regval; + uint32_t sda_pd_dis = 0; + uint32_t scl_pd_dis = 0; + + sda_pd_dis = get_reg_field_value( + reg2, + DC_GPIO_I2CPAD_MASK, + DC_GPIO_SDA_PD_DIS); + + scl_pd_dis = get_reg_field_value( + reg2, + DC_GPIO_I2CPAD_MASK, + DC_GPIO_SCL_PD_DIS); + + if (sda_pd_dis) { + sda_pd_dis = 0; + + dm_write_reg(ptr->ctx, addr, reg2); + + if (config_data->type == + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) + /* should not affect normal I2C R/W */ + /* [anaumov] in DAL2, there was + * dc_service_delay_in_microseconds(2500); */ + dm_sleep_in_milliseconds(ptr->ctx, 3); + } + + if (!scl_pd_dis) { + scl_pd_dis = 1; + + dm_write_reg(ptr->ctx, addr, reg2); + + if (config_data->type == + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) + /* should not affect normal I2C R/W */ + /* [anaumov] in DAL2, there was + * dc_service_delay_in_microseconds(2500); */ + dm_sleep_in_milliseconds(ptr->ctx, 3); + } + } + + if (aux_pad_mode) { + /* let pins to get de-asserted + * before setting pad to I2C mode */ + if (config_data->config.ddc.data_en_bit_present || + config_data->config.ddc.clock_en_bit_present) + /* [anaumov] in DAL2, there was + * dc_service_delay_in_microseconds(2000); */ + dm_sleep_in_milliseconds(ptr->ctx, 2); + + /* set the I2C pad mode */ + /* read the register again, + * some bits may have been changed */ + regval = dm_read_reg(ptr->ctx, addr); + + set_reg_field_value( + regval, + 0, + DC_GPIO_DDC1_MASK, + AUX_PAD1_MODE); + + dm_write_reg(ptr->ctx, addr, regval); + } + + return GPIO_RESULT_OK; + case GPIO_DDC_CONFIG_TYPE_MODE_AUX: + /* set the AUX pad mode */ + if (!aux_pad_mode) { + set_reg_field_value( + regval, + 1, + DC_GPIO_DDC1_MASK, + AUX_PAD1_MODE); + + dm_write_reg(ptr->ctx, addr, regval); + } + + return GPIO_RESULT_OK; + case GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT: + if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && + (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { + setup_i2c_polling( + ptr->ctx, pin->addr.dc_i2c_ddc_setup, 1, 0); + return GPIO_RESULT_OK; + } + break; + case GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT: + if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && + (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { + setup_i2c_polling( + ptr->ctx, pin->addr.dc_i2c_ddc_setup, 1, 1); + return GPIO_RESULT_OK; + } + break; + case GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING: + if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && + (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { + setup_i2c_polling( + ptr->ctx, pin->addr.dc_i2c_ddc_setup, 0, 0); + return GPIO_RESULT_OK; + } + break; + } + + BREAK_TO_DEBUGGER(); + + return GPIO_RESULT_NON_SPECIFIC_ERROR; +} + +struct hw_ddc_dce80_init { + struct hw_gpio_pin_reg hw_gpio_data_reg; + struct hw_ddc_mask hw_ddc_mask; + struct hw_ddc_dce80_addr hw_ddc_dce80_addr; +}; + +static const struct hw_ddc_dce80_init + hw_ddc_dce80_init_data[GPIO_DDC_LINE_COUNT] = { + /* GPIO_DDC_LINE_DDC1 */ + { + { + { + mmDC_GPIO_DDC1_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_MASK_MASK + }, + { + mmDC_GPIO_DDC1_A, + DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK + }, + { + mmDC_GPIO_DDC1_EN, + DC_GPIO_DDC1_EN__DC_GPIO_DDC1DATA_EN_MASK + }, + { + mmDC_GPIO_DDC1_Y, + DC_GPIO_DDC1_Y__DC_GPIO_DDC1DATA_Y_MASK + } + }, + { + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_MASK_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_PD_EN_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1DATA_RECV_MASK, + DC_GPIO_DDC1_MASK__AUX_PAD1_MODE_MASK, + DC_GPIO_DDC1_MASK__AUX1_POL_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_STR_MASK + }, + { + mmDC_I2C_DDC1_SETUP + } + }, + /* GPIO_DDC_LINE_DDC2 */ + { + { + { + mmDC_GPIO_DDC2_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_MASK_MASK + }, + { + mmDC_GPIO_DDC2_A, + DC_GPIO_DDC2_A__DC_GPIO_DDC2DATA_A_MASK + }, + { + mmDC_GPIO_DDC2_EN, + DC_GPIO_DDC2_EN__DC_GPIO_DDC2DATA_EN_MASK + }, + { + mmDC_GPIO_DDC2_Y, + DC_GPIO_DDC2_Y__DC_GPIO_DDC2DATA_Y_MASK + } + }, + { + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_MASK_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_PD_EN_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2DATA_RECV_MASK, + DC_GPIO_DDC2_MASK__AUX_PAD2_MODE_MASK, + DC_GPIO_DDC2_MASK__AUX2_POL_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_STR_MASK + }, + { + mmDC_I2C_DDC2_SETUP + } + }, + /* GPIO_DDC_LINE_DDC3 */ + { + { + { + mmDC_GPIO_DDC3_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_MASK_MASK + }, + { + mmDC_GPIO_DDC3_A, + DC_GPIO_DDC3_A__DC_GPIO_DDC3DATA_A_MASK + }, + { + mmDC_GPIO_DDC3_EN, + DC_GPIO_DDC3_EN__DC_GPIO_DDC3DATA_EN_MASK + }, + { + mmDC_GPIO_DDC3_Y, + DC_GPIO_DDC3_Y__DC_GPIO_DDC3DATA_Y_MASK + } + }, + { + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_MASK_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_PD_EN_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3DATA_RECV_MASK, + DC_GPIO_DDC3_MASK__AUX_PAD3_MODE_MASK, + DC_GPIO_DDC3_MASK__AUX3_POL_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_STR_MASK + }, + { + mmDC_I2C_DDC3_SETUP + } + }, + /* GPIO_DDC_LINE_DDC4 */ + { + { + { + mmDC_GPIO_DDC4_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_MASK_MASK + }, + { + mmDC_GPIO_DDC4_A, + DC_GPIO_DDC4_A__DC_GPIO_DDC4DATA_A_MASK + }, + { + mmDC_GPIO_DDC4_EN, + DC_GPIO_DDC4_EN__DC_GPIO_DDC4DATA_EN_MASK + }, + { + mmDC_GPIO_DDC4_Y, + DC_GPIO_DDC4_Y__DC_GPIO_DDC4DATA_Y_MASK + } + }, + { + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_MASK_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_PD_EN_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4DATA_RECV_MASK, + DC_GPIO_DDC4_MASK__AUX_PAD4_MODE_MASK, + DC_GPIO_DDC4_MASK__AUX4_POL_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_STR_MASK + }, + { + mmDC_I2C_DDC4_SETUP + } + }, + /* GPIO_DDC_LINE_DDC5 */ + { + { + { + mmDC_GPIO_DDC5_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_MASK_MASK + }, + { + mmDC_GPIO_DDC5_A, + DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK + }, + { + mmDC_GPIO_DDC5_EN, + DC_GPIO_DDC5_EN__DC_GPIO_DDC5DATA_EN_MASK + }, + { + mmDC_GPIO_DDC5_Y, + DC_GPIO_DDC5_Y__DC_GPIO_DDC5DATA_Y_MASK + } + }, + { + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_MASK_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_PD_EN_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5DATA_RECV_MASK, + DC_GPIO_DDC5_MASK__AUX_PAD5_MODE_MASK, + DC_GPIO_DDC5_MASK__AUX5_POL_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_STR_MASK + }, + { + mmDC_I2C_DDC5_SETUP + } + }, + /* GPIO_DDC_LINE_DDC6 */ + { + { + { + mmDC_GPIO_DDC6_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_MASK_MASK + }, + { + mmDC_GPIO_DDC6_A, + DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK + }, + { + mmDC_GPIO_DDC6_EN, + DC_GPIO_DDC6_EN__DC_GPIO_DDC6DATA_EN_MASK + }, + { + mmDC_GPIO_DDC6_Y, + DC_GPIO_DDC6_Y__DC_GPIO_DDC6DATA_Y_MASK + } + }, + { + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_MASK_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_PD_EN_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6DATA_RECV_MASK, + DC_GPIO_DDC6_MASK__AUX_PAD6_MODE_MASK, + DC_GPIO_DDC6_MASK__AUX6_POL_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_STR_MASK + }, + { + mmDC_I2C_DDC6_SETUP + } + }, + /* GPIO_DDC_LINE_DDC_VGA */ + { + { + { + mmDC_GPIO_DDCVGA_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_MASK_MASK + }, + { + mmDC_GPIO_DDCVGA_A, + DC_GPIO_DDCVGA_A__DC_GPIO_DDCVGADATA_A_MASK + }, + { + mmDC_GPIO_DDCVGA_EN, + DC_GPIO_DDCVGA_EN__DC_GPIO_DDCVGADATA_EN_MASK + }, + { + mmDC_GPIO_DDCVGA_Y, + DC_GPIO_DDCVGA_Y__DC_GPIO_DDCVGADATA_Y_MASK + } + }, + { + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_MASK_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_PD_EN_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_RECV_MASK, + DC_GPIO_DDCVGA_MASK__AUX_PADVGA_MODE_MASK, + DC_GPIO_DDCVGA_MASK__AUXVGA_POL_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_STR_MASK + }, + { + mmDC_I2C_DDCVGA_SETUP + } + }, + /* GPIO_DDC_LINE_I2CPAD */ + { + { + { + mmDC_GPIO_I2CPAD_MASK, + DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_MASK_MASK + }, + { + mmDC_GPIO_I2CPAD_A, + DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK + }, + { + mmDC_GPIO_I2CPAD_EN, + DC_GPIO_I2CPAD_EN__DC_GPIO_SDA_EN_MASK + }, + { + mmDC_GPIO_I2CPAD_Y, + DC_GPIO_I2CPAD_Y__DC_GPIO_SDA_Y_MASK + } + }, + { + DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_MASK_MASK, + DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_PD_DIS_MASK, + DC_GPIO_I2CPAD_MASK__DC_GPIO_SDA_RECV_MASK, + 0, + 0, + 0 + }, + { + 0 + } + } +}; + +static const struct hw_ddc_dce80_init + hw_ddc_dce80_init_clock[GPIO_DDC_LINE_COUNT] = { + /* GPIO_DDC_LINE_DDC1 */ + { + { + { + mmDC_GPIO_DDC1_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_MASK_MASK + }, + { + mmDC_GPIO_DDC1_A, + DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK + }, + { + mmDC_GPIO_DDC1_EN, + DC_GPIO_DDC1_EN__DC_GPIO_DDC1CLK_EN_MASK + }, + { + mmDC_GPIO_DDC1_Y, + DC_GPIO_DDC1_Y__DC_GPIO_DDC1CLK_Y_MASK + } + }, + { + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_MASK_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_PD_EN_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_RECV_MASK, + DC_GPIO_DDC1_MASK__AUX_PAD1_MODE_MASK, + DC_GPIO_DDC1_MASK__AUX1_POL_MASK, + DC_GPIO_DDC1_MASK__DC_GPIO_DDC1CLK_STR_MASK + }, + { + mmDC_I2C_DDC1_SETUP + } + }, + /* GPIO_DDC_LINE_DDC2 */ + { + { + { + mmDC_GPIO_DDC2_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_MASK_MASK + }, + { + mmDC_GPIO_DDC2_A, + DC_GPIO_DDC2_A__DC_GPIO_DDC2CLK_A_MASK + }, + { + mmDC_GPIO_DDC2_EN, + DC_GPIO_DDC2_EN__DC_GPIO_DDC2CLK_EN_MASK + }, + { + mmDC_GPIO_DDC2_Y, + DC_GPIO_DDC2_Y__DC_GPIO_DDC2CLK_Y_MASK + } + }, + { + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_MASK_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_PD_EN_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_RECV_MASK, + DC_GPIO_DDC2_MASK__AUX_PAD2_MODE_MASK, + DC_GPIO_DDC2_MASK__AUX2_POL_MASK, + DC_GPIO_DDC2_MASK__DC_GPIO_DDC2CLK_STR_MASK + }, + { + mmDC_I2C_DDC2_SETUP + } + }, + /* GPIO_DDC_LINE_DDC3 */ + { + { + { + mmDC_GPIO_DDC3_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_MASK_MASK + }, + { + mmDC_GPIO_DDC3_A, + DC_GPIO_DDC3_A__DC_GPIO_DDC3CLK_A_MASK + }, + { + mmDC_GPIO_DDC3_EN, + DC_GPIO_DDC3_EN__DC_GPIO_DDC3CLK_EN_MASK + }, + { + mmDC_GPIO_DDC3_Y, + DC_GPIO_DDC3_Y__DC_GPIO_DDC3CLK_Y_MASK + } + }, + { + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_MASK_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_PD_EN_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_RECV_MASK, + DC_GPIO_DDC3_MASK__AUX_PAD3_MODE_MASK, + DC_GPIO_DDC3_MASK__AUX3_POL_MASK, + DC_GPIO_DDC3_MASK__DC_GPIO_DDC3CLK_STR_MASK + }, + { + mmDC_I2C_DDC3_SETUP + } + }, + /* GPIO_DDC_LINE_DDC4 */ + { + { + { + mmDC_GPIO_DDC4_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_MASK_MASK + }, + { + mmDC_GPIO_DDC4_A, + DC_GPIO_DDC4_A__DC_GPIO_DDC4CLK_A_MASK + }, + { + mmDC_GPIO_DDC4_EN, + DC_GPIO_DDC4_EN__DC_GPIO_DDC4CLK_EN_MASK + }, + { + mmDC_GPIO_DDC4_Y, + DC_GPIO_DDC4_Y__DC_GPIO_DDC4CLK_Y_MASK + } + }, + { + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_MASK_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_PD_EN_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_RECV_MASK, + DC_GPIO_DDC4_MASK__AUX_PAD4_MODE_MASK, + DC_GPIO_DDC4_MASK__AUX4_POL_MASK, + DC_GPIO_DDC4_MASK__DC_GPIO_DDC4CLK_STR_MASK + }, + { + mmDC_I2C_DDC4_SETUP + } + }, + /* GPIO_DDC_LINE_DDC5 */ + { + { + { + mmDC_GPIO_DDC5_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_MASK_MASK + }, + { + mmDC_GPIO_DDC5_A, + DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK + }, + { + mmDC_GPIO_DDC5_EN, + DC_GPIO_DDC5_EN__DC_GPIO_DDC5CLK_EN_MASK + }, + { + mmDC_GPIO_DDC5_Y, + DC_GPIO_DDC5_Y__DC_GPIO_DDC5CLK_Y_MASK + } + }, + { + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_MASK_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_PD_EN_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_RECV_MASK, + DC_GPIO_DDC5_MASK__AUX_PAD5_MODE_MASK, + DC_GPIO_DDC5_MASK__AUX5_POL_MASK, + DC_GPIO_DDC5_MASK__DC_GPIO_DDC5CLK_STR_MASK + }, + { + mmDC_I2C_DDC5_SETUP + } + }, + /* GPIO_DDC_LINE_DDC6 */ + { + { + { + mmDC_GPIO_DDC6_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_MASK_MASK + }, + { + mmDC_GPIO_DDC6_A, + DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK + }, + { + mmDC_GPIO_DDC6_EN, + DC_GPIO_DDC6_EN__DC_GPIO_DDC6CLK_EN_MASK + }, + { + mmDC_GPIO_DDC6_Y, + DC_GPIO_DDC6_Y__DC_GPIO_DDC6CLK_Y_MASK + } + }, + { + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_MASK_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_PD_EN_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_RECV_MASK, + DC_GPIO_DDC6_MASK__AUX_PAD6_MODE_MASK, + DC_GPIO_DDC6_MASK__AUX6_POL_MASK, + DC_GPIO_DDC6_MASK__DC_GPIO_DDC6CLK_STR_MASK + }, + { + mmDC_I2C_DDC6_SETUP + } + }, + /* GPIO_DDC_LINE_DDC_VGA */ + { + { + { + mmDC_GPIO_DDCVGA_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_MASK_MASK + }, + { + mmDC_GPIO_DDCVGA_A, + DC_GPIO_DDCVGA_A__DC_GPIO_DDCVGACLK_A_MASK + }, + { + mmDC_GPIO_DDCVGA_EN, + DC_GPIO_DDCVGA_EN__DC_GPIO_DDCVGACLK_EN_MASK + }, + { + mmDC_GPIO_DDCVGA_Y, + DC_GPIO_DDCVGA_Y__DC_GPIO_DDCVGACLK_Y_MASK + } + }, + { + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_MASK_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGADATA_PD_EN_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_RECV_MASK, + DC_GPIO_DDCVGA_MASK__AUX_PADVGA_MODE_MASK, + DC_GPIO_DDCVGA_MASK__AUXVGA_POL_MASK, + DC_GPIO_DDCVGA_MASK__DC_GPIO_DDCVGACLK_STR_MASK + }, + { + mmDC_I2C_DDCVGA_SETUP + } + }, + /* GPIO_DDC_LINE_I2CPAD */ + { + { + { + mmDC_GPIO_I2CPAD_MASK, + DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_MASK_MASK + }, + { + mmDC_GPIO_I2CPAD_A, + DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK + }, + { + mmDC_GPIO_I2CPAD_EN, + DC_GPIO_I2CPAD_EN__DC_GPIO_SCL_EN_MASK + }, + { + mmDC_GPIO_I2CPAD_Y, + DC_GPIO_I2CPAD_Y__DC_GPIO_SCL_Y_MASK + } + }, + { + DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_MASK_MASK, + DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_PD_DIS_MASK, + DC_GPIO_I2CPAD_MASK__DC_GPIO_SCL_RECV_MASK, + 0, + 0, + 0 + }, + { + 0 + } + } +}; + +static const struct hw_gpio_pin_funcs funcs = { + .destroy = destroy, + .open = dal_hw_ddc_open, + .get_value = dal_hw_gpio_get_value, + .set_value = dal_hw_gpio_set_value, + .set_config = set_config, + .change_mode = dal_hw_gpio_change_mode, + .close = dal_hw_gpio_close, +}; + +static bool construct( + struct hw_ddc_dce80 *pin, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx) +{ + const struct hw_ddc_dce80_init *init; + + if ((en < GPIO_DDC_LINE_MIN) || (en > GPIO_DDC_LINE_MAX)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_hw_ddc_construct(&pin->base, id, en, ctx)) { + BREAK_TO_DEBUGGER(); + return false; + } + + pin->base.base.base.funcs = &funcs; + + switch (id) { + case GPIO_ID_DDC_DATA: + init = hw_ddc_dce80_init_data + en; + + pin->base.base.pin_reg = init->hw_gpio_data_reg; + pin->base.mask = init->hw_ddc_mask; + pin->addr = init->hw_ddc_dce80_addr; + + return true; + case GPIO_ID_DDC_CLOCK: + init = hw_ddc_dce80_init_clock + en; + + pin->base.base.pin_reg = init->hw_gpio_data_reg; + pin->base.mask = init->hw_ddc_mask; + pin->addr = init->hw_ddc_dce80_addr; + + return true; + default: + BREAK_TO_DEBUGGER(); + } + + dal_hw_ddc_destruct(&pin->base); + + return false; +} + +struct hw_gpio_pin *dal_hw_ddc_dce80_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en) +{ + struct hw_ddc_dce80 *pin = dm_alloc(ctx, sizeof(struct hw_ddc_dce80)); + + if (!pin) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(pin, id, en, ctx)) + return &pin->base.base.base; + + BREAK_TO_DEBUGGER(); + + dm_free(ctx, pin); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.h new file mode 100644 index 0000000..f5bbb83 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_ddc_dce80.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_DDC_DCE80_H__ +#define __DAL_HW_DDC_DCE80_H__ + +struct hw_ddc_dce80_addr { + uint32_t dc_i2c_ddc_setup; +}; + +struct hw_ddc_dce80 { + struct hw_ddc base; + struct hw_ddc_dce80_addr addr; +}; + +struct hw_gpio_pin *dal_hw_ddc_dce80_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + +#define DDC_DCE80_FROM_BASE(ddc_base) \ + container_of(HW_DDC_FROM_BASE(ddc_base), struct hw_ddc_dce80, base) + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.c new file mode 100644 index 0000000..d1ea56d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.c @@ -0,0 +1,78 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + +/* + * Header of this unit + */ + +#include "hw_factory_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "../hw_gpio_pin.h" +#include "../hw_gpio.h" +#include "../hw_gpio_pad.h" +#include "../hw_ddc.h" +#include "hw_ddc_dce80.h" +#include "../hw_hpd.h" +#include "hw_hpd_dce80.h" + +/* + * This unit + */ +static const struct hw_factory_funcs funcs = { + .create_ddc_data = dal_hw_ddc_dce80_create, + .create_ddc_clock = dal_hw_ddc_dce80_create, + .create_generic = NULL, + .create_hpd = dal_hw_hpd_dce80_create, + .create_gpio_pad = NULL, + .create_sync = NULL, + .create_gsl = NULL, +}; + +void dal_hw_factory_dce80_init( + struct hw_factory *factory) +{ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; + factory->number_of_pins[GPIO_ID_GENERIC] = 7; + factory->number_of_pins[GPIO_ID_HPD] = 6; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 2; + factory->number_of_pins[GPIO_ID_GSL] = 4; + + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.h new file mode 100644 index 0000000..e78a8b3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_factory_dce80.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_DCE80_H__ +#define __DAL_HW_FACTORY_DCE80_H__ + +void dal_hw_factory_dce80_init( + struct hw_factory *factory); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.c new file mode 100644 index 0000000..67b249b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.c @@ -0,0 +1,378 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/gpio_types.h" +#include "../hw_gpio_pin.h" +#include "../hw_gpio.h" +#include "../hw_hpd.h" + +/* + * Header of this unit + */ + +#include "hw_hpd_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + + +/* + * This unit + */ + +#define FROM_HW_HPD(ptr) \ + container_of((ptr), struct hw_hpd_dce80, base) + +#define FROM_HW_GPIO(ptr) \ + FROM_HW_HPD(container_of((ptr), struct hw_hpd, base)) + +#define FROM_HW_GPIO_PIN(ptr) \ + FROM_HW_GPIO(container_of((ptr), struct hw_gpio, base)) + +static void destruct( + struct hw_hpd_dce80 *pin) +{ + dal_hw_hpd_destruct(&pin->base); +} + +static void destroy( + struct hw_gpio_pin **ptr) +{ + struct hw_hpd_dce80 *pin = FROM_HW_GPIO_PIN(*ptr); + + destruct(pin); + + dm_free((*ptr)->ctx, pin); + + *ptr = NULL; +} + +static enum gpio_result get_value( + const struct hw_gpio_pin *ptr, + uint32_t *value) +{ + struct hw_hpd_dce80 *pin = FROM_HW_GPIO_PIN(ptr); + + /* in Interrupt mode we ask for SENSE bit */ + + if (ptr->mode == GPIO_MODE_INTERRUPT) { + uint32_t regval; + uint32_t hpd_delayed = 0; + uint32_t hpd_sense = 0; + + regval = dm_read_reg( + ptr->ctx, + pin->addr.DC_HPD_INT_STATUS); + + hpd_delayed = get_reg_field_value( + regval, + DC_HPD1_INT_STATUS, + DC_HPD1_SENSE_DELAYED); + + hpd_sense = get_reg_field_value( + regval, + DC_HPD1_INT_STATUS, + DC_HPD1_SENSE); + + *value = hpd_delayed; + return GPIO_RESULT_OK; + } + + /* in any other modes, operate as normal GPIO */ + + return dal_hw_gpio_get_value(ptr, value); +} + +static enum gpio_result set_config( + struct hw_gpio_pin *ptr, + const struct gpio_config_data *config_data) +{ + struct hw_hpd_dce80 *pin = FROM_HW_GPIO_PIN(ptr); + + if (!config_data) + return GPIO_RESULT_INVALID_DATA; + + { + uint32_t value; + + value = dm_read_reg( + ptr->ctx, + pin->addr.DC_HPD_TOGGLE_FILT_CNTL); + + set_reg_field_value( + value, + config_data->config.hpd.delay_on_connect / 10, + DC_HPD1_TOGGLE_FILT_CNTL, + DC_HPD1_CONNECT_INT_DELAY); + + set_reg_field_value( + value, + config_data->config.hpd.delay_on_disconnect / 10, + DC_HPD1_TOGGLE_FILT_CNTL, + DC_HPD1_DISCONNECT_INT_DELAY); + + dm_write_reg( + ptr->ctx, + pin->addr.DC_HPD_TOGGLE_FILT_CNTL, + value); + + } + + return GPIO_RESULT_OK; +} + +struct hw_gpio_generic_dce80_init { + struct hw_gpio_pin_reg hw_gpio_data_reg; + struct hw_hpd_dce80_addr addr; +}; + +static const struct hw_gpio_generic_dce80_init + hw_gpio_generic_dce80_init[GPIO_HPD_COUNT] = { + /* GPIO_HPD_1 */ + { + { + { + mmDC_GPIO_HPD_MASK, + DC_GPIO_HPD_MASK__DC_GPIO_HPD1_MASK_MASK + }, + { + mmDC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK + }, + { + mmDC_GPIO_HPD_EN, + DC_GPIO_HPD_EN__DC_GPIO_HPD1_EN_MASK + }, + { + mmDC_GPIO_HPD_Y, + DC_GPIO_HPD_Y__DC_GPIO_HPD1_Y_MASK + } + }, + { + mmDC_HPD1_INT_STATUS, + mmDC_HPD1_TOGGLE_FILT_CNTL + } + }, + /* GPIO_HPD_2 */ + { + { + { + mmDC_GPIO_HPD_MASK, + DC_GPIO_HPD_MASK__DC_GPIO_HPD2_MASK_MASK + }, + { + mmDC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK + }, + { + mmDC_GPIO_HPD_EN, + DC_GPIO_HPD_EN__DC_GPIO_HPD2_EN_MASK + }, + { + mmDC_GPIO_HPD_Y, + DC_GPIO_HPD_Y__DC_GPIO_HPD2_Y_MASK + } + }, + { + mmDC_HPD2_INT_STATUS, + mmDC_HPD2_TOGGLE_FILT_CNTL + } + }, + /* GPIO_HPD_3 */ + { + { + { + mmDC_GPIO_HPD_MASK, + DC_GPIO_HPD_MASK__DC_GPIO_HPD3_MASK_MASK + }, + { + mmDC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK + }, + { + mmDC_GPIO_HPD_EN, + DC_GPIO_HPD_EN__DC_GPIO_HPD3_EN_MASK + }, + { + mmDC_GPIO_HPD_Y, + DC_GPIO_HPD_Y__DC_GPIO_HPD3_Y_MASK + } + }, + { + mmDC_HPD3_INT_STATUS, + mmDC_HPD3_TOGGLE_FILT_CNTL + } + }, + /* GPIO_HPD_4 */ + { + { + { + mmDC_GPIO_HPD_MASK, + DC_GPIO_HPD_MASK__DC_GPIO_HPD4_MASK_MASK + }, + { + mmDC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK + }, + { + mmDC_GPIO_HPD_EN, + DC_GPIO_HPD_EN__DC_GPIO_HPD4_EN_MASK + }, + { + mmDC_GPIO_HPD_Y, + DC_GPIO_HPD_Y__DC_GPIO_HPD4_Y_MASK + } + }, + { + mmDC_HPD4_INT_STATUS, + mmDC_HPD4_TOGGLE_FILT_CNTL + } + }, + /* GPIO_HPD_5 */ + { + { + { + mmDC_GPIO_HPD_MASK, + DC_GPIO_HPD_MASK__DC_GPIO_HPD5_MASK_MASK + }, + { + mmDC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK + }, + { + mmDC_GPIO_HPD_EN, + DC_GPIO_HPD_EN__DC_GPIO_HPD5_EN_MASK + }, + { + mmDC_GPIO_HPD_Y, + DC_GPIO_HPD_Y__DC_GPIO_HPD5_Y_MASK + } + }, + { + mmDC_HPD5_INT_STATUS, + mmDC_HPD5_TOGGLE_FILT_CNTL + } + }, + /* GPIO_HPD_1 */ + { + { + { + mmDC_GPIO_HPD_MASK, + DC_GPIO_HPD_MASK__DC_GPIO_HPD6_MASK_MASK + }, + { + mmDC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK + }, + { + mmDC_GPIO_HPD_EN, + DC_GPIO_HPD_EN__DC_GPIO_HPD6_EN_MASK + }, + { + mmDC_GPIO_HPD_Y, + DC_GPIO_HPD_Y__DC_GPIO_HPD6_Y_MASK + } + }, + { + mmDC_HPD6_INT_STATUS, + mmDC_HPD6_TOGGLE_FILT_CNTL + } + } +}; + +static const struct hw_gpio_pin_funcs funcs = { + .destroy = destroy, + .open = dal_hw_gpio_open, + .get_value = get_value, + .set_value = dal_hw_gpio_set_value, + .set_config = set_config, + .change_mode = dal_hw_gpio_change_mode, + .close = dal_hw_gpio_close, +}; + +static bool construct( + struct hw_hpd_dce80 *pin, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx) +{ + const struct hw_gpio_generic_dce80_init *init; + + if (id != GPIO_ID_HPD) { + BREAK_TO_DEBUGGER(); + return false; + } + + if ((en < GPIO_HPD_MIN) || (en > GPIO_HPD_MAX)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_hw_hpd_construct(&pin->base, id, en, ctx)) { + BREAK_TO_DEBUGGER(); + return false; + } + + pin->base.base.base.funcs = &funcs; + + init = hw_gpio_generic_dce80_init + en; + + pin->base.base.pin_reg = init->hw_gpio_data_reg; + + pin->addr = init->addr; + + return true; +} + +struct hw_gpio_pin *dal_hw_hpd_dce80_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en) +{ + struct hw_hpd_dce80 *pin = dm_alloc(ctx, sizeof(struct hw_hpd_dce80)); + + if (!pin) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(pin, id, en, ctx)) + return &pin->base.base.base; + + BREAK_TO_DEBUGGER(); + + dm_free(ctx, pin); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.h new file mode 100644 index 0000000..d74dbec --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_hpd_dce80.h @@ -0,0 +1,44 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_HPD_DCE80_H__ +#define __DAL_HW_HPD_DCE80_H__ + +struct hw_hpd_dce80_addr { + uint32_t DC_HPD_INT_STATUS; + uint32_t DC_HPD_TOGGLE_FILT_CNTL; +}; + +struct hw_hpd_dce80 { + struct hw_hpd base; + struct hw_hpd_dce80_addr addr; +}; + +struct hw_gpio_pin *dal_hw_hpd_dce80_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.c b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.c new file mode 100644 index 0000000..9788106 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.c @@ -0,0 +1,424 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/gpio_types.h" +#include "../hw_translate.h" + +/* + * Header of this unit + */ + +#include "hw_translate_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +#include "../hw_gpio_pin.h" +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +#include "smu/smu_7_0_1_d.h" + +/* + * @brief + * Returns index of first bit (starting with LSB) which is set + */ +static uint32_t index_from_vector( + uint32_t vector) +{ + uint32_t result = 0; + uint32_t mask = 1; + + do { + if (vector == mask) + return result; + + ++result; + mask <<= 1; + } while (mask); + + BREAK_TO_DEBUGGER(); + + return GPIO_ENUM_UNKNOWN; +} + +static bool offset_to_id( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + switch (offset) { + /* GENERIC */ + case mmDC_GPIO_GENERIC_A: + *id = GPIO_ID_GENERIC; + switch (mask) { + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: + *en = GPIO_GENERIC_A; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: + *en = GPIO_GENERIC_B; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: + *en = GPIO_GENERIC_C; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: + *en = GPIO_GENERIC_D; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: + *en = GPIO_GENERIC_E; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: + *en = GPIO_GENERIC_F; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: + *en = GPIO_GENERIC_G; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* HPD */ + case mmDC_GPIO_HPD_A: + *id = GPIO_ID_HPD; + switch (mask) { + case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: + *en = GPIO_HPD_1; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: + *en = GPIO_HPD_2; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: + *en = GPIO_HPD_3; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: + *en = GPIO_HPD_4; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: + *en = GPIO_HPD_5; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: + *en = GPIO_HPD_6; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* SYNCA */ + case mmDC_GPIO_SYNCA_A: + *id = GPIO_ID_SYNC; + switch (mask) { + case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: + *en = GPIO_SYNC_HSYNC_A; + return true; + case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: + *en = GPIO_SYNC_VSYNC_A; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* mmDC_GPIO_GENLK_MASK */ + case mmDC_GPIO_GENLK_A: + *id = GPIO_ID_GSL; + switch (mask) { + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: + *en = GPIO_GSL_GENLOCK_CLOCK; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: + *en = GPIO_GSL_GENLOCK_VSYNC; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: + *en = GPIO_GSL_SWAPLOCK_A; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: + *en = GPIO_GSL_SWAPLOCK_B; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* GPIOPAD */ + case mmGPIOPAD_A: + *id = GPIO_ID_GPIO_PAD; + *en = index_from_vector(mask); + return (*en <= GPIO_GPIO_PAD_MAX); + /* DDC */ + /* we don't care about the GPIO_ID for DDC + * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method */ + case mmDC_GPIO_DDC1_A: + *en = GPIO_DDC_LINE_DDC1; + return true; + case mmDC_GPIO_DDC2_A: + *en = GPIO_DDC_LINE_DDC2; + return true; + case mmDC_GPIO_DDC3_A: + *en = GPIO_DDC_LINE_DDC3; + return true; + case mmDC_GPIO_DDC4_A: + *en = GPIO_DDC_LINE_DDC4; + return true; + case mmDC_GPIO_DDC5_A: + *en = GPIO_DDC_LINE_DDC5; + return true; + case mmDC_GPIO_DDC6_A: + *en = GPIO_DDC_LINE_DDC6; + return true; + case mmDC_GPIO_DDCVGA_A: + *en = GPIO_DDC_LINE_DDC_VGA; + return true; + /* GPIO_I2CPAD */ + case mmDC_GPIO_I2CPAD_A: + *en = GPIO_DDC_LINE_I2C_PAD; + return true; + /* Not implemented */ + case mmDC_GPIO_PWRSEQ_A: + case mmDC_GPIO_PAD_STRENGTH_1: + case mmDC_GPIO_PAD_STRENGTH_2: + case mmDC_GPIO_DEBUG: + return false; + /* UNEXPECTED */ + default: + BREAK_TO_DEBUGGER(); + return false; + } +} + +static bool id_to_offset( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + bool result = true; + + switch (id) { + case GPIO_ID_DDC_DATA: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = mmDC_GPIO_DDC1_A; + break; + case GPIO_DDC_LINE_DDC2: + info->offset = mmDC_GPIO_DDC2_A; + break; + case GPIO_DDC_LINE_DDC3: + info->offset = mmDC_GPIO_DDC3_A; + break; + case GPIO_DDC_LINE_DDC4: + info->offset = mmDC_GPIO_DDC4_A; + break; + case GPIO_DDC_LINE_DDC5: + info->offset = mmDC_GPIO_DDC5_A; + break; + case GPIO_DDC_LINE_DDC6: + info->offset = mmDC_GPIO_DDC6_A; + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = mmDC_GPIO_DDCVGA_A; + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = mmDC_GPIO_I2CPAD_A; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_DDC_CLOCK: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = mmDC_GPIO_DDC1_A; + break; + case GPIO_DDC_LINE_DDC2: + info->offset = mmDC_GPIO_DDC2_A; + break; + case GPIO_DDC_LINE_DDC3: + info->offset = mmDC_GPIO_DDC3_A; + break; + case GPIO_DDC_LINE_DDC4: + info->offset = mmDC_GPIO_DDC4_A; + break; + case GPIO_DDC_LINE_DDC5: + info->offset = mmDC_GPIO_DDC5_A; + break; + case GPIO_DDC_LINE_DDC6: + info->offset = mmDC_GPIO_DDC6_A; + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = mmDC_GPIO_DDCVGA_A; + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = mmDC_GPIO_I2CPAD_A; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_GENERIC: + info->offset = mmDC_GPIO_GENERIC_A; + switch (en) { + case GPIO_GENERIC_A: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; + break; + case GPIO_GENERIC_B: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; + break; + case GPIO_GENERIC_C: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; + break; + case GPIO_GENERIC_D: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; + break; + case GPIO_GENERIC_E: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; + break; + case GPIO_GENERIC_F: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; + break; + case GPIO_GENERIC_G: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_HPD: + info->offset = mmDC_GPIO_HPD_A; + switch (en) { + case GPIO_HPD_1: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; + break; + case GPIO_HPD_2: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; + break; + case GPIO_HPD_3: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; + break; + case GPIO_HPD_4: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; + break; + case GPIO_HPD_5: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; + break; + case GPIO_HPD_6: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_SYNC: + switch (en) { + case GPIO_SYNC_HSYNC_A: + info->offset = mmDC_GPIO_SYNCA_A; + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; + break; + case GPIO_SYNC_VSYNC_A: + info->offset = mmDC_GPIO_SYNCA_A; + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; + break; + case GPIO_SYNC_HSYNC_B: + case GPIO_SYNC_VSYNC_B: + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_GSL: + switch (en) { + case GPIO_GSL_GENLOCK_CLOCK: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; + break; + case GPIO_GSL_GENLOCK_VSYNC: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_A: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_B: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_GPIO_PAD: + info->offset = mmGPIOPAD_A; + info->mask = (1 << en); + result = (info->mask <= GPIO_GPIO_PAD_MAX); + break; + case GPIO_ID_VIP_PAD: + default: + BREAK_TO_DEBUGGER(); + result = false; + } + + if (result) { + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask_y = info->mask; + info->mask_en = info->mask; + info->mask_mask = info->mask; + } + + return result; +} + +static const struct hw_translate_funcs funcs = { + .offset_to_id = offset_to_id, + .id_to_offset = id_to_offset, +}; + +void dal_hw_translate_dce80_init( + struct hw_translate *translate) +{ + translate->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.h b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.h new file mode 100644 index 0000000..374f2f3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpio/dce80/hw_translate_dce80.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_DCE80_H__ +#define __DAL_HW_TRANSLATE_DCE80_H__ + +void dal_hw_translate_dce80_init( + struct hw_translate *tr); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c index e0f6ecf..9c8ff54 100644 --- a/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c +++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_factory.c @@ -40,6 +40,10 @@ * Post-requisites: headers required by this unit */ +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/hw_factory_dce80.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) #include "dce110/hw_factory_dce110.h" #endif @@ -61,6 +65,11 @@ bool dal_hw_factory_init( } switch (dce_version) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + dal_hw_factory_dce80_init(factory); + return true; +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: 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 215322e..d3c6bc8 100644 --- a/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c +++ b/drivers/gpu/drm/amd/dal/dc/gpio/hw_translate.c @@ -40,6 +40,10 @@ * Post-requisites: headers required by this unit */ +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/hw_translate_dce80.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) #include "dce110/hw_translate_dce110.h" #endif @@ -61,7 +65,11 @@ bool dal_hw_translate_init( } switch (dce_version) { - +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + dal_hw_translate_dce80_init(translate); + return true; +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/Makefile b/drivers/gpu/drm/amd/dal/dc/gpu/Makefile index b481a6d..cb23508 100644 --- a/drivers/gpu/drm/amd/dal/dc/gpu/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/gpu/Makefile @@ -9,6 +9,18 @@ AMD_DAL_GPU = $(addprefix $(AMDDALPATH)/dc/gpu/,$(GPU)) AMD_DAL_FILES += $(AMD_DAL_GPU) +############################################################################### +# DCE 80 family +############################################################################### + +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +GPU_DCE80 = display_clock_dce80.o dc_clock_gating_dce80.o + +AMD_DAL_GPU_DCE80 = $(addprefix $(AMDDALPATH)/dc/gpu/dce80/,$(GPU_DCE80)) + +AMD_DAL_FILES += $(AMD_DAL_GPU_DCE80) +endif + ############################################################################### # DCE 110 family diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.c new file mode 100644 index 0000000..5f57577 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.c @@ -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 + * + */ + +#include "dm_services.h" +#include "dc_clock_gating_dce80.h" + +static void enable_hw_base_light_sleep(void) +{ + /* TODO: implement */ +} + +static void disable_sw_manual_control_light_sleep(void) +{ + /* TODO: implement */ +} + +static void enable_sw_manual_control_light_sleep(void) +{ + /* TODO: implement */ +} + +void dal_dc_clock_gating_dce80_power_up(struct dc_context *ctx, bool enable) +{ + if (enable) { + enable_hw_base_light_sleep(); + disable_sw_manual_control_light_sleep(); + } else { + enable_sw_manual_control_light_sleep(); + } +} diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.h new file mode 100644 index 0000000..f4111c5 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/dc_clock_gating_dce80.h @@ -0,0 +1,31 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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_DCE80_H__ +#define __DAL_DC_CLOCK_GATING_DCE80_H__ + +void dal_dc_clock_gating_dce80_power_up(struct dc_context *ctx, bool enable); + +#endif /* __DAL_DC_CLOCK_GATING_DCE80_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.c b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.c new file mode 100644 index 0000000..760705f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.c @@ -0,0 +1,925 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "include/adapter_service_interface.h" +#include "include/bios_parser_interface.h" +#include "include/fixed32_32.h" +#include "include/logger_interface.h" + +#include "../divider_range.h" +#include "display_clock_dce80.h" + +#define DCE80_DFS_BYPASS_THRESHOLD_KHZ 100000 + +/* Max clock values for each state indexed by "enum clocks_state": */ +static struct state_dependent_clocks max_clks_by_state[] = { +/* ClocksStateInvalid - should not be used */ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/* ClocksStateUltraLow - not expected to be used for DCE 8.0 */ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/* ClocksStateLow */ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000}, +/* ClocksStateNominal */ +{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, +/* ClocksStatePerformance */ +{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; + + +/* 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.*/ +}; + +/* 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 */ +}; + +/* 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*/ +}; + +static struct divider_range divider_ranges[DIVIDER_RANGE_MAX]; + +#define FROM_DISPLAY_CLOCK(base) \ + container_of(base, struct display_clock_dce80, disp_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 uint32_t get_scaler_efficiency(struct min_clock_params *params) +{ + uint32_t scaler_efficiency = 3; + + switch (params->scaler_efficiency) { + case V_SCALER_EFFICIENCY_LB18BPP: + case V_SCALER_EFFICIENCY_LB24BPP: + scaler_efficiency = 4; + break; + + case V_SCALER_EFFICIENCY_LB30BPP: + case V_SCALER_EFFICIENCY_LB36BPP: + scaler_efficiency = 3; + break; + + default: + break; + } + + return scaler_efficiency; +} + +static uint32_t get_actual_required_display_clk( + struct display_clock_dce80 *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(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 get_validation_clock(struct display_clock *dc) +{ + uint32_t clk = 0; + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(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*/ + BREAK_TO_DEBUGGER(); + /* 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 uint32_t calc_single_display_min_clks( + struct display_clock *base, + struct min_clock_params *params, + bool set_clk) +{ + struct fixed32_32 h_scale = dal_fixed32_32_from_int(1); + struct fixed32_32 v_scale = dal_fixed32_32_from_int(1); + uint32_t pix_clk_khz = params->requested_pixel_clock; + uint32_t line_total = params->timing_info.h_total; + uint32_t max_clk_khz = get_validation_clock(base); + struct fixed32_32 deep_color_factor = get_deep_color_factor(params); + uint32_t scaler_efficiency = get_scaler_efficiency(params); + struct fixed32_32 v_filter_init; + uint32_t v_filter_init_trunc; + struct fixed32_32 v_filter_init_ceil; + struct fixed32_32 src_lines_per_dst_line; + uint32_t src_wdth_rnd_to_chunks; + struct fixed32_32 scaling_coeff; + struct fixed32_32 fx_disp_clk_khz; + struct fixed32_32 fx_alt_disp_clk_khz; + uint32_t disp_clk_khz; + uint32_t alt_disp_clk_khz; + struct display_clock_dce80 *dc = FROM_DISPLAY_CLOCK(base); + + + if (0 != params->dest_view.height && 0 != params->dest_view.width) { + + h_scale = dal_fixed32_32_from_fraction( + params->source_view.width, + params->dest_view.width); + v_scale = dal_fixed32_32_from_fraction( + params->source_view.height, + params->dest_view.height); + } + + v_filter_init = dal_fixed32_32_from_fraction( + params->scaling_info.v_taps, 2u); + v_filter_init = dal_fixed32_32_add(v_filter_init, + dal_fixed32_32_div_int(v_scale, 2)); + v_filter_init = dal_fixed32_32_add(v_filter_init, + dal_fixed32_32_from_fraction(15, 10)); + + + 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); + v_filter_init_ceil = dal_fixed32_32_div_int(v_filter_init_ceil, 3); + v_filter_init_ceil = dal_fixed32_32_from_int( + dal_fixed32_32_ceil(v_filter_init_ceil)); + + src_lines_per_dst_line = dal_fixed32_32_max( + dal_fixed32_32_from_int(dal_fixed32_32_ceil(v_scale)), + v_filter_init_ceil); + + src_wdth_rnd_to_chunks = + ((params->source_view.width - 1) / 128) * 128 + 256; + + scaling_coeff = dal_fixed32_32_max( + dal_fixed32_32_from_fraction(params->scaling_info.h_taps, 4), + dal_fixed32_32_mul( + dal_fixed32_32_from_fraction( + params->scaling_info.v_taps, + scaler_efficiency), + h_scale)); + + scaling_coeff = dal_fixed32_32_max(scaling_coeff, h_scale); + + fx_disp_clk_khz = dal_fixed32_32_mul( + scaling_coeff, dal_fixed32_32_from_fraction(11, 10)); + if (0 != line_total) { + struct fixed32_32 d_clk = dal_fixed32_32_mul_int( + src_lines_per_dst_line, src_wdth_rnd_to_chunks); + d_clk = dal_fixed32_32_div_int(d_clk, line_total); + d_clk = dal_fixed32_32_mul(d_clk, + dal_fixed32_32_from_fraction(11, 10)); + fx_disp_clk_khz = dal_fixed32_32_max(fx_disp_clk_khz, d_clk); + } + + fx_disp_clk_khz = dal_fixed32_32_max(fx_disp_clk_khz, + dal_fixed32_32_mul(deep_color_factor, + dal_fixed32_32_from_fraction(11, 10))); + + fx_disp_clk_khz = dal_fixed32_32_mul_int(fx_disp_clk_khz, pix_clk_khz); + fx_disp_clk_khz = dal_fixed32_32_mul(fx_disp_clk_khz, + dal_fixed32_32_from_fraction(1005, 1000)); + + fx_alt_disp_clk_khz = scaling_coeff; + + if (0 != line_total) { + struct fixed32_32 d_clk = dal_fixed32_32_mul_int( + src_lines_per_dst_line, src_wdth_rnd_to_chunks); + d_clk = dal_fixed32_32_div_int(d_clk, line_total); + d_clk = dal_fixed32_32_mul(d_clk, + dal_fixed32_32_from_fraction(105, 100)); + fx_alt_disp_clk_khz = dal_fixed32_32_max( + fx_alt_disp_clk_khz, d_clk); + } + fx_alt_disp_clk_khz = dal_fixed32_32_max( + fx_alt_disp_clk_khz, fx_alt_disp_clk_khz); + + fx_alt_disp_clk_khz = dal_fixed32_32_mul_int( + fx_alt_disp_clk_khz, pix_clk_khz); + + /* convert to integer*/ + disp_clk_khz = dal_fixed32_32_floor(fx_disp_clk_khz); + alt_disp_clk_khz = dal_fixed32_32_floor(fx_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( + dc, disp_clk_khz); + alt_disp_clk_khz = get_actual_required_display_clk( + dc, alt_disp_clk_khz); + } + + if ((disp_clk_khz > max_clk_khz) && (alt_disp_clk_khz <= max_clk_khz)) + disp_clk_khz = alt_disp_clk_khz; + + return disp_clk_khz; + +} + +static uint32_t calc_cursor_bw_for_min_clks(struct min_clock_params *params) +{ + + struct fixed32_32 v_scale = dal_fixed32_32_from_int(1); + struct fixed32_32 v_filter_ceiling; + struct fixed32_32 src_lines_per_dst_line; + struct fixed32_32 cursor_bw; + + + /* DCE8 Mode Support and Mode Set Architecture Specification Rev 1.3 + 6.3.3 Cursor data Throughput requirement on DISPCLK + The MCIF to DCP cursor data return throughput is one pixel per DISPCLK + shared among the display heads. + If (Total Cursor Bandwidth in pixels for All heads> DISPCLK) + The mode is not supported + Cursor Bandwidth in Pixels = Cursor Width * + (SourceLinesPerDestinationLine / Line Time) + Assuming that Cursor Width = 128 + */ + /*In the hardware doc they mention an Interlace Factor + It is not used here because we have already used it when + calculating destination view*/ + if (0 != params->dest_view.height) + v_scale = dal_fixed32_32_from_fraction( + params->source_view.height, + params->dest_view.height); + + { + /*Do: Vertical Filter Init = 0.5 + VTAPS/2 + VSR/2 * Interlace Factor*/ + /*Interlace Factor is included in verticalScaleRatio*/ + struct fixed32_32 v_filter = dal_fixed32_32_add( + dal_fixed32_32_from_fraction(params->scaling_info.v_taps, 2), + dal_fixed32_32_div_int(v_scale, 2)); + /*Do : Ceiling (Vertical Filter Init, 2)/3 )*/ + v_filter_ceiling = dal_fixed32_32_div_int(v_filter, 2); + v_filter_ceiling = dal_fixed32_32_mul_int( + dal_fixed32_32_from_int(dal_fixed32_32_ceil(v_filter_ceiling)), + 2); + v_filter_ceiling = dal_fixed32_32_div_int(v_filter_ceiling, 3); + } + /*Do : MAX( CeilCeiling (VSR), Ceiling (Vertical Filter Init, 2)/3 )*/ + /*Do : SourceLinesPerDestinationLine = + * MAX( Ceiling (VSR), Ceiling (Vertical Filter Init, 2)/3 )*/ + src_lines_per_dst_line = dal_fixed32_32_max(v_scale, v_filter_ceiling); + + if ((params->requested_pixel_clock != 0) && + (params->timing_info.h_total != 0)) { + /* pixelClock is in units of KHz. Calc lineTime in us*/ + struct fixed32_32 inv_line_time = dal_fixed32_32_from_fraction( + params->requested_pixel_clock, + params->timing_info.h_total); + cursor_bw = dal_fixed32_32_mul( + dal_fixed32_32_mul_int(inv_line_time, 128), + src_lines_per_dst_line); + } + + /* convert to integer*/ + return dal_fixed32_32_floor(cursor_bw); +} + +static bool validate( + struct display_clock *dc, + struct min_clock_params *params) +{ + uint32_t max_clk_khz = get_validation_clock(dc); + uint32_t req_clk_khz; + + if (params == NULL) + return false; + + req_clk_khz = calc_single_display_min_clks(dc, params, false); + + return (req_clk_khz <= max_clk_khz); +} + +static uint32_t calculate_min_clock( + struct display_clock *dc, + uint32_t path_num, + struct min_clock_params *params) +{ + uint32_t i; + uint32_t validation_clk_khz = get_validation_clock(dc); + uint32_t min_clk_khz = validation_clk_khz; + uint32_t max_clk_khz = 0; + uint32_t total_cursor_bw = 0; + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + + if (disp_clk->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( + dc, params, true); + + /* update the max required clock found*/ + if (disp_clk_khz > max_clk_khz) + max_clk_khz = disp_clk_khz; + + disp_clk_khz = calc_cursor_bw_for_min_clks(params); + + total_cursor_bw += disp_clk_khz; + + params++; + + } + } + + max_clk_khz = (total_cursor_bw > max_clk_khz) ? total_cursor_bw : + max_clk_khz; + + min_clk_khz = max_clk_khz; + + /*"Cursor data Throughput requirement on DISPCLK is now a factor, + * need to change the code */ + ASSERT(total_cursor_bw < validation_clk_khz); + + if (min_clk_khz > validation_clk_khz) + min_clk_khz = validation_clk_khz; + else if (min_clk_khz < dc->min_display_clk_threshold_khz) + min_clk_khz = dc->min_display_clk_threshold_khz; + + return min_clk_khz; +} + +static void set_clock( + struct display_clock *dc, + uint32_t requested_clk_khz) +{ + struct bp_pixel_clock_parameters pxl_clk_params; + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + struct dc_bios *bp = dal_adapter_service_get_bios_parser(dc->as); + + /* Prepare to program display clock*/ + dm_memset(&pxl_clk_params, 0, sizeof(pxl_clk_params)); + + pxl_clk_params.target_pixel_clock = requested_clk_khz; + pxl_clk_params.pll_id = dc->id; + + bp->funcs->program_display_engine_pll(bp, &pxl_clk_params); + + if (disp_clk->dfs_bypass_enabled) { + + /* Cache the fixed display clock*/ + disp_clk->dfs_bypass_disp_clk = + pxl_clk_params.dfs_bypass_display_clock; + } + + /* from power down, we need mark the clock state as ClocksStateNominal + * from HWReset, so when resume we will call pplib voltage regulator.*/ + if (requested_clk_khz == 0) + disp_clk->cur_min_clks_state = CLOCKS_STATE_NOMINAL; +} + +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_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + if (disp_clk->dfs_bypass_enabled && disp_clk->dfs_bypass_disp_clk) + return disp_clk->dfs_bypass_disp_clk; + + /* Read the mmDENTIST_DISPCLK_CNTL to get the currently programmed + DID DENTIST_DISPCLK_WDIVIDER.*/ + value = 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 void set_clock_state( + struct display_clock *dc, + struct display_clock_state clk_state) +{ + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + disp_clk->clock_state = clk_state; +} +static struct display_clock_state get_clock_state( + struct display_clock *dc) +{ + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + return disp_clk->clock_state; +} + +static enum clocks_state get_min_clocks_state(struct display_clock *dc) +{ + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + return disp_clk->cur_min_clks_state; +} + +static enum clocks_state get_required_clocks_state + (struct display_clock *dc, + struct state_dependent_clocks *req_clocks) +{ + int32_t i; + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + enum clocks_state low_req_clk = disp_clk->max_clks_state; + + if (!req_clocks) { + /* NULL pointer*/ + BREAK_TO_DEBUGGER(); + 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 bool set_min_clocks_state( + struct display_clock *dc, + enum clocks_state clocks_state) +{ + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + if (clocks_state > disp_clk->max_clks_state) { + /*Requested state exceeds max supported state.*/ + BREAK_TO_DEBUGGER(); + return false; + } else if (clocks_state == disp_clk->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; + } + + disp_clk->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_dce80 *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 store_max_clocks_state( + struct display_clock *dc, + enum clocks_state max_clocks_state) +{ + struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc); + + switch (max_clocks_state) { + case CLOCKS_STATE_LOW: + case CLOCKS_STATE_NOMINAL: + case CLOCKS_STATE_PERFORMANCE: + case CLOCKS_STATE_ULTRA_LOW: + disp_clk->max_clks_state = max_clocks_state; + break; + + case CLOCKS_STATE_INVALID: + default: + /*Invalid Clocks State!*/ + BREAK_TO_DEBUGGER(); + break; + } +} + +static void display_clock_ss_construct( + struct display_clock_dce80 *disp_clk, + struct adapter_service *as) +{ + uint32_t ss_entry_num = dal_adapter_service_get_ss_info_num(as, + AS_SIGNAL_TYPE_GPU_PLL); + + /*Read SS Info from VBIOS SS Info table for DP Reference Clock spread.*/ + if (ss_entry_num > 0) {/* Should be only one entry */ + struct spread_spectrum_info ss_info; + bool res; + + dm_memset(&ss_info, 0, sizeof(struct spread_spectrum_info)); + + res = dal_adapter_service_get_ss_info(as, + AS_SIGNAL_TYPE_GPU_PLL, 0, &ss_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 (res && ss_info.spread_spectrum_percentage != 0) { + disp_clk->ss_on_gpu_pll = true; + disp_clk->gpu_pll_ss_divider = + ss_info.spread_percentage_divider; + if (ss_info.type.CENTER_MODE == 0) + /* Currently we need only SS + * percentage for down-spread*/ + disp_clk->gpu_pll_ss_percentage = + ss_info.spread_spectrum_percentage; + } + } +} + +static bool display_clock_integrated_info_construct( + struct display_clock_dce80 *disp_clk, + struct adapter_service *as) +{ + struct integrated_info info; + struct firmware_info fw_info; + bool res; + uint32_t i; + + res = dal_adapter_service_get_integrated_info(as, &info); + + disp_clk->dentist_vco_freq_khz = info.dentist_vco_freq; + if (disp_clk->dentist_vco_freq_khz == 0) { + dal_adapter_service_get_firmware_info(as, &fw_info); + disp_clk->dentist_vco_freq_khz = + fw_info.smu_gpu_pll_output_freq; + if (disp_clk->dentist_vco_freq_khz == 0) + disp_clk->dentist_vco_freq_khz = 3600000; + } + disp_clk->disp_clk.min_display_clk_threshold_khz = + disp_clk->dentist_vco_freq_khz / 64; + + if (!res) + return false; + + /* TODO: initialise disp_clk->dfs_bypass_disp_clk */ + + /*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; + } + /*invalid input from bios*/ + ASSERT(info.disp_clk_voltage[i].max_supported_clk >= 100000); + } + disp_clk->dfs_bypass_enabled = + dal_adapter_service_is_dfs_bypass_enabled(as) && + dal_adapter_service_is_feature_supported( + FEATURE_ENABLE_DFS_BYPASS); + + disp_clk->use_max_disp_clk = + dal_adapter_service_is_feature_supported( + FEATURE_USE_MAX_DISPLAY_CLK); + return true; +} + +static uint32_t get_dfs_bypass_threshold(struct display_clock *dc) +{ + return DCE80_DFS_BYPASS_THRESHOLD_KHZ; +} + +static void destroy(struct display_clock **dc) +{ + struct display_clock_dce80 *disp_clk; + + disp_clk = FROM_DISPLAY_CLOCK(*dc); + dm_free((*dc)->ctx, disp_clk); + *dc = NULL; +} + +static const struct display_clock_funcs funcs = { + .calculate_min_clock = calculate_min_clock, + .destroy = destroy, + .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 = + dal_display_clock_base_set_dp_ref_clock_source, + .set_min_clocks_state = set_min_clocks_state, + .store_max_clocks_state = store_max_clocks_state, + .validate = validate, +}; + +static bool display_clock_construct( + struct dc_context *ctx, + struct display_clock_dce80 *disp_clk, + struct adapter_service *as) +{ + struct display_clock *dc_base = &disp_clk->disp_clk; + + if (NULL == as) + return false; + + if (!dal_display_clock_construct_base(dc_base, ctx, as)) + return false; + + dc_base->funcs = &funcs; + /* + * set_dp_ref_clock_source + * set_clock_state + * get_clock_state + * get_dfs_bypass_threshold + */ + + disp_clk->gpu_pll_ss_percentage = 0; + disp_clk->gpu_pll_ss_divider = 1000; + disp_clk->ss_on_gpu_pll = false; + disp_clk->dfs_bypass_enabled = false; + disp_clk->dfs_bypass_disp_clk = 0; + disp_clk->use_max_disp_clk = true;/* false will hang the system! */ + + disp_clk->disp_clk.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_Dce50::StoreMaxClocksState(). This call will come in + * on PPLIB init. This is from DCE5x. in case HW wants to use mixed method.*/ + disp_clk->max_clks_state = CLOCKS_STATE_NOMINAL; +/* Initially set current min clocks state to invalid since we + * cannot make any assumption about PPLIB's initial state. This will be updated + * by HWSS via SetMinClocksState() on first mode set prior to programming + * state dependent clocks.*/ + disp_clk->cur_min_clks_state = CLOCKS_STATE_INVALID; + + display_clock_ss_construct(disp_clk, as); + + if (!display_clock_integrated_info_construct(disp_clk, as)) { + dal_logger_write(dc_base->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "Cannot obtain VBIOS integrated info"); + } + + 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); + return true; +} + +struct display_clock *dal_display_clock_dce80_create( + struct dc_context *ctx, + struct adapter_service *as) +{ + struct display_clock_dce80 *disp_clk; + + disp_clk = dm_alloc(ctx, sizeof(struct display_clock_dce80)); + + if (disp_clk == NULL) + return NULL; + + if (display_clock_construct(ctx, disp_clk, as)) + return &disp_clk->disp_clk; + + dm_free(ctx, disp_clk); + return NULL; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.h b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.h new file mode 100644 index 0000000..2d68704 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/gpu/dce80/display_clock_dce80.h @@ -0,0 +1,58 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DAL_DISPLAY_CLOCK_DCE80_H__ +#define __DAL_DISPLAY_CLOCK_DCE80_H__ + +#include "gpu/display_clock.h" + +struct display_clock_dce80 { + struct display_clock disp_clk; + /* DFS input - GPUPLL VCO frequency - from VBIOS Firmware info. */ + uint32_t dentist_vco_freq_khz; + /* 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; + /* Max display block clocks state*/ + enum clocks_state max_clks_state; + /* Current minimum display block clocks state*/ + enum clocks_state cur_min_clks_state; + /* DFS-bypass feature variable + Cache the status of DFS-bypass feature*/ + bool dfs_bypass_enabled; + /* 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; + bool use_max_disp_clk; + struct display_clock_state clock_state; +}; + +struct display_clock *dal_display_clock_dce80_create( + struct dc_context *ctx, + struct adapter_service *as); + +#endif /* __DAL_DISPLAY_CLOCK_DCE80_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile b/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile index 390d83d..fc76821 100644 --- a/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/Makefile @@ -9,6 +9,17 @@ AMD_DAL_I2CAUX = $(addprefix $(AMDDALPATH)/dc/i2caux/,$(I2CAUX)) AMD_DAL_FILES += $(AMD_DAL_I2CAUX) +############################################################################### +# DCE 8x family +############################################################################### +ifdef CONFIG_DRM_AMD_DAL_DCE8_0 +I2CAUX_DCE80 = i2caux_dce80.o i2c_hw_engine_dce80.o \ + i2c_sw_engine_dce80.o aux_engine_dce80.o + +AMD_DAL_I2CAUX_DCE80 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce80/,$(I2CAUX_DCE80)) + +AMD_DAL_FILES += $(AMD_DAL_I2CAUX_DCE80) +endif ############################################################################### # DCE 11x family diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.c new file mode 100644 index 0000000..a4fc2cd --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.c @@ -0,0 +1,740 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../aux_engine.h" + +/* + * Header of this unit + */ + +#include "aux_engine_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + + +/* + * This unit + */ + +/* + * @brief + * Cast 'struct aux_engine *' + * to 'struct aux_engine_dce80 *' + */ +#define FROM_AUX_ENGINE(ptr) \ + container_of((ptr), struct aux_engine_dce80, base) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct aux_engine_dce80 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_AUX_ENGINE(container_of((ptr), struct aux_engine, base)) + +static void release_engine( + struct engine *engine) +{ + struct aux_engine_dce80 *aux_engine = FROM_ENGINE(engine); + + const uint32_t addr = aux_engine->addr.AUX_ARB_CONTROL; + + uint32_t value = dm_read_reg(engine->ctx, addr); + + set_reg_field_value( + value, + 1, + AUX_ARB_CONTROL, + AUX_SW_DONE_USING_AUX_REG); + + dm_write_reg(engine->ctx, addr, value); +} + +static void destruct( + struct aux_engine_dce80 *engine); + +static void destroy( + struct aux_engine **aux_engine) +{ + struct aux_engine_dce80 *engine = FROM_AUX_ENGINE(*aux_engine); + + destruct(engine); + + dm_free((*aux_engine)->base.ctx, engine); + + *aux_engine = NULL; +} + +#define SW_CAN_ACCESS_AUX 1 + +static bool acquire_engine( + struct aux_engine *engine) +{ + struct aux_engine_dce80 *aux_engine = FROM_AUX_ENGINE(engine); + uint32_t value; + uint32_t field; + + /* enable AUX before request SW to access AUX */ + { + const uint32_t addr = aux_engine->addr.AUX_CONTROL; + + value = dm_read_reg(engine->base.ctx, addr); + + field = get_reg_field_value( + value, + AUX_CONTROL, + AUX_EN); + + if (field == 0) { + set_reg_field_value( + value, + 1, + AUX_CONTROL, + AUX_EN); + + dm_write_reg(engine->base.ctx, addr, value); + } + } + + /* request SW to access AUX */ + { + const uint32_t addr = aux_engine->addr.AUX_ARB_CONTROL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUX_ARB_CONTROL, + AUX_SW_USE_AUX_REG_REQ); + + dm_write_reg(engine->base.ctx, addr, value); + + value = dm_read_reg(engine->base.ctx, addr); + + field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + + return field == SW_CAN_ACCESS_AUX; + } +} + +static void configure( + struct aux_engine *engine, + union aux_config cfg) +{ + struct aux_engine_dce80 *aux_engine = FROM_AUX_ENGINE(engine); + + const uint32_t addr = aux_engine->addr.AUX_CONTROL; + + uint32_t value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + (0 != cfg.bits.ALLOW_AUX_WHEN_HPD_LOW), + AUX_CONTROL, + AUX_IGNORE_HPD_DISCON); + + dm_write_reg(engine->base.ctx, addr, value); +} + +static bool start_gtc_sync( + struct aux_engine *engine) +{ + /* TODO */ + return false; +} + +static void stop_gtc_sync( + struct aux_engine *engine) +{ + /* TODO */ +} + +#define COMPOSE_AUX_SW_DATA_16_20(command, address) \ + ((command) | ((0xF0000 & (address)) >> 16)) + +#define COMPOSE_AUX_SW_DATA_8_15(address) \ + ((0xFF00 & (address)) >> 8) + +#define COMPOSE_AUX_SW_DATA_0_7(address) \ + (0xFF & (address)) + +static void submit_channel_request( + struct aux_engine *engine, + struct aux_request_transaction_data *request) +{ + struct aux_engine_dce80 *aux_engine = FROM_AUX_ENGINE(engine); + uint32_t value; + uint32_t length; + + bool is_write = + ((request->type == AUX_TRANSACTION_TYPE_DP) && + (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || + ((request->type == AUX_TRANSACTION_TYPE_I2C) && + ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || + (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); + + /* clear_aux_error */ + { + const uint32_t addr = mmAUXN_IMPCAL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUXN_IMPCAL, + AUXN_CALOUT_ERROR_AK); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 0, + AUXN_IMPCAL, + AUXN_CALOUT_ERROR_AK); + + dm_write_reg(engine->base.ctx, addr, value); + } + { + const uint32_t addr = mmAUXP_IMPCAL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUXP_IMPCAL, + AUXP_CALOUT_ERROR_AK); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 0, + AUXP_IMPCAL, + AUXP_CALOUT_ERROR_AK); + + dm_write_reg(engine->base.ctx, addr, value); + } + + /* force_default_calibrate */ + { + const uint32_t addr = mmAUXN_IMPCAL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUXN_IMPCAL, + AUXN_IMPCAL_ENABLE); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 0, + AUXN_IMPCAL, + AUXN_IMPCAL_OVERRIDE_ENABLE); + + dm_write_reg(engine->base.ctx, addr, value); + } + { + const uint32_t addr = mmAUXP_IMPCAL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUXP_IMPCAL, + AUXP_IMPCAL_OVERRIDE_ENABLE); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 0, + AUXP_IMPCAL, + AUXP_IMPCAL_OVERRIDE_ENABLE); + + dm_write_reg(engine->base.ctx, addr, value); + } + + /* set the delay and the number of bytes to write */ + { + const uint32_t addr = aux_engine->addr.AUX_SW_CONTROL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + request->delay, + AUX_SW_CONTROL, + AUX_SW_START_DELAY); + + /* The length include + * the 4 bit header and the 20 bit address + * (that is 3 byte). + * If the requested length is non zero this means + * an addition byte specifying the length is required. */ + + length = request->length ? 4 : 3; + if (is_write) + length += request->length; + + set_reg_field_value( + value, + length, + AUX_SW_CONTROL, + AUX_SW_WR_BYTES); + + dm_write_reg(engine->base.ctx, addr, value); + } + + /* program action and address and payload data (if 'is_write') */ + { + const uint32_t addr = aux_engine->addr.AUX_SW_DATA; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 0, + AUX_SW_DATA, + AUX_SW_INDEX); + + set_reg_field_value( + value, + 0, + AUX_SW_DATA, + AUX_SW_DATA_RW); + + set_reg_field_value( + value, + 1, + AUX_SW_DATA, + AUX_SW_AUTOINCREMENT_DISABLE); + + set_reg_field_value( + value, + COMPOSE_AUX_SW_DATA_16_20( + request->action, request->address), + AUX_SW_DATA, + AUX_SW_DATA); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 0, + AUX_SW_DATA, + AUX_SW_AUTOINCREMENT_DISABLE); + + set_reg_field_value( + value, + COMPOSE_AUX_SW_DATA_8_15(request->address), + AUX_SW_DATA, + AUX_SW_DATA); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + COMPOSE_AUX_SW_DATA_0_7(request->address), + AUX_SW_DATA, + AUX_SW_DATA); + + dm_write_reg(engine->base.ctx, addr, value); + + if (request->length) { + set_reg_field_value( + value, + request->length - 1, + AUX_SW_DATA, + AUX_SW_DATA); + + dm_write_reg(engine->base.ctx, addr, value); + } + + if (is_write) { + /* Load the HW buffer with the Data to be sent. + * This is relevant for write operation. + * For read, the data recived data will be + * processed in process_channel_reply(). */ + uint32_t i = 0; + + while (i < request->length) { + + set_reg_field_value( + value, + request->data[i], + AUX_SW_DATA, + AUX_SW_DATA); + + dm_write_reg( + engine->base.ctx, addr, value); + + ++i; + } + } + } + + { + const uint32_t addr = aux_engine->addr.AUX_INTERRUPT_CONTROL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUX_INTERRUPT_CONTROL, + AUX_SW_DONE_ACK); + + dm_write_reg(engine->base.ctx, addr, value); + } + + { + const uint32_t addr = aux_engine->addr.AUX_SW_CONTROL; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 1, + AUX_SW_CONTROL, + AUX_SW_GO); + + dm_write_reg(engine->base.ctx, addr, value); + } +} + +static void process_channel_reply( + struct aux_engine *engine, + struct aux_reply_transaction_data *reply) +{ + struct aux_engine_dce80 *aux_engine = FROM_AUX_ENGINE(engine); + + /* Need to do a read to get the number of bytes to process + * Alternatively, this information can be passed - + * but that causes coupling which isn't good either. */ + + uint32_t bytes_replied; + uint32_t value; + + { + const uint32_t addr = aux_engine->addr.AUX_SW_STATUS; + + value = dm_read_reg(engine->base.ctx, addr); + + bytes_replied = get_reg_field_value( + value, + AUX_SW_STATUS, + AUX_SW_REPLY_BYTE_COUNT); + } + + if (bytes_replied) { + uint32_t reply_result; + + const uint32_t addr = aux_engine->addr.AUX_SW_DATA; + + value = dm_read_reg(engine->base.ctx, addr); + + set_reg_field_value( + value, + 0, + AUX_SW_DATA, + AUX_SW_INDEX); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 1, + AUX_SW_DATA, + AUX_SW_AUTOINCREMENT_DISABLE); + + dm_write_reg(engine->base.ctx, addr, value); + + set_reg_field_value( + value, + 1, + AUX_SW_DATA, + AUX_SW_DATA_RW); + + dm_write_reg(engine->base.ctx, addr, value); + + value = dm_read_reg(engine->base.ctx, addr); + + reply_result = get_reg_field_value( + value, + AUX_SW_DATA, + AUX_SW_DATA); + + reply_result = reply_result >> 4; + + switch (reply_result) { + case 0: /* ACK */ { + uint32_t i = 0; + + /* first byte was already used + * to get the command status */ + --bytes_replied; + + while (i < bytes_replied) { + value = dm_read_reg( + engine->base.ctx, addr); + + reply->data[i] = get_reg_field_value( + value, + AUX_SW_DATA, + AUX_SW_DATA); + + ++i; + } + + reply->status = AUX_TRANSACTION_REPLY_AUX_ACK; + } + break; + case 1: /* NACK */ + reply->status = AUX_TRANSACTION_REPLY_AUX_NACK; + break; + case 2: /* DEFER */ + reply->status = AUX_TRANSACTION_REPLY_AUX_DEFER; + break; + case 4: /* AUX ACK / I2C NACK */ + reply->status = AUX_TRANSACTION_REPLY_I2C_NACK; + break; + case 8: /* AUX ACK / I2C DEFER */ + reply->status = AUX_TRANSACTION_REPLY_I2C_DEFER; + break; + default: + reply->status = AUX_TRANSACTION_REPLY_INVALID; + } + } else { + /* Need to handle an error case... + * hopefully, upper layer function won't call this function + * if the number of bytes in the reply was 0 + * because there was surely an error that was asserted + * that should have been handled + * for hot plug case, this could happens*/ + if (!(value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) + ASSERT_CRITICAL(false); + } +} + +static enum aux_channel_operation_result get_channel_status( + struct aux_engine *engine, + uint8_t *returned_bytes) +{ + struct aux_engine_dce80 *aux_engine = FROM_AUX_ENGINE(engine); + + const uint32_t addr = aux_engine->addr.AUX_SW_STATUS; + + uint32_t value; + uint32_t aux_sw_done; + + if (returned_bytes == NULL) { + /*caller pass NULL pointer*/ + ASSERT_CRITICAL(false); + return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; + } + *returned_bytes = 0; + + /* poll to make sure that SW_DONE is asserted */ + { + uint32_t time_elapsed = 0; + + do { + value = dm_read_reg(engine->base.ctx, addr); + + aux_sw_done = get_reg_field_value( + value, + AUX_SW_STATUS, + AUX_SW_DONE); + + if (aux_sw_done) + break; + + dm_delay_in_microseconds(engine->base.ctx, 10); + + time_elapsed += 10; + } while (time_elapsed < aux_engine->timeout_period); + + + } + + /* Note that the following bits are set in 'status.bits' + * during CTS 4.2.1.2: + * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, + * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. + * + * AUX_SW_RX_MIN_COUNT_VIOL is an internal, + * HW debugging bit and should be ignored. */ + if (aux_sw_done) { + if (get_reg_field_value( + value, + AUX_SW_STATUS, + AUX_SW_RX_TIMEOUT_STATE) || + get_reg_field_value( + value, + AUX_SW_STATUS, + AUX_SW_RX_TIMEOUT)) + return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; + else if (get_reg_field_value( + value, + AUX_SW_STATUS, + AUX_SW_RX_INVALID_STOP)) + return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; + + *returned_bytes = get_reg_field_value( + value, + AUX_SW_STATUS, + AUX_SW_REPLY_BYTE_COUNT); + if (*returned_bytes == 0) + return + AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; + else { + *returned_bytes -= 1; + return AUX_CHANNEL_OPERATION_SUCCEEDED; + } + } else { + /*time_elapsed >= aux_engine->timeout_period */ + if (!(value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) + ASSERT_CRITICAL(false); + return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; + } +} + +static const int32_t aux_channel_offset[] = { + mmDP_AUX0_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, + mmDP_AUX1_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, + mmDP_AUX2_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, + mmDP_AUX3_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, + mmDP_AUX4_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL, + mmDP_AUX5_AUX_CONTROL - mmDP_AUX0_AUX_CONTROL +}; + +static const struct aux_engine_funcs aux_engine_funcs = { + .destroy = destroy, + .acquire_engine = acquire_engine, + .configure = configure, + .start_gtc_sync = start_gtc_sync, + .stop_gtc_sync = stop_gtc_sync, + .submit_channel_request = submit_channel_request, + .process_channel_reply = process_channel_reply, + .get_channel_status = get_channel_status, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .submit_request = dal_aux_engine_submit_request, + .keep_power_up_count = dal_i2caux_keep_power_up_count, + .get_engine_type = dal_aux_engine_get_engine_type, + .acquire = dal_aux_engine_acquire, +}; + +static bool construct( + struct aux_engine_dce80 *engine, + const struct aux_engine_dce80_create_arg *arg) +{ + int32_t offset; + + if (arg->engine_id >= sizeof(aux_channel_offset) / sizeof(int32_t)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_aux_engine_construct(&engine->base, arg->ctx)) { + BREAK_TO_DEBUGGER(); + return false; + } + engine->base.base.funcs = &engine_funcs; + engine->base.funcs = &aux_engine_funcs; + offset = aux_channel_offset[arg->engine_id]; + engine->addr.AUX_CONTROL = mmAUX_CONTROL + offset; + engine->addr.AUX_ARB_CONTROL = mmAUX_ARB_CONTROL + offset; + engine->addr.AUX_SW_DATA = mmAUX_SW_DATA + offset; + engine->addr.AUX_SW_CONTROL = mmAUX_SW_CONTROL + offset; + engine->addr.AUX_INTERRUPT_CONTROL = mmAUX_INTERRUPT_CONTROL + offset; + engine->addr.AUX_SW_STATUS = mmAUX_SW_STATUS + offset; + engine->addr.AUX_GTC_SYNC_CONTROL = mmAUX_GTC_SYNC_CONTROL + offset; + engine->addr.AUX_GTC_SYNC_STATUS = mmAUX_GTC_SYNC_STATUS + offset; + engine->addr.AUX_GTC_SYNC_CONTROLLER_STATUS = + mmAUX_GTC_SYNC_CONTROLLER_STATUS + offset; + + engine->timeout_period = arg->timeout_period; + + return true; +} + +static void destruct( + struct aux_engine_dce80 *engine) +{ + dal_aux_engine_destruct(&engine->base); +} + +struct aux_engine *dal_aux_engine_dce80_create( + const struct aux_engine_dce80_create_arg *arg) +{ + struct aux_engine_dce80 *engine; + + if (!arg) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + engine = dm_alloc(arg->ctx, sizeof(struct aux_engine_dce80)); + + if (!engine) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(engine, arg)) + return &engine->base; + + BREAK_TO_DEBUGGER(); + + dm_free(arg->ctx, engine); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.h new file mode 100644 index 0000000..8523c45 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/aux_engine_dce80.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_AUX_ENGINE_DCE80_H__ +#define __DAL_AUX_ENGINE_DCE80_H__ + +struct aux_engine_dce80 { + struct aux_engine base; + struct { + uint32_t AUX_CONTROL; + uint32_t AUX_ARB_CONTROL; + uint32_t AUX_SW_DATA; + uint32_t AUX_SW_CONTROL; + uint32_t AUX_INTERRUPT_CONTROL; + uint32_t AUX_SW_STATUS; + uint32_t AUX_GTC_SYNC_CONTROL; + uint32_t AUX_GTC_SYNC_STATUS; + uint32_t AUX_GTC_SYNC_CONTROLLER_STATUS; + } addr; + uint32_t timeout_period; +}; + +struct aux_engine_dce80_create_arg { + uint32_t engine_id; + uint32_t timeout_period; + struct dc_context *ctx; +}; + +struct aux_engine *dal_aux_engine_dce80_create( + const struct aux_engine_dce80_create_arg *arg); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.c new file mode 100644 index 0000000..3d61963 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.c @@ -0,0 +1,901 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_hw_engine.h" +#include "../i2c_generic_hw_engine.h" +/* + * Header of this unit + */ + +#include "i2c_hw_engine_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +/* + * This unit + */ + +enum dc_i2c_status { + DC_I2C_STATUS__DC_I2C_STATUS_IDLE, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW +}; + +enum dc_i2c_arbitration { + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH +}; + +enum { + /* No timeout in HW + * (timeout implemented in SW by querying status) */ + I2C_SETUP_TIME_LIMIT = 255, + I2C_HW_BUFFER_SIZE = 144 +}; + +/* + * @brief + * Cast 'struct i2c_hw_engine *' + * to 'struct i2c_hw_engine_dce80 *' + */ +#define FROM_I2C_HW_ENGINE(ptr) \ + container_of((ptr), struct i2c_hw_engine_dce80, base) + +/* + * @brief + * Cast pointer to 'struct i2c_engine *' + * to pointer to 'struct i2c_hw_engine_dce80 *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) + +/* + * @brief + * Cast pointer to 'struct engine *' + * to 'pointer to struct i2c_hw_engine_dce80 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +static void disable_i2c_hw_engine( + struct i2c_hw_engine_dce80 *engine) +{ + const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; + uint32_t value = 0; + + struct dc_context *ctx = NULL; + + ctx = engine->base.base.base.ctx; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_ENABLE); + + dm_write_reg(ctx, addr, value); +} + +static void release_engine( + struct engine *engine) +{ + struct i2c_hw_engine_dce80 *hw_engine = FROM_ENGINE(engine); + + struct i2c_engine *base = NULL; + bool safe_to_reset; + uint32_t value = 0; + + base = &hw_engine->base.base; + + /* Restore original HW engine speed */ + + base->funcs->set_speed(base, hw_engine->base.original_speed); + + /* Release I2C */ + { + value = dm_read_reg(engine->ctx, mmDC_I2C_ARBITRATION); + + set_reg_field_value( + value, + 1, + DC_I2C_ARBITRATION, + DC_I2C_SW_DONE_USING_I2C_REG); + + dm_write_reg(engine->ctx, mmDC_I2C_ARBITRATION, value); + } + + /* Reset HW engine */ + { + uint32_t i2c_sw_status = 0; + + value = dm_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + /* if used by SW, safe to reset */ + safe_to_reset = (i2c_sw_status == 1); + } + { + value = dm_read_reg(engine->ctx, mmDC_I2C_CONTROL); + + if (safe_to_reset) + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + dm_write_reg(engine->ctx, mmDC_I2C_CONTROL, value); + } + + /* HW I2c engine - clock gating feature */ + if (!hw_engine->engine_keep_power_up_count) + disable_i2c_hw_engine(hw_engine); +} + +static void keep_power_up_count( + struct engine *engine, + bool keep_power_up) +{ + struct i2c_hw_engine_dce80 *hw_engine = FROM_ENGINE(engine); + + if (keep_power_up) + ++hw_engine->engine_keep_power_up_count; + else { + --hw_engine->engine_keep_power_up_count; + + if (!hw_engine->engine_keep_power_up_count) + disable_i2c_hw_engine(hw_engine); + } +} + +static void destruct( + struct i2c_hw_engine_dce80 *engine) +{ + dal_i2c_hw_engine_destruct(&engine->base); +} + +static void destroy( + struct i2c_engine **i2c_engine) +{ + struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(*i2c_engine); + + destruct(engine); + + dm_free((*i2c_engine)->base.ctx, engine); + + *i2c_engine = NULL; +} + +static bool setup_engine( + struct i2c_engine *i2c_engine) +{ + uint32_t value = 0; + struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(i2c_engine); + + /* Program pin select */ + { + const uint32_t addr = mmDC_I2C_CONTROL; + + value = dm_read_reg(i2c_engine->base.ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_GO); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SEND_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_TRANSACTION_COUNT); + + set_reg_field_value( + value, + engine->engine_id, + DC_I2C_CONTROL, + DC_I2C_DDC_SELECT); + + + dm_write_reg(i2c_engine->base.ctx, addr, value); + } + + /* Program time limit */ + { + const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; + + value = dm_read_reg(i2c_engine->base.ctx, addr); + + set_reg_field_value( + value, + I2C_SETUP_TIME_LIMIT, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_TIME_LIMIT); + + set_reg_field_value( + value, + 1, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_ENABLE); + + dm_write_reg(i2c_engine->base.ctx, addr, value); + } + + /* Program HW priority + * set to High - interrupt software I2C at any time + * Enable restart of SW I2C that was interrupted by HW + * disable queuing of software while I2C is in use by HW */ + { + value = dm_read_reg(i2c_engine->base.ctx, + mmDC_I2C_ARBITRATION); + + set_reg_field_value( + value, + 0, + DC_I2C_ARBITRATION, + DC_I2C_NO_QUEUED_SW_GO); + + set_reg_field_value( + value, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION, + DC_I2C_SW_PRIORITY); + + dm_write_reg(i2c_engine->base.ctx, + mmDC_I2C_ARBITRATION, value); + } + + return true; +} + +static uint32_t get_speed( + const struct i2c_engine *i2c_engine) +{ + const struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(i2c_engine); + + const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED; + + uint32_t pre_scale = 0; + + uint32_t value = dm_read_reg(i2c_engine->base.ctx, addr); + + pre_scale = get_reg_field_value( + value, + DC_I2C_DDC1_SPEED, + DC_I2C_DDC1_PRESCALE); + + /* [anaumov] it seems following is unnecessary */ + /*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/ + + return pre_scale ? + engine->reference_frequency / pre_scale : + engine->base.default_speed; +} + +static void set_speed( + struct i2c_engine *i2c_engine, + uint32_t speed) +{ + struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(i2c_engine); + + if (speed) { + const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED; + + uint32_t value = dm_read_reg(i2c_engine->base.ctx, addr); + + set_reg_field_value( + value, + engine->reference_frequency / speed, + DC_I2C_DDC1_SPEED, + DC_I2C_DDC1_PRESCALE); + + set_reg_field_value( + value, + 2, + DC_I2C_DDC1_SPEED, + DC_I2C_DDC1_THRESHOLD); + + dm_write_reg(i2c_engine->base.ctx, addr, value); + } +} + +static inline void reset_hw_engine(struct engine *engine) +{ + uint32_t value = dm_read_reg(engine->ctx, mmDC_I2C_CONTROL); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + dm_write_reg(engine->ctx, mmDC_I2C_CONTROL, value); +} + +static bool is_hw_busy(struct engine *engine) +{ + uint32_t i2c_sw_status = 0; + + uint32_t value = dm_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) + return false; + + reset_hw_engine(engine); + + value = dm_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + + return i2c_sw_status != DC_I2C_STATUS__DC_I2C_STATUS_IDLE; +} + +/* + * @brief + * DC_GPIO_DDC MM register offsets + */ +static const uint32_t transaction_addr[] = { + mmDC_I2C_TRANSACTION0, + mmDC_I2C_TRANSACTION1, + mmDC_I2C_TRANSACTION2, + mmDC_I2C_TRANSACTION3 +}; + +static bool process_transaction( + struct i2c_hw_engine_dce80 *engine, + struct i2c_request_transaction_data *request) +{ + uint8_t length = request->length; + uint8_t *buffer = request->data; + + bool last_transaction = false; + uint32_t value = 0; + + struct dc_context *ctx = NULL; + + ctx = engine->base.base.base.ctx; + + { + const uint32_t addr = + transaction_addr[engine->transaction_count]; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 1, + DC_I2C_TRANSACTION0, + DC_I2C_STOP_ON_NACK0); + + set_reg_field_value( + value, + 1, + DC_I2C_TRANSACTION0, + DC_I2C_START0); + + + if ((engine->transaction_count == 3) || + (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || + (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { + + set_reg_field_value( + value, + 1, + DC_I2C_TRANSACTION0, + DC_I2C_STOP0); + + last_transaction = true; + } else + set_reg_field_value( + value, + 0, + DC_I2C_TRANSACTION0, + DC_I2C_STOP0); + + set_reg_field_value( + value, + (0 != (request->action & + I2CAUX_TRANSACTION_ACTION_I2C_READ)), + DC_I2C_TRANSACTION0, + DC_I2C_RW0); + + set_reg_field_value( + value, + length, + DC_I2C_TRANSACTION0, + DC_I2C_COUNT0); + + dm_write_reg(ctx, addr, value); + } + + /* Write the I2C address and I2C data + * into the hardware circular buffer, one byte per entry. + * As an example, the 7-bit I2C slave address for CRT monitor + * for reading DDC/EDID information is 0b1010001. + * For an I2C send operation, the LSB must be programmed to 0; + * for I2C receive operation, the LSB must be programmed to 1. */ + + { + value = 0; + + set_reg_field_value( + value, + false, + DC_I2C_DATA, + DC_I2C_DATA_RW); + + set_reg_field_value( + value, + request->address, + DC_I2C_DATA, + DC_I2C_DATA); + + if (engine->transaction_count == 0) { + set_reg_field_value( + value, + 0, + DC_I2C_DATA, + DC_I2C_INDEX); + + /*enable index write*/ + set_reg_field_value( + value, + 1, + DC_I2C_DATA, + DC_I2C_INDEX_WRITE); + } + + dm_write_reg(ctx, mmDC_I2C_DATA, value); + + if (!(request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { + + set_reg_field_value( + value, + 0, + DC_I2C_DATA, + DC_I2C_INDEX_WRITE); + + while (length) { + + set_reg_field_value( + value, + *buffer++, + DC_I2C_DATA, + DC_I2C_DATA); + + dm_write_reg(ctx, mmDC_I2C_DATA, value); + --length; + } + } + } + + ++engine->transaction_count; + engine->buffer_used_bytes += length + 1; + + return last_transaction; +} + +static void execute_transaction( + struct i2c_hw_engine_dce80 *engine) +{ + uint32_t value = 0; + struct dc_context *ctx = NULL; + + ctx = engine->base.base.base.ctx; + + { + const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_DATA_DRIVE_EN); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_CLK_DRIVE_EN); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_DATA_DRIVE_SEL); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_INTRA_TRANSACTION_DELAY); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_INTRA_BYTE_DELAY); + + dm_write_reg(ctx, addr, value); + } + + { + const uint32_t addr = mmDC_I2C_CONTROL; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SEND_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_GO); + + set_reg_field_value( + value, + engine->transaction_count - 1, + DC_I2C_CONTROL, + DC_I2C_TRANSACTION_COUNT); + + dm_write_reg(ctx, addr, value); + } + + /* start I2C transfer */ + { + const uint32_t addr = mmDC_I2C_CONTROL; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_GO); + + dm_write_reg(ctx, addr, value); + } + + /* all transactions were executed and HW buffer became empty + * (even though it actually happens when status becomes DONE) */ + engine->transaction_count = 0; + engine->buffer_used_bytes = 0; +} + +static void submit_channel_request( + struct i2c_engine *engine, + struct i2c_request_transaction_data *request) +{ + request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; + + if (!process_transaction(FROM_I2C_ENGINE(engine), request)) + return; + + if (is_hw_busy(&engine->base)) { + request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; + return; + } + + execute_transaction(FROM_I2C_ENGINE(engine)); +} + +static void process_channel_reply( + struct i2c_engine *engine, + struct i2c_reply_transaction_data *reply) +{ + uint8_t length = reply->length; + uint8_t *buffer = reply->data; + + uint32_t value = 0; + + /*set index*/ + set_reg_field_value( + value, + length - 1, + DC_I2C_DATA, + DC_I2C_INDEX); + + set_reg_field_value( + value, + 1, + DC_I2C_DATA, + DC_I2C_DATA_RW); + + set_reg_field_value( + value, + 1, + DC_I2C_DATA, + DC_I2C_INDEX_WRITE); + + dm_write_reg(engine->base.ctx, mmDC_I2C_DATA, value); + + while (length) { + /* after reading the status, + * if the I2C operation executed successfully + * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller + * should read data bytes from I2C circular data buffer */ + + value = dm_read_reg(engine->base.ctx, mmDC_I2C_DATA); + + *buffer++ = get_reg_field_value( + value, + DC_I2C_DATA, + DC_I2C_DATA); + + --length; + } +} + +static enum i2c_channel_operation_result get_channel_status( + struct i2c_engine *engine, + uint8_t *returned_bytes) +{ + uint32_t i2c_sw_status = 0; + uint32_t value = dm_read_reg(engine->base.ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) + return I2C_CHANNEL_OPERATION_ENGINE_BUSY; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_STOPPED_ON_NACK_MASK) + return I2C_CHANNEL_OPERATION_NO_RESPONSE; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_TIMEOUT_MASK) + return I2C_CHANNEL_OPERATION_TIMEOUT; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_ABORTED_MASK) + return I2C_CHANNEL_OPERATION_FAILED; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_DONE_MASK) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + /* in DAL2, I2C_RESULT_OK was returned */ + return I2C_CHANNEL_OPERATION_NOT_STARTED; +} + +static uint8_t get_hw_buffer_available_size( + const struct i2c_hw_engine *engine) +{ + return I2C_HW_BUFFER_SIZE - + FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes; +} + +static uint32_t get_transaction_timeout( + const struct i2c_hw_engine *engine, + uint32_t length) +{ + uint32_t speed = engine->base.funcs->get_speed(&engine->base); + + uint32_t period_timeout; + uint32_t num_of_clock_stretches; + + if (!speed) + return 0; + + period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; + + num_of_clock_stretches = 1 + (length << 3) + 1; + num_of_clock_stretches += + (FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes << 3) + + (FROM_I2C_HW_ENGINE(engine)->transaction_count << 1); + + return period_timeout * num_of_clock_stretches; +} + +/* + * @brief + * DC_I2C_DDC1_SETUP MM register offsets + * + * @note + * The indices of this offset array are DDC engine IDs + */ +static const int32_t ddc_setup_offset[] = { + + mmDC_I2C_DDC1_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 1 */ + mmDC_I2C_DDC2_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 2 */ + mmDC_I2C_DDC3_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 3 */ + mmDC_I2C_DDC4_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 4 */ + mmDC_I2C_DDC5_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 5 */ + mmDC_I2C_DDC6_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 6 */ + mmDC_I2C_DDCVGA_SETUP - mmDC_I2C_DDC1_SETUP /* DDC Engine 7 */ +}; + +/* + * @brief + * DC_I2C_DDC1_SPEED MM register offsets + * + * @note + * The indices of this offset array are DDC engine IDs + */ +static const int32_t ddc_speed_offset[] = { + mmDC_I2C_DDC1_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 1 */ + mmDC_I2C_DDC2_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 2 */ + mmDC_I2C_DDC3_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 3 */ + mmDC_I2C_DDC4_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 4 */ + mmDC_I2C_DDC5_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 5 */ + mmDC_I2C_DDC6_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 6 */ + mmDC_I2C_DDCVGA_SPEED - mmDC_I2C_DDC1_SPEED /* DDC Engine 7 */ +}; + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .destroy = destroy, + .get_speed = get_speed, + .set_speed = set_speed, + .setup_engine = setup_engine, + .submit_channel_request = submit_channel_request, + .process_channel_reply = process_channel_reply, + .get_channel_status = get_channel_status, + .acquire_engine = dal_i2c_hw_engine_acquire_engine, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .keep_power_up_count = keep_power_up_count, + .get_engine_type = dal_i2c_hw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_hw_engine_submit_request, +}; + +static const struct i2c_hw_engine_funcs i2c_hw_engine_funcs = { + .get_hw_buffer_available_size = + get_hw_buffer_available_size, + .get_transaction_timeout = + get_transaction_timeout, + .wait_on_operation_result = + dal_i2c_hw_engine_wait_on_operation_result, +}; + +static bool construct( + struct i2c_hw_engine_dce80 *engine, + const struct i2c_hw_engine_dce80_create_arg *arg) +{ + if (arg->engine_id >= sizeof(ddc_setup_offset) / sizeof(int32_t)) + return false; + if (arg->engine_id >= sizeof(ddc_speed_offset) / sizeof(int32_t)) + return false; + + if (!arg->reference_frequency) + return false; + + if (!dal_i2c_hw_engine_construct(&engine->base, arg->ctx)) + return false; + + engine->base.base.base.funcs = &engine_funcs; + engine->base.base.funcs = &i2c_engine_funcs; + engine->base.funcs = &i2c_hw_engine_funcs; + engine->base.default_speed = arg->default_speed; + engine->addr.DC_I2C_DDCX_SETUP = + mmDC_I2C_DDC1_SETUP + ddc_setup_offset[arg->engine_id]; + engine->addr.DC_I2C_DDCX_SPEED = + mmDC_I2C_DDC1_SPEED + ddc_speed_offset[arg->engine_id]; + + engine->engine_id = arg->engine_id; + engine->reference_frequency = arg->reference_frequency; + engine->buffer_used_bytes = 0; + engine->transaction_count = 0; + engine->engine_keep_power_up_count = 1; + + return true; +} + +struct i2c_engine *dal_i2c_hw_engine_dce80_create( + const struct i2c_hw_engine_dce80_create_arg *arg) +{ + struct i2c_hw_engine_dce80 *engine; + + if (!arg) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + engine = dm_alloc(arg->ctx, sizeof(struct i2c_hw_engine_dce80)); + + if (!engine) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(engine, arg)) + return &engine->base.base; + + BREAK_TO_DEBUGGER(); + + dm_free(arg->ctx, engine); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.h new file mode 100644 index 0000000..5c6116f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_hw_engine_dce80.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_I2C_HW_ENGINE_DCE80_H__ +#define __DAL_I2C_HW_ENGINE_DCE80_H__ + +struct i2c_hw_engine_dce80 { + struct i2c_hw_engine base; + struct { + uint32_t DC_I2C_DDCX_SETUP; + uint32_t DC_I2C_DDCX_SPEED; + } addr; + uint32_t engine_id; + /* expressed in kilohertz */ + uint32_t reference_frequency; + /* number of bytes currently used in HW buffer */ + uint32_t buffer_used_bytes; + /* number of pending transactions (before GO) */ + uint32_t transaction_count; + uint32_t engine_keep_power_up_count; +}; + +struct i2c_hw_engine_dce80_create_arg { + uint32_t engine_id; + uint32_t reference_frequency; + uint32_t default_speed; + struct dc_context *ctx; +}; + +struct i2c_engine *dal_i2c_hw_engine_dce80_create( + const struct i2c_hw_engine_dce80_create_arg *arg); +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.c new file mode 100644 index 0000000..e5135c5 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.c @@ -0,0 +1,187 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" + +/* + * Header of this unit + */ + +#include "i2c_sw_engine_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +/* + * This unit + */ + +static const uint32_t ddc_hw_status_addr[] = { + mmDC_I2C_DDC1_HW_STATUS, + mmDC_I2C_DDC2_HW_STATUS, + mmDC_I2C_DDC3_HW_STATUS, + mmDC_I2C_DDC4_HW_STATUS, + mmDC_I2C_DDC5_HW_STATUS, + mmDC_I2C_DDC6_HW_STATUS, + mmDC_I2C_DDCVGA_HW_STATUS +}; + + +/* + * @brief + * Cast 'struct i2c_sw_engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_I2C_SW_ENGINE(ptr) \ + container_of((ptr), struct i2c_sw_engine_dce80, base) + +/* + * @brief + * Cast 'struct i2c_engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_SW_ENGINE(container_of((ptr), struct i2c_sw_engine, base)) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +static void release_engine( + struct engine *engine) +{ + +} + +static void destruct( + struct i2c_sw_engine_dce80 *engine) +{ + dal_i2c_sw_engine_destruct(&engine->base); +} + +static void destroy( + struct i2c_engine **engine) +{ + struct i2c_sw_engine_dce80 *sw_engine = FROM_I2C_ENGINE(*engine); + + destruct(sw_engine); + + dm_free((*engine)->base.ctx, sw_engine); + + *engine = NULL; +} + + +static bool acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc_handle) +{ + return dal_i2caux_i2c_sw_engine_acquire_engine(engine, ddc_handle); +} + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .acquire_engine = acquire_engine, + .destroy = destroy, + .get_speed = dal_i2c_sw_engine_get_speed, + .set_speed = dal_i2c_sw_engine_set_speed, + .setup_engine = dal_i2c_engine_setup_i2c_engine, + .submit_channel_request = dal_i2c_sw_engine_submit_channel_request, + .process_channel_reply = dal_i2c_engine_process_channel_reply, + .get_channel_status = dal_i2c_sw_engine_get_channel_status, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .get_engine_type = dal_i2c_sw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_sw_engine_submit_request, + .keep_power_up_count = dal_i2caux_keep_power_up_count, +}; + +static bool construct( + struct i2c_sw_engine_dce80 *engine, + const struct i2c_sw_engine_dce80_create_arg *arg) +{ + struct i2c_sw_engine_create_arg arg_base; + + arg_base.ctx = arg->ctx; + arg_base.default_speed = arg->default_speed; + + if (!dal_i2c_sw_engine_construct(&engine->base, &arg_base)) { + BREAK_TO_DEBUGGER(); + return false; + } + + engine->base.base.base.funcs = &engine_funcs; + engine->base.base.funcs = &i2c_engine_funcs; + engine->base.default_speed = arg->default_speed; + engine->engine_id = arg->engine_id; + + return true; +} + +struct i2c_engine *dal_i2c_sw_engine_dce80_create( + const struct i2c_sw_engine_dce80_create_arg *arg) +{ + struct i2c_sw_engine_dce80 *engine; + + if (!arg) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + engine = dm_alloc(arg->ctx, sizeof(struct i2c_sw_engine_dce80)); + + if (!engine) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(engine, arg)) + return &engine->base.base; + + BREAK_TO_DEBUGGER(); + + dm_free(arg->ctx, engine); + + return NULL; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.h new file mode 100644 index 0000000..26355c0 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2c_sw_engine_dce80.h @@ -0,0 +1,43 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_SW_ENGINE_DCE80_H__ +#define __DAL_I2C_SW_ENGINE_DCE80_H__ + +struct i2c_sw_engine_dce80 { + struct i2c_sw_engine base; + uint32_t engine_id; +}; + +struct i2c_sw_engine_dce80_create_arg { + uint32_t engine_id; + uint32_t default_speed; + struct dc_context *ctx; +}; + +struct i2c_engine *dal_i2c_sw_engine_dce80_create( + const struct i2c_sw_engine_dce80_create_arg *arg); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.c b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.c new file mode 100644 index 0000000..4abf488 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.c @@ -0,0 +1,264 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../i2caux.h" + +/* + * Header of this unit + */ + +#include "i2caux_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "i2c_sw_engine_dce80.h" +#include "../i2c_hw_engine.h" +#include "i2c_hw_engine_dce80.h" +#include "../i2c_generic_hw_engine.h" +#include "../aux_engine.h" +#include "aux_engine_dce80.h" + + +/* + * This unit + */ + +#define FROM_I2C_AUX(ptr) \ + container_of((ptr), struct i2caux_dce80, base) + +static void destruct( + struct i2caux_dce80 *i2caux_dce80) +{ + dal_i2caux_destruct(&i2caux_dce80->base); +} + +static void destroy( + struct i2caux **i2c_engine) +{ + struct i2caux_dce80 *i2caux_dce80 = FROM_I2C_AUX(*i2c_engine); + + destruct(i2caux_dce80); + + dm_free((*i2c_engine)->ctx, i2caux_dce80); + + *i2c_engine = NULL; +} + +static struct i2c_engine *acquire_i2c_hw_engine( + struct i2caux *i2caux, + struct ddc *ddc) +{ + struct i2caux_dce80 *i2caux_dce80 = FROM_I2C_AUX(i2caux); + + struct i2c_engine *engine = NULL; + bool non_generic; + + if (!ddc) + return NULL; + + if (dal_ddc_is_hw_supported(ddc)) { + enum gpio_ddc_line line = dal_ddc_get_line(ddc); + + if (line < GPIO_DDC_LINE_COUNT) { + non_generic = true; + engine = i2caux->i2c_hw_engines[line]; + } + } + + if (!engine) { + non_generic = false; + engine = i2caux->i2c_generic_hw_engine; + } + + if (!engine) + return NULL; + + if (non_generic) { + if (!i2caux_dce80->i2c_hw_buffer_in_use && + engine->base.funcs->acquire(&engine->base, ddc)) { + i2caux_dce80->i2c_hw_buffer_in_use = true; + return engine; + } + } else { + if (engine->base.funcs->acquire(&engine->base, ddc)) + return engine; + } + + return NULL; +} + +static void release_engine( + struct i2caux *i2caux, + struct engine *engine) +{ + if (engine->funcs->get_engine_type(engine) == + I2CAUX_ENGINE_TYPE_I2C_DDC_HW) + FROM_I2C_AUX(i2caux)->i2c_hw_buffer_in_use = false; + + dal_i2caux_release_engine(i2caux, engine); +} + +static const enum gpio_ddc_line hw_ddc_lines[] = { + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6, + GPIO_DDC_LINE_DDC_VGA +}; + +static const enum gpio_ddc_line hw_aux_lines[] = { + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6 +}; + +static const struct i2caux_funcs i2caux_funcs = { + .destroy = destroy, + .acquire_i2c_hw_engine = acquire_i2c_hw_engine, + .release_engine = release_engine, + .acquire_i2c_sw_engine = dal_i2caux_acquire_i2c_sw_engine, + .acquire_aux_engine = dal_i2caux_acquire_aux_engine, +}; + +static bool construct( + struct i2caux_dce80 *i2caux_dce80, + struct adapter_service *as, + struct dc_context *ctx) +{ + /* Entire family have I2C engine reference clock frequency + * changed from XTALIN (27) to XTALIN/2 (13.5) */ + + struct i2caux *base = &i2caux_dce80->base; + + uint32_t reference_frequency = + dal_i2caux_get_reference_clock(as) >> 1; + + bool use_i2c_sw_engine = dal_adapter_service_is_feature_supported( + FEATURE_RESTORE_USAGE_I2C_SW_ENGINE); + + uint32_t i; + + if (!dal_i2caux_construct(base, as, ctx)) { + BREAK_TO_DEBUGGER(); + return false; + } + + i2caux_dce80->base.funcs = &i2caux_funcs; + i2caux_dce80->i2c_hw_buffer_in_use = false; + + /* Create I2C HW engines (HW + SW pairs) + * for all lines which has assisted HW DDC + * 'i' (loop counter) used as DDC/AUX engine_id */ + + i = 0; + + do { + enum gpio_ddc_line line_id = hw_ddc_lines[i]; + + struct i2c_hw_engine_dce80_create_arg hw_arg; + + if (use_i2c_sw_engine) { + struct i2c_sw_engine_dce80_create_arg sw_arg; + + sw_arg.engine_id = i; + sw_arg.default_speed = base->default_i2c_sw_speed; + sw_arg.ctx = ctx; + base->i2c_sw_engines[line_id] = + dal_i2c_sw_engine_dce80_create(&sw_arg); + } + + hw_arg.engine_id = i; + hw_arg.reference_frequency = reference_frequency; + hw_arg.default_speed = base->default_i2c_hw_speed; + hw_arg.ctx = ctx; + + base->i2c_hw_engines[line_id] = + dal_i2c_hw_engine_dce80_create(&hw_arg); + + ++i; + } while (i < ARRAY_SIZE(hw_ddc_lines)); + + /* Create AUX engines for all lines which has assisted HW AUX + * 'i' (loop counter) used as DDC/AUX engine_id */ + + i = 0; + + do { + enum gpio_ddc_line line_id = hw_aux_lines[i]; + + struct aux_engine_dce80_create_arg arg; + + arg.engine_id = i; + arg.timeout_period = base->aux_timeout_period; + arg.ctx = ctx; + + base->aux_engines[line_id] = + dal_aux_engine_dce80_create(&arg); + + ++i; + } while (i < ARRAY_SIZE(hw_aux_lines)); + + /* TODO Generic I2C SW and HW */ + + return true; +} + +struct i2caux *dal_i2caux_dce80_create( + struct adapter_service *as, + struct dc_context *ctx) +{ + struct i2caux_dce80 *i2caux_dce80 = + dm_alloc(ctx, sizeof(struct i2caux_dce80)); + + if (!i2caux_dce80) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(i2caux_dce80, as, ctx)) + return &i2caux_dce80->base; + + BREAK_TO_DEBUGGER(); + + dm_free(ctx, i2caux_dce80); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.h b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.h new file mode 100644 index 0000000..85417a8 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/dce80/i2caux_dce80.h @@ -0,0 +1,39 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCE80_H__ +#define __DAL_I2C_AUX_DCE80_H__ + +struct i2caux_dce80 { + struct i2caux base; + /* indicate the I2C HW circular buffer is in use */ + bool i2c_hw_buffer_in_use; +}; + +struct i2caux *dal_i2caux_dce80_create( + struct adapter_service *as, + struct dc_context *ctx); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c index 4c2f2cb..47e7922 100644 --- a/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c +++ b/drivers/gpu/drm/amd/dal/dc/i2caux/i2caux.c @@ -48,6 +48,10 @@ * This unit */ +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +#include "dce80/i2caux_dce80.h" +#endif + #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) || defined(CONFIG_DRM_AMD_DAL_DCE10_0) #include "dce110/i2caux_dce110.h" #endif @@ -79,6 +83,10 @@ struct i2caux *dal_i2caux_create( } switch (dce_version) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + return dal_i2caux_dce80_create(as, ctx); +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE11_0) #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: 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 1372331..3e2f232 100644 --- a/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c +++ b/drivers/gpu/drm/amd/dal/dc/irq/irq_service.c @@ -32,6 +32,13 @@ #include "dce110/irq_service_dce110.h" #endif +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + /* + * TODO: implement DCE8.x IRQ service + */ +#include "dce110/irq_service_dce110.h" +#endif + #include "irq_service.h" bool dal_irq_service_construct( @@ -50,6 +57,10 @@ struct irq_service *dal_irq_service_create( struct irq_service_init_data *init_data) { switch (version) { +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + case DCE_VERSION_8_0: + return dal_irq_service_dce110_create(init_data); +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) case DCE_VERSION_10_0: return dal_irq_service_dce110_create(init_data); diff --git a/drivers/gpu/drm/amd/dal/include/dal_types.h b/drivers/gpu/drm/amd/dal/include/dal_types.h index 3739776..8fdde70 100644 --- a/drivers/gpu/drm/amd/dal/include/dal_types.h +++ b/drivers/gpu/drm/amd/dal/include/dal_types.h @@ -34,6 +34,9 @@ struct dc_bios; enum dce_version { DCE_VERSION_UNKNOWN = (-1), +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) + DCE_VERSION_8_0, +#endif #if defined(CONFIG_DRM_AMD_DAL_DCE10_0) DCE_VERSION_10_0, #endif 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 009b583..a621930 100644 --- a/drivers/gpu/drm/amd/dal/include/display_clock_interface.h +++ b/drivers/gpu/drm/amd/dal/include/display_clock_interface.h @@ -143,6 +143,12 @@ struct display_clock *dal_display_clock_dce80_create( struct adapter_service *as); #endif +#if defined(CONFIG_DRM_AMD_DAL_DCE8_0) +struct display_clock *dal_display_clock_dce80_create( + struct dc_context *ctx, + struct adapter_service *as); +#endif + void dal_display_clock_destroy(struct display_clock **to_destroy); bool dal_display_clock_validate( struct display_clock *disp_clk, -- 2.7.4