aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/files/0803-drm-amd-dal-Adding-Hawaii-and-Bonaire-support-to-DAL.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/files/0803-drm-amd-dal-Adding-Hawaii-and-Bonaire-support-to-DAL.patch')
-rw-r--r--common/recipes-kernel/linux/files/0803-drm-amd-dal-Adding-Hawaii-and-Bonaire-support-to-DAL.patch19970
1 files changed, 19970 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0803-drm-amd-dal-Adding-Hawaii-and-Bonaire-support-to-DAL.patch b/common/recipes-kernel/linux/files/0803-drm-amd-dal-Adding-Hawaii-and-Bonaire-support-to-DAL.patch
new file mode 100644
index 00000000..0a8091e8
--- /dev/null
+++ b/common/recipes-kernel/linux/files/0803-drm-amd-dal-Adding-Hawaii-and-Bonaire-support-to-DAL.patch
@@ -0,0 +1,19970 @@
+From f916c629a50d432a9b9b46d859ddbc93e2ca1973 Mon Sep 17 00:00:00 2001
+From: Harry Wentland <harry.wentland@amd.com>
+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 <harry.wentland@amd.com>
+Acked-by: Jordan Lazare <Jordan.Lazare@amd.com>
+---
+ 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(&reg_matrix, 0, sizeof(struct out_csc_color_matrix));
++
++ setup_reg_format(matrix, reg_matrix.regval);
++
++ program_color_matrix(opp80, &reg_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(&reg_matrix, 0, sizeof(struct out_csc_color_matrix));
++
++ setup_reg_format(matrix, reg_matrix.regval);
++
++ program_color_matrix(opp80, &reg_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, &current_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(
++ &divider_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(
++ &divider_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(
++ &divider_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
+