diff mbox

[v2,24/26] drm/amd/dal: Add display core

Message ID bf7fa3b574c7449dbeda3afadb17076256a15328.1455660367.git.harry.wentland@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Harry Wentland Feb. 16, 2016, 10:28 p.m. UTC
Adds a logical representation of our hardware. Provides ability to
- dc_validate_resources - validate a display configuration
- dc_commit_targets - commit a display configuration
- dc_commit_surfaces_to_target - update surfaces
- dc_link_detect - detect displays at link
- dc_resume - resume display HW
- dc_interrupt_set/ack - set and ack interrupts
- etc.

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
---
 drivers/gpu/drm/amd/dal/dc/Makefile               |   28 +
 drivers/gpu/drm/amd/dal/dc/core/dc.c              |  932 +++++++++++
 drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c |   56 +
 drivers/gpu/drm/amd/dal/dc/core/dc_link.c         | 1644 ++++++++++++++++++++
 drivers/gpu/drm/amd/dal/dc/core/dc_link_ddc.c     | 1151 ++++++++++++++
 drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c      | 1728 +++++++++++++++++++++
 drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c    |  201 +++
 drivers/gpu/drm/amd/dal/dc/core/dc_resource.c     | 1243 +++++++++++++++
 drivers/gpu/drm/amd/dal/dc/core/dc_sink.c         |  116 ++
 drivers/gpu/drm/amd/dal/dc/core/dc_stream.c       |  188 +++
 drivers/gpu/drm/amd/dal/dc/core/dc_surface.c      |  123 ++
 drivers/gpu/drm/amd/dal/dc/core/dc_target.c       |  548 +++++++
 12 files changed, 7958 insertions(+)
 create mode 100644 drivers/gpu/drm/amd/dal/dc/Makefile
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link_ddc.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_resource.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_sink.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_stream.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_surface.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/core/dc_target.c
diff mbox

Patch

diff --git a/drivers/gpu/drm/amd/dal/dc/Makefile b/drivers/gpu/drm/amd/dal/dc/Makefile
new file mode 100644
index 000000000000..aed26eec81f9
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/Makefile
@@ -0,0 +1,28 @@ 
+#
+# Makefile for Display Core (dc) component.
+#
+
+DC_LIBS = adapter asic_capability audio basics bios calcs \
+gpio gpu i2caux irq virtual
+
+ifdef CONFIG_DRM_AMD_DAL_DCE11_0
+DC_LIBS += dce110
+endif
+
+ifdef CONFIG_DRM_AMD_DAL_DCE10_0
+DC_LIBS += dce100
+endif
+
+AMD_DC = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/dc/,$(DC_LIBS)))
+
+include $(AMD_DC)
+
+DISPLAY_CORE = dc.o dc_link.o dc_resource.o dc_target.o dc_sink.o dc_stream.o \
+dc_hw_sequencer.o dc_surface.o dc_link_hwss.o dc_link_dp.o dc_link_ddc.o
+
+AMD_DISPLAY_CORE = $(addprefix $(AMDDALPATH)/dc/core/,$(DISPLAY_CORE))
+
+AMD_DAL_FILES += $(AMD_DISPLAY_CORE)
+
+
+
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc.c b/drivers/gpu/drm/amd/dal/dc/core/dc.c
new file mode 100644
index 000000000000..0b8f158c0ec2
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc.c
@@ -0,0 +1,932 @@ 
+/*
+ * 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_status.h"
+#include "core_types.h"
+#include "hw_sequencer.h"
+
+#include "resource.h"
+
+#include "adapter_service_interface.h"
+#include "clock_source.h"
+#include "dc_bios_types.h"
+
+#include "bandwidth_calcs.h"
+#include "include/irq_service_interface.h"
+#include "transform.h"
+#include "timing_generator.h"
+#include "virtual/virtual_link_encoder.h"
+
+#include "link_hwss.h"
+#include "link_encoder.h"
+
+#include "dc_link_ddc.h"
+
+/*******************************************************************************
+ * Private structures
+ ******************************************************************************/
+
+struct dc_target_sync_report {
+	uint32_t h_count;
+	uint32_t v_count;
+};
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static void destroy_links(struct dc *dc)
+{
+	uint32_t i;
+
+	for (i = 0; i < dc->link_count; i++) {
+		if (NULL != dc->links[i])
+			link_destroy(&dc->links[i]);
+	}
+}
+
+static bool create_links(struct dc *dc, const struct dc_init_data *init_params)
+{
+	int i;
+	int connectors_num;
+	struct dc_bios *dcb;
+
+	dc->link_count = 0;
+
+	dcb = dal_adapter_service_get_bios_parser(init_params->adapter_srv);
+
+	connectors_num = dcb->funcs->get_connectors_number(dcb);
+
+	if (connectors_num > ENUM_ID_COUNT) {
+		dm_error(
+			"DC: Number of connectors %d exceeds maximum of %d!\n",
+			connectors_num,
+			ENUM_ID_COUNT);
+		return false;
+	}
+
+	if (connectors_num == 0 && init_params->num_virtual_links == 0) {
+		dm_error("DC: Number of connectors can not be zero!\n");
+		return false;
+	}
+
+	dm_output_to_console(
+		"DC: %s: connectors_num: physical:%d, virtual:%d\n",
+		__func__,
+		connectors_num,
+		init_params->num_virtual_links);
+
+	for (i = 0; i < connectors_num; i++) {
+		struct link_init_data link_init_params = {0};
+		struct core_link *link;
+
+		link_init_params.ctx = init_params->ctx;
+		link_init_params.adapter_srv = init_params->adapter_srv;
+		link_init_params.connector_index = i;
+		link_init_params.link_index = dc->link_count;
+		link_init_params.dc = dc;
+		link = link_create(&link_init_params);
+
+		if (link) {
+			dc->links[dc->link_count] = link;
+			link->dc = dc;
+			++dc->link_count;
+		} else {
+			dm_error("DC: failed to create link!\n");
+		}
+	}
+
+	for (i = 0; i < init_params->num_virtual_links; i++) {
+		struct core_link *link = dm_alloc(
+			dc->ctx,
+			sizeof(*link));
+		struct encoder_init_data enc_init = {0};
+
+		if (link == NULL) {
+			BREAK_TO_DEBUGGER();
+			goto failed_alloc;
+		}
+
+		link->adapter_srv = init_params->adapter_srv;
+		link->ctx = init_params->ctx;
+		link->dc = dc;
+		link->public.connector_signal = SIGNAL_TYPE_VIRTUAL;
+		link->link_id.type = OBJECT_TYPE_CONNECTOR;
+		link->link_id.id = CONNECTOR_ID_VIRTUAL;
+		link->link_id.enum_id = ENUM_ID_1;
+		link->link_enc = dm_alloc(
+			dc->ctx,
+			sizeof(*link->link_enc));
+
+		enc_init.adapter_service = init_params->adapter_srv;
+		enc_init.ctx = init_params->ctx;
+		enc_init.channel = CHANNEL_ID_UNKNOWN;
+		enc_init.hpd_source = HPD_SOURCEID_UNKNOWN;
+		enc_init.transmitter = TRANSMITTER_UNKNOWN;
+		enc_init.connector = link->link_id;
+		enc_init.encoder.type = OBJECT_TYPE_ENCODER;
+		enc_init.encoder.id = ENCODER_ID_INTERNAL_VIRTUAL;
+		enc_init.encoder.enum_id = ENUM_ID_1;
+		virtual_link_encoder_construct(link->link_enc, &enc_init);
+
+		link->public.link_index = dc->link_count;
+		dc->links[dc->link_count] = link;
+		dc->link_count++;
+	}
+
+	return true;
+
+failed_alloc:
+	return false;
+}
+
+
+static void init_hw(struct dc *dc)
+{
+	int i;
+	struct dc_bios *bp;
+	struct transform *xfm;
+
+	bp = dal_adapter_service_get_bios_parser(dc->res_pool.adapter_srv);
+	for(i = 0; i < dc->res_pool.controller_count; i++) {
+		xfm = dc->res_pool.transforms[i];
+
+		dc->hwss.enable_display_power_gating(
+				dc->ctx, i, bp,
+				PIPE_GATING_CONTROL_INIT);
+		dc->hwss.enable_display_power_gating(
+				dc->ctx, i, bp,
+				PIPE_GATING_CONTROL_DISABLE);
+
+		xfm->funcs->transform_power_up(xfm);
+		dc->hwss.enable_display_pipe_clock_gating(
+			dc->ctx,
+			true);
+	}
+
+	dc->hwss.clock_gating_power_up(dc->ctx, false);
+	bp->funcs->power_up(bp);
+	/***************************************/
+
+	for (i = 0; i < dc->link_count; i++) {
+		/****************************************/
+		/* Power up AND update implementation according to the
+		 * required signal (which may be different from the
+		 * default signal on connector). */
+		struct core_link *link = dc->links[i];
+		link->link_enc->funcs->hw_init(link->link_enc);
+	}
+
+	for(i = 0; i < dc->res_pool.controller_count; i++) {
+		struct timing_generator *tg = dc->res_pool.timing_generators[i];
+
+		tg->funcs->disable_vga(tg);
+
+		/* Blank controller using driver code instead of
+		 * command table. */
+		tg->funcs->set_blank(tg, true);
+	}
+
+	for(i = 0; i < dc->res_pool.audio_count; i++) {
+		struct audio *audio = dc->res_pool.audios[i];
+
+		if (dal_audio_power_up(audio) != AUDIO_RESULT_OK)
+			dm_error("Failed audio power up!\n");
+	}
+
+}
+
+static struct adapter_service *create_as(
+		struct dc_init_data *dc_init_data,
+		const struct dal_init_data *init)
+{
+	struct adapter_service *as = NULL;
+	struct as_init_data init_data;
+
+	dm_memset(&init_data, 0, sizeof(init_data));
+
+	init_data.ctx = dc_init_data->ctx;
+
+	/* BIOS parser init data */
+	init_data.bp_init_data.ctx = dc_init_data->ctx;
+	init_data.bp_init_data.bios = init->asic_id.atombios_base_address;
+
+	/* HW init data */
+	init_data.hw_init_data.chip_id = init->asic_id.chip_id;
+	init_data.hw_init_data.chip_family = init->asic_id.chip_family;
+	init_data.hw_init_data.pci_revision_id = init->asic_id.pci_revision_id;
+	init_data.hw_init_data.fake_paths_num = init->asic_id.fake_paths_num;
+	init_data.hw_init_data.feature_flags = init->asic_id.feature_flags;
+	init_data.hw_init_data.hw_internal_rev = init->asic_id.hw_internal_rev;
+	init_data.hw_init_data.runtime_flags = init->asic_id.runtime_flags;
+	init_data.hw_init_data.vram_width = init->asic_id.vram_width;
+	init_data.hw_init_data.vram_type = init->asic_id.vram_type;
+
+	/* bdf is BUS,DEVICE,FUNCTION*/
+	init_data.bdf_info = init->bdf_info;
+
+	init_data.display_param = &init->display_param;
+	init_data.vbios_override = init->vbios_override;
+	init_data.dce_environment = init->dce_environment;
+
+	as = dal_adapter_service_create(&init_data);
+
+	return as;
+}
+
+static void bw_calcs_data_update_from_pplib(struct dc *dc)
+{
+	struct dc_pp_clock_levels clks = {0};
+
+	/*do system clock*/
+	dm_pp_get_clock_levels_by_type(
+			dc->ctx,
+			DC_PP_CLOCK_TYPE_ENGINE_CLK,
+			&clks);
+	/* convert all the clock fro kHz to fix point mHz */
+	dc->bw_vbios.high_sclk = bw_frc_to_fixed(
+			clks.clocks_in_khz[clks.num_levels-1], 1000);
+	dc->bw_vbios.mid_sclk  = bw_frc_to_fixed(
+			clks.clocks_in_khz[clks.num_levels>>1], 1000);
+	dc->bw_vbios.low_sclk  = bw_frc_to_fixed(
+			clks.clocks_in_khz[0], 1000);
+
+	/*do display clock*/
+	dm_pp_get_clock_levels_by_type(
+			dc->ctx,
+			DC_PP_CLOCK_TYPE_DISPLAY_CLK,
+			&clks);
+
+	dc->bw_vbios.high_voltage_max_dispclk = bw_frc_to_fixed(
+			clks.clocks_in_khz[clks.num_levels-1], 1000);
+	dc->bw_vbios.mid_voltage_max_dispclk  = bw_frc_to_fixed(
+			clks.clocks_in_khz[clks.num_levels>>1], 1000);
+	dc->bw_vbios.low_voltage_max_dispclk  = bw_frc_to_fixed(
+			clks.clocks_in_khz[0], 1000);
+
+	/*do memory clock*/
+	dm_pp_get_clock_levels_by_type(
+			dc->ctx,
+			DC_PP_CLOCK_TYPE_MEMORY_CLK,
+			&clks);
+
+	dc->bw_vbios.low_yclk = bw_frc_to_fixed(
+		clks.clocks_in_khz[0] * MEMORY_TYPE_MULTIPLIER, 1000);
+	dc->bw_vbios.mid_yclk = bw_frc_to_fixed(
+		clks.clocks_in_khz[clks.num_levels>>1] * MEMORY_TYPE_MULTIPLIER,
+		1000);
+	dc->bw_vbios.high_yclk = bw_frc_to_fixed(
+		clks.clocks_in_khz[clks.num_levels-1] * MEMORY_TYPE_MULTIPLIER,
+		1000);
+}
+
+static bool construct(struct dc *dc, const struct dal_init_data *init_params)
+{
+	struct dal_logger *logger;
+	/* Tempory code
+	 * TODO: replace dal_init_data with dc_init_data when dal is removed
+	 */
+	struct dc_init_data dc_init_data = {0};
+
+	/* Create dc context */
+	/* A temp dc context is used only to allocate the memory for actual
+	 * dc context */
+	struct dc_context ctx = {0};
+	ctx.cgs_device = init_params->cgs_device;
+	ctx.dc = dc;
+
+	dc_init_data.ctx = dm_alloc(&ctx, sizeof(*dc_init_data.ctx));
+	if (!dc_init_data.ctx) {
+		dm_error("%s: failed to create ctx\n", __func__);
+		goto ctx_fail;
+	}
+	dc_init_data.ctx->driver_context = init_params->driver;
+	dc_init_data.ctx->cgs_device = init_params->cgs_device;
+	dc_init_data.num_virtual_links = init_params->num_virtual_links;
+	dc_init_data.ctx->dc = dc;
+
+	/* Create logger */
+	logger = dal_logger_create(dc_init_data.ctx);
+
+	if (!logger) {
+		/* can *not* call logger. call base driver 'print error' */
+		dm_error("%s: failed to create Logger!\n", __func__);
+		goto logger_fail;
+	}
+	dc_init_data.ctx->logger = logger;
+
+	/* Create adapter service */
+	dc_init_data.adapter_srv = create_as(&dc_init_data, init_params);
+
+	if (!dc_init_data.adapter_srv) {
+		dm_error("%s: create_as() failed!\n", __func__);
+		goto as_fail;
+	}
+
+	/* Initialize HW controlled by Adapter Service */
+	if (false == dal_adapter_service_initialize_hw_data(
+			dc_init_data.adapter_srv)) {
+		dm_error("%s: dal_adapter_service_initialize_hw_data()"\
+				"  failed!\n", __func__);
+		/* Note that AS exist, so have to destroy it.*/
+		goto as_fail;
+	}
+
+	dc->ctx = dc_init_data.ctx;
+
+	dc->ctx->dce_environment = dal_adapter_service_get_dce_environment(
+			dc_init_data.adapter_srv);
+
+	/* Create hardware sequencer */
+	if (!dc_construct_hw_sequencer(dc_init_data.adapter_srv, dc))
+		goto hwss_fail;
+
+	if (!dc_construct_resource_pool(
+		dc_init_data.adapter_srv, dc, dc_init_data.num_virtual_links))
+		goto construct_resource_fail;
+
+	if (!create_links(dc, &dc_init_data))
+		goto create_links_fail;
+
+	bw_calcs_init(&dc->bw_dceip, &dc->bw_vbios);
+
+	bw_calcs_data_update_from_pplib(dc);
+
+	return true;
+
+	/**** error handling here ****/
+construct_resource_fail:
+create_links_fail:
+as_fail:
+	dal_logger_destroy(&dc_init_data.ctx->logger);
+logger_fail:
+hwss_fail:
+	dm_free(&ctx, dc_init_data.ctx);
+ctx_fail:
+	return false;
+}
+
+static void destruct(struct dc *dc)
+{
+	destroy_links(dc);
+	dc->res_pool.funcs->destruct(&dc->res_pool);
+	dal_logger_destroy(&dc->ctx->logger);
+	dm_free(dc->ctx, dc->ctx);
+}
+
+/*******************************************************************************
+ * Public functions
+ ******************************************************************************/
+
+struct dc *dc_create(const struct dal_init_data *init_params)
+ {
+	struct dc_context ctx = {
+		.driver_context = init_params->driver,
+		.cgs_device = init_params->cgs_device
+	};
+	struct dc *dc = dm_alloc(&ctx, sizeof(*dc));
+
+	if (NULL == dc)
+		goto alloc_fail;
+
+	ctx.dc = dc;
+	if (false == construct(dc, init_params))
+		goto construct_fail;
+
+	/*TODO: separate HW and SW initialization*/
+	init_hw(dc);
+
+	return dc;
+
+construct_fail:
+	dm_free(&ctx, dc);
+
+alloc_fail:
+	return NULL;
+}
+
+void dc_destroy(struct dc **dc)
+{
+	struct dc_context ctx = *(*dc)->ctx;
+	destruct(*dc);
+	dm_free(&ctx, *dc);
+	*dc = NULL;
+}
+
+bool dc_validate_resources(
+		const struct dc *dc,
+		const struct dc_validation_set set[],
+		uint8_t set_count)
+{
+	enum dc_status result = DC_ERROR_UNEXPECTED;
+	struct validate_context *context;
+
+	context = dm_alloc(dc->ctx, sizeof(struct validate_context));
+	if(context == NULL)
+		goto context_alloc_fail;
+
+	result = dc->res_pool.funcs->validate_with_context(
+						dc, set, set_count, context);
+
+	dm_free(dc->ctx, context);
+context_alloc_fail:
+
+	return (result == DC_OK);
+
+}
+
+static void program_timing_sync(
+		struct dc_context *dc_ctx,
+		struct validate_context *ctx)
+{
+	uint8_t i;
+	uint8_t j;
+	uint8_t group_size = 0;
+	uint8_t tg_count = ctx->res_ctx.pool.controller_count;
+	struct timing_generator *tg_set[3];
+
+	for (i = 0; i < tg_count; i++) {
+		if (!ctx->res_ctx.controller_ctx[i].stream)
+			continue;
+
+		tg_set[0] = ctx->res_ctx.pool.timing_generators[i];
+		group_size = 1;
+
+		/* Add tg to the set, search rest of the tg's for ones with
+		 * same timing, add all tgs with same timing to the group
+		 */
+		for (j = i + 1; j < tg_count; j++) {
+			if (!ctx->res_ctx.controller_ctx[j].stream)
+				continue;
+
+			if (is_same_timing(
+				&ctx->res_ctx.controller_ctx[j].stream->public
+								.timing,
+				&ctx->res_ctx.controller_ctx[i].stream->public
+								.timing)) {
+				tg_set[group_size] =
+					ctx->res_ctx.pool.timing_generators[j];
+				group_size++;
+			}
+		}
+
+		/* Right now we limit to one timing sync group so if one is
+		 * found we break. A group has to be more than one tg.*/
+		if (group_size > 1)
+			break;
+	}
+
+	if(group_size > 1) {
+		dc_ctx->dc->hwss.enable_timing_synchronization(dc_ctx, group_size, tg_set);
+	}
+}
+
+static bool targets_changed(
+		struct dc *dc,
+		struct dc_target *targets[],
+		uint8_t target_count)
+{
+	uint8_t i;
+
+	if (target_count != dc->current_context.target_count)
+		return true;
+
+	for (i = 0; i < dc->current_context.target_count; i++) {
+		if (&dc->current_context.targets[i]->public != targets[i])
+			return true;
+	}
+
+	return false;
+}
+
+bool dc_commit_targets(
+	struct dc *dc,
+	struct dc_target *targets[],
+	uint8_t target_count)
+{
+	enum dc_status result = DC_ERROR_UNEXPECTED;
+	struct validate_context *context;
+	struct dc_validation_set set[4];
+	uint8_t i;
+
+	if (false == targets_changed(dc, targets, target_count))
+		return DC_OK;
+
+	dal_logger_write(dc->ctx->logger,
+				LOG_MAJOR_INTERFACE_TRACE,
+				LOG_MINOR_COMPONENT_DC,
+				"%s: %d targets\n",
+				__func__,
+				target_count);
+
+	for (i = 0; i < target_count; i++) {
+		struct dc_target *target = targets[i];
+
+		dc_target_log(target,
+				dc->ctx->logger,
+				LOG_MAJOR_INTERFACE_TRACE,
+				LOG_MINOR_COMPONENT_DC);
+
+		set[i].target = targets[i];
+		set[i].surface_count = 0;
+
+	}
+
+	context = dm_alloc(dc->ctx, sizeof(struct validate_context));
+	if (context == NULL)
+		goto context_alloc_fail;
+
+	result = dc->res_pool.funcs->validate_with_context(dc, set, target_count, context);
+	if (result != DC_OK){
+		BREAK_TO_DEBUGGER();
+		goto fail;
+	}
+
+	pplib_apply_safe_state(dc);
+
+	if (!dal_adapter_service_is_in_accelerated_mode(
+						dc->res_pool.adapter_srv)) {
+		dc->hwss.enable_accelerated_mode(dc);
+	}
+
+	for (i = 0; i < dc->current_context.target_count; i++) {
+		/*TODO: optimize this to happen only when necessary*/
+		dc_target_disable_memory_requests(
+				&dc->current_context.targets[i]->public);
+	}
+
+	if (result == DC_OK) {
+		dc->hwss.reset_hw_ctx(dc, context, target_count);
+
+		if (context->target_count > 0)
+			result = dc->hwss.apply_ctx_to_hw(dc, context);
+	}
+
+	for (i = 0; i < context->target_count; i++) {
+		struct dc_target *dc_target = &context->targets[i]->public;
+		if (context->targets[i]->status.surface_count > 0)
+			dc_target_enable_memory_requests(dc_target);
+	}
+
+	/* Release old targets */
+	for (i = 0; i < dc->current_context.target_count; i++) {
+		dc_target_release(
+				&dc->current_context.targets[i]->public);
+		dc->current_context.targets[i] = NULL;
+	}
+	/* Retain new targets*/
+	for (i = 0; i < context->target_count; i++) {
+		dc_target_retain(&context->targets[i]->public);
+	}
+
+	dc->current_context = *context;
+
+	program_timing_sync(dc->ctx, context);
+
+	pplib_apply_display_requirements(dc, context);
+
+	/* TODO: disable unused plls*/
+fail:
+	dm_free(dc->ctx, context);
+
+context_alloc_fail:
+	return (result == DC_OK);
+}
+
+uint8_t dc_get_current_target_count(const struct dc *dc)
+{
+	return dc->current_context.target_count;
+}
+
+struct dc_target *dc_get_target_at_index(const struct dc *dc, uint8_t i)
+{
+	if (i < dc->current_context.target_count)
+		return &dc->current_context.targets[i]->public;
+	return NULL;
+}
+
+const struct dc_link *dc_get_link_at_index(struct dc *dc, uint32_t link_index)
+{
+	return &dc->links[link_index]->public;
+}
+
+const struct graphics_object_id dc_get_link_id_at_index(
+	struct dc *dc, uint32_t link_index)
+{
+	return dc->links[link_index]->link_id;
+}
+
+const struct ddc_service *dc_get_ddc_at_index(
+	struct dc *dc, uint32_t link_index)
+{
+	return dc->links[link_index]->ddc;
+}
+
+const enum dc_irq_source dc_get_hpd_irq_source_at_index(
+	struct dc *dc, uint32_t link_index)
+{
+	return dc->links[link_index]->public.irq_source_hpd;
+}
+
+const struct audio **dc_get_audios(struct dc *dc)
+{
+	return (const struct audio **)dc->res_pool.audios;
+}
+
+void dc_get_caps(const struct dc *dc, struct dc_caps *caps)
+{
+	caps->max_targets = dc->res_pool.controller_count;
+	caps->max_links = dc->link_count;
+	caps->max_audios = dc->res_pool.audio_count;
+}
+
+void dc_flip_surface_addrs(struct dc* dc,
+		const struct dc_surface *const surfaces[],
+		struct dc_flip_addrs flip_addrs[],
+		uint32_t count)
+{
+	uint8_t i;
+	for (i = 0; i < count; i++) {
+		struct core_surface *surface = DC_SURFACE_TO_CORE(surfaces[i]);
+		/*
+		 * TODO figure out a good way to keep track of address. Until
+		 * then we'll have to awkwardly bypass the "const" surface.
+		 */
+		surface->public.address = flip_addrs[i].address;
+		surface->public.flip_immediate = flip_addrs[i].flip_immediate;
+
+		dc->hwss.update_plane_address(
+			dc,
+			surface,
+			DC_TARGET_TO_CORE(surface->status.dc_target));
+	}
+}
+
+enum dc_irq_source dc_interrupt_to_irq_source(
+		struct dc *dc,
+		uint32_t src_id,
+		uint32_t ext_id)
+{
+	return dal_irq_service_to_irq_source(dc->res_pool.irqs, src_id, ext_id);
+}
+
+
+void dc_interrupt_set(const struct dc *dc, enum dc_irq_source src, bool enable)
+{
+	dal_irq_service_set(dc->res_pool.irqs, src, enable);
+}
+
+void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src)
+{
+	dal_irq_service_ack(dc->res_pool.irqs, src);
+}
+
+const struct dc_target *dc_get_target_on_irq_source(
+		const struct dc *dc,
+		enum dc_irq_source src)
+{
+	uint8_t i, j;
+	uint8_t crtc_idx;
+
+	switch (src) {
+	case DC_IRQ_SOURCE_VUPDATE1:
+	case DC_IRQ_SOURCE_VUPDATE2:
+	case DC_IRQ_SOURCE_VUPDATE3:
+	case DC_IRQ_SOURCE_VUPDATE4:
+	case DC_IRQ_SOURCE_VUPDATE5:
+	case DC_IRQ_SOURCE_VUPDATE6:
+		crtc_idx = src - DC_IRQ_SOURCE_VUPDATE1;
+		break;
+	case DC_IRQ_SOURCE_PFLIP1:
+	case DC_IRQ_SOURCE_PFLIP2:
+	case DC_IRQ_SOURCE_PFLIP3:
+	case DC_IRQ_SOURCE_PFLIP4:
+	case DC_IRQ_SOURCE_PFLIP5:
+	case DC_IRQ_SOURCE_PFLIP6:
+	case DC_IRQ_SOURCE_PFLIP_UNDERLAY0:
+		crtc_idx = src - DC_IRQ_SOURCE_PFLIP1;
+		break;
+	default:
+		dm_error("%s: invalid irq source: %d\n!" ,__func__, src);
+		return NULL;
+	}
+
+	for (i = 0; i < dc->current_context.target_count; i++) {
+		struct core_target *target = dc->current_context.targets[i];
+
+		struct dc_target *dc_target;
+
+		if (NULL == target) {
+			dm_error("%s: 'dc_target' is NULL for irq source: %d\n!",
+					__func__, src);
+			continue;
+		}
+
+		dc_target = &target->public;
+
+		for (j = 0; j < target->public.stream_count; j++) {
+			const struct core_stream *stream =
+				DC_STREAM_TO_CORE(dc_target->streams[j]);
+			const uint8_t controller_idx = stream->controller_idx;
+
+			if (controller_idx == crtc_idx)
+				return dc_target;
+		}
+	}
+
+	return NULL;
+}
+
+void dc_set_power_state(
+	struct dc *dc,
+	enum dc_acpi_cm_power_state power_state,
+	enum dc_video_power_state video_power_state)
+{
+	dc->previous_power_state = dc->current_power_state;
+	dc->current_power_state = video_power_state;
+
+	switch (power_state) {
+	case DC_ACPI_CM_POWER_STATE_D0:
+		init_hw(dc);
+		break;
+	default:
+		/* NULL means "reset/release all DC targets" */
+		dc_commit_targets(dc, NULL, 0);
+
+		dc->hwss.power_down(dc);
+		break;
+	}
+
+}
+
+void dc_resume(const struct dc *dc)
+{
+	uint32_t i;
+
+	for (i = 0; i < dc->link_count; i++)
+		core_link_resume(dc->links[i]);
+}
+
+bool dc_read_dpcd(
+		struct dc *dc,
+		uint32_t link_index,
+		uint32_t address,
+		uint8_t *data,
+		uint32_t size)
+{
+	struct core_link *link =
+			DC_LINK_TO_LINK(dc_get_link_at_index(dc, link_index));
+
+	enum ddc_result r = dal_ddc_service_read_dpcd_data(
+			link->ddc,
+			address,
+			data,
+			size);
+	return r == DDC_RESULT_SUCESSFULL;
+}
+
+bool dc_write_dpcd(
+		struct dc *dc,
+		uint32_t link_index,
+		uint32_t address,
+		const uint8_t *data,
+		uint32_t size)
+{
+	struct core_link *link =
+			DC_LINK_TO_LINK(dc_get_link_at_index(dc, link_index));
+
+	enum ddc_result r = dal_ddc_service_write_dpcd_data(
+			link->ddc,
+			address,
+			data,
+			size);
+	return r == DDC_RESULT_SUCESSFULL;
+}
+
+bool dc_link_add_remote_sink(const struct dc_link *link, struct dc_sink *sink)
+{
+	struct core_link *core_link = DC_LINK_TO_LINK(link);
+	struct dc_link *dc_link = &core_link->public;
+
+	if (dc_link->sink_count >= MAX_SINKS_PER_LINK) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	dc_link->remote_sinks[link->sink_count] = sink;
+	dc_link->sink_count++;
+
+	return true;
+}
+
+void dc_link_set_sink(const struct dc_link *link, struct dc_sink *sink)
+{
+	struct core_link *core_link = DC_LINK_TO_LINK(link);
+	struct dc_link *dc_link = &core_link->public;
+
+	dc_link->local_sink = sink;
+
+	if (sink == NULL) {
+		dc_link->sink_count = 0;
+		dc_link->type = dc_connection_none;
+	} else {
+		dc_link->sink_count = 1;
+		dc_link->type = dc_connection_single;
+	}
+}
+
+void dc_link_remove_remote_sink(const struct dc_link *link, const struct dc_sink *sink)
+{
+	int i;
+	struct core_link *core_link = DC_LINK_TO_LINK(link);
+	struct dc_link *dc_link = &core_link->public;
+
+	if (!link->sink_count) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	for (i = 0; i < dc_link->sink_count; i++) {
+		if (dc_link->remote_sinks[i] == sink) {
+			dc_sink_release(sink);
+			dc_link->remote_sinks[i] = NULL;
+
+			/* shrink array to remove empty place */
+			while (i < dc_link->sink_count - 1) {
+				dc_link->remote_sinks[i] = dc_link->remote_sinks[i+1];
+				i++;
+			}
+
+			dc_link->sink_count--;
+			return;
+		}
+	}
+}
+
+uint8_t dc_get_dig_index(const struct dc_stream *stream)
+{
+
+	struct core_stream *core_stream = DC_STREAM_TO_CORE(stream);
+
+	switch (core_stream->stream_enc->id) {
+	case ENGINE_ID_DIGA:
+		return 0;
+	case ENGINE_ID_DIGB:
+		return 1;
+	case ENGINE_ID_DIGC:
+		return 2;
+	case ENGINE_ID_DIGD:
+		return 3;
+	case ENGINE_ID_DIGE:
+		return 4;
+	case ENGINE_ID_DIGF:
+		return 5;
+	case ENGINE_ID_DIGG:
+		return 6;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+enum gpio_ddc_line dc_get_ddc_line(
+		const struct dc_stream *stream)
+{
+
+	struct core_sink *core_sink = DC_SINK_TO_CORE(stream->sink);
+	struct ddc *ddc_line = dal_ddc_service_get_ddc_pin(
+			core_sink->link->ddc);
+
+	return dal_ddc_get_line(ddc_line);
+}
+
+enum signal_type dc_get_display_signal(
+		const struct dc_stream *stream)
+{
+	return stream->sink->sink_signal;
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c
new file mode 100644
index 000000000000..db4f1313e056
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_hw_sequencer.c
@@ -0,0 +1,56 @@ 
+/*
+ * 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 "core_types.h"
+
+#if defined(CONFIG_DRM_AMD_DAL_DCE10_0)
+#include "dce100/dce100_hw_sequencer.h"
+#endif
+#if defined(CONFIG_DRM_AMD_DAL_DCE11_0)
+#include "dce110/dce110_hw_sequencer.h"
+#endif
+
+bool dc_construct_hw_sequencer(
+				struct adapter_service *adapter_serv,
+				struct dc *dc)
+{
+	enum dce_version dce_ver = dal_adapter_service_get_dce_version(adapter_serv);
+
+	switch (dce_ver)
+	{
+#if defined(CONFIG_DRM_AMD_DAL_DCE10_0)
+	case DCE_VERSION_10_0:
+		return dce100_hw_sequencer_construct(dc);
+#endif
+#if defined(CONFIG_DRM_AMD_DAL_DCE11_0)
+	case DCE_VERSION_11_0:
+		return dce110_hw_sequencer_construct(dc);
+#endif
+	default:
+		break;
+	}
+
+	return false;
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link.c
new file mode 100644
index 000000000000..b0ef028ad0fc
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link.c
@@ -0,0 +1,1644 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "dm_helpers.h"
+#include "dc.h"
+#include "core_dc.h"
+#include "adapter_service_interface.h"
+#include "grph_object_id.h"
+#include "gpio_service_interface.h"
+#include "core_status.h"
+#include "dc_link_dp.h"
+#include "dc_link_ddc.h"
+#include "link_hwss.h"
+#include "stream_encoder.h"
+#include "link_encoder.h"
+#include "hw_sequencer.h"
+#include "fixed31_32.h"
+
+
+#define LINK_INFO(...) \
+	dal_logger_write(dc_ctx->logger, \
+		LOG_MAJOR_HW_TRACE, LOG_MINOR_HW_TRACE_HOTPLUG, \
+		__VA_ARGS__)
+
+
+/*******************************************************************************
+ * Private structures
+ ******************************************************************************/
+
+enum {
+	LINK_RATE_REF_FREQ_IN_MHZ = 27,
+	PEAK_FACTOR_X1000 = 1006
+};
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static void destruct(struct core_link *link)
+{
+	if (link->ddc)
+		dal_ddc_service_destroy(&link->ddc);
+
+	if(link->link_enc)
+		link->ctx->dc->res_pool.funcs->link_enc_destroy(&link->link_enc);
+}
+
+/*
+ *  Function: program_hpd_filter
+ *
+ *  @brief
+ *     Programs HPD filter on associated HPD line
+ *
+ *  @param [in] delay_on_connect_in_ms: Connect filter timeout
+ *  @param [in] delay_on_disconnect_in_ms: Disconnect filter timeout
+ *
+ *  @return
+ *     true on success, false otherwise
+ */
+static bool program_hpd_filter(
+	const struct core_link *link)
+{
+	bool result = false;
+
+	struct irq *hpd;
+
+	int delay_on_connect_in_ms = 0;
+	int delay_on_disconnect_in_ms = 0;
+
+	/* Verify feature is supported */
+	switch (link->public.connector_signal) {
+	case SIGNAL_TYPE_DVI_SINGLE_LINK:
+	case SIGNAL_TYPE_DVI_DUAL_LINK:
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+		/* Program hpd filter */
+		delay_on_connect_in_ms = 500;
+		delay_on_disconnect_in_ms = 100;
+		break;
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+		/* Program hpd filter to allow DP signal to settle */
+		delay_on_connect_in_ms = 20;
+		delay_on_disconnect_in_ms = 0;
+		break;
+	case SIGNAL_TYPE_LVDS:
+	case SIGNAL_TYPE_EDP:
+	default:
+		/* Don't program hpd filter */
+		return false;
+	}
+
+	/* Obtain HPD handle */
+	hpd = dal_adapter_service_obtain_hpd_irq(
+		link->adapter_srv, link->link_id);
+
+	if (!hpd)
+		return result;
+
+	/* Setup HPD filtering */
+	if (dal_irq_open(hpd) == GPIO_RESULT_OK) {
+		struct gpio_hpd_config config;
+
+		config.delay_on_connect = delay_on_connect_in_ms;
+		config.delay_on_disconnect = delay_on_disconnect_in_ms;
+
+		dal_irq_setup_hpd_filter(hpd, &config);
+
+		dal_irq_close(hpd);
+
+		result = true;
+	} else {
+		ASSERT_CRITICAL(false);
+	}
+
+	/* Release HPD handle */
+	dal_adapter_service_release_irq(link->adapter_srv, hpd);
+
+	return result;
+}
+
+static bool detect_sink(struct core_link *link, enum dc_connection_type *type)
+{
+	uint32_t is_hpd_high = 0;
+	struct irq *hpd_pin;
+
+	/* todo: may need to lock gpio access */
+	hpd_pin = dal_adapter_service_obtain_hpd_irq(
+			link->adapter_srv,
+			link->link_id);
+	if (hpd_pin == NULL)
+		goto hpd_gpio_failure;
+
+	dal_irq_open(hpd_pin);
+	dal_irq_get_value(hpd_pin, &is_hpd_high);
+	dal_irq_close(hpd_pin);
+	dal_adapter_service_release_irq(
+		link->adapter_srv,
+		hpd_pin);
+
+	if (is_hpd_high) {
+		*type = dc_connection_single;
+		/* TODO: need to do the actual detection */
+	} else {
+		*type = dc_connection_none;
+	}
+
+	return true;
+
+hpd_gpio_failure:
+	return false;
+}
+
+
+enum ddc_transaction_type get_ddc_transaction_type(
+		enum signal_type sink_signal)
+{
+	enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE;
+
+
+	switch (sink_signal) {
+	case SIGNAL_TYPE_DVI_SINGLE_LINK:
+	case SIGNAL_TYPE_DVI_DUAL_LINK:
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+	case SIGNAL_TYPE_LVDS:
+	case SIGNAL_TYPE_RGB:
+		transaction_type = DDC_TRANSACTION_TYPE_I2C;
+		break;
+
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_EDP:
+		transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
+		break;
+
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+		/* MST does not use I2COverAux, but there is the
+		 * SPECIAL use case for "immediate dwnstrm device
+		 * access" (EPR#370830). */
+		transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
+		break;
+
+	default:
+		break;
+	}
+
+
+	return transaction_type;
+}
+
+static enum signal_type get_basic_signal_type(
+	struct graphics_object_id encoder,
+	struct graphics_object_id downstream)
+{
+	if (downstream.type == OBJECT_TYPE_CONNECTOR) {
+		switch (downstream.id) {
+		case CONNECTOR_ID_SINGLE_LINK_DVII:
+			switch (encoder.id) {
+			case ENCODER_ID_INTERNAL_DAC1:
+			case ENCODER_ID_INTERNAL_KLDSCP_DAC1:
+			case ENCODER_ID_INTERNAL_DAC2:
+			case ENCODER_ID_INTERNAL_KLDSCP_DAC2:
+				return SIGNAL_TYPE_RGB;
+			default:
+				return SIGNAL_TYPE_DVI_SINGLE_LINK;
+			}
+		break;
+		case CONNECTOR_ID_DUAL_LINK_DVII:
+		{
+			switch (encoder.id) {
+			case ENCODER_ID_INTERNAL_DAC1:
+			case ENCODER_ID_INTERNAL_KLDSCP_DAC1:
+			case ENCODER_ID_INTERNAL_DAC2:
+			case ENCODER_ID_INTERNAL_KLDSCP_DAC2:
+				return SIGNAL_TYPE_RGB;
+			default:
+				return SIGNAL_TYPE_DVI_DUAL_LINK;
+			}
+		}
+		break;
+		case CONNECTOR_ID_SINGLE_LINK_DVID:
+			return SIGNAL_TYPE_DVI_SINGLE_LINK;
+		case CONNECTOR_ID_DUAL_LINK_DVID:
+			return SIGNAL_TYPE_DVI_DUAL_LINK;
+		case CONNECTOR_ID_VGA:
+			return SIGNAL_TYPE_RGB;
+		case CONNECTOR_ID_HDMI_TYPE_A:
+			return SIGNAL_TYPE_HDMI_TYPE_A;
+		case CONNECTOR_ID_LVDS:
+			return SIGNAL_TYPE_LVDS;
+		case CONNECTOR_ID_DISPLAY_PORT:
+			return SIGNAL_TYPE_DISPLAY_PORT;
+		case CONNECTOR_ID_EDP:
+			return SIGNAL_TYPE_EDP;
+		default:
+			return SIGNAL_TYPE_NONE;
+		}
+	} else if (downstream.type == OBJECT_TYPE_ENCODER) {
+		switch (downstream.id) {
+		case ENCODER_ID_EXTERNAL_NUTMEG:
+		case ENCODER_ID_EXTERNAL_TRAVIS:
+			return SIGNAL_TYPE_DISPLAY_PORT;
+		default:
+			return SIGNAL_TYPE_NONE;
+		}
+	}
+
+	return SIGNAL_TYPE_NONE;
+}
+
+/*
+ * @brief
+ * Check whether there is a dongle on DP connector
+ */
+static bool is_dp_sink_present(struct core_link *link)
+{
+	enum gpio_result gpio_result;
+	uint32_t clock_pin = 0;
+	uint32_t data_pin = 0;
+
+	struct ddc *ddc;
+
+	enum connector_id connector_id =
+		dal_graphics_object_id_get_connector_id(link->link_id);
+
+	bool present =
+		((connector_id == CONNECTOR_ID_DISPLAY_PORT) ||
+		(connector_id == CONNECTOR_ID_EDP));
+
+	ddc = dal_adapter_service_obtain_ddc(link->adapter_srv, link->link_id);
+
+	if (!ddc)
+		return present;
+
+	/* Open GPIO and set it to I2C mode */
+	/* Note: this GpioMode_Input will be converted
+	 * to GpioConfigType_I2cAuxDualMode in GPIO component,
+	 * which indicates we need additional delay */
+
+	if (GPIO_RESULT_OK != dal_ddc_open(
+		ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) {
+		dal_adapter_service_release_ddc(link->adapter_srv, ddc);
+
+		return present;
+	}
+
+	/* Read GPIO: DP sink is present if both clock and data pins are zero */
+	/* [anaumov] in DAL2, there was no check for GPIO failure */
+
+	gpio_result = dal_ddc_get_clock(ddc, &clock_pin);
+	ASSERT(gpio_result == GPIO_RESULT_OK);
+
+	if (gpio_result == GPIO_RESULT_OK)
+		if (link->link_enc->features.flags.bits.
+						DP_SINK_DETECT_POLL_DATA_PIN)
+			gpio_result = dal_ddc_get_data(ddc, &data_pin);
+
+	present = (gpio_result == GPIO_RESULT_OK) && !(clock_pin || data_pin);
+
+	dal_ddc_close(ddc);
+
+	dal_adapter_service_release_ddc(link->adapter_srv, ddc);
+
+	return present;
+}
+
+/*
+ * @brief
+ * Detect output sink type
+ */
+static enum signal_type link_detect_sink(struct core_link *link)
+{
+	enum signal_type result = get_basic_signal_type(
+		link->link_enc->id, link->link_id);
+
+	/* Internal digital encoder will detect only dongles
+	 * that require digital signal */
+
+	/* Detection mechanism is different
+	 * for different native connectors.
+	 * LVDS connector supports only LVDS signal;
+	 * PCIE is a bus slot, the actual connector needs to be detected first;
+	 * eDP connector supports only eDP signal;
+	 * HDMI should check straps for audio */
+
+	/* PCIE detects the actual connector on add-on board */
+
+	if (link->link_id.id == CONNECTOR_ID_PCIE) {
+		/* ZAZTODO implement PCIE add-on card detection */
+	}
+
+	switch (link->link_id.id) {
+	case CONNECTOR_ID_HDMI_TYPE_A: {
+		/* check audio support:
+		 * if native HDMI is not supported, switch to DVI */
+		union audio_support audio_support =
+			dal_adapter_service_get_audio_support(
+				link->adapter_srv);
+
+		if (!audio_support.bits.HDMI_AUDIO_NATIVE)
+			if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A)
+				result = SIGNAL_TYPE_DVI_SINGLE_LINK;
+	}
+	break;
+	case CONNECTOR_ID_DISPLAY_PORT: {
+
+		/* Check whether DP signal detected: if not -
+		 * we assume signal is DVI; it could be corrected
+		 * to HDMI after dongle detection */
+		if (!is_dp_sink_present(link))
+			result = SIGNAL_TYPE_DVI_SINGLE_LINK;
+	}
+	break;
+	default:
+	break;
+	}
+
+	return result;
+}
+
+static enum signal_type decide_signal_from_strap_and_dongle_type(
+		enum display_dongle_type dongle_type,
+		union audio_support *audio_support)
+{
+	enum signal_type signal = SIGNAL_TYPE_NONE;
+
+	switch (dongle_type) {
+	case DISPLAY_DONGLE_DP_HDMI_DONGLE:
+		if (audio_support->bits.HDMI_AUDIO_ON_DONGLE)
+			signal =  SIGNAL_TYPE_HDMI_TYPE_A;
+		else
+			signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+		break;
+	case DISPLAY_DONGLE_DP_DVI_DONGLE:
+		signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+		break;
+	case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
+		if (audio_support->bits.HDMI_AUDIO_NATIVE)
+			signal =  SIGNAL_TYPE_HDMI_TYPE_A;
+		else
+			signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+		break;
+	default:
+		signal = SIGNAL_TYPE_NONE;
+		break;
+	}
+
+	return signal;
+}
+
+static enum signal_type dp_passive_dongle_detection(
+		struct ddc_service *ddc,
+		struct display_sink_capability *sink_cap,
+		union audio_support *audio_support)
+{
+	/* TODO:These 2 functions should be protected for upstreaming purposes
+	 * in case hackers want to save 10 cents hdmi license fee
+	 */
+	dal_ddc_service_i2c_query_dp_dual_mode_adaptor(
+						ddc, sink_cap);
+	return decide_signal_from_strap_and_dongle_type(
+			sink_cap->dongle_type,
+			audio_support);
+}
+
+static void link_disconnect_sink(struct core_link *link)
+{
+	if (link->public.local_sink) {
+		dc_sink_release(link->public.local_sink);
+		link->public.local_sink = NULL;
+	}
+
+	link->dpcd_sink_count = 0;
+}
+
+static enum dc_edid_status read_edid(
+	struct core_link *link,
+	struct core_sink *sink)
+{
+	uint32_t edid_retry = 3;
+	enum dc_edid_status edid_status;
+
+	/* some dongles read edid incorrectly the first time,
+	 * do check sum and retry to make sure read correct edid.
+	 */
+	do {
+		sink->public.dc_edid.length =
+				dal_ddc_service_edid_query(link->ddc);
+
+		if (0 == sink->public.dc_edid.length)
+			return EDID_NO_RESPONSE;
+
+		dal_ddc_service_get_edid_buf(link->ddc,
+				sink->public.dc_edid.raw_edid);
+		edid_status = dm_helpers_parse_edid_caps(
+				sink->ctx,
+				&sink->public.dc_edid,
+				&sink->public.edid_caps);
+		--edid_retry;
+		if (edid_status == EDID_BAD_CHECKSUM)
+			dal_logger_write(link->ctx->logger,
+					LOG_MAJOR_WARNING,
+					LOG_MINOR_DETECTION_EDID_PARSER,
+					"Bad EDID checksum, retry remain: %d\n",
+					edid_retry);
+	} while (edid_status == EDID_BAD_CHECKSUM && edid_retry > 0);
+
+	return edid_status;
+}
+
+static void detect_dp(
+	struct core_link *link,
+	struct display_sink_capability *sink_caps,
+	bool *converter_disable_audio,
+	union audio_support *audio_support,
+	bool boot)
+{
+	sink_caps->signal = link_detect_sink(link);
+	sink_caps->transaction_type =
+		get_ddc_transaction_type(sink_caps->signal);
+
+	if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) {
+		sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT;
+		detect_dp_sink_caps(link);
+
+		/* DP active dongles */
+		if (is_dp_active_dongle(link)) {
+			if (!link->dpcd_caps.sink_count.bits.SINK_COUNT) {
+				link->public.type = dc_connection_none;
+				/*
+				 * active dongle unplug processing for short irq
+				 */
+				link_disconnect_sink(link);
+				return;
+			}
+
+			if (link->dpcd_caps.dongle_type !=
+			DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
+				*converter_disable_audio = true;
+			}
+		}
+		if (is_mst_supported(link)) {
+			sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT_MST;
+
+			/*
+			 * This call will initiate MST topology discovery. Which
+			 * will detect MST ports and add new DRM connector DRM
+			 * framework. Then read EDID via remote i2c over aux. In
+			 * the end, will notify DRM detect result and save EDID
+			 * into DRM framework.
+			 *
+			 * .detect is called by .fill_modes.
+			 * .fill_modes is called by user mode ioctl
+			 * DRM_IOCTL_MODE_GETCONNECTOR.
+			 *
+			 * .get_modes is called by .fill_modes.
+			 *
+			 * call .get_modes, AMDGPU DM implementation will create
+			 * new dc_sink and add to dc_link. For long HPD plug
+			 * in/out, MST has its own handle.
+			 *
+			 * Therefore, just after dc_create, link->sink is not
+			 * created for MST until user mode app calls
+			 * DRM_IOCTL_MODE_GETCONNECTOR.
+			 *
+			 * Need check ->sink usages in case ->sink = NULL
+			 * TODO: s3 resume check
+			 */
+
+			if (dm_helpers_dp_mst_start_top_mgr(
+				link->ctx,
+				&link->public, boot)) {
+				link->public.type = dc_connection_mst_branch;
+			} else {
+				/* MST not supported */
+				sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT;
+			}
+		}
+	} else {
+		/* DP passive dongles */
+		sink_caps->signal = dp_passive_dongle_detection(link->ddc,
+				sink_caps,
+				audio_support);
+	}
+}
+
+bool dc_link_detect(const struct dc_link *dc_link, bool boot)
+{
+	struct core_link *link = DC_LINK_TO_LINK(dc_link);
+	struct dc_sink_init_data sink_init_data = { 0 };
+	struct display_sink_capability sink_caps = { 0 };
+	uint8_t i;
+	bool converter_disable_audio = false;
+	union audio_support audio_support =
+		dal_adapter_service_get_audio_support(
+			link->adapter_srv);
+	enum dc_edid_status edid_status;
+	struct dc_context *dc_ctx = link->ctx;
+	struct dc_sink *dc_sink;
+	struct core_sink *sink = NULL;
+	enum dc_connection_type new_connection_type = dc_connection_none;
+
+	if (link->public.connector_signal == SIGNAL_TYPE_VIRTUAL)
+		return false;
+
+	if (false == detect_sink(link, &new_connection_type)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	link_disconnect_sink(link);
+
+	if (new_connection_type != dc_connection_none) {
+		link->public.type = new_connection_type;
+
+		/* From Disconnected-to-Connected. */
+		switch (link->public.connector_signal) {
+		case SIGNAL_TYPE_HDMI_TYPE_A: {
+			sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
+			if (audio_support.bits.HDMI_AUDIO_NATIVE)
+				sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A;
+			else
+				sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+			break;
+		}
+
+		case SIGNAL_TYPE_DVI_SINGLE_LINK: {
+			sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
+			sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+			break;
+		}
+
+		case SIGNAL_TYPE_DVI_DUAL_LINK: {
+			sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
+			sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK;
+			break;
+		}
+
+		case SIGNAL_TYPE_EDP: {
+			detect_dp_sink_caps(link);
+			sink_caps.transaction_type =
+				DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
+			sink_caps.signal = SIGNAL_TYPE_EDP;
+			break;
+		}
+
+		case SIGNAL_TYPE_DISPLAY_PORT: {
+			detect_dp(
+				link,
+				&sink_caps,
+				&converter_disable_audio,
+				&audio_support, boot);
+
+			/* Active dongle downstream unplug */
+			if (link->public.type == dc_connection_none)
+				return true;
+
+			if (link->public.type == dc_connection_mst_branch) {
+				LINK_INFO("link=%d, mst branch is now Connected\n",
+					link->public.link_index);
+				return false;
+			}
+
+			break;
+		}
+
+		default:
+			DC_ERROR("Invalid connector type! signal:%d\n",
+				link->public.connector_signal);
+			return false;
+		} /* switch() */
+
+		if (link->dpcd_caps.sink_count.bits.SINK_COUNT)
+			link->dpcd_sink_count = link->dpcd_caps.sink_count.
+					bits.SINK_COUNT;
+			else
+				link->dpcd_sink_count = 1;
+
+
+		dal_ddc_service_set_transaction_type(
+						link->ddc,
+						sink_caps.transaction_type);
+
+		sink_init_data.link = &link->public;
+		sink_init_data.sink_signal = sink_caps.signal;
+		sink_init_data.dongle_max_pix_clk =
+			sink_caps.max_hdmi_pixel_clock;
+		sink_init_data.converter_disable_audio =
+			converter_disable_audio;
+
+		dc_sink = dc_sink_create(&sink_init_data);
+		if (!dc_sink) {
+			DC_ERROR("Failed to create sink!\n");
+			return false;
+		}
+
+		sink = DC_SINK_TO_CORE(dc_sink);
+		link->public.local_sink = &sink->public;
+
+		edid_status = read_edid(link, sink);
+
+		switch (edid_status) {
+		case EDID_BAD_CHECKSUM:
+			dal_logger_write(link->ctx->logger,
+				LOG_MAJOR_ERROR,
+				LOG_MINOR_DETECTION_EDID_PARSER,
+				"EDID checksum invalid.\n");
+			break;
+		case EDID_NO_RESPONSE:
+			dal_logger_write(link->ctx->logger,
+				LOG_MAJOR_ERROR,
+				LOG_MINOR_DETECTION_EDID_PARSER,
+				"No EDID read.\n");
+			return false;
+
+		default:
+			break;
+		}
+
+		dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_DETECTION,
+			LOG_MINOR_DETECTION_EDID_PARSER,
+			"%s: "
+			"manufacturer_id = %X, "
+			"product_id = %X, "
+			"serial_number = %X, "
+			"manufacture_week = %d, "
+			"manufacture_year = %d, "
+			"display_name = %s, "
+			"speaker_flag = %d, "
+			"audio_mode_count = %d\n",
+			__func__,
+			sink->public.edid_caps.manufacturer_id,
+			sink->public.edid_caps.product_id,
+			sink->public.edid_caps.serial_number,
+			sink->public.edid_caps.manufacture_week,
+			sink->public.edid_caps.manufacture_year,
+			sink->public.edid_caps.display_name,
+			sink->public.edid_caps.speaker_flags,
+			sink->public.edid_caps.audio_mode_count);
+
+		for (i = 0; i < sink->public.edid_caps.audio_mode_count; i++) {
+			dal_logger_write(link->ctx->logger,
+				LOG_MAJOR_DETECTION,
+				LOG_MINOR_DETECTION_EDID_PARSER,
+				"%s: mode number = %d, "
+				"format_code = %d, "
+				"channel_count = %d, "
+				"sample_rate = %d, "
+				"sample_size = %d\n",
+				__func__,
+				i,
+				sink->public.edid_caps.audio_modes[i].format_code,
+				sink->public.edid_caps.audio_modes[i].channel_count,
+				sink->public.edid_caps.audio_modes[i].sample_rate,
+				sink->public.edid_caps.audio_modes[i].sample_size);
+		}
+
+	} else {
+		/* From Connected-to-Disconnected. */
+		if (link->public.type == dc_connection_mst_branch) {
+			LINK_INFO("link=%d, mst branch is now Disconnected\n",
+				link->public.link_index);
+			dm_helpers_dp_mst_stop_top_mgr(link->ctx, &link->public);
+		}
+
+		link->public.type = dc_connection_none;
+		sink_caps.signal = SIGNAL_TYPE_NONE;
+	}
+
+	LINK_INFO("link=%d, dc_sink_in=%p is now %s\n",
+		link->public.link_index, &sink->public,
+		(sink_caps.signal == SIGNAL_TYPE_NONE ?
+			"Disconnected":"Connected"));
+
+	return true;
+}
+
+static enum hpd_source_id get_hpd_line(
+		struct core_link *link,
+		struct adapter_service *as)
+{
+	struct irq *hpd;
+	enum hpd_source_id hpd_id = HPD_SOURCEID_UNKNOWN;
+
+	hpd = dal_adapter_service_obtain_hpd_irq(as, link->link_id);
+
+	if (hpd) {
+		switch (dal_irq_get_source(hpd)) {
+		case DC_IRQ_SOURCE_HPD1:
+			hpd_id = HPD_SOURCEID1;
+		break;
+		case DC_IRQ_SOURCE_HPD2:
+			hpd_id = HPD_SOURCEID2;
+		break;
+		case DC_IRQ_SOURCE_HPD3:
+			hpd_id = HPD_SOURCEID3;
+		break;
+		case DC_IRQ_SOURCE_HPD4:
+			hpd_id = HPD_SOURCEID4;
+		break;
+		case DC_IRQ_SOURCE_HPD5:
+			hpd_id = HPD_SOURCEID5;
+		break;
+		case DC_IRQ_SOURCE_HPD6:
+			hpd_id = HPD_SOURCEID6;
+		break;
+		default:
+			BREAK_TO_DEBUGGER();
+		break;
+		}
+
+		dal_adapter_service_release_irq(as, hpd);
+	}
+
+	return hpd_id;
+}
+
+static enum channel_id get_ddc_line(struct core_link *link, struct adapter_service *as)
+{
+	struct ddc *ddc;
+	enum channel_id channel = CHANNEL_ID_UNKNOWN;
+
+	ddc = dal_adapter_service_obtain_ddc(as, link->link_id);
+
+	if (ddc) {
+		switch (dal_ddc_get_line(ddc)) {
+		case GPIO_DDC_LINE_DDC1:
+			channel = CHANNEL_ID_DDC1;
+			break;
+		case GPIO_DDC_LINE_DDC2:
+			channel = CHANNEL_ID_DDC2;
+			break;
+		case GPIO_DDC_LINE_DDC3:
+			channel = CHANNEL_ID_DDC3;
+			break;
+		case GPIO_DDC_LINE_DDC4:
+			channel = CHANNEL_ID_DDC4;
+			break;
+		case GPIO_DDC_LINE_DDC5:
+			channel = CHANNEL_ID_DDC5;
+			break;
+		case GPIO_DDC_LINE_DDC6:
+			channel = CHANNEL_ID_DDC6;
+			break;
+		case GPIO_DDC_LINE_DDC_VGA:
+			channel = CHANNEL_ID_DDC_VGA;
+			break;
+		case GPIO_DDC_LINE_I2C_PAD:
+			channel = CHANNEL_ID_I2C_PAD;
+			break;
+		default:
+			BREAK_TO_DEBUGGER();
+			break;
+		}
+
+		dal_adapter_service_release_ddc(as, ddc);
+	}
+
+	return channel;
+}
+
+static enum transmitter translate_encoder_to_transmitter(
+	struct graphics_object_id encoder)
+{
+	switch (encoder.id) {
+	case ENCODER_ID_INTERNAL_UNIPHY:
+		switch (encoder.enum_id) {
+		case ENUM_ID_1:
+			return TRANSMITTER_UNIPHY_A;
+		case ENUM_ID_2:
+			return TRANSMITTER_UNIPHY_B;
+		default:
+			return TRANSMITTER_UNKNOWN;
+		}
+	break;
+	case ENCODER_ID_INTERNAL_UNIPHY1:
+		switch (encoder.enum_id) {
+		case ENUM_ID_1:
+			return TRANSMITTER_UNIPHY_C;
+		case ENUM_ID_2:
+			return TRANSMITTER_UNIPHY_D;
+		default:
+			return TRANSMITTER_UNKNOWN;
+		}
+	break;
+	case ENCODER_ID_INTERNAL_UNIPHY2:
+		switch (encoder.enum_id) {
+		case ENUM_ID_1:
+			return TRANSMITTER_UNIPHY_E;
+		case ENUM_ID_2:
+			return TRANSMITTER_UNIPHY_F;
+		default:
+			return TRANSMITTER_UNKNOWN;
+		}
+	break;
+	case ENCODER_ID_INTERNAL_UNIPHY3:
+		switch (encoder.enum_id) {
+		case ENUM_ID_1:
+			return TRANSMITTER_UNIPHY_G;
+		default:
+			return TRANSMITTER_UNKNOWN;
+		}
+	break;
+	case ENCODER_ID_EXTERNAL_NUTMEG:
+		switch (encoder.enum_id) {
+		case ENUM_ID_1:
+			return TRANSMITTER_NUTMEG_CRT;
+		default:
+			return TRANSMITTER_UNKNOWN;
+		}
+	break;
+	case ENCODER_ID_EXTERNAL_TRAVIS:
+		switch (encoder.enum_id) {
+		case ENUM_ID_1:
+			return TRANSMITTER_TRAVIS_CRT;
+		case ENUM_ID_2:
+			return TRANSMITTER_TRAVIS_LCD;
+		default:
+			return TRANSMITTER_UNKNOWN;
+		}
+	break;
+	default:
+		return TRANSMITTER_UNKNOWN;
+	}
+}
+
+
+static bool construct(
+	struct core_link *link,
+	const struct link_init_data *init_params)
+{
+	uint8_t i;
+	struct adapter_service *as = init_params->adapter_srv;
+	struct irq *hpd_gpio = NULL;
+	struct ddc_service_init_data ddc_service_init_data = { 0 };
+	struct dc_context *dc_ctx = init_params->ctx;
+	struct encoder_init_data enc_init_data = { 0 };
+	struct integrated_info info = {{{ 0 }}};
+
+	link->dc = init_params->dc;
+	link->adapter_srv = as;
+	link->ctx = dc_ctx;
+	link->public.link_index = init_params->link_index;
+
+	link->link_id = dal_adapter_service_get_connector_obj_id(
+			as,
+			init_params->connector_index);
+
+	if (link->link_id.type != OBJECT_TYPE_CONNECTOR) {
+		dm_error("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d!\n",
+				__func__, init_params->connector_index);
+		goto create_fail;
+	}
+
+	switch (link->link_id.id) {
+	case CONNECTOR_ID_HDMI_TYPE_A:
+		link->public.connector_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+		break;
+	case CONNECTOR_ID_SINGLE_LINK_DVID:
+	case CONNECTOR_ID_SINGLE_LINK_DVII:
+		link->public.connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+		break;
+	case CONNECTOR_ID_DUAL_LINK_DVID:
+	case CONNECTOR_ID_DUAL_LINK_DVII:
+		link->public.connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK;
+		break;
+	case CONNECTOR_ID_DISPLAY_PORT:
+		link->public.connector_signal =	SIGNAL_TYPE_DISPLAY_PORT;
+		hpd_gpio = dal_adapter_service_obtain_hpd_irq(
+					as,
+					link->link_id);
+
+		if (hpd_gpio != NULL) {
+			link->public.irq_source_hpd_rx =
+					dal_irq_get_rx_source(hpd_gpio);
+			dal_adapter_service_release_irq(
+					as, hpd_gpio);
+		}
+
+		break;
+	case CONNECTOR_ID_EDP:
+		link->public.connector_signal = SIGNAL_TYPE_EDP;
+		hpd_gpio = dal_adapter_service_obtain_hpd_irq(
+					as,
+					link->link_id);
+
+		if (hpd_gpio != NULL) {
+			link->public.irq_source_hpd_rx =
+					dal_irq_get_rx_source(hpd_gpio);
+			dal_adapter_service_release_irq(
+					as, hpd_gpio);
+		}
+		break;
+	default:
+		dal_logger_write(dc_ctx->logger,
+			LOG_MAJOR_WARNING, LOG_MINOR_TM_LINK_SRV,
+			"Unsupported Connector type:%d!\n", link->link_id.id);
+		goto create_fail;
+	}
+
+	/* TODO: #DAL3 Implement id to str function.*/
+	LINK_INFO("Connector[%d] description:"
+			"signal %d\n",
+			init_params->connector_index,
+			link->public.connector_signal);
+
+	hpd_gpio = dal_adapter_service_obtain_hpd_irq(as, link->link_id);
+
+	if (hpd_gpio != NULL) {
+		link->public.irq_source_hpd = dal_irq_get_source(hpd_gpio);
+		dal_adapter_service_release_irq(as, hpd_gpio);
+	}
+
+	ddc_service_init_data.as = as;
+	ddc_service_init_data.ctx = link->ctx;
+	ddc_service_init_data.id = link->link_id;
+	link->ddc = dal_ddc_service_create(&ddc_service_init_data);
+
+	if (NULL == link->ddc) {
+		DC_ERROR("Failed to create ddc_service!\n");
+		goto create_fail;
+	}
+
+	enc_init_data.adapter_service = as;
+	enc_init_data.ctx = dc_ctx;
+	enc_init_data.encoder = dal_adapter_service_get_src_obj(
+							as, link->link_id, 0);
+	enc_init_data.connector = link->link_id;
+	enc_init_data.channel = get_ddc_line(link, as);
+	enc_init_data.hpd_source = get_hpd_line(link, as);
+	enc_init_data.transmitter =
+			translate_encoder_to_transmitter(enc_init_data.encoder);
+	link->link_enc = dc_ctx->dc->res_pool.funcs->link_enc_create(
+								&enc_init_data);
+
+	if( link->link_enc == NULL) {
+		DC_ERROR("Failed to create link encoder!\n");
+		goto create_fail;
+	}
+
+	dal_adapter_service_get_integrated_info(as, &info);
+
+	for (i = 0; ; i++) {
+		if (!dal_adapter_service_get_device_tag(
+				as, link->link_id, i, &link->device_tag)) {
+			DC_ERROR("Failed to find device tag!\n");
+			goto create_fail;
+		}
+
+		/* Look for device tag that matches connector signal,
+		 * CRT for rgb, LCD for other supported signal tyes
+		 */
+		if (!dal_adapter_service_is_device_id_supported(
+						as, link->device_tag.dev_id))
+			continue;
+		if (link->device_tag.dev_id.device_type == DEVICE_TYPE_CRT
+			&& link->public.connector_signal != SIGNAL_TYPE_RGB)
+			continue;
+		if (link->device_tag.dev_id.device_type == DEVICE_TYPE_LCD
+			&& link->public.connector_signal == SIGNAL_TYPE_RGB)
+			continue;
+		if (link->device_tag.dev_id.device_type == DEVICE_TYPE_WIRELESS
+			&& link->public.connector_signal != SIGNAL_TYPE_WIRELESS)
+			continue;
+		break;
+	}
+
+	/* Look for channel mapping corresponding to connector and device tag */
+	for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) {
+		struct external_display_path *path =
+			&info.ext_disp_conn_info.path[i];
+		if (path->device_connector_id.enum_id == link->link_id.enum_id
+			&& path->device_connector_id.id == link->link_id.id
+			&& path->device_connector_id.type == link->link_id.type
+			&& path->device_acpi_enum
+					== link->device_tag.acpi_device) {
+			link->ddi_channel_mapping = path->channel_mapping;
+			break;
+		}
+	}
+
+	/*
+	 * TODO check if GPIO programmed correctly
+	 *
+	 * If GPIO isn't programmed correctly HPD might not rise or drain
+	 * fast enough, leading to bounces.
+	 */
+	program_hpd_filter(link);
+
+	return true;
+
+create_fail:
+	return false;
+}
+
+/*******************************************************************************
+ * Public functions
+ ******************************************************************************/
+struct core_link *link_create(const struct link_init_data *init_params)
+{
+	struct core_link *link =
+			dm_alloc(init_params->ctx, sizeof(*link));
+
+	if (NULL == link)
+		goto alloc_fail;
+
+	if (false == construct(link, init_params))
+		goto construct_fail;
+
+	return link;
+
+construct_fail:
+	dm_free(init_params->ctx, link);
+
+alloc_fail:
+	return NULL;
+}
+
+void link_destroy(struct core_link **link)
+{
+	destruct(*link);
+	dm_free((*link)->ctx, *link);
+	*link = NULL;
+}
+
+static void dpcd_configure_panel_mode(
+	struct core_link *link,
+	enum dp_panel_mode panel_mode)
+{
+	union dpcd_edp_config edp_config_set;
+	bool panel_mode_edp = false;
+
+	dm_memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config));
+
+	if (DP_PANEL_MODE_DEFAULT != panel_mode) {
+
+		switch (panel_mode) {
+		case DP_PANEL_MODE_EDP:
+		case DP_PANEL_MODE_SPECIAL:
+			panel_mode_edp = true;
+			break;
+
+		default:
+			break;
+		}
+
+		/*set edp panel mode in receiver*/
+		core_link_read_dpcd(
+			link,
+			DPCD_ADDRESS_EDP_CONFIG_SET,
+			&edp_config_set.raw,
+			sizeof(edp_config_set.raw));
+
+		if (edp_config_set.bits.PANEL_MODE_EDP
+			!= panel_mode_edp) {
+			enum ddc_result result = DDC_RESULT_UNKNOWN;
+
+			edp_config_set.bits.PANEL_MODE_EDP =
+			panel_mode_edp;
+			result = core_link_write_dpcd(
+				link,
+				DPCD_ADDRESS_EDP_CONFIG_SET,
+				&edp_config_set.raw,
+				sizeof(edp_config_set.raw));
+
+			ASSERT(result == DDC_RESULT_SUCESSFULL);
+		}
+	}
+	dal_logger_write(link->ctx->logger, LOG_MAJOR_DETECTION,
+			LOG_MINOR_DETECTION_DP_CAPS,
+			"Link: %d eDP panel mode supported: %d "
+			"eDP panel mode enabled: %d \n",
+			link->public.link_index,
+			link->dpcd_caps.panel_mode_edp,
+			panel_mode_edp);
+}
+
+static enum dc_status enable_link_dp(struct core_stream *stream)
+{
+	enum dc_status status;
+	bool skip_video_pattern;
+	struct core_link *link = stream->sink->link;
+	struct link_settings link_settings = {0};
+	enum dp_panel_mode panel_mode;
+
+	/* get link settings for video mode timing */
+	decide_link_settings(stream, &link_settings);
+	dp_enable_link_phy(
+		stream->sink->link,
+		stream->signal,
+		&link_settings);
+
+	panel_mode = dp_get_panel_mode(link);
+	dpcd_configure_panel_mode(link, panel_mode);
+
+	skip_video_pattern = true;
+
+	if (link_settings.link_rate == LINK_RATE_LOW)
+			skip_video_pattern = false;
+
+	if (perform_link_training(link, &link_settings, skip_video_pattern)) {
+		link->cur_link_settings = link_settings;
+		status = DC_OK;
+	}
+	else
+		status = DC_ERROR_UNEXPECTED;
+
+	return status;
+}
+
+static enum dc_status enable_link_dp_mst(struct core_stream *stream)
+{
+	struct core_link *link = stream->sink->link;
+
+	/* sink signal type after MST branch is MST. Multiple MST sinks
+	 * share one link. Link DP PHY is enable or training only once.
+	 */
+	if (link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN)
+		return DC_OK;
+
+	return enable_link_dp(stream);
+}
+
+static void enable_link_hdmi(struct core_stream *stream)
+{
+	struct core_link *link = stream->sink->link;
+
+	/* enable video output */
+	/* here we need to specify that encoder output settings
+	 * need to be calculated as for the set mode,
+	 * it will lead to querying dynamic link capabilities
+	 * which should be done before enable output */
+	uint32_t normalized_pix_clk = stream->public.timing.pix_clk_khz;
+	switch (stream->public.timing.display_color_depth) {
+	case COLOR_DEPTH_888:
+		break;
+	case COLOR_DEPTH_101010:
+		normalized_pix_clk = (normalized_pix_clk * 30) / 24;
+		break;
+	case COLOR_DEPTH_121212:
+		normalized_pix_clk = (normalized_pix_clk * 36) / 24;
+		break;
+	case COLOR_DEPTH_161616:
+		normalized_pix_clk = (normalized_pix_clk * 48) / 24;
+		break;
+	default:
+		break;
+	}
+
+	if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+		dal_ddc_service_write_scdc_data(
+			stream->sink->link->ddc,
+			normalized_pix_clk,
+			stream->public.timing.flags.LTE_340MCSC_SCRAMBLE);
+
+	dm_memset(&stream->sink->link->cur_link_settings, 0,
+			sizeof(struct link_settings));
+
+	link->link_enc->funcs->enable_tmds_output(
+			link->link_enc,
+			stream->clock_source->id,
+			stream->public.timing.display_color_depth,
+			stream->signal == SIGNAL_TYPE_HDMI_TYPE_A,
+			stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK,
+			stream->public.timing.pix_clk_khz);
+
+	if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+		dal_ddc_service_read_scdc_data(link->ddc);
+}
+
+/****************************enable_link***********************************/
+static enum dc_status enable_link(struct core_stream *stream)
+{
+	enum dc_status status = DC_ERROR_UNEXPECTED;
+	switch (stream->signal) {
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_EDP:
+		status = enable_link_dp(stream);
+		break;
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+		status = enable_link_dp_mst(stream);
+		dm_sleep_in_milliseconds(stream->ctx, 200);
+		break;
+	case SIGNAL_TYPE_DVI_SINGLE_LINK:
+	case SIGNAL_TYPE_DVI_DUAL_LINK:
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+		enable_link_hdmi(stream);
+		status = DC_OK;
+		break;
+	case SIGNAL_TYPE_VIRTUAL:
+		status = DC_OK;
+		break;
+	default:
+		break;
+	}
+
+	if (stream->audio && status == DC_OK) {
+		/* notify audio driver for audio modes of monitor */
+		dal_audio_enable_azalia_audio_jack_presence(stream->audio,
+				stream->stream_enc->id);
+
+		/* un-mute audio */
+		dal_audio_unmute(stream->audio, stream->stream_enc->id,
+				stream->signal);
+	}
+
+	return status;
+}
+
+static void disable_link(struct core_stream *stream)
+{
+	/* TODO  dp_set_hw_test_pattern */
+
+	/* here we need to specify that encoder output settings
+	 * need to be calculated as for the set mode,
+	 * it will lead to querying dynamic link capabilities
+	 * which should be done before enable output */
+
+	if (dc_is_dp_signal(stream->signal)) {
+		/* SST DP, eDP */
+		if (dc_is_dp_sst_signal(stream->signal))
+			dp_disable_link_phy(
+					stream->sink->link, stream->signal);
+		else {
+			dp_disable_link_phy_mst(
+					stream->sink->link, stream);
+		}
+	} else {
+		struct link_encoder *encoder =
+				stream->sink->link->link_enc;
+
+		encoder->funcs->disable_output(encoder, stream->signal);
+	}
+}
+
+enum dc_status dc_link_validate_mode_timing(
+		const struct core_sink *sink,
+		struct core_link *link,
+		const struct dc_crtc_timing *timing)
+{
+	uint32_t max_pix_clk = sink->dongle_max_pix_clk;
+
+	if (0 != max_pix_clk && timing->pix_clk_khz > max_pix_clk)
+		return DC_EXCEED_DONGLE_MAX_CLK;
+
+	switch (sink->public.sink_signal) {
+		case SIGNAL_TYPE_DISPLAY_PORT:
+			if(!dp_validate_mode_timing(
+					link,
+					timing))
+				return DC_NO_DP_LINK_BANDWIDTH;
+			break;
+
+		default:
+			break;
+	}
+
+	return DC_OK;
+}
+
+bool dc_link_set_backlight_level(const struct dc_link *public, uint32_t level)
+{
+	struct core_link *link = DC_LINK_TO_CORE(public);
+	struct dc_context *ctx = link->ctx;
+
+	dal_logger_write(ctx->logger, LOG_MAJOR_BACKLIGHT,
+			LOG_MINOR_BACKLIGHT_INTERFACE,
+			"New Backlight level: %d (0x%X)\n", level, level);
+
+	link->link_enc->funcs->set_lcd_backlight_level(link->link_enc, level);
+
+	return true;
+}
+
+void core_link_resume(struct core_link *link)
+{
+	if (link->public.connector_signal != SIGNAL_TYPE_VIRTUAL)
+		program_hpd_filter(link);
+}
+
+static struct fixed31_32 get_pbn_per_slot(struct core_stream *stream)
+{
+	struct link_settings *link_settings =
+			&stream->sink->link->cur_link_settings;
+	uint32_t link_rate_in_mbps =
+			link_settings->link_rate * LINK_RATE_REF_FREQ_IN_MHZ;
+	struct fixed31_32 mbps = dal_fixed31_32_from_int(
+			link_rate_in_mbps * link_settings->lane_count);
+
+	return dal_fixed31_32_div_int(mbps, 54);
+}
+
+static int get_color_depth(struct core_stream *stream)
+{
+	switch (stream->pix_clk_params.color_depth) {
+	case COLOR_DEPTH_666: return 6;
+	case COLOR_DEPTH_888: return 8;
+	case COLOR_DEPTH_101010: return 10;
+	case COLOR_DEPTH_121212: return 12;
+	case COLOR_DEPTH_141414: return 14;
+	case COLOR_DEPTH_161616: return 16;
+	default: return 0;
+	}
+}
+
+static struct fixed31_32 get_pbn_from_timing(struct core_stream *stream)
+{
+	uint32_t bpc;
+	uint64_t kbps;
+	struct fixed31_32 peak_kbps;
+	uint32_t numerator;
+	uint32_t denominator;
+
+	bpc = get_color_depth(stream);
+	kbps = stream->pix_clk_params.requested_pix_clk * bpc * 3;
+
+	/*
+	 * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
+	 * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on
+	 * common multiplier to render an integer PBN for all link rate/lane
+	 * counts combinations
+	 * calculate
+	 * peak_kbps *= (1006/1000)
+	 * peak_kbps *= (64/54)
+	 * peak_kbps *= 8    convert to bytes
+	 */
+
+	numerator = 64 * PEAK_FACTOR_X1000;
+	denominator = 54 * 8 * 1000 * 1000;
+	kbps *= numerator;
+	peak_kbps = dal_fixed31_32_from_fraction(kbps, denominator);
+
+	return peak_kbps;
+}
+
+static void update_mst_stream_alloc_table(
+	struct core_link *link,
+	struct core_stream *stream,
+	const struct dp_mst_stream_allocation_table *proposed_table)
+{
+	struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = {
+			{ 0 } };
+	struct link_mst_stream_allocation *dc_alloc;
+
+	int i;
+	int j;
+
+	/* if DRM proposed_table has more than one new payload */
+	ASSERT(proposed_table->stream_count -
+			link->mst_stream_alloc_table.stream_count < 2);
+
+	/* copy proposed_table to core_link, add stream encoder */
+	for (i = 0; i < proposed_table->stream_count; i++) {
+
+		for (j = 0; j < link->mst_stream_alloc_table.stream_count; j++) {
+			dc_alloc =
+			&link->mst_stream_alloc_table.stream_allocations[j];
+
+			if (dc_alloc->vcp_id ==
+				proposed_table->stream_allocations[i].vcp_id) {
+
+				work_table[i] = *dc_alloc;
+				break; /* exit j loop */
+			}
+		}
+
+		/* new vcp_id */
+		if (j == link->mst_stream_alloc_table.stream_count) {
+			work_table[i].vcp_id =
+				proposed_table->stream_allocations[i].vcp_id;
+			work_table[i].slot_count =
+				proposed_table->stream_allocations[i].slot_count;
+			work_table[i].stream_enc = stream->stream_enc;
+		}
+	}
+
+	/* update link->mst_stream_alloc_table with work_table */
+	link->mst_stream_alloc_table.stream_count =
+			proposed_table->stream_count;
+	for (i = 0; i < MAX_CONTROLLER_NUM; i++)
+		link->mst_stream_alloc_table.stream_allocations[i] =
+				work_table[i];
+}
+
+/* convert link_mst_stream_alloc_table to dm dp_mst_stream_alloc_table
+ * because stream_encoder is not exposed to dm
+ */
+static enum dc_status allocate_mst_payload(struct core_stream *stream)
+{
+	struct core_link *link = stream->sink->link;
+	struct link_encoder *link_encoder = link->link_enc;
+	struct stream_encoder *stream_encoder = stream->stream_enc;
+	struct dp_mst_stream_allocation_table proposed_table = {0};
+	struct fixed31_32 avg_time_slots_per_mtp;
+	struct fixed31_32 pbn;
+	struct fixed31_32 pbn_per_slot;
+	uint8_t i;
+
+	/* enable_link_dp_mst already check link->enabled_stream_count
+	 * and stream is in link->stream[]. This is called during set mode,
+	 * stream_enc is available.
+	 */
+
+	/* get calculate VC payload for stream: stream_alloc */
+	dm_helpers_dp_mst_write_payload_allocation_table(
+		stream->ctx,
+		&stream->public,
+		&proposed_table,
+		true);
+
+	update_mst_stream_alloc_table(link, stream, &proposed_table);
+
+	dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_MST,
+			LOG_MINOR_MST_PROGRAMMING,
+			"%s  "
+			"stream_count: %d: \n ",
+			__func__,
+			link->mst_stream_alloc_table.stream_count);
+
+	for (i = 0; i < MAX_CONTROLLER_NUM; i++) {
+		dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_MST,
+		LOG_MINOR_MST_PROGRAMMING,
+		"stream_enc[%d]: 0x%x      "
+		"stream[%d].vcp_id: %d      "
+		"stream[%d].slot_count: %d\n",
+		i,
+		link->mst_stream_alloc_table.stream_allocations[i].stream_enc,
+		i,
+		link->mst_stream_alloc_table.stream_allocations[i].vcp_id,
+		i,
+		link->mst_stream_alloc_table.stream_allocations[i].slot_count);
+	}
+
+	ASSERT(proposed_table.stream_count > 0);
+
+	/*
+	 * temporary fix. Unplug of MST chain happened (two displays),
+	 * table is empty on first reset mode, and cause 0 division in
+	 * avg_time_slots_per_mtp calculation
+	 */
+
+	/* to be removed or debugged */
+	if (proposed_table.stream_count == 0)
+		return DC_OK;
+
+	/* program DP source TX for payload */
+	link_encoder->funcs->update_mst_stream_allocation_table(
+		link_encoder,
+		&link->mst_stream_alloc_table);
+
+	/* send down message */
+	dm_helpers_dp_mst_poll_for_allocation_change_trigger(
+			stream->ctx,
+			&stream->public);
+
+	dm_helpers_dp_mst_send_payload_allocation(
+			stream->ctx,
+			&stream->public,
+			true);
+
+	/* slot X.Y for only current stream */
+	pbn_per_slot = get_pbn_per_slot(stream);
+	pbn = get_pbn_from_timing(stream);
+	avg_time_slots_per_mtp = dal_fixed31_32_div(pbn, pbn_per_slot);
+
+
+
+	stream_encoder->funcs->set_mst_bandwidth(
+		stream_encoder,
+		avg_time_slots_per_mtp);
+
+	return DC_OK;
+
+}
+
+static enum dc_status deallocate_mst_payload(struct core_stream *stream)
+{
+	struct core_link *link = stream->sink->link;
+	struct link_encoder *link_encoder = link->link_enc;
+	struct stream_encoder *stream_encoder = stream->stream_enc;
+	struct dp_mst_stream_allocation_table proposed_table = {0};
+	struct fixed31_32 avg_time_slots_per_mtp = dal_fixed31_32_from_int(0);
+	uint8_t i;
+	bool mst_mode = (link->public.type == dc_connection_mst_branch);
+
+	/* deallocate_mst_payload is called before disable link. When mode or
+	 * disable/enable monitor, new stream is created which is not in link
+	 * stream[] yet. For this, payload is not allocated yet, so de-alloc
+	 * should not done. For new mode set, map_resources will get engine
+	 * for new stream, so stream_enc->id should be validated until here.
+	 */
+
+	/* slot X.Y */
+	stream_encoder->funcs->set_mst_bandwidth(
+		stream_encoder,
+		avg_time_slots_per_mtp);
+
+	/* TODO: which component is responsible for remove payload table? */
+	if (mst_mode)
+		dm_helpers_dp_mst_write_payload_allocation_table(
+				stream->ctx,
+				&stream->public,
+				&proposed_table,
+				false);
+
+	update_mst_stream_alloc_table(link, stream, &proposed_table);
+
+	dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_MST,
+			LOG_MINOR_MST_PROGRAMMING,
+			"%s"
+			"stream_count: %d: ",
+			__func__,
+			link->mst_stream_alloc_table.stream_count);
+
+	for (i = 0; i < MAX_CONTROLLER_NUM; i++) {
+		dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_MST,
+		LOG_MINOR_MST_PROGRAMMING,
+		"stream_enc[%d]: 0x%x      "
+		"stream[%d].vcp_id: %d      "
+		"stream[%d].slot_count: %d\n",
+		i,
+		link->mst_stream_alloc_table.stream_allocations[i].stream_enc,
+		i,
+		link->mst_stream_alloc_table.stream_allocations[i].vcp_id,
+		i,
+		link->mst_stream_alloc_table.stream_allocations[i].slot_count);
+	}
+
+	link_encoder->funcs->update_mst_stream_allocation_table(
+		link_encoder,
+		&link->mst_stream_alloc_table);
+
+	if (mst_mode) {
+		dm_helpers_dp_mst_poll_for_allocation_change_trigger(
+			stream->ctx,
+			&stream->public);
+
+		dm_helpers_dp_mst_send_payload_allocation(
+			stream->ctx,
+			&stream->public,
+			false);
+	}
+
+	return DC_OK;
+}
+
+void core_link_enable_stream(
+		struct core_link *link,
+		struct core_stream *stream)
+{
+	struct dc *dc = stream->ctx->dc;
+
+	if (DC_OK != enable_link(stream)) {
+			BREAK_TO_DEBUGGER();
+			return;
+	}
+
+	dc->hwss.enable_stream(stream);
+
+	if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+		allocate_mst_payload(stream);
+}
+
+void core_link_disable_stream(
+		struct core_link *link,
+		struct core_stream *stream)
+{
+	struct dc *dc = stream->ctx->dc;
+
+	if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+		deallocate_mst_payload(stream);
+
+	dc->hwss.disable_stream(stream);
+
+	disable_link(stream);
+
+}
+
+
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link_ddc.c
new file mode 100644
index 000000000000..62b8c264a593
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link_ddc.c
@@ -0,0 +1,1151 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_interface.h"
+#include "include/ddc_service_types.h"
+#include "include/grph_object_id.h"
+#include "include/dpcd_defs.h"
+#include "include/logger_interface.h"
+#include "include/vector.h"
+
+#include "dc_link_ddc.h"
+
+#define AUX_POWER_UP_WA_DELAY 500
+#define I2C_OVER_AUX_DEFER_WA_DELAY 70
+
+/* CV smart dongle slave address for retrieving supported HDTV modes*/
+#define CV_SMART_DONGLE_ADDRESS 0x20
+/* DVI-HDMI dongle slave address for retrieving dongle signature*/
+#define DVI_HDMI_DONGLE_ADDRESS 0x68
+static const int8_t dvi_hdmi_dongle_signature_str[] = "6140063500G";
+struct dvi_hdmi_dongle_signature_data {
+	int8_t vendor[3];/* "AMD" */
+	uint8_t version[2];
+	uint8_t size;
+	int8_t id[11];/* "6140063500G"*/
+};
+/* DP-HDMI dongle slave address for retrieving dongle signature*/
+#define DP_HDMI_DONGLE_ADDRESS 0x40
+static const uint8_t dp_hdmi_dongle_signature_str[] = "DP-HDMI ADAPTOR";
+#define DP_HDMI_DONGLE_SIGNATURE_EOT 0x04
+
+struct dp_hdmi_dongle_signature_data {
+	int8_t id[15];/* "DP-HDMI ADAPTOR"*/
+	uint8_t eot;/* end of transmition '\x4' */
+};
+
+/* Address range from 0x00 to 0x1F.*/
+#define DP_ADAPTOR_TYPE2_SIZE 0x20
+#define DP_ADAPTOR_TYPE2_REG_ID 0x10
+#define DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK 0x1D
+/* Identifies adaptor as Dual-mode adaptor */
+#define DP_ADAPTOR_TYPE2_ID 0xA0
+/* MHz*/
+#define DP_ADAPTOR_TYPE2_MAX_TMDS_CLK 600
+/* MHz*/
+#define DP_ADAPTOR_TYPE2_MIN_TMDS_CLK 25
+/* kHZ*/
+#define DP_ADAPTOR_DVI_MAX_TMDS_CLK 165000
+/* kHZ*/
+#define DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK 165000
+
+#define DDC_I2C_COMMAND_ENGINE I2C_COMMAND_ENGINE_SW
+
+enum edid_read_result {
+	EDID_READ_RESULT_EDID_MATCH = 0,
+	EDID_READ_RESULT_EDID_MISMATCH,
+	EDID_READ_RESULT_CHECKSUM_READ_ERR,
+	EDID_READ_RESULT_VENDOR_READ_ERR
+};
+
+/* SCDC Address defines (HDMI 2.0)*/
+#define HDMI_SCDC_WRITE_UPDATE_0_ARRAY 3
+#define HDMI_SCDC_ADDRESS  0x54
+#define HDMI_SCDC_SINK_VERSION 0x01
+#define HDMI_SCDC_SOURCE_VERSION 0x02
+#define HDMI_SCDC_UPDATE_0 0x10
+#define HDMI_SCDC_TMDS_CONFIG 0x20
+#define HDMI_SCDC_SCRAMBLER_STATUS 0x21
+#define HDMI_SCDC_CONFIG_0 0x30
+#define HDMI_SCDC_STATUS_FLAGS 0x40
+#define HDMI_SCDC_ERR_DETECT 0x50
+#define HDMI_SCDC_TEST_CONFIG 0xC0
+
+
+union hdmi_scdc_update_read_data {
+	uint8_t byte[2];
+	struct {
+		uint8_t STATUS_UPDATE:1;
+		uint8_t CED_UPDATE:1;
+		uint8_t RR_TEST:1;
+		uint8_t RESERVED:5;
+		uint8_t RESERVED2:8;
+	} fields;
+};
+
+union hdmi_scdc_status_flags_data {
+	uint8_t byte[2];
+	struct {
+		uint8_t CLOCK_DETECTED:1;
+		uint8_t CH0_LOCKED:1;
+		uint8_t CH1_LOCKED:1;
+		uint8_t CH2_LOCKED:1;
+		uint8_t RESERVED:4;
+		uint8_t RESERVED2:8;
+	} fields;
+};
+
+union hdmi_scdc_ced_data {
+	uint8_t byte[7];
+	struct {
+		uint8_t CH0_8LOW:8;
+		uint8_t CH0_7HIGH:7;
+		uint8_t CH0_VALID:1;
+		uint8_t CH1_8LOW:8;
+		uint8_t CH1_7HIGH:7;
+		uint8_t CH1_VALID:1;
+		uint8_t CH2_8LOW:8;
+		uint8_t CH2_7HIGH:7;
+		uint8_t CH2_VALID:1;
+		uint8_t CHECKSUM:8;
+	} fields;
+};
+
+union hdmi_scdc_test_config_Data {
+	uint8_t byte;
+	struct {
+		uint8_t TEST_READ_REQUEST_DELAY:7;
+		uint8_t TEST_READ_REQUEST: 1;
+	} fields;
+};
+
+
+
+union ddc_wa {
+	struct {
+		uint32_t DP_SKIP_POWER_OFF:1;
+		uint32_t DP_AUX_POWER_UP_WA_DELAY:1;
+	} bits;
+	uint32_t raw;
+};
+
+struct ddc_flags {
+	uint8_t EDID_QUERY_DONE_ONCE:1;
+	uint8_t IS_INTERNAL_DISPLAY:1;
+	uint8_t FORCE_READ_REPEATED_START:1;
+	uint8_t EDID_STRESS_READ:1;
+
+};
+
+struct ddc_service {
+	struct ddc *ddc_pin;
+	struct ddc_flags flags;
+	union ddc_wa wa;
+	enum ddc_transaction_type transaction_type;
+	enum display_dongle_type dongle_type;
+	struct dp_receiver_id_info dp_receiver_id_info;
+	struct adapter_service *as;
+	struct dc_context *ctx;
+
+	uint32_t address;
+	uint32_t edid_buf_len;
+	uint8_t edid_buf[MAX_EDID_BUFFER_SIZE];
+};
+
+struct i2c_payloads {
+	struct vector payloads;
+};
+
+struct aux_payloads {
+	struct vector payloads;
+};
+
+struct i2c_payloads *dal_ddc_i2c_payloads_create(struct dc_context *ctx, uint32_t count)
+{
+	struct i2c_payloads *payloads;
+
+	payloads = dm_alloc(ctx, sizeof(struct i2c_payloads));
+
+	if (!payloads)
+		return NULL;
+
+	if (dal_vector_construct(
+		&payloads->payloads, ctx, count, sizeof(struct i2c_payload)))
+		return payloads;
+
+	dm_free(ctx, payloads);
+	return NULL;
+
+}
+
+struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p)
+{
+	return (struct i2c_payload *)p->payloads.container;
+}
+
+uint32_t  dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p)
+{
+	return p->payloads.count;
+}
+
+void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p)
+{
+	if (!p || !*p)
+		return;
+	dal_vector_destruct(&(*p)->payloads);
+	dm_free((*p)->payloads.ctx, *p);
+	*p = NULL;
+
+}
+
+struct aux_payloads *dal_ddc_aux_payloads_create(struct dc_context *ctx, uint32_t count)
+{
+	struct aux_payloads *payloads;
+
+	payloads = dm_alloc(ctx, sizeof(struct aux_payloads));
+
+	if (!payloads)
+		return NULL;
+
+	if (dal_vector_construct(
+		&payloads->payloads, ctx, count, sizeof(struct aux_payloads)))
+		return payloads;
+
+	dm_free(ctx, payloads);
+	return NULL;
+}
+
+struct aux_payload *dal_ddc_aux_payloads_get(struct aux_payloads *p)
+{
+	return (struct aux_payload *)p->payloads.container;
+}
+
+uint32_t  dal_ddc_aux_payloads_get_count(struct aux_payloads *p)
+{
+	return p->payloads.count;
+}
+
+
+void dal_ddc_aux_payloads_destroy(struct aux_payloads **p)
+{
+	if (!p || !*p)
+		return;
+
+	dal_vector_destruct(&(*p)->payloads);
+	dm_free((*p)->payloads.ctx, *p);
+	*p = NULL;
+}
+
+#define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+void dal_ddc_i2c_payloads_add(
+	struct i2c_payloads *payloads,
+	uint32_t address,
+	uint32_t len,
+	uint8_t *data,
+	bool write)
+{
+	uint32_t payload_size = EDID_SEGMENT_SIZE;
+	uint32_t pos;
+
+	for (pos = 0; pos < len; pos += payload_size) {
+		struct i2c_payload payload = {
+			.write = write,
+			.address = address,
+			.length = DDC_MIN(payload_size, len - pos),
+			.data = data + pos };
+		dal_vector_append(&payloads->payloads, &payload);
+	}
+
+}
+
+void dal_ddc_aux_payloads_add(
+	struct aux_payloads *payloads,
+	uint32_t address,
+	uint32_t len,
+	uint8_t *data,
+	bool write)
+{
+	uint32_t payload_size = DEFAULT_AUX_MAX_DATA_SIZE;
+	uint32_t pos;
+
+	for (pos = 0; pos < len; pos += payload_size) {
+		struct aux_payload payload = {
+			.i2c_over_aux = true,
+			.write = write,
+			.address = address,
+			.length = DDC_MIN(payload_size, len - pos),
+			.data = data + pos };
+		dal_vector_append(&payloads->payloads, &payload);
+	}
+}
+
+
+static bool construct(
+	struct ddc_service *ddc_service,
+	struct ddc_service_init_data *init_data)
+{
+	enum connector_id connector_id =
+		dal_graphics_object_id_get_connector_id(init_data->id);
+
+	ddc_service->ctx = init_data->ctx;
+	ddc_service->as = init_data->as;
+	ddc_service->ddc_pin = dal_adapter_service_obtain_ddc(
+			init_data->as, init_data->id);
+
+	ddc_service->flags.EDID_QUERY_DONE_ONCE = false;
+
+	ddc_service->flags.FORCE_READ_REPEATED_START =
+		dal_adapter_service_is_feature_supported(
+			FEATURE_DDC_READ_FORCE_REPEATED_START);
+
+	ddc_service->flags.EDID_STRESS_READ =
+			dal_adapter_service_is_feature_supported(
+				FEATURE_EDID_STRESS_READ);
+
+
+	ddc_service->flags.IS_INTERNAL_DISPLAY =
+		connector_id == CONNECTOR_ID_EDP ||
+		connector_id == CONNECTOR_ID_LVDS;
+
+	ddc_service->wa.raw = 0;
+	return true;
+}
+
+struct ddc_service *dal_ddc_service_create(
+	struct ddc_service_init_data *init_data)
+{
+	struct ddc_service *ddc_service;
+
+	ddc_service = dm_alloc(init_data->ctx, sizeof(struct ddc_service));
+
+	if (!ddc_service)
+		return NULL;
+
+	if (construct(ddc_service, init_data))
+		return ddc_service;
+
+	dm_free(init_data->ctx, ddc_service);
+	return NULL;
+}
+
+static void destruct(struct ddc_service *ddc)
+{
+	if (ddc->ddc_pin)
+		dal_adapter_service_release_ddc(ddc->as, ddc->ddc_pin);
+}
+
+void dal_ddc_service_destroy(struct ddc_service **ddc)
+{
+	if (!ddc || !*ddc) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+	destruct(*ddc);
+	dm_free((*ddc)->ctx, *ddc);
+	*ddc = NULL;
+}
+
+enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc)
+{
+	return DDC_SERVICE_TYPE_CONNECTOR;
+}
+
+void dal_ddc_service_set_transaction_type(
+	struct ddc_service *ddc,
+	enum ddc_transaction_type type)
+{
+	ddc->transaction_type = type;
+}
+
+bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc)
+{
+	switch (ddc->transaction_type) {
+	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX:
+	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER:
+	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER:
+		return true;
+	default:
+		break;
+	}
+	return false;
+}
+
+void ddc_service_set_dongle_type(struct ddc_service *ddc,
+		enum display_dongle_type dongle_type)
+{
+	ddc->dongle_type = dongle_type;
+}
+
+static uint32_t defer_delay_converter_wa(
+	struct ddc_service *ddc,
+	uint32_t defer_delay)
+{
+	struct dp_receiver_id_info dp_rec_info = {0};
+
+	if (dal_ddc_service_get_dp_receiver_id_info(ddc, &dp_rec_info) &&
+		(dp_rec_info.branch_id == DP_BRANCH_DEVICE_ID_4) &&
+		!dm_strncmp(dp_rec_info.branch_name,
+			DP_DVI_CONVERTER_ID_4,
+			sizeof(dp_rec_info.branch_name)))
+		return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ?
+			defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY;
+
+	return defer_delay;
+
+}
+
+#define DP_TRANSLATOR_DELAY 5
+
+static uint32_t get_defer_delay(struct ddc_service *ddc)
+{
+	uint32_t defer_delay = 0;
+
+	switch (ddc->transaction_type) {
+	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX:
+		if ((DISPLAY_DONGLE_DP_VGA_CONVERTER == ddc->dongle_type) ||
+			(DISPLAY_DONGLE_DP_DVI_CONVERTER == ddc->dongle_type) ||
+			(DISPLAY_DONGLE_DP_HDMI_CONVERTER ==
+				ddc->dongle_type)) {
+
+			defer_delay = DP_TRANSLATOR_DELAY;
+
+			defer_delay =
+				defer_delay_converter_wa(ddc, defer_delay);
+
+		} else /*sink has a delay different from an Active Converter*/
+			defer_delay = 0;
+		break;
+	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER:
+		defer_delay = DP_TRANSLATOR_DELAY;
+		break;
+	default:
+		break;
+	}
+	return defer_delay;
+}
+
+static bool i2c_read(
+	struct ddc_service *ddc,
+	uint32_t address,
+	uint8_t *buffer,
+	uint32_t len)
+{
+	uint8_t offs_data = 0;
+	struct i2c_payload payloads[2] = {
+		{
+		.write = true,
+		.address = address,
+		.length = 1,
+		.data = &offs_data },
+		{
+		.write = false,
+		.address = address,
+		.length = len,
+		.data = buffer } };
+
+	struct i2c_command command = {
+		.payloads = payloads,
+		.number_of_payloads = 2,
+		.engine = DDC_I2C_COMMAND_ENGINE,
+		.speed = dal_adapter_service_get_sw_i2c_speed(ddc->as) };
+
+	return dal_i2caux_submit_i2c_command(
+		dal_adapter_service_get_i2caux(ddc->as),
+		ddc->ddc_pin,
+		&command);
+}
+
+static uint8_t aux_read_edid_block(
+	struct ddc_service *ddc,
+	uint8_t address,
+	uint8_t index,
+	uint8_t *buf)
+{
+	struct aux_command cmd = {
+		.payloads = NULL,
+		.number_of_payloads = 0,
+		.defer_delay = get_defer_delay(ddc),
+		.max_defer_write_retry = 0 };
+
+	uint8_t retrieved = 0;
+	uint8_t base_offset =
+		(index % DDC_EDID_BLOCKS_PER_SEGMENT) * DDC_EDID_BLOCK_SIZE;
+	uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT;
+
+	for (retrieved = 0; retrieved < DDC_EDID_BLOCK_SIZE;
+		retrieved += DEFAULT_AUX_MAX_DATA_SIZE) {
+
+		uint8_t offset = base_offset + retrieved;
+
+		struct aux_payload payloads[3] = {
+			{
+			.i2c_over_aux = true,
+			.write = true,
+			.address = DDC_EDID_SEGMENT_ADDRESS,
+			.length = 1,
+			.data = &segment },
+			{
+			.i2c_over_aux = true,
+			.write = true,
+			.address = address,
+			.length = 1,
+			.data = &offset },
+			{
+			.i2c_over_aux = true,
+			.write = false,
+			.address = address,
+			.length = DEFAULT_AUX_MAX_DATA_SIZE,
+			.data = &buf[retrieved] } };
+
+		if (segment == 0) {
+			cmd.payloads = &payloads[1];
+			cmd.number_of_payloads = 2;
+		} else {
+			cmd.payloads = payloads;
+			cmd.number_of_payloads = 3;
+		}
+
+		if (!dal_i2caux_submit_aux_command(
+			dal_adapter_service_get_i2caux(ddc->as),
+			ddc->ddc_pin,
+			&cmd))
+			/* cannot read, break*/
+			break;
+	}
+
+	/* Reset segment to 0. Needed by some panels */
+	if (0 != segment) {
+		struct aux_payload payloads[1] = { {
+			.i2c_over_aux = true,
+			.write = true,
+			.address = DDC_EDID_SEGMENT_ADDRESS,
+			.length = 1,
+			.data = &segment } };
+		bool result = false;
+
+		segment = 0;
+
+		cmd.number_of_payloads = ARRAY_SIZE(payloads);
+		cmd.payloads = payloads;
+
+		result = dal_i2caux_submit_aux_command(
+			dal_adapter_service_get_i2caux(ddc->as),
+			ddc->ddc_pin,
+			&cmd);
+
+		if (false == result)
+			dal_logger_write(
+				ddc->ctx->logger,
+				LOG_MAJOR_ERROR,
+				LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE,
+				"%s: Writing of EDID Segment (0x30) failed!\n",
+				__func__);
+	}
+
+	return retrieved;
+}
+
+static uint8_t i2c_read_edid_block(
+	struct ddc_service *ddc,
+	uint8_t address,
+	uint8_t index,
+	uint8_t *buf)
+{
+	bool ret = false;
+	uint8_t offset = (index % DDC_EDID_BLOCKS_PER_SEGMENT) *
+		DDC_EDID_BLOCK_SIZE;
+	uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT;
+
+	struct i2c_command cmd = {
+		.payloads = NULL,
+		.number_of_payloads = 0,
+		.engine = DDC_I2C_COMMAND_ENGINE,
+		.speed = dal_adapter_service_get_sw_i2c_speed(ddc->as) };
+
+	struct i2c_payload payloads[3] = {
+		{
+		.write = true,
+		.address = DDC_EDID_SEGMENT_ADDRESS,
+		.length = 1,
+		.data = &segment },
+		{
+		.write = true,
+		.address = address,
+		.length = 1,
+		.data = &offset },
+		{
+		.write = false,
+		.address = address,
+		.length = DDC_EDID_BLOCK_SIZE,
+		.data = buf } };
+/*
+ * Some I2C engines don't handle stop/start between write-offset and read-data
+ * commands properly. For those displays, we have to force the newer E-DDC
+ * behavior of repeated-start which can be enabled by runtime parameter. */
+/* Originally implemented for OnLive using NXP receiver chip */
+
+	if (index == 0 && !ddc->flags.FORCE_READ_REPEATED_START) {
+		/* base block, use use DDC2B, submit as 2 commands */
+		cmd.payloads = &payloads[1];
+		cmd.number_of_payloads = 1;
+
+		if (dal_i2caux_submit_i2c_command(
+			dal_adapter_service_get_i2caux(ddc->as),
+			ddc->ddc_pin,
+			&cmd)) {
+
+			cmd.payloads = &payloads[2];
+			cmd.number_of_payloads = 1;
+
+			ret = dal_i2caux_submit_i2c_command(
+				dal_adapter_service_get_i2caux(ddc->as),
+				ddc->ddc_pin,
+				&cmd);
+		}
+
+	} else {
+		/*
+		 * extension block use E-DDC, submit as 1 command
+		 * or if repeated-start is forced by runtime parameter
+		 */
+		if (segment != 0) {
+			/* include segment offset in command*/
+			cmd.payloads = payloads;
+			cmd.number_of_payloads = 3;
+		} else {
+			/* we are reading first segment,
+			 * segment offset is not required */
+			cmd.payloads = &payloads[1];
+			cmd.number_of_payloads = 2;
+		}
+
+		ret = dal_i2caux_submit_i2c_command(
+			dal_adapter_service_get_i2caux(ddc->as),
+			ddc->ddc_pin,
+			&cmd);
+	}
+
+	return ret ? DDC_EDID_BLOCK_SIZE : 0;
+}
+
+static uint32_t query_edid_block(
+	struct ddc_service *ddc,
+	uint8_t address,
+	uint8_t index,
+	uint8_t *buf,
+	uint32_t size)
+{
+	uint32_t size_retrieved = 0;
+
+	if (size < DDC_EDID_BLOCK_SIZE)
+		return 0;
+
+	if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) {
+
+		ASSERT(index < 2);
+		size_retrieved =
+			aux_read_edid_block(ddc, address, index, buf);
+	} else {
+		size_retrieved =
+			i2c_read_edid_block(ddc, address, index, buf);
+	}
+
+	return size_retrieved;
+}
+
+#define DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS 0x261
+#define DDC_TEST_ACK_ADDRESS 0x260
+#define DDC_DPCD_EDID_TEST_ACK 0x04
+#define DDC_DPCD_EDID_TEST_MASK 0x04
+#define DDC_DPCD_TEST_REQUEST_ADDRESS 0x218
+
+/* AG TODO GO throug DM callback here like for DPCD */
+
+static void write_dp_edid_checksum(
+	struct ddc_service *ddc,
+	uint8_t checksum)
+{
+	uint8_t dpcd_data;
+
+	dal_ddc_service_read_dpcd_data(
+		ddc,
+		DDC_DPCD_TEST_REQUEST_ADDRESS,
+		&dpcd_data,
+		1);
+
+	if (dpcd_data & DDC_DPCD_EDID_TEST_MASK) {
+
+		dal_ddc_service_write_dpcd_data(
+			ddc,
+			DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS,
+			&checksum,
+			1);
+
+		dpcd_data = DDC_DPCD_EDID_TEST_ACK;
+
+		dal_ddc_service_write_dpcd_data(
+			ddc,
+			DDC_TEST_ACK_ADDRESS,
+			&dpcd_data,
+			1);
+	}
+}
+
+uint32_t dal_ddc_service_edid_query(struct ddc_service *ddc)
+{
+	uint32_t bytes_read = 0;
+	uint32_t ext_cnt = 0;
+
+	uint8_t address;
+	uint32_t i;
+
+	for (address = DDC_EDID_ADDRESS_START;
+		address <= DDC_EDID_ADDRESS_END; ++address) {
+
+		bytes_read = query_edid_block(
+			ddc,
+			address,
+			0,
+			ddc->edid_buf,
+			sizeof(ddc->edid_buf) - bytes_read);
+
+		if (bytes_read != DDC_EDID_BLOCK_SIZE)
+			continue;
+
+		/* get the number of ext blocks*/
+		ext_cnt = ddc->edid_buf[DDC_EDID_EXT_COUNT_OFFSET];
+
+		/* EDID 2.0, need to read 1 more block because EDID2.0 is
+		 * 256 byte in size*/
+		if (ddc->edid_buf[DDC_EDID_20_SIGNATURE_OFFSET] ==
+			DDC_EDID_20_SIGNATURE)
+				ext_cnt = 1;
+
+		for (i = 0; i < ext_cnt; i++) {
+			/* read additional ext blocks accordingly */
+			bytes_read += query_edid_block(
+					ddc,
+					address,
+					i+1,
+					&ddc->edid_buf[bytes_read],
+					sizeof(ddc->edid_buf) - bytes_read);
+		}
+
+		/*this is special code path for DP compliance*/
+		if (DDC_TRANSACTION_TYPE_I2C_OVER_AUX == ddc->transaction_type)
+			write_dp_edid_checksum(
+				ddc,
+				ddc->edid_buf[(ext_cnt * DDC_EDID_BLOCK_SIZE) +
+				DDC_EDID1X_CHECKSUM_OFFSET]);
+
+		/*remembers the address where we fetch the EDID from
+		 * for later signature check use */
+		ddc->address = address;
+
+		break;/* already read edid, done*/
+	}
+
+	ddc->edid_buf_len = bytes_read;
+	return bytes_read;
+}
+
+uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc)
+{
+	return ddc->edid_buf_len;
+}
+
+void dal_ddc_service_get_edid_buf(struct ddc_service *ddc, uint8_t *edid_buf)
+{
+	dm_memmove(edid_buf,
+			ddc->edid_buf, ddc->edid_buf_len);
+}
+
+void dal_ddc_service_i2c_query_dp_dual_mode_adaptor(
+	struct ddc_service *ddc,
+	struct display_sink_capability *sink_cap)
+{
+	uint8_t i;
+	bool is_valid_hdmi_signature;
+	enum display_dongle_type *dongle = &sink_cap->dongle_type;
+	uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE];
+	bool is_type2_dongle = false;
+	struct dp_hdmi_dongle_signature_data *dongle_signature;
+
+	/* Assume we have no valid DP passive dongle connected */
+	*dongle = DISPLAY_DONGLE_NONE;
+	sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK;
+
+	/* Read DP-HDMI dongle I2c (no response interpreted as DP-DVI dongle)*/
+	if (!i2c_read(
+		ddc,
+		DP_HDMI_DONGLE_ADDRESS,
+		type2_dongle_buf,
+		sizeof(type2_dongle_buf))) {
+		dal_logger_write(ddc->ctx->logger,
+			LOG_MAJOR_DCS,
+			LOG_MINOR_DCS_DONGLE_DETECTION,
+			"Detected DP-DVI dongle.\n");
+		*dongle = DISPLAY_DONGLE_DP_DVI_DONGLE;
+		sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK;
+		return;
+	}
+
+	/* Check if Type 2 dongle.*/
+	if (type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_ID] == DP_ADAPTOR_TYPE2_ID)
+		is_type2_dongle = true;
+
+	dongle_signature =
+		(struct dp_hdmi_dongle_signature_data *)type2_dongle_buf;
+
+	is_valid_hdmi_signature = true;
+
+	/* Check EOT */
+	if (dongle_signature->eot != DP_HDMI_DONGLE_SIGNATURE_EOT) {
+		is_valid_hdmi_signature = false;
+	}
+
+	/* Check signature */
+	for (i = 0; i < sizeof(dongle_signature->id); ++i) {
+		/* If its not the right signature,
+		 * skip mismatch in subversion byte.*/
+		if (dongle_signature->id[i] !=
+			dp_hdmi_dongle_signature_str[i] && i != 3) {
+
+			if (is_type2_dongle) {
+				is_valid_hdmi_signature = false;
+				break;
+			}
+
+		}
+	}
+
+	if (is_type2_dongle) {
+		uint32_t max_tmds_clk =
+			type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK];
+
+		max_tmds_clk = max_tmds_clk * 2 + max_tmds_clk / 2;
+
+		if (0 == max_tmds_clk ||
+				max_tmds_clk < DP_ADAPTOR_TYPE2_MIN_TMDS_CLK ||
+				max_tmds_clk > DP_ADAPTOR_TYPE2_MAX_TMDS_CLK) {
+			dal_logger_write(ddc->ctx->logger,
+				LOG_MAJOR_DCS,
+				LOG_MINOR_DCS_DONGLE_DETECTION,
+				"Invalid Maximum TMDS clock");
+			*dongle = DISPLAY_DONGLE_DP_DVI_DONGLE;
+		} else {
+			if (is_valid_hdmi_signature == true) {
+				*dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE;
+				dal_logger_write(ddc->ctx->logger,
+					LOG_MAJOR_DCS,
+					LOG_MINOR_DCS_DONGLE_DETECTION,
+					"Detected Type 2 DP-HDMI Maximum TMDS "
+					"clock, max TMDS clock: %d MHz",
+					max_tmds_clk);
+			} else {
+				*dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE;
+				dal_logger_write(ddc->ctx->logger,
+					LOG_MAJOR_DCS,
+					LOG_MINOR_DCS_DONGLE_DETECTION,
+					"Detected Type 2 DP-HDMI (no valid HDMI"
+					" signature) Maximum TMDS clock, max "
+					"TMDS clock: %d MHz",
+					max_tmds_clk);
+			}
+
+			/* Multiply by 1000 to convert to kHz. */
+			sink_cap->max_hdmi_pixel_clock =
+				max_tmds_clk * 1000;
+		}
+
+	} else {
+		if (is_valid_hdmi_signature == true) {
+			dal_logger_write(ddc->ctx->logger,
+				LOG_MAJOR_DCS,
+				LOG_MINOR_DCS_DONGLE_DETECTION,
+				"Detected Type 1 DP-HDMI dongle.\n");
+			*dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE;
+		} else {
+			dal_logger_write(ddc->ctx->logger,
+				LOG_MAJOR_DCS,
+				LOG_MINOR_DCS_DONGLE_DETECTION,
+				"Detected Type 1 DP-HDMI dongle (no valid HDMI "
+				"signature).\n");
+
+			*dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE;
+		}
+	}
+
+	return;
+}
+
+enum {
+	DP_SINK_CAP_SIZE =
+		DPCD_ADDRESS_EDP_CONFIG_CAP - DPCD_ADDRESS_DPCD_REV + 1
+};
+
+bool dal_ddc_service_query_ddc_data(
+	struct ddc_service *ddc,
+	uint32_t address,
+	uint8_t *write_buf,
+	uint32_t write_size,
+	uint8_t *read_buf,
+	uint32_t read_size)
+{
+	bool ret;
+	uint32_t payload_size =
+		dal_ddc_service_is_in_aux_transaction_mode(ddc) ?
+			DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE;
+
+	uint32_t write_payloads =
+		(write_size + payload_size - 1) / payload_size;
+
+	uint32_t read_payloads =
+		(read_size + payload_size - 1) / payload_size;
+
+	uint32_t payloads_num = write_payloads + read_payloads;
+
+	if (write_size > EDID_SEGMENT_SIZE || read_size > EDID_SEGMENT_SIZE)
+		return false;
+
+	/*TODO: len of payload data for i2c and aux is uint8!!!!,
+	 *  but we want to read 256 over i2c!!!!*/
+	if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) {
+
+		struct aux_payloads *payloads =
+			dal_ddc_aux_payloads_create(ddc->ctx, payloads_num);
+
+		struct aux_command command = {
+			.payloads = dal_ddc_aux_payloads_get(payloads),
+			.number_of_payloads = 0,
+			.defer_delay = get_defer_delay(ddc),
+			.max_defer_write_retry = 0 };
+
+		dal_ddc_aux_payloads_add(
+			payloads, address, write_size, write_buf, true);
+
+		dal_ddc_aux_payloads_add(
+			payloads, address, read_size, read_buf, false);
+
+		command.number_of_payloads =
+			dal_ddc_aux_payloads_get_count(payloads);
+
+		ret = dal_i2caux_submit_aux_command(
+				dal_adapter_service_get_i2caux(ddc->as),
+				ddc->ddc_pin,
+				&command);
+
+		dal_ddc_aux_payloads_destroy(&payloads);
+
+	} else {
+		struct i2c_payloads *payloads =
+			dal_ddc_i2c_payloads_create(ddc->ctx, payloads_num);
+
+		struct i2c_command command = {
+			.payloads = dal_ddc_i2c_payloads_get(payloads),
+			.number_of_payloads = 0,
+			.engine = DDC_I2C_COMMAND_ENGINE,
+			.speed =
+				dal_adapter_service_get_sw_i2c_speed(ddc->as) };
+
+		dal_ddc_i2c_payloads_add(
+			payloads, address, write_size, write_buf, true);
+
+		dal_ddc_i2c_payloads_add(
+			payloads, address, read_size, read_buf, false);
+
+		command.number_of_payloads =
+			dal_ddc_i2c_payloads_get_count(payloads);
+
+		ret = dal_i2caux_submit_i2c_command(
+				dal_adapter_service_get_i2caux(ddc->as),
+				ddc->ddc_pin,
+				&command);
+
+		dal_ddc_i2c_payloads_destroy(&payloads);
+	}
+
+	return ret;
+}
+
+bool dal_ddc_service_get_dp_receiver_id_info(
+	struct ddc_service *ddc,
+	struct dp_receiver_id_info *info)
+{
+	if (!info)
+		return false;
+
+	*info = ddc->dp_receiver_id_info;
+	return true;
+}
+
+enum ddc_result dal_ddc_service_read_dpcd_data(
+	struct ddc_service *ddc,
+	uint32_t address,
+	uint8_t *data,
+	uint32_t len)
+{
+	struct aux_payload read_payload = {
+		.i2c_over_aux = false,
+		.write = false,
+		.address = address,
+		.length = len,
+		.data = data,
+	};
+	struct aux_command command = {
+		.payloads = &read_payload,
+		.number_of_payloads = 1,
+		.defer_delay = 0,
+		.max_defer_write_retry = 0,
+	};
+
+	if (len > DEFAULT_AUX_MAX_DATA_SIZE) {
+		BREAK_TO_DEBUGGER();
+		return DDC_RESULT_FAILED_INVALID_OPERATION;
+	}
+
+	if (dal_i2caux_submit_aux_command(
+		dal_adapter_service_get_i2caux(ddc->as),
+		ddc->ddc_pin,
+		&command))
+		return DDC_RESULT_SUCESSFULL;
+
+	return DDC_RESULT_FAILED_OPERATION;
+}
+
+enum ddc_result dal_ddc_service_write_dpcd_data(
+	struct ddc_service *ddc,
+	uint32_t address,
+	const uint8_t *data,
+	uint32_t len)
+{
+	struct aux_payload write_payload = {
+		.i2c_over_aux = false,
+		.write = true,
+		.address = address,
+		.length = len,
+		.data = (uint8_t *)data,
+	};
+	struct aux_command command = {
+		.payloads = &write_payload,
+		.number_of_payloads = 1,
+		.defer_delay = 0,
+		.max_defer_write_retry = 0,
+	};
+
+	if (len > DEFAULT_AUX_MAX_DATA_SIZE) {
+		BREAK_TO_DEBUGGER();
+		return DDC_RESULT_FAILED_INVALID_OPERATION;
+	}
+
+	if (dal_i2caux_submit_aux_command(
+		dal_adapter_service_get_i2caux(ddc->as),
+		ddc->ddc_pin,
+		&command))
+		return DDC_RESULT_SUCESSFULL;
+
+	return DDC_RESULT_FAILED_OPERATION;
+}
+
+/*test only function*/
+void dal_ddc_service_set_ddc_pin(
+	struct ddc_service *ddc_service,
+	struct ddc *ddc)
+{
+	ddc_service->ddc_pin = ddc;
+}
+
+struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service)
+{
+	return ddc_service->ddc_pin;
+}
+
+
+void dal_ddc_service_reset_dp_receiver_id_info(struct ddc_service *ddc_service)
+{
+	dm_memset(&ddc_service->dp_receiver_id_info,
+		0, sizeof(struct dp_receiver_id_info));
+}
+
+void dal_ddc_service_write_scdc_data(struct ddc_service *ddc_service,
+		uint32_t pix_clk,
+		bool lte_340_scramble)
+{
+	bool over_340_mhz = pix_clk > 340000 ? 1 : 0;
+	uint8_t slave_address = HDMI_SCDC_ADDRESS;
+	uint8_t offset = HDMI_SCDC_SINK_VERSION;
+	uint8_t sink_version = 0;
+	uint8_t write_buffer[2] = {0};
+	/*Lower than 340 Scramble bit from SCDC caps*/
+
+	dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset,
+			sizeof(offset), &sink_version, sizeof(sink_version));
+	if (sink_version == 1) {
+		/*Source Version = 1*/
+		write_buffer[0] = HDMI_SCDC_SOURCE_VERSION;
+		write_buffer[1] = 1;
+		dal_ddc_service_query_ddc_data(ddc_service, slave_address,
+				write_buffer, sizeof(write_buffer), NULL, 0);
+		/*Read Request from SCDC caps*/
+	}
+	write_buffer[0] = HDMI_SCDC_TMDS_CONFIG;
+
+	if (over_340_mhz) {
+		write_buffer[1] = 3;
+	} else if (lte_340_scramble) {
+		write_buffer[1] = 1;
+	} else {
+		write_buffer[1] = 0;
+	}
+	dal_ddc_service_query_ddc_data(ddc_service, slave_address, write_buffer,
+			sizeof(write_buffer), NULL, 0);
+}
+
+void dal_ddc_service_read_scdc_data(struct ddc_service *ddc_service)
+{
+	uint8_t slave_address = HDMI_SCDC_ADDRESS;
+	uint8_t offset = HDMI_SCDC_TMDS_CONFIG;
+	uint8_t tmds_config = 0;
+
+	dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset,
+			sizeof(offset), &tmds_config, sizeof(tmds_config));
+	if (tmds_config & 0x1) {
+		union hdmi_scdc_status_flags_data status_data = { {0} };
+		uint8_t scramble_status = 0;
+
+		offset = HDMI_SCDC_SCRAMBLER_STATUS;
+		dal_ddc_service_query_ddc_data(ddc_service, slave_address,
+				&offset, sizeof(offset), &scramble_status,
+				sizeof(scramble_status));
+		offset = HDMI_SCDC_STATUS_FLAGS;
+		dal_ddc_service_query_ddc_data(ddc_service, slave_address,
+				&offset, sizeof(offset), status_data.byte,
+				sizeof(status_data.byte));
+	}
+}
+
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c
new file mode 100644
index 000000000000..742ab756cb48
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link_dp.c
@@ -0,0 +1,1728 @@ 
+/* Copyright 2015 Advanced Micro Devices, Inc. */
+#include "dm_services.h"
+#include "dc.h"
+#include "dc_link_dp.h"
+#include "dm_helpers.h"
+
+#include "inc/core_types.h"
+#include "link_hwss.h"
+#include "dc_link_ddc.h"
+#include "core_status.h"
+#include "dpcd_defs.h"
+
+/* maximum pre emphasis level allowed for each voltage swing level*/
+static const enum pre_emphasis voltage_swing_to_pre_emphasis[] = {
+		PRE_EMPHASIS_LEVEL3,
+		PRE_EMPHASIS_LEVEL2,
+		PRE_EMPHASIS_LEVEL1,
+		PRE_EMPHASIS_DISABLED };
+
+enum {
+	POST_LT_ADJ_REQ_LIMIT = 6,
+	POST_LT_ADJ_REQ_TIMEOUT = 200
+};
+
+enum {
+	LINK_TRAINING_MAX_RETRY_COUNT = 5,
+	/* to avoid infinite loop where-in the receiver
+	 * switches between different VS
+	 */
+	LINK_TRAINING_MAX_CR_RETRY = 100
+};
+
+static const struct link_settings link_training_fallback_table[] = {
+/* 2160 Mbytes/sec*/
+{ LANE_COUNT_FOUR, LINK_RATE_HIGH2, LINK_SPREAD_DISABLED },
+/* 1080 Mbytes/sec*/
+{ LANE_COUNT_FOUR, LINK_RATE_HIGH, LINK_SPREAD_DISABLED },
+/* 648 Mbytes/sec*/
+{ LANE_COUNT_FOUR, LINK_RATE_LOW, LINK_SPREAD_DISABLED },
+/* 1080 Mbytes/sec*/
+{ LANE_COUNT_TWO, LINK_RATE_HIGH2, LINK_SPREAD_DISABLED },
+/* 540 Mbytes/sec*/
+{ LANE_COUNT_TWO, LINK_RATE_HIGH, LINK_SPREAD_DISABLED },
+/* 324 Mbytes/sec*/
+{ LANE_COUNT_TWO, LINK_RATE_LOW, LINK_SPREAD_DISABLED },
+/* 540 Mbytes/sec*/
+{ LANE_COUNT_ONE, LINK_RATE_HIGH2, LINK_SPREAD_DISABLED },
+/* 270 Mbytes/sec*/
+{ LANE_COUNT_ONE, LINK_RATE_HIGH, LINK_SPREAD_DISABLED },
+/* 162 Mbytes/sec*/
+{ LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED } };
+
+static void wait_for_training_aux_rd_interval(
+	struct core_link* link,
+	uint32_t default_wait_in_micro_secs)
+{
+	uint8_t training_rd_interval;
+
+	/* overwrite the delay if rev > 1.1*/
+	if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) {
+		/* DP 1.2 or later - retrieve delay through
+		 * "DPCD_ADDR_TRAINING_AUX_RD_INTERVAL" register */
+		core_link_read_dpcd(
+			link,
+			DPCD_ADDRESS_TRAINING_AUX_RD_INTERVAL,
+			&training_rd_interval,
+			sizeof(training_rd_interval));
+		default_wait_in_micro_secs = training_rd_interval ?
+			(training_rd_interval * 4000) :
+			default_wait_in_micro_secs;
+	}
+
+	dm_delay_in_microseconds(link->ctx, default_wait_in_micro_secs);
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s:\n wait = %d\n",
+		__func__,
+		default_wait_in_micro_secs);
+}
+
+static void dpcd_set_training_pattern(
+	struct core_link* link,
+	union dpcd_training_pattern dpcd_pattern)
+{
+	core_link_write_dpcd(
+		link,
+		DPCD_ADDRESS_TRAINING_PATTERN_SET,
+		&dpcd_pattern.raw,
+		1);
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s\n %x pattern = %x\n",
+		__func__,
+		DPCD_ADDRESS_TRAINING_PATTERN_SET,
+		dpcd_pattern.bits.TRAINING_PATTERN_SET);
+}
+
+static void dpcd_set_link_settings(
+	struct core_link* link,
+	const struct link_training_settings *lt_settings)
+{
+	uint8_t rate = (uint8_t)
+	(lt_settings->link_settings.link_rate);
+
+	union down_spread_ctrl downspread = {{0}};
+	union lane_count_set lane_count_set = {{0}};
+	uint8_t link_set_buffer[2];
+
+
+	downspread.raw = (uint8_t)
+	(lt_settings->link_settings.link_spread);
+
+	lane_count_set.bits.LANE_COUNT_SET =
+	lt_settings->link_settings.lane_count;
+
+	lane_count_set.bits.ENHANCED_FRAMING = 1;
+
+	lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
+		link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;
+
+	link_set_buffer[0] = rate;
+	link_set_buffer[1] = lane_count_set.raw;
+
+	core_link_write_dpcd(link, DPCD_ADDRESS_LINK_BW_SET,
+	link_set_buffer, 2);
+	core_link_write_dpcd(link, DPCD_ADDRESS_DOWNSPREAD_CNTL,
+	&downspread.raw, sizeof(downspread));
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n",
+		__func__,
+		DPCD_ADDRESS_LINK_BW_SET,
+		lt_settings->link_settings.link_rate,
+		DPCD_ADDRESS_LANE_COUNT_SET,
+		lt_settings->link_settings.lane_count,
+		DPCD_ADDRESS_DOWNSPREAD_CNTL,
+		lt_settings->link_settings.link_spread);
+
+}
+
+static enum dpcd_training_patterns
+	hw_training_pattern_to_dpcd_training_pattern(
+	struct core_link* link,
+	enum hw_dp_training_pattern pattern)
+{
+	enum dpcd_training_patterns dpcd_tr_pattern =
+	DPCD_TRAINING_PATTERN_VIDEOIDLE;
+
+	switch (pattern) {
+	case HW_DP_TRAINING_PATTERN_1:
+		dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1;
+		break;
+	case HW_DP_TRAINING_PATTERN_2:
+		dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2;
+		break;
+	case HW_DP_TRAINING_PATTERN_3:
+		dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3;
+		break;
+	default:
+		ASSERT(0);
+		dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_HW_TRACE,
+			LOG_MINOR_HW_TRACE_LINK_TRAINING,
+			"%s: Invalid HW Training pattern: %d\n",
+			__func__, pattern);
+		break;
+	}
+
+	return dpcd_tr_pattern;
+
+}
+
+static void dpcd_set_lt_pattern_and_lane_settings(
+	struct core_link* link,
+	const struct link_training_settings *lt_settings,
+	enum hw_dp_training_pattern pattern)
+{
+	union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}};
+	const uint32_t dpcd_base_lt_offset =
+	DPCD_ADDRESS_TRAINING_PATTERN_SET;
+	uint8_t dpcd_lt_buffer[5] = {0};
+	union dpcd_training_pattern dpcd_pattern = {{0}};
+	uint32_t lane;
+	uint32_t size_in_bytes;
+	bool edp_workaround = false; /* TODO link_prop.INTERNAL */
+
+	/*****************************************************************
+	* DpcdAddress_TrainingPatternSet
+	*****************************************************************/
+	dpcd_pattern.bits.TRAINING_PATTERN_SET =
+		hw_training_pattern_to_dpcd_training_pattern(link, pattern);
+
+	dpcd_lt_buffer[DPCD_ADDRESS_TRAINING_PATTERN_SET - dpcd_base_lt_offset]
+		= dpcd_pattern.raw;
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s\n %x pattern = %x\n",
+		__func__,
+		DPCD_ADDRESS_TRAINING_PATTERN_SET,
+		dpcd_pattern.bits.TRAINING_PATTERN_SET);
+
+
+	/*****************************************************************
+	* DpcdAddress_Lane0Set -> DpcdAddress_Lane3Set
+	*****************************************************************/
+	for (lane = 0; lane <
+		(uint32_t)(lt_settings->link_settings.lane_count); lane++) {
+
+		dpcd_lane[lane].bits.VOLTAGE_SWING_SET =
+		(uint8_t)(lt_settings->lane_settings[lane].VOLTAGE_SWING);
+		dpcd_lane[lane].bits.PRE_EMPHASIS_SET =
+		(uint8_t)(lt_settings->lane_settings[lane].PRE_EMPHASIS);
+
+		dpcd_lane[lane].bits.MAX_SWING_REACHED =
+		(lt_settings->lane_settings[lane].VOLTAGE_SWING ==
+		VOLTAGE_SWING_MAX_LEVEL ? 1 : 0);
+		dpcd_lane[lane].bits.MAX_PRE_EMPHASIS_REACHED =
+		(lt_settings->lane_settings[lane].PRE_EMPHASIS ==
+		PRE_EMPHASIS_MAX_LEVEL ? 1 : 0);
+	}
+
+	/* concatinate everything into one buffer*/
+
+	size_in_bytes = lt_settings->link_settings.lane_count * sizeof(dpcd_lane[0]);
+
+	 // 0x00103 - 0x00102
+	dm_memmove(
+		&dpcd_lt_buffer[DPCD_ADDRESS_LANE0_SET - dpcd_base_lt_offset],
+		dpcd_lane,
+		size_in_bytes);
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s:\n %x VS set = %x  PE set = %x \
+		max VS Reached = %x  max PE Reached = %x\n",
+		__func__,
+		DPCD_ADDRESS_LANE0_SET,
+		dpcd_lane[0].bits.VOLTAGE_SWING_SET,
+		dpcd_lane[0].bits.PRE_EMPHASIS_SET,
+		dpcd_lane[0].bits.MAX_SWING_REACHED,
+		dpcd_lane[0].bits.MAX_PRE_EMPHASIS_REACHED);
+
+
+	if (edp_workaround) {
+		/* for eDP write in 2 parts because the 5-byte burst is
+		* causing issues on some eDP panels (EPR#366724)
+		*/
+		core_link_write_dpcd(
+			link,
+			DPCD_ADDRESS_TRAINING_PATTERN_SET,
+			&dpcd_pattern.raw,
+			sizeof(dpcd_pattern.raw) );
+
+		core_link_write_dpcd(
+			link,
+			DPCD_ADDRESS_LANE0_SET,
+			(uint8_t *)(dpcd_lane),
+			size_in_bytes);
+
+		} else
+		/* write it all in (1 + number-of-lanes)-byte burst*/
+			core_link_write_dpcd(
+				link,
+				dpcd_base_lt_offset,
+				dpcd_lt_buffer,
+				size_in_bytes + sizeof(dpcd_pattern.raw) );
+
+	link->ln_setting = lt_settings->lane_settings[0];
+}
+
+static bool is_cr_done(enum lane_count ln_count,
+	union lane_status *dpcd_lane_status)
+{
+	bool done = true;
+	uint32_t lane;
+	/*LANEx_CR_DONE bits All 1's?*/
+	for (lane = 0; lane < (uint32_t)(ln_count); lane++) {
+		if (!dpcd_lane_status[lane].bits.CR_DONE_0)
+			done = false;
+	}
+	return done;
+
+}
+
+static bool is_ch_eq_done(enum lane_count ln_count,
+	union lane_status *dpcd_lane_status,
+	union lane_align_status_updated *lane_status_updated)
+{
+	bool done = true;
+	uint32_t lane;
+	if (!lane_status_updated->bits.INTERLANE_ALIGN_DONE)
+		done = false;
+	else {
+		for (lane = 0; lane < (uint32_t)(ln_count); lane++) {
+			if (!dpcd_lane_status[lane].bits.SYMBOL_LOCKED_0 ||
+				!dpcd_lane_status[lane].bits.CHANNEL_EQ_DONE_0)
+				done = false;
+		}
+	}
+	return done;
+
+}
+
+static void update_drive_settings(
+		struct link_training_settings *dest,
+		struct link_training_settings src)
+{
+	uint32_t lane;
+	for (lane = 0; lane < src.link_settings.lane_count; lane++) {
+		dest->lane_settings[lane].VOLTAGE_SWING =
+			src.lane_settings[lane].VOLTAGE_SWING;
+		dest->lane_settings[lane].PRE_EMPHASIS =
+			src.lane_settings[lane].PRE_EMPHASIS;
+		dest->lane_settings[lane].POST_CURSOR2 =
+			src.lane_settings[lane].POST_CURSOR2;
+	}
+}
+
+static uint8_t get_nibble_at_index(const uint8_t *buf,
+	uint32_t index)
+{
+	uint8_t nibble;
+	nibble = buf[index / 2];
+
+	if (index % 2)
+		nibble >>= 4;
+	else
+		nibble &= 0x0F;
+
+	return nibble;
+}
+
+static enum pre_emphasis get_max_pre_emphasis_for_voltage_swing(
+	enum voltage_swing voltage)
+{
+	enum pre_emphasis pre_emphasis;
+	pre_emphasis = PRE_EMPHASIS_MAX_LEVEL;
+
+	if (voltage <= VOLTAGE_SWING_MAX_LEVEL)
+		pre_emphasis = voltage_swing_to_pre_emphasis[voltage];
+
+	return pre_emphasis;
+
+}
+
+static void find_max_drive_settings(
+	const struct link_training_settings *link_training_setting,
+	struct link_training_settings *max_lt_setting)
+{
+	uint32_t lane;
+	struct lane_settings max_requested;
+
+	max_requested.VOLTAGE_SWING =
+		link_training_setting->
+		lane_settings[0].VOLTAGE_SWING;
+	max_requested.PRE_EMPHASIS =
+		link_training_setting->
+		lane_settings[0].PRE_EMPHASIS;
+	/*max_requested.postCursor2 =
+	 * link_training_setting->laneSettings[0].postCursor2;*/
+
+	/* Determine what the maximum of the requested settings are*/
+	for (lane = 1; lane < link_training_setting->link_settings.lane_count;
+			lane++) {
+		if (link_training_setting->lane_settings[lane].VOLTAGE_SWING >
+			max_requested.VOLTAGE_SWING)
+
+			max_requested.VOLTAGE_SWING =
+			link_training_setting->
+			lane_settings[lane].VOLTAGE_SWING;
+
+
+		if (link_training_setting->lane_settings[lane].PRE_EMPHASIS >
+				max_requested.PRE_EMPHASIS)
+			max_requested.PRE_EMPHASIS =
+			link_training_setting->
+			lane_settings[lane].PRE_EMPHASIS;
+
+		/*
+		if (link_training_setting->laneSettings[lane].postCursor2 >
+		 max_requested.postCursor2)
+		{
+		max_requested.postCursor2 =
+		link_training_setting->laneSettings[lane].postCursor2;
+		}
+		*/
+	}
+
+	/* make sure the requested settings are
+	 * not higher than maximum settings*/
+	if (max_requested.VOLTAGE_SWING > VOLTAGE_SWING_MAX_LEVEL)
+		max_requested.VOLTAGE_SWING = VOLTAGE_SWING_MAX_LEVEL;
+
+	if (max_requested.PRE_EMPHASIS > PRE_EMPHASIS_MAX_LEVEL)
+		max_requested.PRE_EMPHASIS = PRE_EMPHASIS_MAX_LEVEL;
+	/*
+	if (max_requested.postCursor2 > PostCursor2_MaxLevel)
+	max_requested.postCursor2 = PostCursor2_MaxLevel;
+	*/
+
+	/* make sure the pre-emphasis matches the voltage swing*/
+	if (max_requested.PRE_EMPHASIS >
+		get_max_pre_emphasis_for_voltage_swing(
+			max_requested.VOLTAGE_SWING))
+		max_requested.PRE_EMPHASIS =
+		get_max_pre_emphasis_for_voltage_swing(
+			max_requested.VOLTAGE_SWING);
+
+	/*
+	 * Post Cursor2 levels are completely independent from
+	 * pre-emphasis (Post Cursor1) levels. But Post Cursor2 levels
+	 * can only be applied to each allowable combination of voltage
+	 * swing and pre-emphasis levels */
+	 /* if ( max_requested.postCursor2 >
+	  *  getMaxPostCursor2ForVoltageSwing(max_requested.voltageSwing))
+	  *  max_requested.postCursor2 =
+	  *  getMaxPostCursor2ForVoltageSwing(max_requested.voltageSwing);
+	  */
+
+	max_lt_setting->link_settings.link_rate =
+		link_training_setting->link_settings.link_rate;
+	max_lt_setting->link_settings.lane_count =
+	link_training_setting->link_settings.lane_count;
+	max_lt_setting->link_settings.link_spread =
+		link_training_setting->link_settings.link_spread;
+
+	for (lane = 0; lane <
+		link_training_setting->link_settings.lane_count;
+		lane++) {
+		max_lt_setting->lane_settings[lane].VOLTAGE_SWING =
+			max_requested.VOLTAGE_SWING;
+		max_lt_setting->lane_settings[lane].PRE_EMPHASIS =
+			max_requested.PRE_EMPHASIS;
+		/*max_lt_setting->laneSettings[lane].postCursor2 =
+		 * max_requested.postCursor2;
+		 */
+	}
+
+}
+
+static void get_lane_status_and_drive_settings(
+	struct core_link* link,
+	const struct link_training_settings *link_training_setting,
+	union lane_status *ln_status,
+	union lane_align_status_updated *ln_status_updated,
+	struct link_training_settings *req_settings)
+{
+	uint8_t dpcd_buf[6] = {0};
+	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {{{0}}};
+	struct link_training_settings request_settings = {{0}};
+	uint32_t lane;
+
+	dm_memset(req_settings, '\0', sizeof(struct link_training_settings));
+
+	core_link_read_dpcd(
+		link,
+		DPCD_ADDRESS_LANE_01_STATUS,
+		(uint8_t *)(dpcd_buf),
+		sizeof(dpcd_buf));
+
+
+	for (lane = 0; lane <
+		(uint32_t)(link_training_setting->link_settings.lane_count);
+		lane++) {
+
+		ln_status[lane].raw =
+			get_nibble_at_index(&dpcd_buf[0], lane);
+		dpcd_lane_adjust[lane].raw =
+			get_nibble_at_index(&dpcd_buf[4], lane);
+	}
+
+	ln_status_updated->raw = dpcd_buf[2];
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s:\n%x Lane01Status = %x\n %x Lane23Status = %x\n ",
+		__func__,
+		DPCD_ADDRESS_LANE_01_STATUS, dpcd_buf[0],
+		DPCD_ADDRESS_LANE_23_STATUS, dpcd_buf[1]);
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s:\n %x Lane01AdjustRequest = %x\n %x Lane23AdjustRequest = %x\n",
+		__func__,
+		DPCD_ADDRESS_ADJUST_REQUEST_LANE0_1,
+		dpcd_buf[4],
+		DPCD_ADDRESS_ADJUST_REQUEST_LANE2_3,
+		dpcd_buf[5]);
+
+	/*copy to req_settings*/
+	request_settings.link_settings.lane_count =
+		link_training_setting->link_settings.lane_count;
+	request_settings.link_settings.link_rate =
+		link_training_setting->link_settings.link_rate;
+	request_settings.link_settings.link_spread =
+		link_training_setting->link_settings.link_spread;
+
+	for (lane = 0; lane <
+		(uint32_t)(link_training_setting->link_settings.lane_count);
+		lane++) {
+
+		request_settings.lane_settings[lane].VOLTAGE_SWING =
+			(enum voltage_swing)(dpcd_lane_adjust[lane].bits.
+				VOLTAGE_SWING_LANE);
+		request_settings.lane_settings[lane].PRE_EMPHASIS =
+			(enum pre_emphasis)(dpcd_lane_adjust[lane].bits.
+				PRE_EMPHASIS_LANE);
+	}
+
+	/*Note: for postcursor2, read adjusted
+	 * postcursor2 settings from*/
+	/*DpcdAddress_AdjustRequestPostCursor2 =
+	 *0x020C (not implemented yet)*/
+
+	/* we find the maximum of the requested settings across all lanes*/
+	/* and set this maximum for all lanes*/
+	find_max_drive_settings(&request_settings, req_settings);
+
+	/* if post cursor 2 is needed in the future,
+	 * read DpcdAddress_AdjustRequestPostCursor2 = 0x020C
+	 */
+
+}
+
+static void dpcd_set_lane_settings(
+	struct core_link* link,
+	const struct link_training_settings *link_training_setting)
+{
+	union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}};
+	uint32_t lane;
+
+	for (lane = 0; lane <
+		(uint32_t)(link_training_setting->
+		link_settings.lane_count);
+		lane++) {
+		dpcd_lane[lane].bits.VOLTAGE_SWING_SET =
+			(uint8_t)(link_training_setting->
+			lane_settings[lane].VOLTAGE_SWING);
+		dpcd_lane[lane].bits.PRE_EMPHASIS_SET =
+			(uint8_t)(link_training_setting->
+			lane_settings[lane].PRE_EMPHASIS);
+		dpcd_lane[lane].bits.MAX_SWING_REACHED =
+			(link_training_setting->
+			lane_settings[lane].VOLTAGE_SWING ==
+			VOLTAGE_SWING_MAX_LEVEL ? 1 : 0);
+		dpcd_lane[lane].bits.MAX_PRE_EMPHASIS_REACHED =
+			(link_training_setting->
+			lane_settings[lane].PRE_EMPHASIS ==
+			PRE_EMPHASIS_MAX_LEVEL ? 1 : 0);
+	}
+
+	core_link_write_dpcd(link,
+		DPCD_ADDRESS_LANE0_SET,
+		(uint8_t *)(dpcd_lane),
+		link_training_setting->link_settings.lane_count);
+
+	/*
+	if (LTSettings.link.rate == LinkRate_High2)
+	{
+		DpcdTrainingLaneSet2 dpcd_lane2[lane_count_DPMax] = {0};
+		for ( uint32_t lane = 0;
+		lane < lane_count_DPMax; lane++)
+		{
+			dpcd_lane2[lane].bits.post_cursor2_set =
+			static_cast<unsigned char>(
+			LTSettings.laneSettings[lane].postCursor2);
+			dpcd_lane2[lane].bits.max_post_cursor2_reached = 0;
+		}
+		m_pDpcdAccessSrv->WriteDpcdData(
+		DpcdAddress_Lane0Set2,
+		reinterpret_cast<unsigned char*>(dpcd_lane2),
+		LTSettings.link.lanes);
+	}
+	*/
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_LINK_TRAINING,
+		"%s\n %x VS set = %x  PE set = %x \
+		max VS Reached = %x  max PE Reached = %x\n",
+		__func__,
+		DPCD_ADDRESS_LANE0_SET,
+		dpcd_lane[0].bits.VOLTAGE_SWING_SET,
+		dpcd_lane[0].bits.PRE_EMPHASIS_SET,
+		dpcd_lane[0].bits.MAX_SWING_REACHED,
+		dpcd_lane[0].bits.MAX_PRE_EMPHASIS_REACHED);
+
+	link->ln_setting = link_training_setting->lane_settings[0];
+
+}
+
+static bool is_max_vs_reached(
+	const struct link_training_settings *lt_settings)
+{
+	uint32_t lane;
+	for (lane = 0; lane <
+		(uint32_t)(lt_settings->link_settings.lane_count);
+		lane++) {
+		if (lt_settings->lane_settings[lane].VOLTAGE_SWING
+			== VOLTAGE_SWING_MAX_LEVEL)
+			return true;
+	}
+	return false;
+
+}
+
+void set_drive_settings(
+	struct core_link *link,
+	struct link_training_settings *lt_settings)
+{
+	/* program ASIC PHY settings*/
+	dp_set_hw_lane_settings(link, lt_settings);
+
+	/* Notify DP sink the PHY settings from source */
+	dpcd_set_lane_settings(link, lt_settings);
+}
+
+static bool perform_post_lt_adj_req_sequence(
+	struct core_link *link,
+	struct link_training_settings *lt_settings)
+{
+	enum lane_count lane_count =
+	lt_settings->link_settings.lane_count;
+
+	uint32_t adj_req_count;
+	uint32_t adj_req_timer;
+	bool req_drv_setting_changed;
+	uint32_t lane;
+
+	req_drv_setting_changed = false;
+	for (adj_req_count = 0; adj_req_count < POST_LT_ADJ_REQ_LIMIT;
+	adj_req_count++) {
+
+		req_drv_setting_changed = false;
+
+		for (adj_req_timer = 0;
+			adj_req_timer < POST_LT_ADJ_REQ_TIMEOUT;
+			adj_req_timer++) {
+
+			struct link_training_settings req_settings;
+			union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
+			union lane_align_status_updated
+				dpcd_lane_status_updated;
+
+			get_lane_status_and_drive_settings(
+			link,
+			lt_settings,
+			dpcd_lane_status,
+			&dpcd_lane_status_updated,
+			&req_settings);
+
+			if (dpcd_lane_status_updated.bits.
+					POST_LT_ADJ_REQ_IN_PROGRESS == 0)
+				return true;
+
+			if (!is_cr_done(lane_count, dpcd_lane_status))
+				return false;
+
+			if (!is_ch_eq_done(
+				lane_count,
+				dpcd_lane_status,
+				&dpcd_lane_status_updated))
+				return false;
+
+			for (lane = 0; lane < (uint32_t)(lane_count); lane++) {
+
+				if (lt_settings->
+				lane_settings[lane].VOLTAGE_SWING !=
+				req_settings.lane_settings[lane].
+				VOLTAGE_SWING ||
+				lt_settings->lane_settings[lane].PRE_EMPHASIS !=
+				req_settings.lane_settings[lane].PRE_EMPHASIS) {
+
+					req_drv_setting_changed = true;
+					break;
+				}
+			}
+
+			if (req_drv_setting_changed) {
+				update_drive_settings(
+					lt_settings,req_settings);
+
+				set_drive_settings(link, lt_settings);
+				break;
+			}
+
+			dm_sleep_in_milliseconds(link->ctx, 1);
+		}
+
+		if (!req_drv_setting_changed) {
+			dal_logger_write(link->ctx->logger,
+				LOG_MAJOR_WARNING,
+				LOG_MINOR_COMPONENT_LINK_SERVICE,
+				"%s: Post Link Training Adjust Request Timed out\n",
+				__func__);
+
+			ASSERT(0);
+			return true;
+		}
+	}
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_WARNING,
+		LOG_MINOR_COMPONENT_LINK_SERVICE,
+		"%s: Post Link Training Adjust Request limit reached\n",
+		__func__);
+
+	ASSERT(0);
+	return true;
+
+}
+
+static bool perform_channel_equalization_sequence(
+	struct core_link *link,
+	struct link_training_settings *lt_settings)
+{
+	struct link_training_settings req_settings;
+	enum hw_dp_training_pattern hw_tr_pattern;
+	uint32_t retries_ch_eq;
+	enum lane_count lane_count = lt_settings->link_settings.lane_count;
+	union lane_align_status_updated dpcd_lane_status_updated = {{0}};
+	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {{{0}}};;
+
+	/*TODO hw_tr_pattern = HW_DP_TRAINING_PATTERN_3;*/
+	hw_tr_pattern = HW_DP_TRAINING_PATTERN_2;
+
+	dp_set_hw_training_pattern(link, hw_tr_pattern);
+
+	for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
+		retries_ch_eq++) {
+
+		dp_set_hw_lane_settings(link, lt_settings);
+
+		/* 2. update DPCD*/
+		if (!retries_ch_eq)
+			/* EPR #361076 - write as a 5-byte burst,
+			 * but only for the 1-st iteration*/
+			dpcd_set_lt_pattern_and_lane_settings(
+				link,
+				lt_settings,
+				hw_tr_pattern);
+		else
+			dpcd_set_lane_settings(link, lt_settings);
+
+		/* 3. wait for receiver to lock-on*/
+		wait_for_training_aux_rd_interval(link, 400);
+
+		/* 4. Read lane status and requested
+		 * drive settings as set by the sink*/
+
+		get_lane_status_and_drive_settings(
+			link,
+			lt_settings,
+			dpcd_lane_status,
+			&dpcd_lane_status_updated,
+			&req_settings);
+
+		/* 5. check CR done*/
+		if (!is_cr_done(lane_count, dpcd_lane_status))
+			return false;
+
+		/* 6. check CHEQ done*/
+		if (is_ch_eq_done(lane_count,
+			dpcd_lane_status,
+			&dpcd_lane_status_updated))
+			return true;
+
+		/* 7. update VS/PE/PC2 in lt_settings*/
+		update_drive_settings(lt_settings, req_settings);
+	}
+
+	return false;
+
+}
+
+static bool perform_clock_recovery_sequence(
+	struct core_link *link,
+	struct link_training_settings *lt_settings)
+{
+	uint32_t retries_cr;
+	uint32_t retry_count;
+	uint32_t lane;
+	struct link_training_settings req_settings;
+	enum lane_count lane_count =
+	lt_settings->link_settings.lane_count;
+	enum hw_dp_training_pattern hw_tr_pattern = HW_DP_TRAINING_PATTERN_1;
+	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
+	union lane_align_status_updated dpcd_lane_status_updated;
+
+	retries_cr = 0;
+	retry_count = 0;
+	/* initial drive setting (VS/PE/PC2)*/
+	for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
+		lt_settings->lane_settings[lane].VOLTAGE_SWING =
+		VOLTAGE_SWING_LEVEL0;
+		lt_settings->lane_settings[lane].PRE_EMPHASIS =
+		PRE_EMPHASIS_DISABLED;
+		lt_settings->lane_settings[lane].POST_CURSOR2 =
+		POST_CURSOR2_DISABLED;
+	}
+
+	dp_set_hw_training_pattern(link, hw_tr_pattern);
+
+	/* najeeb - The synaptics MST hub can put the LT in
+	* infinite loop by switching the VS
+	*/
+	/* between level 0 and level 1 continuously, here
+	* we try for CR lock for LinkTrainingMaxCRRetry count*/
+	while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
+	(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
+
+		dm_memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
+		dm_memset(&dpcd_lane_status_updated, '\0',
+		sizeof(dpcd_lane_status_updated));
+
+		/* 1. call HWSS to set lane settings*/
+		dp_set_hw_lane_settings(
+				link,
+				lt_settings);
+
+		/* 2. update DPCD of the receiver*/
+		if (!retries_cr)
+			/* EPR #361076 - write as a 5-byte burst,
+			 * but only for the 1-st iteration.*/
+			dpcd_set_lt_pattern_and_lane_settings(
+					link,
+					lt_settings,
+					hw_tr_pattern);
+		else
+			dpcd_set_lane_settings(
+					link,
+					lt_settings);
+
+
+		/* 3. wait receiver to lock-on*/
+		wait_for_training_aux_rd_interval(
+				link,
+				100);
+
+		/* 4. Read lane status and requested drive
+		* settings as set by the sink
+		*/
+		get_lane_status_and_drive_settings(
+				link,
+				lt_settings,
+				dpcd_lane_status,
+				&dpcd_lane_status_updated,
+				&req_settings);
+
+
+		/* 5. check CR done*/
+		if (is_cr_done(lane_count, dpcd_lane_status))
+			return true;
+
+		/* 6. max VS reached*/
+		if (is_max_vs_reached(lt_settings))
+			return false;
+
+		/* 7. same voltage*/
+		/* Note: VS same for all lanes,
+		* so comparing first lane is sufficient*/
+		if (lt_settings->lane_settings[0].VOLTAGE_SWING ==
+			req_settings.lane_settings[0].VOLTAGE_SWING)
+			retries_cr++;
+		else
+			retries_cr = 0;
+
+
+			/* 8. update VS/PE/PC2 in lt_settings*/
+			update_drive_settings(lt_settings, req_settings);
+
+			retry_count++;
+	}
+
+	if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
+		ASSERT(0);
+		dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_LINK_SERVICE,
+			"%s: Link Training Error, could not \
+			 get CR after %d tries. \
+			Possibly voltage swing issue", __func__,
+			LINK_TRAINING_MAX_CR_RETRY);
+
+	}
+
+	return false;
+}
+
+ bool perform_link_training(
+	struct core_link *link,
+	const struct link_settings *link_setting,
+	bool skip_video_pattern)
+{
+	bool status;
+	union dpcd_training_pattern dpcd_pattern = {{0}};
+	union lane_count_set lane_count_set = {{0}};
+	const int8_t *link_rate = "Unknown";
+	struct link_training_settings lt_settings;
+
+	status = false;
+	dm_memset(&lt_settings, '\0', sizeof(lt_settings));
+
+	lt_settings.link_settings.link_rate = link_setting->link_rate;
+	lt_settings.link_settings.lane_count = link_setting->lane_count;
+
+	/*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/
+
+	/* TODO hard coded to SS for now
+	 * lt_settings.link_settings.link_spread =
+	 * dal_display_path_is_ss_supported(
+	 * path_mode->display_path) ?
+	 * LINK_SPREAD_05_DOWNSPREAD_30KHZ :
+	 * LINK_SPREAD_DISABLED;
+	 */
+	lt_settings.link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ;
+
+	/* 1. set link rate, lane count and spread*/
+	dpcd_set_link_settings(link, &lt_settings);
+
+	/* 2. perform link training (set link training done
+	 *  to false is done as well)*/
+	if (perform_clock_recovery_sequence(link, &lt_settings)) {
+
+		if (perform_channel_equalization_sequence(link, &lt_settings))
+			status = true;
+	}
+
+	if (status || !skip_video_pattern) {
+
+		/* 3. set training not in progress*/
+		dpcd_pattern.bits.TRAINING_PATTERN_SET =
+			DPCD_TRAINING_PATTERN_VIDEOIDLE;
+		dpcd_set_training_pattern(link, dpcd_pattern);
+
+		/* 4. mainlink output idle pattern*/
+		dp_set_hw_test_pattern(link, DP_TEST_PATTERN_VIDEO_MODE);
+
+		/* 5. post training adjust if required*/
+		if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED
+			== 1) {
+			if (status == true) {
+				if (perform_post_lt_adj_req_sequence(
+					link, &lt_settings) == false)
+					status = false;
+			}
+
+			lane_count_set.bits.LANE_COUNT_SET =
+				lt_settings.link_settings.lane_count;
+			lane_count_set.bits.ENHANCED_FRAMING = 1;
+			lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0;
+
+			core_link_write_dpcd(
+				link,
+				DPCD_ADDRESS_LANE_COUNT_SET,
+				&lane_count_set.raw,
+				sizeof(lane_count_set));
+		}
+	}
+
+	/* 6. print status message*/
+	switch (lt_settings.link_settings.link_rate) {
+
+	case LINK_RATE_LOW:
+		link_rate = "Low";
+		break;
+	case LINK_RATE_HIGH:
+		link_rate = "High";
+		break;
+	case LINK_RATE_HIGH2:
+		link_rate = "High2";
+		break;
+	case LINK_RATE_RBR2:
+		link_rate = "RBR2";
+		break;
+	default:
+		break;
+	}
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_MST,
+		LOG_MINOR_MST_PROGRAMMING,
+		"Link training for %d lanes at %s rate %s with PE %d, VS %d\n",
+		lt_settings.link_settings.lane_count,
+		link_rate,
+		status ? "succeeded" : "failed",
+		lt_settings.lane_settings[0].PRE_EMPHASIS,
+		lt_settings.lane_settings[0].VOLTAGE_SWING);
+
+	return status;
+}
+
+/*TODO add more check to see if link support request link configuration */
+static bool is_link_setting_supported(
+	const struct link_settings *link_setting,
+	const struct link_settings *max_link_setting)
+{
+	if (link_setting->lane_count > max_link_setting->lane_count ||
+		link_setting->link_rate > max_link_setting->link_rate)
+		return false;
+	return true;
+}
+
+static const uint32_t get_link_training_fallback_table_len(
+	struct core_link *link)
+{
+	return ARRAY_SIZE(link_training_fallback_table);
+}
+
+static const struct link_settings *get_link_training_fallback_table(
+	struct core_link *link, uint32_t i)
+{
+	return &link_training_fallback_table[i];
+}
+
+static bool exceeded_limit_link_setting(const struct link_settings *link_setting,
+			const struct link_settings *limit_link_setting)
+{
+	return (link_setting->lane_count * link_setting->link_rate
+		 > limit_link_setting->lane_count * limit_link_setting->link_rate ?
+				 true : false);
+}
+
+
+bool dp_hbr_verify_link_cap(
+	struct core_link *link,
+	struct link_settings *known_limit_link_setting)
+{
+	struct link_settings max_link_cap = {0};
+	bool success;
+	bool skip_link_training;
+	const struct link_settings *cur;
+	bool skip_video_pattern;
+	uint32_t i;
+
+	success = false;
+	skip_link_training = false;
+
+	/* TODO confirm this is correct for cz */
+	max_link_cap.lane_count = LANE_COUNT_FOUR;
+	max_link_cap.link_rate = LINK_RATE_HIGH2;
+	max_link_cap.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ;
+
+	/* TODO implement override and monitor patch later */
+
+	/* try to train the link from high to low to
+	 * find the physical link capability
+	 */
+	/* disable PHY done possible by BIOS, will be done by driver itself */
+	dp_disable_link_phy(link, link->public.connector_signal);
+
+	for (i = 0; i < get_link_training_fallback_table_len(link) &&
+		!success; i++) {
+		cur = get_link_training_fallback_table(link, i);
+
+		if (known_limit_link_setting->lane_count != LANE_COUNT_UNKNOWN &&
+			exceeded_limit_link_setting(cur,
+					known_limit_link_setting))
+			continue;
+
+		if (!is_link_setting_supported(cur, &max_link_cap))
+			continue;
+
+		skip_video_pattern = true;
+		if (cur->link_rate == LINK_RATE_LOW)
+			skip_video_pattern = false;
+
+		dp_enable_link_phy(
+				link,
+				link->public.connector_signal,
+				cur);
+
+		if (skip_link_training)
+			success = true;
+		else {
+			uint8_t num_retries = 3;
+			uint8_t j;
+			uint8_t delay_between_retries = 10;
+
+			for (j = 0; j < num_retries; ++j) {
+				success = perform_link_training(
+					link,
+					cur,
+					skip_video_pattern);
+
+				if (success)
+					break;
+
+				dm_sleep_in_milliseconds(
+					link->ctx,
+					delay_between_retries);
+
+				delay_between_retries += 10;
+			}
+		}
+
+		if (success)
+			link->verified_link_cap = *cur;
+
+		/* always disable the link before trying another
+		 * setting or before returning we'll enable it later
+		 * based on the actual mode we're driving
+		 */
+		dp_disable_link_phy(link, link->public.connector_signal);
+	}
+
+	/* Link Training failed for all Link Settings
+	 *  (Lane Count is still unknown)
+	 */
+	if (!success) {
+		/* If all LT fails for all settings,
+		 * set verified = failed safe (1 lane low)
+		 */
+		link->verified_link_cap.lane_count = LANE_COUNT_ONE;
+		link->verified_link_cap.link_rate = LINK_RATE_LOW;
+
+		link->verified_link_cap.link_spread =
+		LINK_SPREAD_DISABLED;
+	}
+
+	link->max_link_setting = link->verified_link_cap;
+
+	return success;
+}
+
+static uint32_t bandwidth_in_kbps_from_timing(
+	const struct dc_crtc_timing *timing)
+{
+	uint32_t bits_per_channel = 0;
+	uint32_t kbps;
+	switch (timing->display_color_depth) {
+
+	case COLOR_DEPTH_666:
+		bits_per_channel = 6;
+		break;
+	case COLOR_DEPTH_888:
+		bits_per_channel = 8;
+		break;
+	case COLOR_DEPTH_101010:
+		bits_per_channel = 10;
+		break;
+	case COLOR_DEPTH_121212:
+		bits_per_channel = 12;
+		break;
+	case COLOR_DEPTH_141414:
+		bits_per_channel = 14;
+		break;
+	case COLOR_DEPTH_161616:
+		bits_per_channel = 16;
+		break;
+	default:
+		break;
+	}
+	ASSERT(bits_per_channel != 0);
+
+	kbps = timing->pix_clk_khz;
+	kbps *= bits_per_channel;
+
+	if (timing->flags.Y_ONLY != 1)
+		/*Only YOnly make reduce bandwidth by 1/3 compares to RGB*/
+		kbps *= 3;
+
+	return kbps;
+
+}
+
+static uint32_t bandwidth_in_kbps_from_link_settings(
+	const struct link_settings *link_setting)
+{
+	uint32_t link_rate_in_kbps = link_setting->link_rate *
+		LINK_RATE_REF_FREQ_IN_KHZ;
+
+	uint32_t lane_count  = link_setting->lane_count;
+	uint32_t kbps = link_rate_in_kbps;
+	kbps *= lane_count;
+	kbps *= 8;   /* 8 bits per byte*/
+
+	return kbps;
+
+}
+
+bool dp_validate_mode_timing(
+	struct core_link *link,
+	const struct dc_crtc_timing *timing)
+{
+	uint32_t req_bw;
+	uint32_t max_bw;
+
+	const struct link_settings *link_setting;
+
+	/*always DP fail safe mode*/
+	if (timing->pix_clk_khz == (uint32_t)25175 &&
+		timing->h_addressable == (uint32_t)640 &&
+		timing->v_addressable == (uint32_t)480)
+		return true;
+
+	/* For static validation we always use reported
+	 * link settings for other cases, when no modelist
+	 * changed we can use verified link setting*/
+	link_setting = &link->reported_link_cap;
+
+	/* TODO: DYNAMIC_VALIDATION needs to be implemented */
+	/*if (flags.DYNAMIC_VALIDATION == 1 &&
+		link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN)
+		link_setting = &link->verified_link_cap;
+	*/
+
+	req_bw = bandwidth_in_kbps_from_timing(timing);
+	max_bw = bandwidth_in_kbps_from_link_settings(link_setting);
+
+	if (req_bw < max_bw) {
+		/* remember the biggest mode here, during
+		 * initial link training (to get
+		 * verified_link_cap), LS sends event about
+		 * cannot train at reported cap to upper
+		 * layer and upper layer will re-enumerate modes.
+		 * this is not necessary if the lower
+		 * verified_link_cap is enough to drive
+		 * all the modes */
+
+		/* TODO: DYNAMIC_VALIDATION needs to be implemented */
+		/* if (flags.DYNAMIC_VALIDATION == 1)
+			dpsst->max_req_bw_for_verified_linkcap = dal_max(
+				dpsst->max_req_bw_for_verified_linkcap, req_bw); */
+		return true;
+	} else
+		return false;
+}
+
+void decide_link_settings(struct core_stream *stream,
+	struct link_settings *link_setting)
+{
+
+	const struct link_settings *cur_ls;
+	struct core_link* link;
+	uint32_t req_bw;
+	uint32_t link_bw;
+	uint32_t i;
+
+	req_bw = bandwidth_in_kbps_from_timing(
+			&stream->public.timing);
+
+	/* if preferred is specified through AMDDP, use it, if it's enough
+	 * to drive the mode
+	 */
+	link = stream->sink->link;
+
+	if ((link->reported_link_cap.lane_count != LANE_COUNT_UNKNOWN) &&
+		(link->reported_link_cap.link_rate <=
+				link->verified_link_cap.link_rate)) {
+
+		link_bw = bandwidth_in_kbps_from_link_settings(
+				&link->reported_link_cap);
+
+		if (req_bw < link_bw) {
+			*link_setting = link->reported_link_cap;
+			return;
+		}
+	}
+
+	/* search for first suitable setting for the requested
+	 * bandwidth
+	 */
+	for (i = 0; i < get_link_training_fallback_table_len(link); i++) {
+
+		cur_ls = get_link_training_fallback_table(link, i);
+
+		link_bw =
+				bandwidth_in_kbps_from_link_settings(
+				cur_ls);
+
+		if (req_bw < link_bw) {
+			if (is_link_setting_supported(
+				cur_ls,
+				&link->max_link_setting)) {
+				*link_setting = *cur_ls;
+				return;
+			}
+		}
+	}
+
+	BREAK_TO_DEBUGGER();
+	ASSERT(link->verified_link_cap.lane_count !=
+		LANE_COUNT_UNKNOWN);
+
+	*link_setting = link->verified_link_cap;
+}
+
+/*************************Short Pulse IRQ***************************/
+
+static bool hpd_rx_irq_check_link_loss_status(
+	struct core_link *link,
+	union hpd_irq_data *hpd_irq_dpcd_data)
+{
+	uint8_t irq_reg_rx_power_state;
+	enum dc_status dpcd_result = DC_ERROR_UNEXPECTED;
+	union lane_status lane_status;
+	uint32_t lane;
+	bool sink_status_changed;
+	bool return_code;
+
+	sink_status_changed = false;
+	return_code = false;
+
+	if (link->cur_link_settings.lane_count == 0)
+		return return_code;
+	/*1. Check that we can handle interrupt: Not in FS DOS,
+	 *  Not in "Display Timeout" state, Link is trained.
+	 */
+
+	dpcd_result = core_link_read_dpcd(link,
+		DPCD_ADDRESS_POWER_STATE,
+		&irq_reg_rx_power_state,
+		sizeof(irq_reg_rx_power_state));
+
+	if (dpcd_result != DC_OK) {
+		irq_reg_rx_power_state = DP_PWR_STATE_D0;
+		dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_HW_TRACE,
+			LOG_MINOR_HW_TRACE_HPD_IRQ,
+			"%s: DPCD read failed to obtain power state.\n",
+			__func__);
+	}
+
+	if (irq_reg_rx_power_state == DP_PWR_STATE_D0) {
+
+		/*2. Check that Link Status changed, before re-training.*/
+
+		/*parse lane status*/
+		for (lane = 0;
+			lane < link->cur_link_settings.lane_count;
+			lane++) {
+
+			/* check status of lanes 0,1
+			 * changed DpcdAddress_Lane01Status (0x202)*/
+			lane_status.raw = get_nibble_at_index(
+				&hpd_irq_dpcd_data->bytes.lane01_status.raw,
+				lane);
+
+			if (!lane_status.bits.CHANNEL_EQ_DONE_0 ||
+				!lane_status.bits.CR_DONE_0 ||
+				!lane_status.bits.SYMBOL_LOCKED_0) {
+				/* if one of the channel equalization, clock
+				 * recovery or symbol lock is dropped
+				 * consider it as (link has been
+				 * dropped) dp sink status has changed*/
+				sink_status_changed = true;
+				break;
+			}
+
+		}
+
+		/* Check interlane align.*/
+		if (sink_status_changed ||
+			!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.
+			INTERLANE_ALIGN_DONE) {
+
+			dal_logger_write(link->ctx->logger,
+				LOG_MAJOR_HW_TRACE,
+				LOG_MINOR_HW_TRACE_HPD_IRQ,
+				"%s: Link Status changed.\n",
+				__func__);
+
+			return_code = true;
+		}
+	}
+
+	return return_code;
+}
+
+static enum dc_status read_hpd_rx_irq_data(
+	struct core_link *link,
+	union hpd_irq_data *irq_data)
+{
+	/* The HW reads 16 bytes from 200h on HPD,
+	 * but if we get an AUX_DEFER, the HW cannot retry
+	 * and this causes the CTS tests 4.3.2.1 - 3.2.4 to
+	 * fail, so we now explicitly read 6 bytes which is
+	 * the req from the above mentioned test cases.
+	 */
+	return core_link_read_dpcd(
+	link,
+	DPCD_ADDRESS_SINK_COUNT,
+	irq_data->raw,
+	sizeof(union hpd_irq_data));
+}
+
+static bool allow_hpd_rx_irq(const struct core_link *link)
+{
+	/*
+	 * Don't handle RX IRQ unless one of following is met:
+	 * 1) The link is established (cur_link_settings != unknown)
+	 * 2) We kicked off MST detection
+	 * 3) We know we're dealing with an active dongle
+	 */
+
+	if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
+		(link->public.type == dc_connection_mst_branch) ||
+		is_dp_active_dongle(link))
+		return true;
+
+	return false;
+}
+
+bool dc_link_handle_hpd_rx_irq(const struct dc_link *dc_link)
+{
+	struct core_link *link = DC_LINK_TO_LINK(dc_link);
+	union hpd_irq_data hpd_irq_dpcd_data = {{{{0}}}};
+	enum dc_status result = DDC_RESULT_UNKNOWN;
+	bool status = false;
+	/* For use cases related to down stream connection status change,
+	 * PSR and device auto test, refer to function handle_sst_hpd_irq
+	 * in DAL2.1*/
+
+	dal_logger_write(link->ctx->logger,
+		LOG_MAJOR_HW_TRACE,
+		LOG_MINOR_HW_TRACE_HPD_IRQ,
+		"%s: Got short pulse HPD on link %d\n",
+		__func__, link->public.link_index);
+
+	if (!allow_hpd_rx_irq(link)) {
+		dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_HW_TRACE,
+			LOG_MINOR_HW_TRACE_HPD_IRQ,
+			"%s: skipping HPD handling on %d\n",
+			__func__, link->public.link_index);
+		return false;
+	}
+
+	 /* All the "handle_hpd_irq_xxx()" methods
+	 * should be called only after
+	 * dal_dpsst_ls_read_hpd_irq_data
+	 * Order of calls is important too
+	 */
+	result = read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data);
+
+	if (result != DC_OK) {
+		dal_logger_write(link->ctx->logger,
+			LOG_MAJOR_HW_TRACE,
+			LOG_MINOR_HW_TRACE_HPD_IRQ,
+			"%s: DPCD read failed to obtain irq data\n",
+			__func__);
+		return false;
+	}
+
+	/* check if we have MST msg and return since we poll for it */
+	if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY ||
+		hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY)
+		return false;
+
+
+	/* For now we only handle 'Downstream port status' case. */
+	/* If we got sink count changed it means Downstream port status changed,
+	 * then DM should call DC to do the detection. */
+	if (hpd_rx_irq_check_link_loss_status(
+		link,
+		&hpd_irq_dpcd_data)) {
+		perform_link_training(link, &link->cur_link_settings, true);
+		status = false;
+	}
+
+	if (hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
+				!= link->dpcd_sink_count)
+		status = true;
+
+	/* reasons for HPD RX:
+	 * 1. Link Loss - ie Re-train the Link
+	 * 2. MST sideband message
+	 * 3. Automated Test - ie. Internal Commit
+	 * 4. CP (copy protection) - (not interesting for DM???)
+	 * 5. DRR
+	 * 6. Downstream Port status changed -ie. Detect - this the only one
+	 * which is interesting for DM because it must call dc_link_detect.
+	 */
+	return status;
+}
+
+/*query dpcd for version and mst cap addresses*/
+bool is_mst_supported(struct core_link *link)
+{
+	bool mst          = false;
+	enum dc_status st = DC_OK;
+	union dpcd_rev rev;
+	union mstm_cap cap;
+
+	rev.raw  = 0;
+	cap.raw  = 0;
+
+	st = core_link_read_dpcd(link, DPCD_ADDRESS_DPCD_REV, &rev.raw,
+			sizeof(rev));
+
+	if (st == DC_OK && rev.raw >= DPCD_REV_12) {
+
+		st = core_link_read_dpcd(link, DPCD_ADDRESS_MSTM_CAP,
+				&cap.raw, sizeof(cap));
+		if (st == DC_OK && cap.bits.MST_CAP == 1)
+			mst = true;
+	}
+	return mst;
+
+}
+
+bool is_dp_active_dongle(const struct core_link *link)
+{
+	enum display_dongle_type dongle_type = link->dpcd_caps.dongle_type;
+
+	return (dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) ||
+			(dongle_type == DISPLAY_DONGLE_DP_DVI_CONVERTER) ||
+			(dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER);
+}
+
+static void get_active_converter_info(
+	uint8_t data, struct core_link *link)
+{
+	union dp_downstream_port_present ds_port = { .byte = data };
+
+	/* decode converter info*/
+	if (!ds_port.fields.PORT_PRESENT) {
+		link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE;
+		ddc_service_set_dongle_type(link->ddc,
+				link->dpcd_caps.dongle_type);
+		return;
+	}
+
+	switch (ds_port.fields.PORT_TYPE) {
+	case DOWNSTREAM_VGA:
+		link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_VGA_CONVERTER;
+		break;
+	case DOWNSTREAM_DVI_HDMI:
+		/* At this point we don't know is it DVI or HDMI,
+		 * assume DVI.*/
+		link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_CONVERTER;
+		break;
+	default:
+		link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE;
+		break;
+	}
+
+	if (link->dpcd_caps.dpcd_rev.raw >= DCS_DPCD_REV_11) {
+		uint8_t det_caps[4];
+		union dwnstream_port_caps_byte0 *port_caps =
+			(union dwnstream_port_caps_byte0 *)det_caps;
+		core_link_read_dpcd(link, DPCD_ADDRESS_DWN_STRM_PORT0_CAPS,
+				det_caps, sizeof(det_caps));
+
+		switch (port_caps->bits.DWN_STRM_PORTX_TYPE) {
+		case DOWN_STREAM_DETAILED_VGA:
+			link->dpcd_caps.dongle_type =
+				DISPLAY_DONGLE_DP_VGA_CONVERTER;
+			break;
+		case DOWN_STREAM_DETAILED_DVI:
+			link->dpcd_caps.dongle_type =
+				DISPLAY_DONGLE_DP_DVI_CONVERTER;
+			break;
+		case DOWN_STREAM_DETAILED_HDMI:
+			link->dpcd_caps.dongle_type =
+				DISPLAY_DONGLE_DP_HDMI_CONVERTER;
+
+			if (ds_port.fields.DETAILED_CAPS) {
+
+				union dwnstream_port_caps_byte3_hdmi
+					hdmi_caps = {.raw = det_caps[3] };
+
+				link->dpcd_caps.is_dp_hdmi_s3d_converter =
+					hdmi_caps.bits.FRAME_SEQ_TO_FRAME_PACK;
+			}
+			break;
+		}
+	}
+	ddc_service_set_dongle_type(link->ddc,
+			link->dpcd_caps.dongle_type);
+}
+
+static void dp_wa_power_up_0010FA(struct core_link *link, uint8_t *dpcd_data,
+		int length)
+{
+	int retry = 0;
+	struct dp_device_vendor_id dp_id;
+	union dp_downstream_port_present ds_port = { 0 };
+
+	if (!link->dpcd_caps.dpcd_rev.raw) {
+		do {
+			dp_receiver_power_ctrl(link, true);
+			core_link_read_dpcd(link, DPCD_ADDRESS_DPCD_REV,
+							dpcd_data, length);
+			link->dpcd_caps.dpcd_rev.raw = dpcd_data[
+				DPCD_ADDRESS_DPCD_REV -
+				DPCD_ADDRESS_DPCD_REV];
+		} while (retry++ < 4 && !link->dpcd_caps.dpcd_rev.raw);
+	}
+
+	ds_port.byte = dpcd_data[DPCD_ADDRESS_DOWNSTREAM_PORT_PRESENT -
+				 DPCD_ADDRESS_DPCD_REV];
+
+	get_active_converter_info(ds_port.byte, link);
+
+	/* read IEEE branch device id */
+	core_link_read_dpcd(link, DPCD_ADDRESS_BRANCH_DEVICE_ID_START,
+			(uint8_t *)&dp_id, sizeof(dp_id));
+	link->dpcd_caps.branch_dev_id =
+			(dp_id.ieee_oui[0] << 16) +
+			(dp_id.ieee_oui[1] << 8) +
+			dp_id.ieee_oui[2];
+
+	if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) {
+		switch (link->dpcd_caps.branch_dev_id) {
+		/* Some active dongles (DP-VGA, DP-DLDVI converters) power down
+		 * all internal circuits including AUX communication preventing
+		 * reading DPCD table and EDID (spec violation).
+		 * Encoder will skip DP RX power down on disable_output to
+		 * keep receiver powered all the time.*/
+		case DP_BRANCH_DEVICE_ID_1:
+		case DP_BRANCH_DEVICE_ID_4:
+			link->wa_flags.dp_keep_receiver_powered = true;
+			break;
+
+		/* TODO: May need work around for other dongles. */
+		default:
+			link->wa_flags.dp_keep_receiver_powered = false;
+			break;
+		}
+	} else
+		link->wa_flags.dp_keep_receiver_powered = false;
+}
+
+static void retrieve_link_cap(struct core_link *link)
+{
+	uint8_t dpcd_data[
+			DPCD_ADDRESS_EDP_CONFIG_CAP -
+			DPCD_ADDRESS_DPCD_REV + 1];
+
+	union down_stream_port_count down_strm_port_count;
+	union edp_configuration_cap edp_config_cap;
+	union max_down_spread max_down_spread;
+	union dp_downstream_port_present ds_port = { 0 };
+
+	dm_memset(dpcd_data, '\0', sizeof(dpcd_data));
+	dm_memset(&down_strm_port_count,
+		'\0', sizeof(union down_stream_port_count));
+	dm_memset(&edp_config_cap, '\0',
+		sizeof(union edp_configuration_cap));
+	dm_memset(&max_down_spread, '\0',
+		sizeof(union max_down_spread));
+
+	core_link_read_dpcd(link, DPCD_ADDRESS_DPCD_REV,
+			dpcd_data, sizeof(dpcd_data));
+	link->dpcd_caps.dpcd_rev.raw = dpcd_data[
+		DPCD_ADDRESS_DPCD_REV -
+		DPCD_ADDRESS_DPCD_REV];
+
+	ds_port.byte = dpcd_data[DPCD_ADDRESS_DOWNSTREAM_PORT_PRESENT -
+				 DPCD_ADDRESS_DPCD_REV];
+
+	get_active_converter_info(ds_port.byte, link);
+
+	dp_wa_power_up_0010FA(link, dpcd_data, sizeof(dpcd_data));
+
+	link->dpcd_caps.allow_invalid_MSA_timing_param =
+		down_strm_port_count.bits.IGNORE_MSA_TIMING_PARAM;
+
+	link->dpcd_caps.max_ln_count.raw = dpcd_data[
+		DPCD_ADDRESS_MAX_LANE_COUNT - DPCD_ADDRESS_DPCD_REV];
+
+	max_down_spread.raw = dpcd_data[
+		DPCD_ADDRESS_MAX_DOWNSPREAD - DPCD_ADDRESS_DPCD_REV];
+
+	link->reported_link_cap.lane_count =
+		link->dpcd_caps.max_ln_count.bits.MAX_LANE_COUNT;
+	link->reported_link_cap.link_rate = dpcd_data[
+		DPCD_ADDRESS_MAX_LINK_RATE - DPCD_ADDRESS_DPCD_REV];
+	link->reported_link_cap.link_spread =
+		max_down_spread.bits.MAX_DOWN_SPREAD ?
+		LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED;
+
+	edp_config_cap.raw = dpcd_data[
+		DPCD_ADDRESS_EDP_CONFIG_CAP - DPCD_ADDRESS_DPCD_REV];
+	link->dpcd_caps.panel_mode_edp =
+		edp_config_cap.bits.ALT_SCRAMBLER_RESET;
+
+	link->edp_revision = DPCD_EDP_REVISION_EDP_UNKNOWN;
+
+	/* read sink count */
+	core_link_read_dpcd(link,
+			DPCD_ADDRESS_SINK_COUNT,
+			&link->dpcd_caps.sink_count.raw,
+			sizeof(link->dpcd_caps.sink_count.raw));
+
+	/* Display control registers starting at DPCD 700h are only valid and
+	 * enabled if this eDP config cap bit is set. */
+	if (edp_config_cap.bits.DPCD_DISPLAY_CONTROL_CAPABLE) {
+		/* Read the Panel's eDP revision at DPCD 700h. */
+		core_link_read_dpcd(link,
+			DPCD_ADDRESS_EDP_REV,
+			(uint8_t *)(&link->edp_revision),
+			sizeof(link->edp_revision));
+	}
+	/* TODO: Confirm if need retrieve_psr_link_cap */
+}
+
+void detect_dp_sink_caps(struct core_link *link)
+{
+	retrieve_link_cap(link);
+
+	/* dc init_hw has power encoder using default
+	 * signal for connector. For native DP, no
+	 * need to power up encoder again. If not native
+	 * DP, hw_init may need check signal or power up
+	 * encoder here.
+	 */
+
+	if (is_mst_supported(link)) {
+		link->verified_link_cap = link->reported_link_cap;
+	} else {
+		dp_hbr_verify_link_cap(link,
+			&link->reported_link_cap);
+	}
+	/* TODO save sink caps in link->sink */
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c
new file mode 100644
index 000000000000..39aa734680c6
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_link_hwss.c
@@ -0,0 +1,201 @@ 
+/* Copyright 2015 Advanced Micro Devices, Inc. */
+
+#include "dm_services.h"
+#include "dc.h"
+#include "inc/core_dc.h"
+#include "include/ddc_service_types.h"
+#include "include/i2caux_interface.h"
+#include "link_hwss.h"
+#include "hw_sequencer.h"
+#include "dc_link_ddc.h"
+#include "dm_helpers.h"
+#include "dce110/dce110_link_encoder.h"
+#include "dce110/dce110_stream_encoder.h"
+
+
+enum dc_status core_link_read_dpcd(
+	struct core_link* link,
+	uint32_t address,
+	uint8_t *data,
+	uint32_t size)
+{
+	if (!dm_helper_dp_read_dpcd(link->ctx,
+			&link->public,
+			address, data, size))
+			return DC_ERROR_UNEXPECTED;
+
+	return DC_OK;
+}
+
+enum dc_status core_link_write_dpcd(
+	struct core_link* link,
+	uint32_t address,
+	const uint8_t *data,
+	uint32_t size)
+{
+	if (!dm_helper_dp_write_dpcd(link->ctx,
+			&link->public,
+			address, data, size))
+				return DC_ERROR_UNEXPECTED;
+
+	return DC_OK;
+}
+
+void dp_receiver_power_ctrl(struct core_link *link, bool on)
+{
+	uint8_t state;
+
+	state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3;
+
+	core_link_write_dpcd(link, DPCD_ADDRESS_POWER_STATE, &state,
+			sizeof(state));
+}
+
+void dp_enable_link_phy(
+	struct core_link *link,
+	enum signal_type signal,
+	const struct link_settings *link_settings)
+{
+	struct link_encoder *link_enc = link->link_enc;
+
+	if (dc_is_dp_sst_signal(signal)) {
+		if (signal == SIGNAL_TYPE_EDP) {
+			link_enc->funcs->power_control(link_enc, true);
+			link_enc->funcs->backlight_control(link_enc, true);
+		}
+
+		link_enc->funcs->enable_dp_output(
+						link_enc,
+						link_settings,
+						CLOCK_SOURCE_ID_EXTERNAL);
+	} else {
+		link_enc->funcs->enable_dp_mst_output(
+						link_enc,
+						link_settings,
+						CLOCK_SOURCE_ID_EXTERNAL);
+	}
+
+	dp_receiver_power_ctrl(link, true);
+}
+
+void dp_disable_link_phy(struct core_link *link, enum signal_type signal)
+{
+	if (!link->wa_flags.dp_keep_receiver_powered)
+		dp_receiver_power_ctrl(link, false);
+
+	if (signal == SIGNAL_TYPE_EDP)
+		link->link_enc->funcs->backlight_control(link->link_enc, false);
+
+	link->link_enc->funcs->disable_output(link->link_enc, signal);
+
+	/* Clear current link setting.*/
+	dm_memset(&link->cur_link_settings, 0,
+			sizeof(link->cur_link_settings));
+}
+
+void dp_disable_link_phy_mst(struct core_link *link, struct core_stream *stream)
+{
+	/* MST disable link only when no stream use the link */
+	if (link->mst_stream_alloc_table.stream_count > 0)
+		return;
+
+	dp_disable_link_phy(link, stream->signal);
+}
+
+bool dp_set_hw_training_pattern(
+	struct core_link *link,
+	enum hw_dp_training_pattern pattern)
+{
+	enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
+	struct encoder_set_dp_phy_pattern_param pattern_param = {0};
+	struct link_encoder *encoder = link->link_enc;
+
+	switch (pattern) {
+	case HW_DP_TRAINING_PATTERN_1:
+		test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN1;
+		break;
+	case HW_DP_TRAINING_PATTERN_2:
+		test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN2;
+		break;
+	case HW_DP_TRAINING_PATTERN_3:
+		test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN3;
+		break;
+	default:
+		break;
+	}
+
+	pattern_param.dp_phy_pattern = test_pattern;
+	pattern_param.custom_pattern = NULL;
+	pattern_param.custom_pattern_size = 0;
+	pattern_param.dp_panel_mode = dp_get_panel_mode(link);
+
+	encoder->funcs->dp_set_phy_pattern(encoder, &pattern_param);
+
+	return true;
+}
+
+
+void dp_set_hw_lane_settings(
+	struct core_link *link,
+	const struct link_training_settings *link_settings)
+{
+	struct link_encoder *encoder = link->link_enc;
+
+	/* call Encoder to set lane settings */
+	encoder->funcs->dp_set_lane_settings(encoder, link_settings);
+}
+
+enum dp_panel_mode dp_get_panel_mode(struct core_link *link)
+{
+	/* We need to explicitly check that connector
+	 * is not DP. Some Travis_VGA get reported
+	 * by video bios as DP.
+	 */
+	if (link->public.connector_signal != SIGNAL_TYPE_DISPLAY_PORT) {
+
+		switch (link->dpcd_caps.branch_dev_id) {
+		case DP_BRANCH_DEVICE_ID_2:
+			if (strncmp(
+				link->dpcd_caps.branch_dev_name,
+				DP_VGA_LVDS_CONVERTER_ID_2,
+				sizeof(
+				link->dpcd_caps.
+				branch_dev_name)) == 0) {
+				return DP_PANEL_MODE_SPECIAL;
+			}
+			break;
+		case DP_BRANCH_DEVICE_ID_3:
+			if (strncmp(link->dpcd_caps.branch_dev_name,
+				DP_VGA_LVDS_CONVERTER_ID_3,
+				sizeof(
+				link->dpcd_caps.
+				branch_dev_name)) == 0) {
+				return DP_PANEL_MODE_SPECIAL;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (link->dpcd_caps.panel_mode_edp) {
+			return DP_PANEL_MODE_EDP;
+		}
+	}
+
+	return DP_PANEL_MODE_DEFAULT;
+}
+
+void dp_set_hw_test_pattern(
+	struct core_link *link,
+	enum dp_test_pattern test_pattern)
+{
+	struct encoder_set_dp_phy_pattern_param pattern_param = {0};
+	struct link_encoder *encoder = link->link_enc;
+
+	pattern_param.dp_phy_pattern = test_pattern;
+	pattern_param.custom_pattern = NULL;
+	pattern_param.custom_pattern_size = 0;
+	pattern_param.dp_panel_mode = dp_get_panel_mode(link);
+
+	encoder->funcs->dp_set_phy_pattern(encoder, &pattern_param);
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c
new file mode 100644
index 000000000000..8cb756e99bfd
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_resource.c
@@ -0,0 +1,1243 @@ 
+/*
+* Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "resource.h"
+#include "include/irq_service_interface.h"
+#include "link_encoder.h"
+#include "stream_encoder.h"
+#include "opp.h"
+#include "transform.h"
+
+#if defined(CONFIG_DRM_AMD_DAL_DCE10_0)
+#include "dce100/dce100_resource.h"
+#endif
+#if defined(CONFIG_DRM_AMD_DAL_DCE11_0)
+#include "dce110/dce110_resource.h"
+#endif
+
+bool dc_construct_resource_pool(struct adapter_service *adapter_serv,
+				struct dc *dc,
+				uint8_t num_virtual_links)
+{
+	enum dce_version dce_ver = dal_adapter_service_get_dce_version(adapter_serv);
+
+	switch (dce_ver) {
+#if defined(CONFIG_DRM_AMD_DAL_DCE10_0)
+	case DCE_VERSION_10_0:
+		return dce100_construct_resource_pool(
+			adapter_serv, num_virtual_links, dc, &dc->res_pool);
+#endif
+#if defined(CONFIG_DRM_AMD_DAL_DCE11_0)
+	case DCE_VERSION_11_0:
+		return dce110_construct_resource_pool(
+			adapter_serv, num_virtual_links, dc, &dc->res_pool);
+#endif
+	default:
+		break;
+	}
+
+	return false;
+}
+
+void unreference_clock_source(
+		struct resource_context *res_ctx,
+		struct clock_source *clock_source)
+{
+	int i;
+	for (i = 0; i < res_ctx->pool.clk_src_count; i++) {
+		if (res_ctx->pool.clock_sources[i] == clock_source) {
+			res_ctx->clock_source_ref_count[i]--;
+
+		if (res_ctx->clock_source_ref_count[i] == 0)
+			clock_source->funcs->cs_power_down(clock_source);
+		}
+	}
+
+
+}
+
+void reference_clock_source(
+		struct resource_context *res_ctx,
+		struct clock_source *clock_source)
+{
+	int i;
+	for (i = 0; i < res_ctx->pool.clk_src_count; i++) {
+		if (res_ctx->pool.clock_sources[i] == clock_source) {
+			res_ctx->clock_source_ref_count[i]++;
+		}
+	}
+}
+
+bool is_same_timing(
+	const struct dc_crtc_timing *timing1,
+	const struct dc_crtc_timing *timing2)
+{
+	return dm_memcmp(timing1, timing2, sizeof(struct dc_crtc_timing)) == 0;
+}
+
+static bool is_sharable_clk_src(
+	const struct core_stream *stream_with_clk_src,
+	const struct core_stream *stream)
+{
+	enum clock_source_id id = stream_with_clk_src->clock_source->id;
+
+	if (stream_with_clk_src->clock_source == NULL)
+		return false;
+
+	if (id == CLOCK_SOURCE_ID_EXTERNAL)
+		return false;
+
+	/* Sharing dual link is not working */
+	if (stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK ||
+			stream_with_clk_src->signal == SIGNAL_TYPE_DVI_DUAL_LINK)
+			return false;
+
+	if(!is_same_timing(
+		&stream_with_clk_src->public.timing, &stream->public.timing))
+		return false;
+
+	return true;
+}
+
+struct clock_source *find_used_clk_src_for_sharing(
+					struct validate_context *context,
+					struct core_stream *stream)
+{
+	uint8_t i, j;
+	for (i = 0; i < context->target_count; i++) {
+		struct core_target *target = context->targets[i];
+		for (j = 0; j < target->public.stream_count; j++) {
+			struct core_stream *clock_source_stream =
+				DC_STREAM_TO_CORE(target->public.streams[j]);
+
+			if (clock_source_stream->clock_source == NULL)
+				continue;
+
+			if (is_sharable_clk_src(clock_source_stream, stream))
+				return clock_source_stream->clock_source;
+		}
+	}
+
+	return NULL;
+}
+
+static enum pixel_format convert_pixel_format_to_dalsurface(
+		enum surface_pixel_format surface_pixel_format)
+{
+	enum pixel_format dal_pixel_format = PIXEL_FORMAT_UNKNOWN;
+
+	switch (surface_pixel_format) {
+	case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS:
+		dal_pixel_format = PIXEL_FORMAT_INDEX8;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555:
+		dal_pixel_format = PIXEL_FORMAT_RGB565;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_RGB565:
+		dal_pixel_format = PIXEL_FORMAT_RGB565;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888:
+		dal_pixel_format = PIXEL_FORMAT_ARGB8888;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888:
+		dal_pixel_format = PIXEL_FORMAT_ARGB8888;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010:
+		dal_pixel_format = PIXEL_FORMAT_ARGB2101010;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010:
+		dal_pixel_format = PIXEL_FORMAT_ARGB2101010;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS:
+		dal_pixel_format = PIXEL_FORMAT_ARGB2101010_XRBIAS;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
+		dal_pixel_format = PIXEL_FORMAT_FP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:
+		dal_pixel_format = PIXEL_FORMAT_FP16;
+		break;
+
+
+	case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
+		dal_pixel_format = PIXEL_FORMAT_420BPP12;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
+		dal_pixel_format = PIXEL_FORMAT_420BPP12;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_422_YCb:
+		dal_pixel_format = PIXEL_FORMAT_422BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_422_YCr:
+		dal_pixel_format = PIXEL_FORMAT_422BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_422_CbY:
+		dal_pixel_format = PIXEL_FORMAT_422BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_422_CrY:
+		dal_pixel_format = PIXEL_FORMAT_422BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb1555:
+		dal_pixel_format = PIXEL_FORMAT_444BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_CrYCb565:
+		dal_pixel_format = PIXEL_FORMAT_444BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb4444:
+		dal_pixel_format = PIXEL_FORMAT_444BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_CbYCrA5551:
+		dal_pixel_format = PIXEL_FORMAT_444BPP16;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb8888:
+		dal_pixel_format = PIXEL_FORMAT_444BPP32;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_ACrYCb2101010:
+		dal_pixel_format = PIXEL_FORMAT_444BPP32;
+		break;
+	case SURFACE_PIXEL_FORMAT_VIDEO_444_CbYCrA1010102:
+		dal_pixel_format = PIXEL_FORMAT_444BPP32;
+		break;
+	default:
+		dal_pixel_format = PIXEL_FORMAT_UNKNOWN;
+		break;
+	}
+	return dal_pixel_format;
+}
+
+static void calculate_viewport(
+		const struct dc_surface *surface,
+		struct core_stream *stream)
+{
+	const struct rect src = surface->src_rect;
+	const struct rect clip = surface->clip_rect;
+	const struct rect dst = surface->dst_rect;
+
+	/* offset = src.ofs + (clip.ofs - dst.ofs) * scl_ratio
+	 * num_pixels = clip.num_pix * scl_ratio
+	 */
+	stream->viewport.x = src.x + (clip.x - dst.x) * src.width / dst.width;
+	stream->viewport.width = clip.width * src.width / dst.width;
+
+	stream->viewport.y = src.y + (clip.y - dst.y) * src.height / dst.height;
+	stream->viewport.height = clip.height * src.height / dst.height;
+
+	/* Minimum viewport such that 420/422 chroma vp is non 0 */
+	if (stream->viewport.width < 2)
+	{
+		stream->viewport.width = 2;
+	}
+	if (stream->viewport.height < 2)
+	{
+		stream->viewport.height = 2;
+	}
+}
+
+static void calculate_overscan(
+		const struct dc_surface *surface,
+		struct core_stream *stream)
+{
+	stream->overscan.left = stream->public.dst.x;
+	if (stream->public.src.x < surface->clip_rect.x)
+		stream->overscan.left += (surface->clip_rect.x
+			- stream->public.src.x) * stream->public.dst.width
+			/ stream->public.src.width;
+
+	stream->overscan.right = stream->public.timing.h_addressable
+		- stream->public.dst.x - stream->public.dst.width;
+	if (stream->public.src.x + stream->public.src.width
+		> surface->clip_rect.x + surface->clip_rect.width)
+		stream->overscan.right = stream->public.timing.h_addressable -
+			dal_fixed31_32_floor(dal_fixed31_32_div(
+				dal_fixed31_32_from_int(
+						stream->viewport.width),
+						stream->ratios.horz)) -
+						stream->overscan.left;
+
+
+	stream->overscan.top = stream->public.dst.y;
+	if (stream->public.src.y < surface->clip_rect.y)
+		stream->overscan.top += (surface->clip_rect.y
+			- stream->public.src.y) * stream->public.dst.height
+			/ stream->public.src.height;
+
+	stream->overscan.bottom = stream->public.timing.v_addressable
+		- stream->public.dst.y - stream->public.dst.height;
+	if (stream->public.src.y + stream->public.src.height
+		> surface->clip_rect.y + surface->clip_rect.height)
+		stream->overscan.bottom = stream->public.timing.v_addressable -
+			dal_fixed31_32_floor(dal_fixed31_32_div(
+				dal_fixed31_32_from_int(
+						stream->viewport.height),
+						stream->ratios.vert)) -
+						stream->overscan.top;
+
+
+	/* TODO: Add timing overscan to finalize overscan calculation*/
+}
+
+static void calculate_scaling_ratios(
+		const struct dc_surface *surface,
+		struct core_stream *stream)
+{
+	const uint32_t in_w = stream->public.src.width;
+	const uint32_t in_h = stream->public.src.height;
+	const uint32_t out_w = stream->public.dst.width;
+	const uint32_t out_h = stream->public.dst.height;
+
+	stream->ratios.horz = dal_fixed31_32_from_fraction(
+					surface->src_rect.width,
+					surface->dst_rect.width);
+	stream->ratios.vert = dal_fixed31_32_from_fraction(
+					surface->src_rect.height,
+					surface->dst_rect.height);
+
+	if (surface->stereo_format == PLANE_STEREO_FORMAT_SIDE_BY_SIDE)
+		stream->ratios.horz.value *= 2;
+	else if (surface->stereo_format
+					== PLANE_STEREO_FORMAT_TOP_AND_BOTTOM)
+		stream->ratios.vert.value *= 2;
+
+	stream->ratios.vert.value = div64_s64(stream->ratios.vert.value * in_h,
+			out_h);
+	stream->ratios.horz.value = div64_s64(stream->ratios.horz.value * in_w ,
+			out_w);
+
+	stream->ratios.horz_c = stream->ratios.horz;
+	stream->ratios.vert_c = stream->ratios.vert;
+
+	if (stream->format == PIXEL_FORMAT_420BPP12) {
+		stream->ratios.horz_c.value /= 2;
+		stream->ratios.vert_c.value /= 2;
+	} else if (stream->format == PIXEL_FORMAT_422BPP16) {
+		stream->ratios.horz_c.value /= 2;
+	}
+}
+
+/*TODO: per pipe not per stream*/
+void build_scaling_params(
+	const struct dc_surface *surface,
+	struct core_stream *stream)
+{
+	/* Important: scaling ratio calculation requires pixel format,
+	 * overscan calculation requires scaling ratios and viewport
+	 * and lb depth/taps calculation requires overscan. Call sequence
+	 * is therefore important */
+	stream->format = convert_pixel_format_to_dalsurface(surface->format);
+
+	calculate_viewport(surface, stream);
+
+	calculate_scaling_ratios(surface, stream);
+
+	calculate_overscan(surface, stream);
+
+	/* Check if scaling is required update taps if not */
+	if (dal_fixed31_32_u2d19(stream->ratios.horz) == 1 << 19)
+		stream->taps.h_taps = 1;
+	else
+		stream->taps.h_taps = surface->scaling_quality.h_taps;
+
+	if (dal_fixed31_32_u2d19(stream->ratios.horz_c) == 1 << 19)
+		stream->taps.h_taps_c = 1;
+	else
+		stream->taps.h_taps_c = surface->scaling_quality.h_taps_c;
+
+	if (dal_fixed31_32_u2d19(stream->ratios.vert) == 1 << 19)
+		stream->taps.v_taps = 1;
+	else
+		stream->taps.v_taps = surface->scaling_quality.v_taps;
+
+	if (dal_fixed31_32_u2d19(stream->ratios.vert_c) == 1 << 19)
+		stream->taps.v_taps_c = 1;
+	else
+		stream->taps.v_taps_c = surface->scaling_quality.v_taps_c;
+
+	dal_logger_write(stream->ctx->logger,
+				LOG_MAJOR_DCP,
+				LOG_MINOR_DCP_SCALER,
+				"%s: Overscan:\n bot:%d left:%d right:%d "
+				"top:%d\nViewport:\nheight:%d width:%d x:%d "
+				"y:%d\n dst_rect:\nheight:%d width:%d x:%d "
+				"y:%d\n",
+				__func__,
+				stream->overscan.bottom,
+				stream->overscan.left,
+				stream->overscan.right,
+				stream->overscan.top,
+				stream->viewport.height,
+				stream->viewport.width,
+				stream->viewport.x,
+				stream->viewport.y,
+				surface->dst_rect.height,
+				surface->dst_rect.width,
+				surface->dst_rect.x,
+				surface->dst_rect.y);
+}
+
+void build_scaling_params_for_context(
+	const struct dc *dc,
+	struct validate_context *context)
+{
+	uint8_t i, j, k;
+	for (i = 0; i < context->target_count; i++) {
+		struct core_target *target = context->targets[i];
+		if (context->target_flags[i].unchanged)
+			continue;
+		for (j = 0; j < target->status.surface_count; j++) {
+			const struct dc_surface *surface =
+				target->status.surfaces[j];
+			for (k = 0; k < target->public.stream_count; k++) {
+				struct core_stream *stream =
+					DC_STREAM_TO_CORE(
+						target->public.streams[k]);
+
+				build_scaling_params(surface, stream);
+			}
+		}
+	}
+}
+
+bool logical_attach_surfaces_to_target(
+		struct dc_surface *surfaces[],
+		uint8_t surface_count,
+		struct dc_target *dc_target)
+{
+	uint8_t i;
+	struct core_target *target = DC_TARGET_TO_CORE(dc_target);
+
+	if (surface_count > MAX_SURFACE_NUM) {
+		dm_error("Surface: can not attach %d surfaces! Maximum is: %d\n",
+			surface_count, MAX_SURFACE_NUM);
+		return false;
+	}
+
+	for (i = 0; i < target->status.surface_count; i++)
+		dc_surface_release(target->status.surfaces[i]);
+
+	for (i = 0; i < surface_count; i++) {
+		struct core_surface *surface = DC_SURFACE_TO_CORE(surfaces[i]);
+		surface->status.dc_target = &target->public;
+		target->status.surfaces[i] = surfaces[i];
+		dc_surface_retain(target->status.surfaces[i]);
+	}
+	target->status.surface_count = surface_count;
+
+	return true;
+}
+
+static uint32_t get_min_vblank_time_us(const struct validate_context *context)
+{
+	uint8_t i, j;
+	uint32_t min_vertical_blank_time = -1;
+
+	for (i = 0; i < context->target_count; i++) {
+		const struct core_target *target = context->targets[i];
+
+		for (j = 0; j < target->public.stream_count; j++) {
+			const struct dc_stream *stream =
+						target->public.streams[j];
+			uint32_t vertical_blank_in_pixels = 0;
+			uint32_t vertical_blank_time = 0;
+
+			vertical_blank_in_pixels = stream->timing.h_total *
+				(stream->timing.v_total
+					- stream->timing.v_addressable);
+			vertical_blank_time = vertical_blank_in_pixels
+				* 1000 / stream->timing.pix_clk_khz;
+			if (min_vertical_blank_time > vertical_blank_time)
+				min_vertical_blank_time = vertical_blank_time;
+		}
+	}
+	return min_vertical_blank_time;
+}
+
+static void fill_display_configs(
+	const struct validate_context *context,
+	struct dc_pp_display_configuration *pp_display_cfg)
+{
+	uint8_t i, j;
+	uint8_t num_cfgs = 0;
+
+	for (i = 0; i < context->target_count; i++) {
+		const struct core_target *target = context->targets[i];
+
+		for (j = 0; j < target->public.stream_count; j++) {
+			const struct core_stream *stream =
+			DC_STREAM_TO_CORE(target->public.streams[j]);
+			struct dc_pp_single_disp_config *cfg =
+					&pp_display_cfg->disp_configs[num_cfgs];
+
+			num_cfgs++;
+			cfg->signal = stream->signal;
+			cfg->pipe_idx = stream->opp->inst;
+			cfg->src_height = stream->public.src.height;
+			cfg->src_width = stream->public.src.width;
+			cfg->ddi_channel_mapping =
+				stream->sink->link->ddi_channel_mapping.raw;
+			cfg->transmitter =
+				stream->sink->link->link_enc->transmitter;
+			cfg->link_settings =
+					stream->sink->link->cur_link_settings;
+			cfg->sym_clock = stream->public.timing.pix_clk_khz;
+			switch (stream->public.timing.display_color_depth) {
+			case COLOR_DEPTH_101010:
+				cfg->sym_clock = (cfg->sym_clock * 30) / 24;
+				break;
+			case COLOR_DEPTH_121212:
+				cfg->sym_clock = (cfg->sym_clock * 36) / 24;
+				break;
+			case COLOR_DEPTH_161616:
+				cfg->sym_clock = (cfg->sym_clock * 48) / 24;
+				break;
+			default:
+				break;
+			}
+			/* TODO: unhardcode*/
+			cfg->v_refresh = 60;
+		}
+	}
+	pp_display_cfg->display_count = num_cfgs;
+}
+
+void pplib_apply_safe_state(
+	const struct dc *dc)
+{
+	dm_pp_apply_safe_state(dc->ctx);
+}
+
+void pplib_apply_display_requirements(
+	const struct dc *dc,
+	const struct validate_context *context)
+{
+	struct dc_pp_display_configuration pp_display_cfg = { 0 };
+
+	pp_display_cfg.all_displays_in_sync =
+		context->bw_results.all_displays_in_sync;
+	pp_display_cfg.nb_pstate_switch_disable =
+			context->bw_results.nbp_state_change_enable == false;
+	pp_display_cfg.cpu_cc6_disable =
+			context->bw_results.cpuc_state_change_enable == false;
+	pp_display_cfg.cpu_pstate_disable =
+			context->bw_results.cpup_state_change_enable == false;
+	pp_display_cfg.cpu_pstate_separation_time =
+			context->bw_results.required_blackout_duration_us;
+
+	pp_display_cfg.min_memory_clock_khz = context->bw_results.required_yclk
+		/ MEMORY_TYPE_MULTIPLIER;
+	pp_display_cfg.min_engine_clock_khz = context->bw_results.required_sclk;
+	pp_display_cfg.min_engine_clock_deep_sleep_khz
+			= context->bw_results.required_sclk_deep_sleep;
+
+	pp_display_cfg.avail_mclk_switch_time_us =
+						get_min_vblank_time_us(context);
+	pp_display_cfg.avail_mclk_switch_time_in_disp_active_us = 0;
+
+	pp_display_cfg.disp_clk_khz = context->bw_results.dispclk_khz;
+
+	fill_display_configs(context, &pp_display_cfg);
+
+	/* TODO: is this still applicable?*/
+	if (pp_display_cfg.display_count == 1) {
+		const struct dc_crtc_timing *timing =
+			&context->targets[0]->public.streams[0]->timing;
+
+		pp_display_cfg.crtc_index =
+			pp_display_cfg.disp_configs[0].pipe_idx;
+		pp_display_cfg.line_time_in_us = timing->h_total * 1000
+							/ timing->pix_clk_khz;
+	}
+
+	dm_pp_apply_display_requirements(dc->ctx, &pp_display_cfg);
+}
+
+/* Maximum TMDS single link pixel clock 165MHz */
+#define TMDS_MAX_PIXEL_CLOCK_IN_KHZ 165000
+
+static void attach_stream_to_controller(
+		struct resource_context *res_ctx,
+		struct core_stream *stream)
+{
+	res_ctx->controller_ctx[stream->controller_idx].stream = stream;
+}
+
+static void set_stream_engine_in_use(
+		struct resource_context *res_ctx,
+		struct stream_encoder *stream_enc)
+{
+	int i;
+
+	for (i = 0; i < res_ctx->pool.stream_enc_count; i++) {
+		if (res_ctx->pool.stream_enc[i] == stream_enc)
+			res_ctx->is_stream_enc_acquired[i] = true;
+	}
+}
+
+/* TODO: release audio object */
+static void set_audio_in_use(
+		struct resource_context *res_ctx,
+		struct audio *audio)
+{
+	int i;
+	for (i = 0; i < res_ctx->pool.audio_count; i++) {
+		if (res_ctx->pool.audios[i] == audio) {
+			res_ctx->is_audio_acquired[i] = true;
+		}
+	}
+}
+
+static bool assign_first_free_controller(
+		struct resource_context *res_ctx,
+		struct core_stream *stream)
+{
+	uint8_t i;
+	for (i = 0; i < res_ctx->pool.controller_count; i++) {
+		if (!res_ctx->controller_ctx[i].stream) {
+			stream->tg = res_ctx->pool.timing_generators[i];
+			stream->mi = res_ctx->pool.mis[i];
+			stream->ipp = res_ctx->pool.ipps[i];
+			stream->xfm = res_ctx->pool.transforms[i];
+			stream->opp = res_ctx->pool.opps[i];
+			stream->controller_idx = i;
+			stream->dis_clk = res_ctx->pool.display_clock;
+			return true;
+		}
+	}
+	return false;
+}
+
+static struct stream_encoder *find_first_free_match_stream_enc_for_link(
+		struct resource_context *res_ctx,
+		struct core_link *link)
+{
+	uint8_t i;
+	int8_t j = -1;
+	const struct dc_sink *sink = NULL;
+
+	for (i = 0; i < res_ctx->pool.stream_enc_count; i++) {
+		if (!res_ctx->is_stream_enc_acquired[i] &&
+					res_ctx->pool.stream_enc[i]) {
+			/* Store first available for MST second display
+			 * in daisy chain use case */
+			j = i;
+			if (res_ctx->pool.stream_enc[i]->id ==
+					link->link_enc->preferred_engine)
+				return res_ctx->pool.stream_enc[i];
+		}
+	}
+
+	/*
+	 * below can happen in cases when stream encoder is acquired:
+	 * 1) for second MST display in chain, so preferred engine already
+	 * acquired;
+	 * 2) for another link, which preferred engine already acquired by any
+	 * MST configuration.
+	 *
+	 * If signal is of DP type and preferred engine not found, return last available
+	 *
+	 * TODO - This is just a patch up and a generic solution is
+	 * required for non DP connectors.
+	 */
+
+	sink = link->public.local_sink ? link->public.local_sink : link->public.remote_sinks[0];
+
+	if (sink && j >= 0 &&  dc_is_dp_signal(sink->sink_signal))
+		return res_ctx->pool.stream_enc[j];
+
+	return NULL;
+}
+
+static struct audio *find_first_free_audio(struct resource_context *res_ctx)
+{
+	int i;
+	for (i = 0; i < res_ctx->pool.audio_count; i++) {
+		if (res_ctx->is_audio_acquired[i] == false) {
+			return res_ctx->pool.audios[i];
+		}
+	}
+
+	return 0;
+}
+
+static bool check_timing_change(struct core_stream *cur_stream,
+		struct core_stream *new_stream)
+{
+	if (cur_stream == NULL)
+		return true;
+
+	/* If sink pointer changed, it means this is a hotplug, we should do
+	 * full hw setting.
+	 */
+	if (cur_stream->sink != new_stream->sink)
+		return true;
+
+	return !is_same_timing(
+					&cur_stream->public.timing,
+					&new_stream->public.timing);
+}
+
+static void set_stream_signal(struct core_stream *stream)
+{
+	struct dc_sink *dc_sink = (struct dc_sink *)stream->public.sink;
+
+	/* For asic supports dual link DVI, we should adjust signal type
+	 * based on timing pixel clock. If pixel clock more than 165Mhz,
+	 * signal is dual link, otherwise, single link.
+	 */
+	if (dc_sink->sink_signal == SIGNAL_TYPE_DVI_SINGLE_LINK ||
+			dc_sink->sink_signal == SIGNAL_TYPE_DVI_DUAL_LINK) {
+		if (stream->public.timing.pix_clk_khz >
+			TMDS_MAX_PIXEL_CLOCK_IN_KHZ)
+			dc_sink->sink_signal = SIGNAL_TYPE_DVI_DUAL_LINK;
+		else
+			dc_sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+	}
+
+	stream->signal = dc_sink->sink_signal;
+}
+
+enum dc_status map_resources(
+		const struct dc *dc,
+		struct validate_context *context)
+{
+	uint8_t i, j;
+
+	/* mark resources used for targets that are already active */
+	for (i = 0; i < context->target_count; i++) {
+		struct core_target *target = context->targets[i];
+
+		if (!context->target_flags[i].unchanged)
+			continue;
+
+		for (j = 0; j < target->public.stream_count; j++) {
+			struct core_stream *stream =
+				DC_STREAM_TO_CORE(target->public.streams[j]);
+
+			attach_stream_to_controller(
+				&context->res_ctx,
+				stream);
+
+			set_stream_engine_in_use(
+				&context->res_ctx,
+				stream->stream_enc);
+
+			reference_clock_source(
+				&context->res_ctx,
+				stream->clock_source);
+
+			if (stream->audio) {
+				set_audio_in_use(&context->res_ctx,
+					stream->audio);
+			}
+		}
+	}
+
+	/* acquire new resources */
+	for (i = 0; i < context->target_count; i++) {
+		struct core_target *target = context->targets[i];
+
+		if (context->target_flags[i].unchanged)
+			continue;
+
+		for (j = 0; j < target->public.stream_count; j++) {
+			struct core_stream *stream =
+				DC_STREAM_TO_CORE(target->public.streams[j]);
+			struct core_stream *curr_stream;
+
+			if (!assign_first_free_controller(
+					&context->res_ctx, stream))
+				return DC_NO_CONTROLLER_RESOURCE;
+
+			attach_stream_to_controller(&context->res_ctx, stream);
+
+			set_stream_signal(stream);
+
+			curr_stream =
+				dc->current_context.res_ctx.controller_ctx
+				[stream->controller_idx].stream;
+			context->res_ctx.controller_ctx[stream->controller_idx]
+			.flags.timing_changed =
+				check_timing_change(curr_stream, stream);
+
+			stream->stream_enc =
+				find_first_free_match_stream_enc_for_link(
+					&context->res_ctx,
+					stream->sink->link);
+
+			if (!stream->stream_enc)
+				return DC_NO_STREAM_ENG_RESOURCE;
+
+			set_stream_engine_in_use(
+					&context->res_ctx,
+					stream->stream_enc);
+
+			/* TODO: Add check if ASIC support and EDID audio */
+			if (!stream->sink->converter_disable_audio &&
+						dc_is_audio_capable_signal(
+						stream->signal)) {
+				stream->audio = find_first_free_audio(
+						&context->res_ctx);
+
+				if (!stream->audio)
+					return DC_NO_STREAM_AUDIO_RESOURCE;
+
+				set_audio_in_use(&context->res_ctx,
+						stream->audio);
+			}
+		}
+	}
+
+	return DC_OK;
+}
+
+static enum ds_color_space build_default_color_space(
+		struct core_stream *stream)
+{
+	enum ds_color_space color_space =
+			DS_COLOR_SPACE_SRGB_FULLRANGE;
+	struct dc_crtc_timing *timing = &stream->public.timing;
+
+	switch (stream->signal) {
+	/* TODO: implement other signal color space setting */
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+	case SIGNAL_TYPE_EDP:
+		break;
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+	{
+		uint32_t pix_clk_khz;
+
+		if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 &&
+			timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
+			if (timing->timing_standard ==
+					TIMING_STANDARD_CEA770 &&
+				timing->timing_standard ==
+						TIMING_STANDARD_CEA861)
+				color_space = DS_COLOR_SPACE_SRGB_FULLRANGE;
+
+			pix_clk_khz = timing->pix_clk_khz / 10;
+			if (timing->h_addressable == 640 &&
+				timing->v_addressable == 480 &&
+				(pix_clk_khz == 2520 || pix_clk_khz == 2517))
+				color_space = DS_COLOR_SPACE_SRGB_FULLRANGE;
+		} else {
+			if (timing->timing_standard ==
+					TIMING_STANDARD_CEA770 ||
+					timing->timing_standard ==
+					TIMING_STANDARD_CEA861) {
+
+				color_space =
+					(timing->pix_clk_khz > PIXEL_CLOCK) ?
+						DS_COLOR_SPACE_YCBCR709 :
+						DS_COLOR_SPACE_YCBCR601;
+			}
+		}
+		break;
+	}
+	default:
+		switch (timing->pixel_encoding) {
+		case PIXEL_ENCODING_YCBCR422:
+		case PIXEL_ENCODING_YCBCR444:
+			if (timing->pix_clk_khz > PIXEL_CLOCK)
+				color_space = DS_COLOR_SPACE_YCBCR709;
+			else
+				color_space = DS_COLOR_SPACE_YCBCR601;
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	return color_space;
+}
+
+static void translate_info_frame(const struct hw_info_frame *hw_info_frame,
+	struct encoder_info_frame *encoder_info_frame)
+{
+	dm_memset(
+		encoder_info_frame, 0, sizeof(struct encoder_info_frame));
+
+	/* For gamut we recalc checksum */
+	if (hw_info_frame->gamut_packet.valid) {
+		uint8_t chk_sum = 0;
+		uint8_t *ptr;
+		uint8_t i;
+
+		dm_memmove(
+						&encoder_info_frame->gamut,
+						&hw_info_frame->gamut_packet,
+						sizeof(struct hw_info_packet));
+
+		/*start of the Gamut data. */
+		ptr = &encoder_info_frame->gamut.sb[3];
+
+		for (i = 0; i <= encoder_info_frame->gamut.sb[1]; i++)
+			chk_sum += ptr[i];
+
+		encoder_info_frame->gamut.sb[2] = (uint8_t) (0x100 - chk_sum);
+	}
+
+	if (hw_info_frame->avi_info_packet.valid) {
+		dm_memmove(
+						&encoder_info_frame->avi,
+						&hw_info_frame->avi_info_packet,
+						sizeof(struct hw_info_packet));
+	}
+
+	if (hw_info_frame->vendor_info_packet.valid) {
+		dm_memmove(
+						&encoder_info_frame->vendor,
+						&hw_info_frame->vendor_info_packet,
+						sizeof(struct hw_info_packet));
+	}
+
+	if (hw_info_frame->spd_packet.valid) {
+		dm_memmove(
+						&encoder_info_frame->spd,
+						&hw_info_frame->spd_packet,
+						sizeof(struct hw_info_packet));
+	}
+
+	if (hw_info_frame->vsc_packet.valid) {
+		dm_memmove(
+						&encoder_info_frame->vsc,
+						&hw_info_frame->vsc_packet,
+						sizeof(struct hw_info_packet));
+	}
+}
+
+static void set_avi_info_frame(struct hw_info_packet *info_packet,
+		struct core_stream *stream)
+{
+	enum ds_color_space color_space = DS_COLOR_SPACE_UNKNOWN;
+	struct info_frame info_frame = { {0} };
+	uint32_t pixel_encoding = 0;
+	enum scanning_type scan_type = SCANNING_TYPE_NODATA;
+	enum dc_aspect_ratio aspect = ASPECT_RATIO_NO_DATA;
+	bool itc = false;
+	uint8_t cn0_cn1 = 0;
+	uint8_t *check_sum = NULL;
+	uint8_t byte_index = 0;
+
+	if (info_packet == NULL)
+		return;
+
+	color_space = build_default_color_space(stream);
+
+	/* Initialize header */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.header.
+			info_frame_type = INFO_FRAME_AVI;
+	/* InfoFrameVersion_3 is defined by CEA861F (Section 6.4), but shall
+	* not be used in HDMI 2.0 (Section 10.1) */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.header.version =
+			INFO_FRAME_VERSION_2;
+	info_frame.avi_info_packet.info_packet_hdmi.bits.header.length =
+			INFO_FRAME_SIZE_AVI;
+
+	/* IDO-defined (Y2,Y1,Y0 = 1,1,1) shall not be used by devices built
+	* according to HDMI 2.0 spec (Section 10.1)
+	* Add "case PixelEncoding_YCbCr420:    pixelEncoding = 3; break;"
+	* when YCbCr 4:2:0 is supported by DAL hardware. */
+
+	switch (stream->public.timing.pixel_encoding) {
+	case PIXEL_ENCODING_YCBCR422:
+		pixel_encoding = 1;
+		break;
+
+	case PIXEL_ENCODING_YCBCR444:
+		pixel_encoding = 2;
+		break;
+
+	case PIXEL_ENCODING_RGB:
+	default:
+		pixel_encoding = 0;
+	}
+
+	/* Y0_Y1_Y2 : The pixel encoding */
+	/* H14b AVI InfoFrame has extension on Y-field from 2 bits to 3 bits */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.Y0_Y1_Y2 =
+		pixel_encoding;
+
+
+	/* A0 = 1 Active Format Information valid */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.A0 =
+		ACTIVE_FORMAT_VALID;
+
+	/* B0, B1 = 3; Bar info data is valid */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.B0_B1 =
+		BAR_INFO_BOTH_VALID;
+
+	info_frame.avi_info_packet.info_packet_hdmi.bits.SC0_SC1 =
+			PICTURE_SCALING_UNIFORM;
+
+	/* S0, S1 : Underscan / Overscan */
+	/* TODO: un-hardcode scan type */
+	scan_type = SCANNING_TYPE_UNDERSCAN;
+	info_frame.avi_info_packet.info_packet_hdmi.bits.S0_S1 = scan_type;
+
+	/* C0, C1 : Colorimetry */
+	if (color_space == DS_COLOR_SPACE_YCBCR709)
+		info_frame.avi_info_packet.info_packet_hdmi.bits.C0_C1 =
+				COLORIMETRY_ITU709;
+	else if (color_space == DS_COLOR_SPACE_YCBCR601)
+		info_frame.avi_info_packet.info_packet_hdmi.bits.C0_C1 =
+				COLORIMETRY_ITU601;
+	else
+		info_frame.avi_info_packet.info_packet_hdmi.bits.C0_C1 =
+				COLORIMETRY_NO_DATA;
+
+
+	/* TODO: un-hardcode aspect ratio */
+	aspect = stream->public.timing.aspect_ratio;
+
+	switch (aspect) {
+	case ASPECT_RATIO_4_3:
+	case ASPECT_RATIO_16_9:
+		info_frame.avi_info_packet.info_packet_hdmi.bits.M0_M1 = aspect;
+		break;
+
+	case ASPECT_RATIO_NO_DATA:
+	case ASPECT_RATIO_64_27:
+	case ASPECT_RATIO_256_135:
+	default:
+		info_frame.avi_info_packet.info_packet_hdmi.bits.M0_M1 = 0;
+	}
+
+	/* Active Format Aspect ratio - same as Picture Aspect Ratio. */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.R0_R3 =
+			ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE;
+
+	/* TODO: un-hardcode cn0_cn1 and itc */
+	cn0_cn1 = 0;
+	itc = false;
+
+	if (itc) {
+		info_frame.avi_info_packet.info_packet_hdmi.bits.ITC = 1;
+		info_frame.avi_info_packet.info_packet_hdmi.bits.CN0_CN1 =
+			cn0_cn1;
+	}
+
+	/* TODO: un-hardcode q0_q1 */
+	if (color_space == DS_COLOR_SPACE_SRGB_FULLRANGE)
+		info_frame.avi_info_packet.info_packet_hdmi.bits.Q0_Q1 =
+						RGB_QUANTIZATION_FULL_RANGE;
+	else if (color_space == DS_COLOR_SPACE_SRGB_LIMITEDRANGE)
+		info_frame.avi_info_packet.info_packet_hdmi.bits.Q0_Q1 =
+						RGB_QUANTIZATION_LIMITED_RANGE;
+	else
+		info_frame.avi_info_packet.info_packet_hdmi.bits.Q0_Q1 =
+						RGB_QUANTIZATION_DEFAULT_RANGE;
+
+	/* TODO : We should handle YCC quantization,
+	 * but we do not have matrix calculation */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.YQ0_YQ1 =
+					YYC_QUANTIZATION_LIMITED_RANGE;
+
+	info_frame.avi_info_packet.info_packet_hdmi.bits.VIC0_VIC7 =
+					stream->public.timing.vic;
+
+	/* pixel repetition
+	 * PR0 - PR3 start from 0 whereas pHwPathMode->mode.timing.flags.pixel
+	 * repetition start from 1 */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.PR0_PR3 = 0;
+
+	/* Bar Info
+	 * barTop:    Line Number of End of Top Bar.
+	 * barBottom: Line Number of Start of Bottom Bar.
+	 * barLeft:   Pixel Number of End of Left Bar.
+	 * barRight:  Pixel Number of Start of Right Bar. */
+	info_frame.avi_info_packet.info_packet_hdmi.bits.bar_top =
+			stream->public.timing.v_border_top;
+	info_frame.avi_info_packet.info_packet_hdmi.bits.bar_bottom =
+		(stream->public.timing.v_border_top
+			- stream->public.timing.v_border_bottom + 1);
+	info_frame.avi_info_packet.info_packet_hdmi.bits.bar_left =
+			stream->public.timing.h_border_left;
+	info_frame.avi_info_packet.info_packet_hdmi.bits.bar_right =
+		(stream->public.timing.h_total
+			- stream->public.timing.h_border_right + 1);
+
+	/* check_sum - Calculate AFMT_AVI_INFO0 ~ AFMT_AVI_INFO3 */
+	check_sum =
+		&info_frame.
+		avi_info_packet.info_packet_hdmi.packet_raw_data.sb[0];
+	*check_sum = INFO_FRAME_AVI + INFO_FRAME_SIZE_AVI
+			+ INFO_FRAME_VERSION_2;
+
+	for (byte_index = 1; byte_index <= INFO_FRAME_SIZE_AVI; byte_index++)
+		*check_sum += info_frame.avi_info_packet.info_packet_hdmi.
+				packet_raw_data.sb[byte_index];
+
+	/* one byte complement */
+	*check_sum = (uint8_t) (0x100 - *check_sum);
+
+	/* Store in hw_path_mode */
+	info_packet->hb0 =
+		info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.hb0;
+	info_packet->hb1 =
+		info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.hb1;
+	info_packet->hb2 =
+		info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.hb2;
+
+	for (byte_index = 0; byte_index < sizeof(info_packet->sb); byte_index++)
+		info_packet->sb[byte_index] = info_frame.avi_info_packet.
+		info_packet_hdmi.packet_raw_data.sb[byte_index];
+
+	info_packet->valid = true;
+}
+
+static void set_vendor_info_packet(struct core_stream *stream,
+		struct hw_info_packet *info_packet)
+{
+	uint32_t length = 0;
+	bool hdmi_vic_mode = false;
+	uint8_t checksum = 0;
+	uint32_t i = 0;
+	enum dc_timing_3d_format format;
+
+	ASSERT_CRITICAL(stream != NULL);
+	ASSERT_CRITICAL(info_packet != NULL);
+
+	format = stream->public.timing.timing_3d_format;
+
+	/* Can be different depending on packet content */
+	length = 5;
+
+	if (stream->public.timing.hdmi_vic != 0
+			&& stream->public.timing.h_total >= 3840
+			&& stream->public.timing.v_total >= 2160)
+		hdmi_vic_mode = true;
+
+	/* According to HDMI 1.4a CTS, VSIF should be sent
+	 * for both 3D stereo and HDMI VIC modes.
+	 * For all other modes, there is no VSIF sent.  */
+
+	if (format == TIMING_3D_FORMAT_NONE && !hdmi_vic_mode)
+		return;
+
+	/* 24bit IEEE Registration identifier (0x000c03). LSB first. */
+	info_packet->sb[1] = 0x03;
+	info_packet->sb[2] = 0x0C;
+	info_packet->sb[3] = 0x00;
+
+	/*PB4: 5 lower bytes = 0 (reserved). 3 higher bits = HDMI_Video_Format.
+	 * The value for HDMI_Video_Format are:
+	 * 0x0 (0b000) - No additional HDMI video format is presented in this
+	 * packet
+	 * 0x1 (0b001) - Extended resolution format present. 1 byte of HDMI_VIC
+	 * parameter follows
+	 * 0x2 (0b010) - 3D format indication present. 3D_Structure and
+	 * potentially 3D_Ext_Data follows
+	 * 0x3..0x7 (0b011..0b111) - reserved for future use */
+	if (format != TIMING_3D_FORMAT_NONE)
+		info_packet->sb[4] = (2 << 5);
+	else if (hdmi_vic_mode)
+		info_packet->sb[4] = (1 << 5);
+
+	/* PB5: If PB4 claims 3D timing (HDMI_Video_Format = 0x2):
+	 * 4 lower bites = 0 (reserved). 4 higher bits = 3D_Structure.
+	 * The value for 3D_Structure are:
+	 * 0x0 - Frame Packing
+	 * 0x1 - Field Alternative
+	 * 0x2 - Line Alternative
+	 * 0x3 - Side-by-Side (full)
+	 * 0x4 - L + depth
+	 * 0x5 - L + depth + graphics + graphics-depth
+	 * 0x6 - Top-and-Bottom
+	 * 0x7 - Reserved for future use
+	 * 0x8 - Side-by-Side (Half)
+	 * 0x9..0xE - Reserved for future use
+	 * 0xF - Not used */
+	switch (format) {
+	case TIMING_3D_FORMAT_HW_FRAME_PACKING:
+	case TIMING_3D_FORMAT_SW_FRAME_PACKING:
+		info_packet->sb[5] = (0x0 << 4);
+		break;
+
+	case TIMING_3D_FORMAT_SIDE_BY_SIDE:
+	case TIMING_3D_FORMAT_SBS_SW_PACKED:
+		info_packet->sb[5] = (0x8 << 4);
+		length = 6;
+		break;
+
+	case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
+	case TIMING_3D_FORMAT_TB_SW_PACKED:
+		info_packet->sb[5] = (0x6 << 4);
+		break;
+
+	default:
+		break;
+	}
+
+	/*PB5: If PB4 is set to 0x1 (extended resolution format)
+	 * fill PB5 with the correct HDMI VIC code */
+	if (hdmi_vic_mode)
+		info_packet->sb[5] = stream->public.timing.hdmi_vic;
+
+	/* Header */
+	info_packet->hb0 = 0x81; /* VSIF packet type. */
+	info_packet->hb1 = 0x01; /* Version */
+
+	/* 4 lower bits = Length, 4 higher bits = 0 (reserved) */
+	info_packet->hb2 = (uint8_t) (length);
+
+	/* Calculate checksum */
+	checksum = 0;
+	checksum += info_packet->hb0;
+	checksum += info_packet->hb1;
+	checksum += info_packet->hb2;
+
+	for (i = 1; i <= length; i++)
+		checksum += info_packet->sb[i];
+
+	info_packet->sb[0] = (uint8_t) (0x100 - checksum);
+
+	info_packet->valid = true;
+}
+
+void build_info_frame(struct core_stream *stream)
+{
+	enum signal_type signal = SIGNAL_TYPE_NONE;
+	struct hw_info_frame info_frame = { { 0 } };
+
+	/* default all packets to invalid */
+	info_frame.avi_info_packet.valid = false;
+	info_frame.gamut_packet.valid = false;
+	info_frame.vendor_info_packet.valid = false;
+	info_frame.spd_packet.valid = false;
+	info_frame.vsc_packet.valid = false;
+
+	signal = stream->sink->public.sink_signal;
+
+	/* HDMi and DP have different info packets*/
+	if (signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+		set_avi_info_frame(&info_frame.avi_info_packet,
+				stream);
+		set_vendor_info_packet(stream, &info_frame.vendor_info_packet);
+	}
+
+	translate_info_frame(&info_frame,
+			&stream->encoder_info_frame);
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_sink.c b/drivers/gpu/drm/amd/dal/dc/core/dc_sink.c
new file mode 100644
index 000000000000..c5a770e61812
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_sink.c
@@ -0,0 +1,116 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services.h"
+#include "dm_helpers.h"
+#include "core_types.h"
+
+/*******************************************************************************
+ * Private definitions
+ ******************************************************************************/
+
+struct sink {
+	struct core_sink protected;
+	int ref_count;
+};
+
+#define DC_SINK_TO_SINK(dc_sink) \
+			container_of(dc_sink, struct sink, protected.public)
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+
+static void destruct(struct sink *sink)
+{
+
+}
+
+static bool construct(struct sink *sink, const struct dc_sink_init_data *init_params)
+{
+
+	struct core_link *core_link = DC_LINK_TO_LINK(init_params->link);
+
+	sink->protected.public.sink_signal = init_params->sink_signal;
+	sink->protected.link = core_link;
+	sink->protected.ctx = core_link->ctx;
+	sink->protected.dongle_max_pix_clk = init_params->dongle_max_pix_clk;
+	sink->protected.converter_disable_audio =
+			init_params->converter_disable_audio;
+
+	return true;
+}
+
+/*******************************************************************************
+ * Public functions
+ ******************************************************************************/
+
+void dc_sink_retain(const struct dc_sink *dc_sink)
+{
+	struct sink *sink = DC_SINK_TO_SINK(dc_sink);
+
+	++sink->ref_count;
+}
+
+void dc_sink_release(const struct dc_sink *dc_sink)
+{
+	struct core_sink *core_sink = DC_SINK_TO_CORE(dc_sink);
+	struct sink *sink = DC_SINK_TO_SINK(dc_sink);
+
+	--sink->ref_count;
+
+	if (sink->ref_count == 0) {
+		destruct(sink);
+		dm_free(core_sink->ctx, sink);
+	}
+}
+
+struct dc_sink *dc_sink_create(const struct dc_sink_init_data *init_params)
+{
+	struct core_link *core_link = DC_LINK_TO_LINK(init_params->link);
+
+	struct sink *sink = dm_alloc(core_link->ctx, sizeof(*sink));
+
+	if (NULL == sink)
+		goto alloc_fail;
+
+	if (false == construct(sink, init_params))
+		goto construct_fail;
+
+	/* TODO should we move this outside to where the assignment actually happens? */
+	dc_sink_retain(&sink->protected.public);
+
+	return &sink->protected.public;
+
+construct_fail:
+	dm_free(core_link->ctx, sink);
+
+alloc_fail:
+	return NULL;
+}
+
+/*******************************************************************************
+ * Protected functions - visible only inside of DC (not visible in DM)
+ ******************************************************************************/
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_stream.c b/drivers/gpu/drm/amd/dal/dc/core/dc_stream.c
new file mode 100644
index 000000000000..d7012bcda10a
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_stream.c
@@ -0,0 +1,188 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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_types.h"
+#include "resource.h"
+
+/*******************************************************************************
+ * Private definitions
+ ******************************************************************************/
+
+struct stream {
+	struct core_stream protected;
+	int ref_count;
+};
+
+#define DC_STREAM_TO_STREAM(dc_stream) container_of(dc_stream, struct stream, protected.public)
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static void build_bit_depth_reduction_params(
+		const struct core_stream *stream,
+		struct bit_depth_reduction_params *fmt_bit_depth)
+{
+	dm_memset(fmt_bit_depth, 0, sizeof(*fmt_bit_depth));
+
+	/*TODO: Need to un-hardcode, refer to function with same name
+	 * in dal2 hw_sequencer*/
+
+	fmt_bit_depth->flags.TRUNCATE_ENABLED = 0;
+	fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 0;
+	fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 0;
+
+	/* Diagnostics need consistent CRC of the image, that means
+	 * dithering should not be enabled for Diagnostics. */
+	if (IS_DIAG_DC(stream->ctx->dce_environment) == false) {
+
+		fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 1;
+		fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1;
+
+		/* frame random is on by default */
+		fmt_bit_depth->flags.FRAME_RANDOM = 1;
+		/* apply RGB dithering */
+		fmt_bit_depth->flags.RGB_RANDOM = true;
+	}
+
+	return;
+}
+
+static void setup_pixel_encoding(
+	struct clamping_and_pixel_encoding_params *clamping)
+{
+	/*TODO: Need to un-hardcode, refer to function with same name
+		 * in dal2 hw_sequencer*/
+
+	clamping->pixel_encoding = PIXEL_ENCODING_RGB;
+
+	return;
+}
+
+static bool construct(struct core_stream *stream,
+	const struct dc_sink *dc_sink_data)
+{
+	uint32_t i = 0;
+
+	stream->sink = DC_SINK_TO_CORE(dc_sink_data);
+	stream->ctx = stream->sink->ctx;
+	stream->public.sink = dc_sink_data;
+
+	dc_sink_retain(dc_sink_data);
+
+	build_bit_depth_reduction_params(stream, &stream->bit_depth_params);
+	setup_pixel_encoding(&stream->clamping);
+
+	/* Copy audio modes */
+	/* TODO - Remove this translation */
+	for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++)
+	{
+		stream->public.audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count;
+		stream->public.audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code;
+		stream->public.audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate;
+		stream->public.audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size;
+	}
+	stream->public.audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count;
+	stream->public.audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency;
+	stream->public.audio_info.video_latency = dc_sink_data->edid_caps.video_latency;
+	dm_memmove(
+		stream->public.audio_info.display_name,
+		dc_sink_data->edid_caps.display_name,
+		AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
+	stream->public.audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id;
+	stream->public.audio_info.product_id = dc_sink_data->edid_caps.product_id;
+	stream->public.audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags;
+
+	/* TODO - Unhardcode port_id */
+	stream->public.audio_info.port_id[0] = 0x5558859e;
+	stream->public.audio_info.port_id[1] = 0xd989449;
+
+	/* EDID CAP translation for HDMI 2.0 */
+	stream->public.timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble;
+	return true;
+}
+
+static void destruct(struct core_stream *stream)
+{
+	dc_sink_release(&stream->sink->public);
+}
+
+void dc_stream_retain(struct dc_stream *dc_stream)
+{
+	struct stream *stream = DC_STREAM_TO_STREAM(dc_stream);
+	stream->ref_count++;
+}
+
+void dc_stream_release(struct dc_stream *public)
+{
+	struct stream *stream = DC_STREAM_TO_STREAM(public);
+	struct core_stream *protected = DC_STREAM_TO_CORE(public);
+	struct dc_context *ctx = protected->ctx;
+	stream->ref_count--;
+
+	if (stream->ref_count == 0) {
+		destruct(protected);
+		dm_free(ctx, stream);
+	}
+}
+
+struct dc_stream *dc_create_stream_for_sink(const struct dc_sink *dc_sink)
+{
+	struct core_sink *sink = DC_SINK_TO_CORE(dc_sink);
+	struct stream *stream;
+
+	if (sink == NULL)
+		goto alloc_fail;
+
+	stream = dm_alloc(sink->ctx, sizeof(struct stream));
+
+	if (NULL == stream)
+		goto alloc_fail;
+
+	if (false == construct(&stream->protected, dc_sink))
+			goto construct_fail;
+
+	dc_stream_retain(&stream->protected.public);
+
+	return &stream->protected.public;
+
+construct_fail:
+	dm_free(sink->ctx, stream);
+
+alloc_fail:
+	return NULL;
+}
+
+void dc_update_stream(const struct dc_stream *dc_stream,
+		struct rect *src,
+		struct rect *dst)
+{
+	struct core_stream *stream = DC_STREAM_TO_CORE(dc_stream);
+
+	stream->public.src = *src;
+	stream->public.dst = *dst;
+}
+
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_surface.c b/drivers/gpu/drm/amd/dal/dc/core/dc_surface.c
new file mode 100644
index 000000000000..1a9ee8f97757
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_surface.c
@@ -0,0 +1,123 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+/* DC interface (public) */
+#include "dm_services.h"
+#include "dc.h"
+
+/* DC core (private) */
+#include "core_dc.h"
+#include "inc/transform.h"
+
+/*******************************************************************************
+ * Private structures
+ ******************************************************************************/
+struct surface {
+	struct core_surface protected;
+	enum dc_irq_source irq_source;
+	int ref_count;
+};
+
+#define DC_SURFACE_TO_SURFACE(dc_surface) container_of(dc_surface, struct surface, protected.public)
+#define CORE_SURFACE_TO_SURFACE(core_surface) container_of(core_surface, struct surface, protected)
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static bool construct(struct dc_context *ctx, struct surface *surface)
+{
+	uint32_t i;
+	struct gamma_ramp *gamma =
+			&surface->protected.public.gamma_correction;
+
+	/* construct gamma default value. */
+	for (i = 0; i < NUM_OF_RAW_GAMMA_RAMP_RGB_256; i++) {
+		gamma->gamma_ramp_rgb256x3x16.red[i] =
+				(unsigned short) (i << 8);
+		gamma->gamma_ramp_rgb256x3x16.green[i] =
+				(unsigned short) (i << 8);
+		gamma->gamma_ramp_rgb256x3x16.blue[i] =
+				(unsigned short) (i << 8);
+	}
+	gamma->type = GAMMA_RAMP_TYPE_RGB256;
+	gamma->size = sizeof(gamma->gamma_ramp_rgb256x3x16);
+
+	surface->protected.ctx = ctx;
+	return true;
+}
+
+static void destruct(struct surface *surface)
+{
+}
+
+/*******************************************************************************
+ * Public functions
+ ******************************************************************************/
+void enable_surface_flip_reporting(struct dc_surface *dc_surface,
+		uint32_t controller_id)
+{
+	struct surface *surface = DC_SURFACE_TO_SURFACE(dc_surface);
+	surface->irq_source = controller_id + DC_IRQ_SOURCE_PFLIP1 - 1;
+	/*register_flip_interrupt(surface);*/
+}
+
+struct dc_surface *dc_create_surface(const struct dc *dc)
+{
+	struct surface *surface = dm_alloc(dc->ctx, sizeof(*surface));
+
+	if (NULL == surface)
+		goto alloc_fail;
+
+	if (false == construct(dc->ctx, surface))
+		goto construct_fail;
+
+	dc_surface_retain(&surface->protected.public);
+
+	return &surface->protected.public;
+
+construct_fail:
+	dm_free(dc->ctx, surface);
+
+alloc_fail:
+	return NULL;
+}
+
+void dc_surface_retain(const struct dc_surface *dc_surface)
+{
+	struct surface *surface = DC_SURFACE_TO_SURFACE(dc_surface);
+
+	++surface->ref_count;
+}
+
+void dc_surface_release(const struct dc_surface *dc_surface)
+{
+	struct surface *surface = DC_SURFACE_TO_SURFACE(dc_surface);
+	--surface->ref_count;
+
+	if (surface->ref_count == 0) {
+		destruct(surface);
+		dm_free(surface->protected.ctx, surface);
+	}
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/core/dc_target.c b/drivers/gpu/drm/amd/dal/dc/core/dc_target.c
new file mode 100644
index 000000000000..e93e73d13448
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/core/dc_target.c
@@ -0,0 +1,548 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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 "hw_sequencer.h"
+#include "resource.h"
+#include "ipp.h"
+#include "timing_generator.h"
+
+#define COEFF_RANGE	3
+#define REGAMMA_COEFF_A0	31308
+#define REGAMMA_COEFF_A1	12920
+#define REGAMMA_COEFF_A2	55
+#define REGAMMA_COEFF_A3	55
+#define REGAMMA_COEFF_GAMMA	2400
+
+struct target {
+	struct core_target protected;
+	int ref_count;
+};
+
+#define DC_TARGET_TO_TARGET(dc_target) \
+	container_of(dc_target, struct target, protected.public)
+#define CORE_TARGET_TO_TARGET(core_target) \
+	container_of(core_target, struct target, protected)
+
+static void construct(
+	struct core_target *target,
+	struct dc_context *ctx,
+	struct dc_stream *dc_streams[],
+	uint8_t stream_count)
+{
+	uint8_t i;
+	for (i = 0; i < stream_count; i++) {
+		target->public.streams[i] = dc_streams[i];
+		dc_stream_retain(dc_streams[i]);
+	}
+
+	target->ctx = ctx;
+	target->public.stream_count = stream_count;
+}
+
+static void destruct(struct core_target *core_target)
+{
+	int i;
+
+	for (i = 0; i < core_target->status.surface_count; i++) {
+		dc_surface_release(core_target->status.surfaces[i]);
+		core_target->status.surfaces[i] = NULL;
+	}
+	for (i = 0; i < core_target->public.stream_count; i++) {
+		dc_stream_release(
+			(struct dc_stream *)core_target->public.streams[i]);
+		core_target->public.streams[i] = NULL;
+	}
+}
+
+void dc_target_retain(struct dc_target *dc_target)
+{
+	struct target *target = DC_TARGET_TO_TARGET(dc_target);
+
+	target->ref_count++;
+}
+
+void dc_target_release(struct dc_target *dc_target)
+{
+	struct target *target = DC_TARGET_TO_TARGET(dc_target);
+	struct core_target *protected = DC_TARGET_TO_CORE(dc_target);
+
+	ASSERT(target->ref_count > 0);
+	target->ref_count--;
+	if (target->ref_count == 0) {
+		destruct(protected);
+		dm_free(protected->ctx, target);
+	}
+}
+
+const struct dc_target_status *dc_target_get_status(
+					const struct dc_target* dc_target)
+{
+	struct core_target* target = DC_TARGET_TO_CORE(dc_target);
+	return &target->status;
+}
+
+struct dc_target *dc_create_target_for_streams(
+		struct dc_stream *dc_streams[],
+		uint8_t stream_count)
+{
+	struct core_stream *stream;
+	struct target *target;
+
+	if (0 == stream_count)
+		goto target_alloc_fail;
+
+	stream = DC_STREAM_TO_CORE(dc_streams[0]);
+
+	target = dm_alloc(stream->ctx, sizeof(struct target));
+
+	if (NULL == target)
+		goto target_alloc_fail;
+
+	construct(&target->protected, stream->ctx, dc_streams, stream_count);
+
+	dc_target_retain(&target->protected.public);
+
+	return &target->protected.public;
+
+
+target_alloc_fail:
+	return NULL;
+}
+
+static void build_gamma_params(
+		enum pixel_format pixel_format,
+		struct gamma_parameters *gamma_param)
+{
+	uint32_t i;
+
+	/* translate parameters */
+	gamma_param->surface_pixel_format = pixel_format;
+
+	gamma_param->regamma_adjust_type = GRAPHICS_REGAMMA_ADJUST_SW;
+	gamma_param->degamma_adjust_type = GRAPHICS_REGAMMA_ADJUST_SW;
+
+	gamma_param->selected_gamma_lut = GRAPHICS_GAMMA_LUT_REGAMMA;
+
+	/* TODO support non-legacy gamma */
+	gamma_param->disable_adjustments = false;
+	gamma_param->flag.bits.config_is_changed = 0;
+	gamma_param->flag.bits.regamma_update = 1;
+	gamma_param->flag.bits.gamma_update = 1;
+
+	/* Set regamma */
+	gamma_param->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB = 1;
+	gamma_param->regamma.features.bits.OVERLAY_DEGAMMA_SRGB = 1;
+	gamma_param->regamma.features.bits.GAMMA_RAMP_ARRAY = 0;
+	gamma_param->regamma.features.bits.APPLY_DEGAMMA = 0;
+
+	for (i = 0; i < COEFF_RANGE; i++) {
+		gamma_param->regamma.gamma_coeff.a0[i] = REGAMMA_COEFF_A0;
+		gamma_param->regamma.gamma_coeff.a1[i] = REGAMMA_COEFF_A1;
+		gamma_param->regamma.gamma_coeff.a2[i] = REGAMMA_COEFF_A2;
+		gamma_param->regamma.gamma_coeff.a3[i] = REGAMMA_COEFF_A3;
+		gamma_param->regamma.gamma_coeff.gamma[i] = REGAMMA_COEFF_GAMMA;
+	}
+}
+
+
+static bool program_gamma(
+		struct dc_context *ctx,
+		struct dc_surface *surface,
+		struct input_pixel_processor *ipp,
+		struct output_pixel_processor *opp)
+{
+	struct gamma_parameters *gamma_param;
+	bool result= false;
+
+	gamma_param = dm_alloc(ctx, sizeof(struct gamma_parameters));
+
+	if (!gamma_param)
+		goto gamma_param_fail;
+
+	build_gamma_params(surface->format, gamma_param);
+
+	result = ctx->dc->hwss.set_gamma_ramp(ipp, opp,
+			&surface->gamma_correction,
+			gamma_param);
+
+	dm_free(ctx, gamma_param);
+
+gamma_param_fail:
+	return result;
+}
+
+static bool validate_surface_address(
+		struct dc_plane_address address)
+{
+	bool is_valid_address = false;
+
+	switch (address.type) {
+	case PLN_ADDR_TYPE_GRAPHICS:
+		if (address.grph.addr.quad_part != 0)
+			is_valid_address = true;
+		break;
+	case PLN_ADDR_TYPE_GRPH_STEREO:
+		if ((address.grph_stereo.left_addr.quad_part != 0) &&
+			(address.grph_stereo.right_addr.quad_part != 0)) {
+			is_valid_address = true;
+		}
+		break;
+	case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE:
+	default:
+		/* not supported */
+		BREAK_TO_DEBUGGER();
+		break;
+	}
+
+	return is_valid_address;
+}
+
+bool dc_commit_surfaces_to_target(
+		struct dc *dc,
+		struct dc_surface *new_surfaces[],
+		uint8_t new_surface_count,
+		struct dc_target *dc_target)
+
+{
+	int i, j;
+	uint32_t prev_disp_clk = dc->current_context.bw_results.dispclk_khz;
+	struct core_target *target = DC_TARGET_TO_CORE(dc_target);
+
+	int current_enabled_surface_count = 0;
+	int new_enabled_surface_count = 0;
+
+	if (!dal_adapter_service_is_in_accelerated_mode(
+						dc->res_pool.adapter_srv) ||
+			dc->current_context.target_count == 0) {
+		return false;
+	}
+
+	for (i = 0; i < dc->current_context.target_count; i++)
+		if (target == dc->current_context.targets[i])
+			break;
+
+	/* Cannot commit surface to a target that is not commited */
+	if (i == dc->current_context.target_count)
+		return false;
+
+	for (i = 0; i < target->status.surface_count; i++)
+		if (target->status.surfaces[i]->visible)
+			current_enabled_surface_count++;
+
+	for (i = 0; i < new_surface_count; i++)
+		if (new_surfaces[i]->visible)
+			new_enabled_surface_count++;
+
+	dal_logger_write(dc->ctx->logger,
+				LOG_MAJOR_INTERFACE_TRACE,
+				LOG_MINOR_COMPONENT_DC,
+				"%s: commit %d surfaces to target 0x%x\n",
+				__func__,
+				new_surface_count,
+				dc_target);
+
+
+	if (!logical_attach_surfaces_to_target(
+						new_surfaces,
+						new_surface_count,
+						dc_target)) {
+		BREAK_TO_DEBUGGER();
+		goto unexpected_fail;
+	}
+
+	for (i = 0; i < new_surface_count; i++)
+		for (j = 0; j < target->public.stream_count; j++)
+			build_scaling_params(
+				new_surfaces[i],
+				DC_STREAM_TO_CORE(target->public.streams[j]));
+
+	if (dc->res_pool.funcs->validate_bandwidth(dc, &dc->current_context)
+								!= DC_OK) {
+		BREAK_TO_DEBUGGER();
+		goto unexpected_fail;
+	}
+
+	if (prev_disp_clk < dc->current_context.bw_results.dispclk_khz) {
+		dc->hwss.program_bw(dc, &dc->current_context);
+		pplib_apply_display_requirements(dc, &dc->current_context);
+	}
+
+	if (current_enabled_surface_count > 0 && new_enabled_surface_count == 0)
+		dc_target_disable_memory_requests(dc_target);
+
+	for (i = 0; i < new_surface_count; i++) {
+		struct dc_surface *surface = new_surfaces[i];
+		struct core_surface *core_surface = DC_SURFACE_TO_CORE(surface);
+		bool is_valid_address =
+				validate_surface_address(surface->address);
+
+		dal_logger_write(dc->ctx->logger,
+					LOG_MAJOR_INTERFACE_TRACE,
+					LOG_MINOR_COMPONENT_DC,
+					"0x%x:",
+					surface);
+
+		program_gamma(dc->ctx, surface,
+			DC_STREAM_TO_CORE(target->public.streams[0])->ipp,
+			DC_STREAM_TO_CORE(target->public.streams[0])->opp);
+
+		dc->hwss.set_plane_config(dc, core_surface, target);
+
+		if (is_valid_address)
+			dc->hwss.update_plane_address(dc, core_surface, target);
+	}
+
+	if (current_enabled_surface_count == 0 && new_enabled_surface_count > 0)
+		dc_target_enable_memory_requests(dc_target);
+
+	/* Lower display clock if necessary */
+	if (prev_disp_clk > dc->current_context.bw_results.dispclk_khz) {
+		dc->hwss.program_bw(dc, &dc->current_context);
+		pplib_apply_display_requirements(dc, &dc->current_context);
+	}
+
+	return true;
+
+unexpected_fail:
+	for (i = 0; i < new_surface_count; i++) {
+		target->status.surfaces[i] = NULL;
+	}
+	target->status.surface_count = 0;
+
+	return false;
+}
+
+bool dc_target_is_connected_to_sink(
+		const struct dc_target * dc_target,
+		const struct dc_sink *dc_sink)
+{
+	struct core_target *target = DC_TARGET_TO_CORE(dc_target);
+	uint8_t i;
+	for (i = 0; i < target->public.stream_count; i++) {
+		if (target->public.streams[i]->sink == dc_sink)
+			return true;
+	}
+	return false;
+}
+
+void dc_target_enable_memory_requests(struct dc_target *target)
+{
+	uint8_t i;
+	struct core_target *core_target = DC_TARGET_TO_CORE(target);
+	for (i = 0; i < core_target->public.stream_count; i++) {
+		struct timing_generator *tg =
+			DC_STREAM_TO_CORE(core_target->public.streams[i])->tg;
+
+		if (!tg->funcs->set_blank(tg, false)) {
+			dm_error("DC: failed to unblank crtc!\n");
+			BREAK_TO_DEBUGGER();
+		}
+	}
+}
+
+void dc_target_disable_memory_requests(struct dc_target *target)
+{
+	uint8_t i;
+	struct core_target *core_target = DC_TARGET_TO_CORE(target);
+	for (i = 0; i < core_target->public.stream_count; i++) {
+	struct timing_generator *tg =
+		DC_STREAM_TO_CORE(core_target->public.streams[i])->tg;
+
+		if (NULL == tg) {
+			dm_error("DC: timing generator is NULL!\n");
+			BREAK_TO_DEBUGGER();
+			continue;
+		}
+
+		if (false == tg->funcs->set_blank(tg, true)) {
+			dm_error("DC: failed to blank crtc!\n");
+			BREAK_TO_DEBUGGER();
+		}
+	}
+}
+
+/**
+ * Update the cursor attributes and set cursor surface address
+ */
+bool dc_target_set_cursor_attributes(
+	struct dc_target *dc_target,
+	const struct dc_cursor_attributes *attributes)
+{
+	struct core_target *core_target;
+	struct input_pixel_processor *ipp;
+
+	if (NULL == dc_target) {
+		dm_error("DC: dc_target is NULL!\n");
+			return false;
+
+	}
+
+	core_target = DC_TARGET_TO_CORE(dc_target);
+	ipp = DC_STREAM_TO_CORE(core_target->public.streams[0])->ipp;
+
+	if (NULL == ipp) {
+		dm_error("DC: input pixel processor is NULL!\n");
+		return false;
+	}
+
+	if (true == ipp->funcs->ipp_cursor_set_attributes(ipp, attributes))
+		return true;
+
+	return false;
+}
+
+bool dc_target_set_cursor_position(
+	struct dc_target *dc_target,
+	const struct dc_cursor_position *position)
+{
+	struct core_target *core_target;
+	struct input_pixel_processor *ipp;
+
+	if (NULL == dc_target) {
+		dm_error("DC: dc_target is NULL!\n");
+		return false;
+	}
+
+	if (NULL == position) {
+		dm_error("DC: cursor position is NULL!\n");
+		return false;
+	}
+
+	core_target = DC_TARGET_TO_CORE(dc_target);
+	ipp = DC_STREAM_TO_CORE(core_target->public.streams[0])->ipp;
+
+	if (NULL == ipp) {
+		dm_error("DC: input pixel processor is NULL!\n");
+		return false;
+	}
+
+
+	if (true == ipp->funcs->ipp_cursor_set_position(ipp, position))
+		return true;
+
+	return false;
+}
+
+/* TODO: #flip temporary to make flip work */
+uint8_t dc_target_get_link_index(const struct dc_target *dc_target)
+{
+	const struct core_target *target = CONST_DC_TARGET_TO_CORE(dc_target);
+	const struct core_sink *sink =
+		DC_SINK_TO_CORE(target->public.streams[0]->sink);
+
+	return sink->link->public.link_index;
+}
+
+uint32_t dc_target_get_vblank_counter(const struct dc_target *dc_target)
+{
+	struct core_target *core_target = DC_TARGET_TO_CORE(dc_target);
+	struct timing_generator *tg =
+		DC_STREAM_TO_CORE(core_target->public.streams[0])->tg;
+
+	return tg->funcs->get_frame_count(tg);
+}
+
+enum dc_irq_source dc_target_get_irq_src(
+	const struct dc_target *dc_target, const enum irq_type irq_type)
+{
+	struct core_target *core_target = DC_TARGET_TO_CORE(dc_target);
+
+	/* #TODO - Remove the assumption that the controller is always in the
+	 * first stream of a core target */
+	struct core_stream *stream =
+		DC_STREAM_TO_CORE(core_target->public.streams[0]);
+	uint8_t controller_idx = stream->controller_idx;
+
+	/* Get controller id */
+	enum controller_id crtc_id = controller_idx + 1;
+
+	/* Calculate controller offset */
+	unsigned int offset = crtc_id - CONTROLLER_ID_D0;
+	unsigned int base = irq_type;
+
+	/* Calculate irq source */
+	enum dc_irq_source src = base + offset;
+
+	return src;
+}
+
+void dc_target_log(
+	const struct dc_target *dc_target,
+	struct dal_logger *dal_logger,
+	enum log_major log_major,
+	enum log_minor log_minor)
+{
+	int i;
+
+	const struct core_target *core_target =
+			CONST_DC_TARGET_TO_CORE(dc_target);
+
+	dal_logger_write(dal_logger,
+			log_major,
+			log_minor,
+			"core_target 0x%x: surface_count=%d, stream_count=%d\n",
+			core_target,
+			core_target->status.surface_count,
+			core_target->public.stream_count);
+
+	for (i = 0; i < core_target->public.stream_count; i++) {
+		const struct core_stream *core_stream =
+			DC_STREAM_TO_CORE(core_target->public.streams[i]);
+
+		dal_logger_write(dal_logger,
+			log_major,
+			log_minor,
+			"core_stream 0x%x: src: %d, %d, %d, %d; dst: %d, %d, %d, %d;\n",
+			core_stream,
+			core_stream->public.src.x,
+			core_stream->public.src.y,
+			core_stream->public.src.width,
+			core_stream->public.src.height,
+			core_stream->public.dst.x,
+			core_stream->public.dst.y,
+			core_stream->public.dst.width,
+			core_stream->public.dst.height);
+		dal_logger_write(dal_logger,
+			log_major,
+			log_minor,
+			"\tpix_clk_khz: %d, h_total: %d, v_total: %d\n",
+			core_stream->public.timing.pix_clk_khz,
+			core_stream->public.timing.h_total,
+			core_stream->public.timing.v_total);
+		dal_logger_write(dal_logger,
+			log_major,
+			log_minor,
+			"\tsink name: %s, serial: %d\n",
+			core_stream->sink->public.edid_caps.display_name,
+			core_stream->sink->public.edid_caps.serial_number);
+		dal_logger_write(dal_logger,
+			log_major,
+			log_minor,
+			"\tlink: %d\n",
+			core_stream->sink->link->public.link_index);
+	}
+}