diff mbox

[v2,13/26] drm/amd/dal: Add encoder HW programming

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

Commit Message

Harry Wentland Feb. 16, 2016, 10:27 p.m. UTC
Responsible for programming back-end of display path, such as DIG,
UNIPHY, DP, DAC, and DVO.
Supports:
- DisplayPort (single stream)
- HDMI
- DVI
- eDP

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
---
 .../drm/amd/dal/dc/dce110/dce110_link_encoder.c    | 1927 ++++++++++++++++++++
 .../drm/amd/dal/dc/dce110/dce110_link_encoder.h    |  156 ++
 .../drm/amd/dal/dc/dce110/dce110_stream_encoder.c  | 1123 ++++++++++++
 .../drm/amd/dal/dc/dce110/dce110_stream_encoder.h  |  122 ++
 4 files changed, 3328 insertions(+)
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h
diff mbox

Patch

diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c
new file mode 100644
index 000000000000..f714215c0dd5
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.c
@@ -0,0 +1,1927 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services.h"
+#include "core_types.h"
+#include "link_encoder.h"
+#include "stream_encoder.h"
+#include "dce110_link_encoder.h"
+
+#include "i2caux_interface.h"
+#include "dc_bios_types.h"
+
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+#include "dce/dce_11_0_enum.h"
+
+#define LINK_REG(reg)\
+	(enc110->link_regs->reg)
+
+#define AUX_REG(reg)\
+	(enc110->aux_regs->reg)
+
+#define BL_REG(reg)\
+	(enc110->bl_regs->reg)
+
+/* For current ASICs pixel clock - 600MHz */
+#define MAX_ENCODER_CLK 600000
+
+#define DCE11_UNIPHY_MAX_PIXEL_CLK_IN_KHZ 600000
+
+#define DEFAULT_AUX_MAX_DATA_SIZE 16
+#define AUX_MAX_DEFER_WRITE_RETRY 20
+/*
+ * @brief
+ * Trigger Source Select
+ * ASIC-dependent, actual values for register programming
+ */
+#define DCE110_DIG_FE_SOURCE_SELECT_INVALID 0x0
+#define DCE110_DIG_FE_SOURCE_SELECT_DIGA 0x1
+#define DCE110_DIG_FE_SOURCE_SELECT_DIGB 0x2
+#define DCE110_DIG_FE_SOURCE_SELECT_DIGC 0x4
+#define DCE110_DIG_FE_SOURCE_SELECT_DIGD 0x08
+#define DCE110_DIG_FE_SOURCE_SELECT_DIGE 0x10
+#define DCE110_DIG_FE_SOURCE_SELECT_DIGF 0x20
+
+/* all values are in milliseconds */
+/* For eDP, after power-up/power/down,
+ * 300/500 msec max. delay from LCDVCC to black video generation */
+#define PANEL_POWER_UP_TIMEOUT 300
+#define PANEL_POWER_DOWN_TIMEOUT 500
+#define HPD_CHECK_INTERVAL 10
+
+/* Minimum pixel clock, in KHz. For TMDS signal is 25.00 MHz */
+#define TMDS_MIN_PIXEL_CLOCK 25000
+/* Maximum pixel clock, in KHz. For TMDS signal is 165.00 MHz */
+#define TMDS_MAX_PIXEL_CLOCK 165000
+/* For current ASICs pixel clock - 600MHz */
+#define MAX_ENCODER_CLOCK 600000
+
+enum {
+	DP_MST_UPDATE_MAX_RETRY = 50
+};
+
+#define DIG_REG(reg)\
+	(reg + enc110->offsets.dig)
+
+#define DP_REG(reg)\
+	(reg + enc110->offsets.dp)
+
+static struct link_encoder_funcs dce110_lnk_enc_funcs = {
+	.validate_output_with_stream =
+		dce110_link_encoder_validate_output_with_stream,
+	.hw_init = dce110_link_encoder_hw_init,
+	.setup = dce110_link_encoder_setup,
+	.enable_tmds_output = dce110_link_encoder_enable_tmds_output,
+	.enable_dp_output = dce110_link_encoder_enable_dp_output,
+	.enable_dp_mst_output = dce110_link_encoder_enable_dp_mst_output,
+	.disable_output = dce110_link_encoder_disable_output,
+	.dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings,
+	.dp_set_phy_pattern = dce110_link_encoder_dp_set_phy_pattern,
+	.update_mst_stream_allocation_table =
+		dce110_link_encoder_update_mst_stream_allocation_table,
+	.set_lcd_backlight_level = dce110_link_encoder_set_lcd_backlight_level,
+	.backlight_control = dce110_link_encoder_edp_backlight_control,
+	.power_control = dce110_link_encoder_edp_power_control,
+	.connect_dig_be_to_fe = dce110_link_encoder_connect_dig_be_to_fe
+};
+
+static enum bp_result link_transmitter_control(
+	struct dce110_link_encoder *enc110,
+	struct bp_transmitter_control *cntl)
+{
+	enum bp_result result;
+	struct dc_bios *bp = dal_adapter_service_get_bios_parser(
+					enc110->base.adapter_service);
+
+	result = bp->funcs->transmitter_control(bp, cntl);
+
+	return result;
+}
+
+static void enable_phy_bypass_mode(
+	struct dce110_link_encoder *enc110,
+	bool enable)
+{
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+	struct dc_context *ctx = enc110->base.ctx;
+
+	const uint32_t addr = LINK_REG(DP_DPHY_CNTL);
+
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, enable, DP_DPHY_CNTL, DPHY_BYPASS);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+static void disable_prbs_symbols(
+	struct dce110_link_encoder *enc110,
+	bool disable)
+{
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+	struct dc_context *ctx = enc110->base.ctx;
+
+	const uint32_t addr = LINK_REG(DP_DPHY_CNTL);
+
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, disable,
+			DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE0);
+
+	set_reg_field_value(value, disable,
+			DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE1);
+
+	set_reg_field_value(value, disable,
+			DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE2);
+
+	set_reg_field_value(value, disable,
+			DP_DPHY_CNTL, DPHY_ATEST_SEL_LANE3);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+static void disable_prbs_mode(
+	struct dce110_link_encoder *enc110)
+{
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+	struct dc_context *ctx = enc110->base.ctx;
+
+	const uint32_t addr = LINK_REG(DP_DPHY_PRBS_CNTL);
+	uint32_t value;
+
+	value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, 0, DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+static void program_pattern_symbols(
+	struct dce110_link_encoder *enc110,
+	uint16_t pattern_symbols[8])
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t value;
+
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+
+	addr = LINK_REG(DP_DPHY_SYM0);
+
+	value = 0;
+	set_reg_field_value(value, pattern_symbols[0],
+			DP_DPHY_SYM0, DPHY_SYM1);
+	set_reg_field_value(value, pattern_symbols[1],
+			DP_DPHY_SYM0, DPHY_SYM2);
+	set_reg_field_value(value, pattern_symbols[2],
+			DP_DPHY_SYM0, DPHY_SYM3);
+	dm_write_reg(ctx, addr, value);
+
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+
+	addr = LINK_REG(DP_DPHY_SYM1);
+
+	value = 0;
+	set_reg_field_value(value, pattern_symbols[3],
+			DP_DPHY_SYM1, DPHY_SYM4);
+	set_reg_field_value(value, pattern_symbols[4],
+			DP_DPHY_SYM1, DPHY_SYM5);
+	set_reg_field_value(value, pattern_symbols[5],
+			DP_DPHY_SYM1, DPHY_SYM6);
+	dm_write_reg(ctx, addr, value);
+
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+	addr = LINK_REG(DP_DPHY_SYM2);
+	value = 0;
+	set_reg_field_value(value, pattern_symbols[6],
+			DP_DPHY_SYM2, DPHY_SYM7);
+	set_reg_field_value(value, pattern_symbols[6],
+			DP_DPHY_SYM2, DPHY_SYM8);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+static void set_dp_phy_pattern_d102(
+	struct dce110_link_encoder *enc110)
+{
+	/* Disable PHY Bypass mode to setup the test pattern */
+	enable_phy_bypass_mode(enc110, false);
+
+	/* For 10-bit PRBS or debug symbols
+	 * please use the following sequence: */
+
+	/* Enable debug symbols on the lanes */
+
+	disable_prbs_symbols(enc110, true);
+
+	/* Disable PRBS mode,
+	 * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */
+
+	disable_prbs_mode(enc110);
+
+	/* Program debug symbols to be output */
+	{
+		uint16_t pattern_symbols[8] = {
+			0x2AA, 0x2AA, 0x2AA, 0x2AA,
+			0x2AA, 0x2AA, 0x2AA, 0x2AA
+		};
+
+		program_pattern_symbols(enc110, pattern_symbols);
+	}
+
+	/* Enable phy bypass mode to enable the test pattern */
+
+	enable_phy_bypass_mode(enc110, true);
+}
+
+static void set_link_training_complete(
+	struct dce110_link_encoder *enc110,
+	bool complete)
+{
+	/* This register resides in DP back end block;
+	 * transmitter is used for the offset */
+	struct dc_context *ctx = enc110->base.ctx;
+	const uint32_t addr = LINK_REG(DP_LINK_CNTL);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, complete,
+			DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+static void set_dp_phy_pattern_training_pattern(
+	struct dce110_link_encoder *enc110,
+	uint32_t index)
+{
+	/* Write Training Pattern */
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr = LINK_REG(DP_DPHY_TRAINING_PATTERN_SEL);
+
+	dm_write_reg(ctx, addr, index);
+
+	/* Set HW Register Training Complete to false */
+
+	set_link_training_complete(enc110, false);
+
+	/* Disable PHY Bypass mode to output Training Pattern */
+
+	enable_phy_bypass_mode(enc110, false);
+
+	/* Disable PRBS mode,
+	 * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */
+
+	disable_prbs_mode(enc110);
+}
+
+static void set_dp_phy_pattern_symbol_error(
+	struct dce110_link_encoder *enc110)
+{
+	/* Disable PHY Bypass mode to setup the test pattern */
+	struct dc_context *ctx = enc110->base.ctx;
+
+	enable_phy_bypass_mode(enc110, false);
+
+	/* program correct panel mode*/
+	{
+		const uint32_t addr = LINK_REG(DP_DPHY_INTERNAL_CTRL);
+		uint32_t value = 0x0;
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* A PRBS23 pattern is used for most DP electrical measurements. */
+
+	/* Enable PRBS symbols on the lanes */
+
+	disable_prbs_symbols(enc110, false);
+
+	/* For PRBS23 Set bit DPHY_PRBS_SEL=1 and Set bit DPHY_PRBS_EN=1 */
+	{
+		const uint32_t addr = LINK_REG(DP_DPHY_PRBS_CNTL);
+		uint32_t value = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(value, 1,
+				DP_DPHY_PRBS_CNTL, DPHY_PRBS_SEL);
+		set_reg_field_value(value, 1,
+				DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN);
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* Enable phy bypass mode to enable the test pattern */
+
+	enable_phy_bypass_mode(enc110, true);
+}
+
+static void set_dp_phy_pattern_prbs7(
+	struct dce110_link_encoder *enc110)
+{
+	/* Disable PHY Bypass mode to setup the test pattern */
+	struct dc_context *ctx = enc110->base.ctx;
+
+	enable_phy_bypass_mode(enc110, false);
+
+	/* A PRBS7 pattern is used for most DP electrical measurements. */
+
+	/* Enable PRBS symbols on the lanes */
+
+	disable_prbs_symbols(enc110, false);
+
+	/* For PRBS7 Set bit DPHY_PRBS_SEL=0 and Set bit DPHY_PRBS_EN=1 */
+	{
+		const uint32_t addr = LINK_REG(DP_DPHY_PRBS_CNTL);
+
+		uint32_t value = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(value, 0,
+				DP_DPHY_PRBS_CNTL, DPHY_PRBS_SEL);
+
+		set_reg_field_value(value, 1,
+				DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN);
+
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* Enable phy bypass mode to enable the test pattern */
+
+	enable_phy_bypass_mode(enc110, true);
+}
+
+static void set_dp_phy_pattern_80bit_custom(
+	struct dce110_link_encoder *enc110,
+	const uint8_t *pattern)
+{
+	/* Disable PHY Bypass mode to setup the test pattern */
+	enable_phy_bypass_mode(enc110, false);
+
+	/* Enable debug symbols on the lanes */
+
+	disable_prbs_symbols(enc110, true);
+
+	/* Enable PHY bypass mode to enable the test pattern */
+	/* TODO is it really needed ? */
+
+	enable_phy_bypass_mode(enc110, true);
+
+	/* Program 80 bit custom pattern */
+	{
+		uint16_t pattern_symbols[8];
+
+		pattern_symbols[0] =
+			((pattern[1] & 0x03) << 8) | pattern[0];
+		pattern_symbols[1] =
+			((pattern[2] & 0x0f) << 6) | ((pattern[1] >> 2) & 0x3f);
+		pattern_symbols[2] =
+			((pattern[3] & 0x3f) << 4) | ((pattern[2] >> 4) & 0x0f);
+		pattern_symbols[3] =
+			(pattern[4] << 2) | ((pattern[3] >> 6) & 0x03);
+		pattern_symbols[4] =
+			((pattern[6] & 0x03) << 8) | pattern[5];
+		pattern_symbols[5] =
+			((pattern[7] & 0x0f) << 6) | ((pattern[6] >> 2) & 0x3f);
+		pattern_symbols[6] =
+			((pattern[8] & 0x3f) << 4) | ((pattern[7] >> 4) & 0x0f);
+		pattern_symbols[7] =
+			(pattern[9] << 2) | ((pattern[8] >> 6) & 0x03);
+
+		program_pattern_symbols(enc110, pattern_symbols);
+	}
+
+	/* Enable phy bypass mode to enable the test pattern */
+
+	enable_phy_bypass_mode(enc110, true);
+}
+
+static void set_dp_phy_pattern_hbr2_compliance(
+	struct dce110_link_encoder *enc110)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t value;
+
+	/* previously there is a register DP_HBR2_EYE_PATTERN
+	 * that is enabled to get the pattern.
+	 * But it does not work with the latest spec change,
+	 * so we are programming the following registers manually.
+	 *
+	 * The following settings have been confirmed
+	 * by Nick Chorney and Sandra Liu */
+
+	/* Disable PHY Bypass mode to setup the test pattern */
+
+	enable_phy_bypass_mode(enc110, false);
+
+	/* Setup DIG encoder in DP SST mode */
+
+	enc110->base.funcs->setup(&enc110->base, SIGNAL_TYPE_DISPLAY_PORT);
+
+	/* program correct panel mode*/
+	{
+		const uint32_t addr = LINK_REG(DP_DPHY_INTERNAL_CTRL);
+		uint32_t value = 0x0;
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* no vbid after BS (SR)
+	 * DP_LINK_FRAMING_CNTL changed history Sandra Liu
+	 * 11000260 / 11000104 / 110000FC */
+
+	/* TODO DP_LINK_FRAMING_CNTL should always use hardware default value
+	 * output  except output hbr2_compliance pattern for physical PHY
+	 * measurement. This is not normal usage case. SW should reset this
+	 * register to hardware default value after end use of HBR2 eye
+	 */
+	BREAK_TO_DEBUGGER();
+	/* TODO: do we still need this, find out at compliance test
+	addr = mmDP_LINK_FRAMING_CNTL + fe_addr_offset;
+
+	value = (ctx, addr);
+
+	set_reg_field_value(value, 0xFC,
+			DP_LINK_FRAMING_CNTL, DP_IDLE_BS_INTERVAL);
+	set_reg_field_value(value, 1,
+			DP_LINK_FRAMING_CNTL, DP_VBID_DISABLE);
+	set_reg_field_value(value, 1,
+			DP_LINK_FRAMING_CNTL, DP_VID_ENHANCED_FRAME_MODE);
+
+	dal_write_reg(ctx, addr, value);
+	 */
+
+	/*TODO add support for this test pattern
+	 * support_dp_hbr2_eye_pattern
+	 */
+
+	/* set link training complete */
+	set_link_training_complete(enc110, true);
+	/* do not enable video stream */
+	addr = LINK_REG(DP_VID_STREAM_CNTL);
+
+	value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, 0, DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE);
+
+	dm_write_reg(ctx, addr, value);
+
+	/* Disable PHY Bypass mode to setup the test pattern */
+
+	enable_phy_bypass_mode(enc110, false);
+}
+
+static void set_dp_phy_pattern_passthrough_mode(
+	struct dce110_link_encoder *enc110,
+	enum dp_panel_mode panel_mode)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+
+	/* program correct panel mode */
+	{
+		const uint32_t addr = LINK_REG(DP_DPHY_INTERNAL_CTRL);
+
+		uint32_t value;
+
+		value = dm_read_reg(ctx, addr);
+
+		switch (panel_mode) {
+		case DP_PANEL_MODE_EDP:
+			value = 0x1;
+		break;
+		case DP_PANEL_MODE_SPECIAL:
+			value = 0x11;
+		break;
+		default:
+			value = 0x0;
+			break;
+		}
+
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* set link training complete */
+
+	set_link_training_complete(enc110, true);
+
+	/* Disable PHY Bypass mode to setup the test pattern */
+
+	enable_phy_bypass_mode(enc110, false);
+
+	/* Disable PRBS mode,
+	 * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */
+
+	disable_prbs_mode(enc110);
+}
+
+/* return value is bit-vector */
+static uint8_t get_frontend_source(
+	enum engine_id engine)
+{
+	switch (engine) {
+	case ENGINE_ID_DIGA:
+		return DCE110_DIG_FE_SOURCE_SELECT_DIGA;
+	case ENGINE_ID_DIGB:
+		return DCE110_DIG_FE_SOURCE_SELECT_DIGB;
+	case ENGINE_ID_DIGC:
+		return DCE110_DIG_FE_SOURCE_SELECT_DIGC;
+	case ENGINE_ID_DIGD:
+		return DCE110_DIG_FE_SOURCE_SELECT_DIGD;
+	case ENGINE_ID_DIGE:
+		return DCE110_DIG_FE_SOURCE_SELECT_DIGE;
+	case ENGINE_ID_DIGF:
+		return DCE110_DIG_FE_SOURCE_SELECT_DIGF;
+	default:
+		ASSERT_CRITICAL(false);
+		return DCE110_DIG_FE_SOURCE_SELECT_INVALID;
+	}
+}
+
+static void configure_encoder(
+	struct dce110_link_encoder *enc110,
+	const struct link_settings *link_settings)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t value;
+
+	/* set number of lanes */
+	addr = LINK_REG(DP_CONFIG);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, link_settings->lane_count - LANE_COUNT_ONE,
+			DP_CONFIG, DP_UDI_LANES);
+	dm_write_reg(ctx, addr, value);
+
+}
+
+static bool is_panel_powered_on(struct dce110_link_encoder *enc110)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t value;
+	bool ret;
+
+	value = dm_read_reg(ctx,
+			BL_REG(LVTMA_PWRSEQ_STATE));
+
+	ret = get_reg_field_value(value,
+			LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R);
+
+	return ret == 1;
+}
+
+/*
+ * @brief
+ * eDP only.
+ */
+static void link_encoder_edp_wait_for_hpd_ready(
+	struct dce110_link_encoder *enc110,
+	bool power_up)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	struct adapter_service *as = enc110->base.adapter_service;
+	struct graphics_object_id connector = enc110->base.connector;
+	struct irq *hpd;
+	bool edp_hpd_high = false;
+	uint32_t time_elapsed = 0;
+	uint32_t timeout = power_up ?
+		PANEL_POWER_UP_TIMEOUT : PANEL_POWER_DOWN_TIMEOUT;
+
+	if (dal_graphics_object_id_get_connector_id(connector) !=
+		CONNECTOR_ID_EDP) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	if (!power_up && dal_adapter_service_is_feature_supported(
+		FEATURE_NO_HPD_LOW_POLLING_VCC_OFF))
+		/* from KV, we will not HPD low after turning off VCC -
+		 * instead, we will check the SW timer in power_up(). */
+		return;
+
+	/* when we power on/off the eDP panel,
+	 * we need to wait until SENSE bit is high/low */
+
+	/* obtain HPD */
+
+	hpd = dal_adapter_service_obtain_hpd_irq(as, connector);
+
+	if (!hpd) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	dal_irq_open(hpd);
+
+	/* wait until timeout or panel detected */
+
+	do {
+		uint32_t detected = 0;
+
+		dal_irq_get_value(hpd, &detected);
+
+		if (!(detected ^ power_up)) {
+			edp_hpd_high = true;
+			break;
+		}
+
+		dm_sleep_in_milliseconds(ctx, HPD_CHECK_INTERVAL);
+
+		time_elapsed += HPD_CHECK_INTERVAL;
+	} while (time_elapsed < timeout);
+
+	dal_irq_close(hpd);
+
+	dal_adapter_service_release_irq(as, hpd);
+
+	if (false == edp_hpd_high) {
+		dal_logger_write(ctx->logger,
+				LOG_MAJOR_ERROR,
+				LOG_MINOR_HW_TRACE_RESUME_S3,
+				"%s: wait timed out!\n", __func__);
+	}
+}
+
+/*
+ * @brief
+ * eDP only. Control the power of the eDP panel.
+ */
+void dce110_link_encoder_edp_power_control(
+	struct link_encoder *enc,
+	bool power_up)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+	enum bp_result bp_result;
+
+	if (dal_graphics_object_id_get_connector_id(enc110->base.connector) !=
+		CONNECTOR_ID_EDP) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	if ((power_up && !is_panel_powered_on(enc110)) ||
+		(!power_up && is_panel_powered_on(enc110))) {
+
+		/* Send VBIOS command to prompt eDP panel power */
+
+		dal_logger_write(ctx->logger,
+				LOG_MAJOR_HW_TRACE,
+				LOG_MINOR_HW_TRACE_RESUME_S3,
+				"%s: Panel Power action: %s\n",
+				__func__, (power_up ? "On":"Off"));
+
+		cntl.action = power_up ?
+			TRANSMITTER_CONTROL_POWER_ON :
+			TRANSMITTER_CONTROL_POWER_OFF;
+		cntl.transmitter = enc110->base.transmitter;
+		cntl.connector_obj_id = enc110->base.connector;
+		cntl.coherent = false;
+		cntl.lanes_number = LANE_COUNT_FOUR;
+		cntl.hpd_sel = enc110->base.hpd_source;
+
+		bp_result = link_transmitter_control(enc110, &cntl);
+
+		if (BP_RESULT_OK != bp_result) {
+
+			dal_logger_write(ctx->logger,
+					LOG_MAJOR_ERROR,
+					LOG_MINOR_HW_TRACE_RESUME_S3,
+					"%s: Panel Power bp_result: %d\n",
+					__func__, bp_result);
+		}
+	} else {
+		dal_logger_write(ctx->logger,
+				LOG_MAJOR_HW_TRACE,
+				LOG_MINOR_HW_TRACE_RESUME_S3,
+				"%s: Skipping Panel Power action: %s\n",
+				__func__, (power_up ? "On":"Off"));
+	}
+
+	link_encoder_edp_wait_for_hpd_ready(enc110, true);
+}
+
+static void aux_initialize(
+	struct dce110_link_encoder *enc110)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	enum hpd_source_id hpd_source = enc110->base.hpd_source;
+	uint32_t addr = AUX_REG(AUX_CONTROL);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, hpd_source, AUX_CONTROL, AUX_HPD_SEL);
+	set_reg_field_value(value, 0, AUX_CONTROL, AUX_LS_READ_EN);
+	dm_write_reg(ctx, addr, value);
+
+	addr = AUX_REG(AUX_DPHY_RX_CONTROL0);
+	value = dm_read_reg(ctx, addr);
+
+	/* 1/4 window (the maximum allowed) */
+	set_reg_field_value(value, 1,
+			AUX_DPHY_RX_CONTROL0, AUX_RX_RECEIVE_WINDOW);
+	dm_write_reg(ctx, addr, value);
+
+}
+
+/*todo: cloned in stream enc, fix*/
+static bool is_panel_backlight_on(struct dce110_link_encoder *enc110)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t value;
+
+	value = dm_read_reg(ctx, BL_REG(LVTMA_PWRSEQ_CNTL));
+
+	return get_reg_field_value(value, LVTMA_PWRSEQ_CNTL, LVTMA_BLON);
+}
+
+/*todo: cloned in stream enc, fix*/
+/*
+ * @brief
+ * eDP only. Control the backlight of the eDP panel
+ */
+void dce110_link_encoder_edp_backlight_control(
+	struct link_encoder *enc,
+	bool enable)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+
+	if (dal_graphics_object_id_get_connector_id(enc110->base.connector)
+		!= CONNECTOR_ID_EDP) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	if (enable && is_panel_backlight_on(enc110)) {
+		dal_logger_write(ctx->logger,
+				LOG_MAJOR_HW_TRACE,
+				LOG_MINOR_HW_TRACE_RESUME_S3,
+				"%s: panel already powered up. Do nothing.\n",
+				__func__);
+		return;
+	}
+
+	if (!enable && !is_panel_powered_on(enc110)) {
+		dal_logger_write(ctx->logger,
+				LOG_MAJOR_HW_TRACE,
+				LOG_MINOR_HW_TRACE_RESUME_S3,
+				"%s: panel already powered down. Do nothing.\n",
+				__func__);
+		return;
+	}
+
+	/* Send VBIOS command to control eDP panel backlight */
+
+	dal_logger_write(ctx->logger,
+			LOG_MAJOR_HW_TRACE,
+			LOG_MINOR_HW_TRACE_RESUME_S3,
+			"%s: backlight action: %s\n",
+			__func__, (enable ? "On":"Off"));
+
+	cntl.action = enable ?
+		TRANSMITTER_CONTROL_BACKLIGHT_ON :
+		TRANSMITTER_CONTROL_BACKLIGHT_OFF;
+	/*cntl.engine_id = ctx->engine;*/
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.connector_obj_id = enc110->base.connector;
+	/*todo: unhardcode*/
+	cntl.lanes_number = LANE_COUNT_FOUR;
+	cntl.hpd_sel = enc110->base.hpd_source;
+
+	/* For eDP, the following delays might need to be considered
+	 * after link training completed:
+	 * idle period - min. accounts for required BS-Idle pattern,
+	 * max. allows for source frame synchronization);
+	 * 50 msec max. delay from valid video data from source
+	 * to video on dislpay or backlight enable.
+	 *
+	 * Disable the delay for now.
+	 * Enable it in the future if necessary.
+	 */
+	/* dc_service_sleep_in_milliseconds(50); */
+	link_transmitter_control(enc110, &cntl);
+}
+
+static bool is_dig_enabled(const struct dce110_link_encoder *enc110)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t value;
+
+	value = dm_read_reg(ctx, LINK_REG(DIG_BE_EN_CNTL));
+
+	return get_reg_field_value(value, DIG_BE_EN_CNTL, DIG_ENABLE);
+}
+
+static void link_encoder_disable(struct dce110_link_encoder *enc110)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t value;
+
+	/* reset training pattern */
+	addr = LINK_REG(DP_DPHY_TRAINING_PATTERN_SEL);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 0,
+			DP_DPHY_TRAINING_PATTERN_SEL,
+			DPHY_TRAINING_PATTERN_SEL);
+	dm_write_reg(ctx, addr, value);
+
+	/* reset training complete */
+	addr = LINK_REG(DP_LINK_CNTL);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 0, DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE);
+	dm_write_reg(ctx, addr, value);
+
+	/* reset panel mode */
+	addr = LINK_REG(DP_DPHY_INTERNAL_CTRL);
+	value = 0;
+	dm_write_reg(ctx, addr, value);
+}
+
+static void hpd_initialize(
+	struct dce110_link_encoder *enc110)
+{
+	/* Associate HPD with DIG_BE */
+	struct dc_context *ctx = enc110->base.ctx;
+	enum hpd_source_id hpd_source = enc110->base.hpd_source;
+	const uint32_t addr = LINK_REG(DIG_BE_CNTL);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, hpd_source, DIG_BE_CNTL, DIG_HPD_SELECT);
+	dm_write_reg(ctx, addr, value);
+}
+
+static bool validate_dvi_output(
+	const struct dce110_link_encoder *enc110,
+	enum signal_type connector_signal,
+	enum signal_type signal,
+	const struct dc_crtc_timing *crtc_timing)
+{
+	uint32_t max_pixel_clock = TMDS_MAX_PIXEL_CLOCK;
+
+	if (enc110->base.features.max_pixel_clock < TMDS_MAX_PIXEL_CLOCK)
+		max_pixel_clock = enc110->base.features.max_pixel_clock;
+
+	if (signal == SIGNAL_TYPE_DVI_DUAL_LINK)
+		max_pixel_clock <<= 1;
+
+	/* This handles the case of HDMI downgrade to DVI we don't want to
+	 * we don't want to cap the pixel clock if the DDI is not DVI.
+	 */
+	if (connector_signal != SIGNAL_TYPE_DVI_DUAL_LINK &&
+			connector_signal != SIGNAL_TYPE_DVI_SINGLE_LINK)
+		max_pixel_clock = enc110->base.features.max_pixel_clock;
+
+	/* DVI only support RGB pixel encoding */
+	if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB)
+		return false;
+
+	if (crtc_timing->pix_clk_khz < TMDS_MIN_PIXEL_CLOCK)
+		return false;
+
+	if (crtc_timing->pix_clk_khz > max_pixel_clock)
+		return false;
+
+	/* DVI supports 6/8bpp single-link and 10/16bpp dual-link */
+	switch (crtc_timing->display_color_depth) {
+	case COLOR_DEPTH_666:
+	case COLOR_DEPTH_888:
+	break;
+	case COLOR_DEPTH_101010:
+	case COLOR_DEPTH_161616:
+		if (signal != SIGNAL_TYPE_DVI_DUAL_LINK)
+			return false;
+	break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+static bool validate_hdmi_output(
+	const struct dce110_link_encoder *enc110,
+	const struct dc_crtc_timing *crtc_timing,
+	uint32_t max_tmds_clk_from_edid_in_mhz,
+	enum dc_color_depth max_hdmi_deep_color,
+	uint32_t max_hdmi_pixel_clock)
+{
+	enum dc_color_depth max_deep_color = max_hdmi_deep_color;
+	/* expressed in KHz */
+	uint32_t pixel_clock = 0;
+
+	/*TODO: unhardcode*/
+	max_tmds_clk_from_edid_in_mhz = 0;
+	max_hdmi_deep_color = COLOR_DEPTH_121212;
+	max_hdmi_pixel_clock = 600000;
+
+	if (max_deep_color > enc110->base.features.max_deep_color)
+		max_deep_color = enc110->base.features.max_deep_color;
+
+	if (max_deep_color < crtc_timing->display_color_depth)
+		return false;
+
+	if (crtc_timing->pix_clk_khz < TMDS_MIN_PIXEL_CLOCK)
+		return false;
+
+	switch (crtc_timing->display_color_depth) {
+	case COLOR_DEPTH_666:
+		pixel_clock = (crtc_timing->pix_clk_khz * 3) >> 2;
+	break;
+	case COLOR_DEPTH_888:
+		pixel_clock = crtc_timing->pix_clk_khz;
+	break;
+	case COLOR_DEPTH_101010:
+		pixel_clock = (crtc_timing->pix_clk_khz * 10) >> 3;
+	break;
+	case COLOR_DEPTH_121212:
+		pixel_clock = (crtc_timing->pix_clk_khz * 3) >> 1;
+	break;
+	case COLOR_DEPTH_161616:
+		pixel_clock = crtc_timing->pix_clk_khz << 1;
+	break;
+	default:
+	break;
+	}
+
+	if (max_tmds_clk_from_edid_in_mhz > 0)
+		if (pixel_clock > max_tmds_clk_from_edid_in_mhz * 1000)
+			return false;
+
+	if ((pixel_clock == 0) ||
+		(pixel_clock > max_hdmi_pixel_clock) ||
+		(pixel_clock > enc110->base.features.max_pixel_clock))
+		return false;
+
+	/*
+	 * Restriction: allow non-CE mode (IT mode) to support RGB only.
+	 * When it is IT mode, the format mode will be 0,
+	 * but currently the code is broken,
+	 * VIDEO FORMAT is always 0 in validatepathMode().
+	 * Due to overscan change - need fix there and test the impact - to do.
+	 */
+	if (crtc_timing->timing_standard != TIMING_STANDARD_CEA861 &&
+		crtc_timing->timing_standard != TIMING_STANDARD_HDMI)
+		if (crtc_timing->pixel_encoding !=
+			PIXEL_ENCODING_RGB)
+			return false;
+
+	/* DCE11 HW does not support 420 */
+	if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
+		return false;
+
+	return true;
+}
+
+static bool validate_rgb_output(
+	const struct dce110_link_encoder *enc110,
+	const struct dc_crtc_timing *crtc_timing)
+{
+	if (crtc_timing->pix_clk_khz > enc110->base.features.max_pixel_clock)
+		return false;
+
+	if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB)
+		return false;
+
+	return true;
+}
+
+static bool validate_dp_output(
+	const struct dce110_link_encoder *enc110,
+	const struct dc_crtc_timing *crtc_timing)
+{
+	if (crtc_timing->pix_clk_khz > enc110->base.features.max_pixel_clock)
+		return false;
+
+	/* default RGB only */
+	if (crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB)
+		return true;
+
+	if (enc110->base.features.flags.bits.IS_YCBCR_CAPABLE)
+		return true;
+
+	/* for DCE 8.x or later DP Y-only feature,
+	 * we need ASIC cap + FeatureSupportDPYonly, not support 666 */
+	if (crtc_timing->flags.Y_ONLY &&
+		enc110->base.features.flags.bits.IS_YCBCR_CAPABLE &&
+		crtc_timing->display_color_depth != COLOR_DEPTH_666)
+		return true;
+
+	return false;
+}
+
+static bool validate_wireless_output(
+	const struct dce110_link_encoder *enc110,
+	const struct dc_crtc_timing *crtc_timing)
+{
+	if (crtc_timing->pix_clk_khz > enc110->base.features.max_pixel_clock)
+		return false;
+
+	/* Wireless only supports YCbCr444 */
+	if (crtc_timing->pixel_encoding ==
+			PIXEL_ENCODING_YCBCR444)
+		return true;
+
+	return false;
+}
+
+bool dce110_link_encoder_construct(
+	struct dce110_link_encoder *enc110,
+	const struct encoder_init_data *init_data,
+	const struct dce110_link_enc_registers *link_regs,
+	const struct dce110_link_enc_aux_registers *aux_regs,
+	const struct dce110_link_enc_bl_registers *bl_regs)
+{
+	struct graphics_object_encoder_cap_info enc_cap_info = {0};
+
+	enc110->base.funcs = &dce110_lnk_enc_funcs;
+	enc110->base.ctx = init_data->ctx;
+	enc110->base.id = init_data->encoder;
+
+	enc110->base.hpd_source = init_data->hpd_source;
+	enc110->base.connector = init_data->connector;
+	enc110->base.input_signals = SIGNAL_TYPE_ALL;
+
+	enc110->base.adapter_service = init_data->adapter_service;
+
+	enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
+
+	enc110->base.features.flags.raw = 0;
+
+	enc110->base.transmitter = init_data->transmitter;
+
+	enc110->base.features.flags.bits.IS_AUDIO_CAPABLE = true;
+
+	enc110->base.features.max_pixel_clock =
+		DCE11_UNIPHY_MAX_PIXEL_CLK_IN_KHZ;
+
+	/* set the flag to indicate whether driver poll the I2C data pin
+	 * while doing the DP sink detect
+	 */
+
+	if (dal_adapter_service_is_feature_supported(
+		FEATURE_DP_SINK_DETECT_POLL_DATA_PIN))
+		enc110->base.features.flags.bits.
+			DP_SINK_DETECT_POLL_DATA_PIN = true;
+
+	enc110->base.output_signals =
+		SIGNAL_TYPE_DVI_SINGLE_LINK |
+		SIGNAL_TYPE_DVI_DUAL_LINK |
+		SIGNAL_TYPE_LVDS |
+		SIGNAL_TYPE_DISPLAY_PORT |
+		SIGNAL_TYPE_DISPLAY_PORT_MST |
+		SIGNAL_TYPE_EDP |
+		SIGNAL_TYPE_HDMI_TYPE_A;
+
+	/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
+	 * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
+	 * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
+	 * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS.
+	 * Prefer DIG assignment is decided by board design.
+	 * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design
+	 * and VBIOS will filter out 7 UNIPHY for DCE 8.0.
+	 * By this, adding DIGG should not hurt DCE 8.0.
+	 * This will let DCE 8.1 share DCE 8.0 as much as possible
+	 */
+
+	enc110->link_regs = link_regs;
+	enc110->aux_regs = aux_regs;
+	enc110->bl_regs = bl_regs;
+
+	switch (enc110->base.transmitter) {
+	case TRANSMITTER_UNIPHY_A:
+		enc110->base.preferred_engine = ENGINE_ID_DIGA;
+	break;
+	case TRANSMITTER_UNIPHY_B:
+		enc110->base.preferred_engine = ENGINE_ID_DIGB;
+	break;
+	case TRANSMITTER_UNIPHY_C:
+		enc110->base.preferred_engine = ENGINE_ID_DIGC;
+	break;
+	case TRANSMITTER_UNIPHY_D:
+		enc110->base.preferred_engine = ENGINE_ID_DIGD;
+	break;
+	case TRANSMITTER_UNIPHY_E:
+		enc110->base.preferred_engine = ENGINE_ID_DIGE;
+	break;
+	case TRANSMITTER_UNIPHY_F:
+		enc110->base.preferred_engine = ENGINE_ID_DIGF;
+	break;
+	default:
+		ASSERT_CRITICAL(false);
+		enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
+	}
+
+	dal_logger_write(init_data->ctx->logger,
+			LOG_MAJOR_I2C_AUX,
+			LOG_MINOR_I2C_AUX_CFG,
+			"Using channel: %s [%d]\n",
+			DECODE_CHANNEL_ID(init_data->channel),
+			init_data->channel);
+
+	/* Override features with DCE-specific values */
+	if (dal_adapter_service_get_encoder_cap_info(
+			enc110->base.adapter_service,
+			enc110->base.id, &enc_cap_info))
+		enc110->base.features.flags.bits.IS_HBR2_CAPABLE =
+				enc_cap_info.dp_hbr2_cap;
+
+	/* test pattern 3 support */
+	enc110->base.features.flags.bits.IS_TPS3_CAPABLE = true;
+	enc110->base.features.max_deep_color = COLOR_DEPTH_121212;
+
+	enc110->base.features.flags.bits.IS_Y_ONLY_CAPABLE =
+		dal_adapter_service_is_feature_supported(
+			FEATURE_SUPPORT_DP_Y_ONLY);
+
+	enc110->base.features.flags.bits.IS_YCBCR_CAPABLE =
+		dal_adapter_service_is_feature_supported(
+			FEATURE_SUPPORT_DP_YUV);
+	return true;
+}
+
+bool dce110_link_encoder_validate_output_with_stream(
+	struct link_encoder *enc,
+	struct core_stream *stream)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	bool is_valid;
+
+	switch (stream->signal) {
+	case SIGNAL_TYPE_DVI_SINGLE_LINK:
+	case SIGNAL_TYPE_DVI_DUAL_LINK:
+		is_valid = validate_dvi_output(
+			enc110,
+			stream->sink->link->public.connector_signal,
+			stream->signal,
+			&stream->public.timing);
+	break;
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+		is_valid = validate_hdmi_output(
+				enc110,
+				&stream->public.timing,
+				stream->max_tmds_clk_from_edid_in_mhz,
+				stream->max_hdmi_deep_color,
+				stream->max_hdmi_pixel_clock);
+	break;
+	case SIGNAL_TYPE_RGB:
+		is_valid = validate_rgb_output(
+			enc110, &stream->public.timing);
+	break;
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+	case SIGNAL_TYPE_EDP:
+		is_valid = validate_dp_output(
+			enc110, &stream->public.timing);
+	break;
+	case SIGNAL_TYPE_WIRELESS:
+		is_valid = validate_wireless_output(
+			enc110, &stream->public.timing);
+	break;
+	default:
+		is_valid = true;
+	break;
+	}
+
+	return is_valid;
+}
+
+void dce110_link_encoder_hw_init(
+	struct link_encoder *enc)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+	enum bp_result result;
+
+	cntl.action = TRANSMITTER_CONTROL_INIT;
+	cntl.engine_id = ENGINE_ID_UNKNOWN;
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.connector_obj_id = enc110->base.connector;
+	cntl.lanes_number = LANE_COUNT_FOUR;
+	cntl.coherent = false;
+	cntl.hpd_sel = enc110->base.hpd_source;
+
+	result = link_transmitter_control(enc110, &cntl);
+
+	if (result != BP_RESULT_OK) {
+		dal_logger_write(ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"%s: Failed to execute VBIOS command table!\n",
+			__func__);
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	if (enc110->base.connector.id == CONNECTOR_ID_LVDS) {
+		cntl.action = TRANSMITTER_CONTROL_BACKLIGHT_BRIGHTNESS;
+
+		result = link_transmitter_control(enc110, &cntl);
+
+		ASSERT(result == BP_RESULT_OK);
+
+	} else if (enc110->base.connector.id == CONNECTOR_ID_EDP) {
+		enc->funcs->power_control(&enc110->base, true);
+	}
+	aux_initialize(enc110);
+
+	/* reinitialize HPD.
+	 * hpd_initialize() will pass DIG_FE id to HW context.
+	 * All other routine within HW context will use fe_engine_offset
+	 * as DIG_FE id even caller pass DIG_FE id.
+	 * So this routine must be called first. */
+	hpd_initialize(enc110);
+}
+
+void dce110_link_encoder_setup(
+	struct link_encoder *enc,
+	enum signal_type signal)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	const uint32_t addr = LINK_REG(DIG_BE_CNTL);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	switch (signal) {
+	case SIGNAL_TYPE_EDP:
+	case SIGNAL_TYPE_DISPLAY_PORT:
+		/* DP SST */
+		set_reg_field_value(value, 0, DIG_BE_CNTL, DIG_MODE);
+		break;
+	case SIGNAL_TYPE_LVDS:
+		/* LVDS */
+		set_reg_field_value(value, 1, DIG_BE_CNTL, DIG_MODE);
+		break;
+	case SIGNAL_TYPE_DVI_SINGLE_LINK:
+	case SIGNAL_TYPE_DVI_DUAL_LINK:
+		/* TMDS-DVI */
+		set_reg_field_value(value, 2, DIG_BE_CNTL, DIG_MODE);
+		break;
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+		/* TMDS-HDMI */
+		set_reg_field_value(value, 3, DIG_BE_CNTL, DIG_MODE);
+		break;
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+		/* DP MST */
+		set_reg_field_value(value, 5, DIG_BE_CNTL, DIG_MODE);
+		break;
+	default:
+		ASSERT_CRITICAL(false);
+		/* invalid mode ! */
+		break;
+	}
+
+	dm_write_reg(ctx, addr, value);
+}
+
+/* TODO: still need depth or just pass in adjusted pixel clock? */
+void dce110_link_encoder_enable_tmds_output(
+	struct link_encoder *enc,
+	enum clock_source_id clock_source,
+	enum dc_color_depth color_depth,
+	bool hdmi,
+	bool dual_link,
+	uint32_t pixel_clock)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+	enum bp_result result;
+
+	/* Enable the PHY */
+
+	cntl.action = TRANSMITTER_CONTROL_ENABLE;
+	cntl.engine_id = ENGINE_ID_UNKNOWN;
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.pll_id = clock_source;
+	if (hdmi) {
+		cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A;
+		cntl.lanes_number = 4;
+	} else if (dual_link) {
+		cntl.signal = SIGNAL_TYPE_DVI_DUAL_LINK;
+		cntl.lanes_number = 8;
+	} else {
+		cntl.signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
+		cntl.lanes_number = 4;
+	}
+	cntl.hpd_sel = enc110->base.hpd_source;
+
+	cntl.pixel_clock = pixel_clock;
+	cntl.color_depth = color_depth;
+
+	result = link_transmitter_control(enc110, &cntl);
+
+	if (result != BP_RESULT_OK) {
+		dal_logger_write(ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"%s: Failed to execute VBIOS command table!\n",
+			__func__);
+		BREAK_TO_DEBUGGER();
+	}
+}
+
+/* enables DP PHY output */
+void dce110_link_encoder_enable_dp_output(
+	struct link_encoder *enc,
+	const struct link_settings *link_settings,
+	enum clock_source_id clock_source)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+	enum bp_result result;
+
+	/* Enable the PHY */
+
+	/* number_of_lanes is used for pixel clock adjust,
+	 * but it's not passed to asic_control.
+	 * We need to set number of lanes manually.
+	 */
+	configure_encoder(enc110, link_settings);
+
+	cntl.action = TRANSMITTER_CONTROL_ENABLE;
+	cntl.engine_id = enc->preferred_engine;
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.pll_id = clock_source;
+	cntl.signal = SIGNAL_TYPE_DISPLAY_PORT;
+	cntl.lanes_number = link_settings->lane_count;
+	cntl.hpd_sel = enc110->base.hpd_source;
+	cntl.pixel_clock = link_settings->link_rate
+						* LINK_RATE_REF_FREQ_IN_KHZ;
+	/* TODO: check if undefined works */
+	cntl.color_depth = COLOR_DEPTH_UNDEFINED;
+
+	result = link_transmitter_control(enc110, &cntl);
+
+	if (result != BP_RESULT_OK) {
+		dal_logger_write(ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"%s: Failed to execute VBIOS command table!\n",
+			__func__);
+		BREAK_TO_DEBUGGER();
+	}
+}
+
+/* enables DP PHY output in MST mode */
+void dce110_link_encoder_enable_dp_mst_output(
+	struct link_encoder *enc,
+	const struct link_settings *link_settings,
+	enum clock_source_id clock_source)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+	enum bp_result result;
+
+	/* Enable the PHY */
+
+	/* number_of_lanes is used for pixel clock adjust,
+	 * but it's not passed to asic_control.
+	 * We need to set number of lanes manually.
+	 */
+	configure_encoder(enc110, link_settings);
+
+	cntl.action = TRANSMITTER_CONTROL_ENABLE;
+	cntl.engine_id = enc->preferred_engine;
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.pll_id = clock_source;
+	cntl.signal = SIGNAL_TYPE_DISPLAY_PORT_MST;
+	cntl.lanes_number = link_settings->lane_count;
+	cntl.hpd_sel = enc110->base.hpd_source;
+	cntl.pixel_clock = link_settings->link_rate
+						* LINK_RATE_REF_FREQ_IN_KHZ;
+	/* TODO: check if undefined works */
+	cntl.color_depth = COLOR_DEPTH_UNDEFINED;
+
+	result = link_transmitter_control(enc110, &cntl);
+
+	if (result != BP_RESULT_OK) {
+		dal_logger_write(ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"%s: Failed to execute VBIOS command table!\n",
+			__func__);
+		BREAK_TO_DEBUGGER();
+	}
+}
+/*
+ * @brief
+ * Disable transmitter and its encoder
+ */
+void dce110_link_encoder_disable_output(
+	struct link_encoder *enc,
+	enum signal_type signal)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	struct bp_transmitter_control cntl = { 0 };
+	enum bp_result result;
+
+	if (!is_dig_enabled(enc110) &&
+		dal_adapter_service_should_optimize(
+			enc110->base.adapter_service,
+			OF_SKIP_POWER_DOWN_INACTIVE_ENCODER)) {
+		return;
+	}
+	/* Power-down RX and disable GPU PHY should be paired.
+	 * Disabling PHY without powering down RX may cause
+	 * symbol lock loss, on which we will get DP Sink interrupt. */
+
+	/* There is a case for the DP active dongles
+	 * where we want to disable the PHY but keep RX powered,
+	 * for those we need to ignore DP Sink interrupt
+	 * by checking lane count that has been set
+	 * on the last do_enable_output(). */
+
+	/* disable transmitter */
+	cntl.action = TRANSMITTER_CONTROL_DISABLE;
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.hpd_sel = enc110->base.hpd_source;
+	cntl.signal = signal;
+	cntl.connector_obj_id = enc110->base.connector;
+
+	result = link_transmitter_control(enc110, &cntl);
+
+	if (result != BP_RESULT_OK) {
+		dal_logger_write(ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"%s: Failed to execute VBIOS command table!\n",
+			__func__);
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	/* disable encoder */
+	if (dc_is_dp_signal(signal))
+		link_encoder_disable(enc110);
+
+	if (enc110->base.connector.id == CONNECTOR_ID_EDP) {
+		/* power down eDP panel */
+		/* TODO: Power control cause regression, we should implement
+		 * it properly, for now just comment it.
+		 *
+		 * link_encoder_edp_wait_for_hpd_ready(
+			link_enc,
+			link_enc->connector,
+			false);
+
+		 * link_encoder_edp_power_control(
+				link_enc, false); */
+	}
+}
+
+void dce110_link_encoder_dp_set_lane_settings(
+	struct link_encoder *enc,
+	const struct link_training_settings *link_settings)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	union dpcd_training_lane_set training_lane_set = { { 0 } };
+	int32_t lane = 0;
+	struct bp_transmitter_control cntl = { 0 };
+
+	if (!link_settings) {
+		BREAK_TO_DEBUGGER();
+		return;
+	}
+
+	cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS;
+	cntl.transmitter = enc110->base.transmitter;
+	cntl.connector_obj_id = enc110->base.connector;
+	cntl.lanes_number = link_settings->link_settings.lane_count;
+	cntl.hpd_sel = enc110->base.hpd_source;
+	cntl.pixel_clock = link_settings->link_settings.link_rate *
+						LINK_RATE_REF_FREQ_IN_KHZ;
+
+	for (lane = 0; lane < link_settings->link_settings.lane_count; ++lane) {
+		/* translate lane settings */
+
+		training_lane_set.bits.VOLTAGE_SWING_SET =
+			link_settings->lane_settings[lane].VOLTAGE_SWING;
+		training_lane_set.bits.PRE_EMPHASIS_SET =
+			link_settings->lane_settings[lane].PRE_EMPHASIS;
+
+		/* post cursor 2 setting only applies to HBR2 link rate */
+		if (link_settings->link_settings.link_rate == LINK_RATE_HIGH2) {
+			/* this is passed to VBIOS
+			 * to program post cursor 2 level */
+
+			training_lane_set.bits.POST_CURSOR2_SET =
+				link_settings->lane_settings[lane].POST_CURSOR2;
+		}
+
+		cntl.lane_select = lane;
+		cntl.lane_settings = training_lane_set.raw;
+
+		/* call VBIOS table to set voltage swing and pre-emphasis */
+		link_transmitter_control(enc110, &cntl);
+	}
+}
+
+/* set DP PHY test and training patterns */
+void dce110_link_encoder_dp_set_phy_pattern(
+	struct link_encoder *enc,
+	const struct encoder_set_dp_phy_pattern_param *param)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+
+	switch (param->dp_phy_pattern) {
+	case DP_TEST_PATTERN_TRAINING_PATTERN1:
+		set_dp_phy_pattern_training_pattern(enc110, 0);
+		break;
+	case DP_TEST_PATTERN_TRAINING_PATTERN2:
+		set_dp_phy_pattern_training_pattern(enc110, 1);
+		break;
+	case DP_TEST_PATTERN_TRAINING_PATTERN3:
+		set_dp_phy_pattern_training_pattern(enc110, 2);
+		break;
+	case DP_TEST_PATTERN_D102:
+		set_dp_phy_pattern_d102(enc110);
+		break;
+	case DP_TEST_PATTERN_SYMBOL_ERROR:
+		set_dp_phy_pattern_symbol_error(enc110);
+		break;
+	case DP_TEST_PATTERN_PRBS7:
+		set_dp_phy_pattern_prbs7(enc110);
+		break;
+	case DP_TEST_PATTERN_80BIT_CUSTOM:
+		set_dp_phy_pattern_80bit_custom(
+			enc110, param->custom_pattern);
+		break;
+	case DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE:
+		set_dp_phy_pattern_hbr2_compliance(enc110);
+		break;
+	case DP_TEST_PATTERN_VIDEO_MODE: {
+		set_dp_phy_pattern_passthrough_mode(
+			enc110, param->dp_panel_mode);
+		break;
+	}
+
+	default:
+		/* invalid phy pattern */
+		ASSERT_CRITICAL(false);
+		break;
+	}
+}
+
+static void fill_stream_allocation_row_info(
+	const struct link_mst_stream_allocation *stream_allocation,
+	uint32_t *src,
+	uint32_t *slots)
+{
+	const struct stream_encoder *stream_enc = stream_allocation->stream_enc;
+
+	if (stream_enc) {
+		*src = stream_enc->id;
+		*slots = stream_allocation->slot_count;
+	} else {
+		*src = 0;
+		*slots = 0;
+	}
+}
+
+/* programs DP MST VC payload allocation */
+void dce110_link_encoder_update_mst_stream_allocation_table(
+	struct link_encoder *enc,
+	const struct link_mst_stream_allocation_table *table)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t value0 = 0;
+	uint32_t value1 = 0;
+	uint32_t value2 = 0;
+	uint32_t slots = 0;
+	uint32_t src = 0;
+	uint32_t retries = 0;
+
+	/* For CZ, there are only 3 pipes. So Virtual channel is up 3.*/
+
+	/* --- Set MSE Stream Attribute -
+	 * Setup VC Payload Table on Tx Side,
+	 * Issue allocation change trigger
+	 * to commit payload on both tx and rx side */
+
+	/* we should clean-up table each time */
+	value0 = dm_read_reg(ctx, LINK_REG(DP_MSE_SAT0));
+	value1 = dm_read_reg(ctx, LINK_REG(DP_MSE_SAT1));
+
+	if (table->stream_count >= 1) {
+		fill_stream_allocation_row_info(
+			&table->stream_allocations[0],
+			&src,
+			&slots);
+	} else {
+		src = 0;
+		slots = 0;
+	}
+
+	set_reg_field_value(
+		value0,
+		src,
+		DP_MSE_SAT0,
+		DP_MSE_SAT_SRC0);
+
+	set_reg_field_value(
+		value0,
+		slots,
+		DP_MSE_SAT0,
+		DP_MSE_SAT_SLOT_COUNT0);
+
+	if (table->stream_count >= 2) {
+		fill_stream_allocation_row_info(
+			&table->stream_allocations[1],
+			&src,
+			&slots);
+	} else {
+		src = 0;
+		slots = 0;
+	}
+
+	set_reg_field_value(
+		value0,
+		src,
+		DP_MSE_SAT0,
+		DP_MSE_SAT_SRC1);
+
+	set_reg_field_value(
+		value0,
+		slots,
+		DP_MSE_SAT0,
+		DP_MSE_SAT_SLOT_COUNT1);
+
+	if (table->stream_count >= 3) {
+		fill_stream_allocation_row_info(
+			&table->stream_allocations[2],
+			&src,
+			&slots);
+	} else {
+		src = 0;
+		slots = 0;
+	}
+
+	set_reg_field_value(
+		value1,
+		src,
+		DP_MSE_SAT1,
+		DP_MSE_SAT_SRC2);
+
+	set_reg_field_value(
+		value1,
+		slots,
+		DP_MSE_SAT1,
+		DP_MSE_SAT_SLOT_COUNT2);
+
+	/* update ASIC MSE stream allocation table */
+	dm_write_reg(ctx, LINK_REG(DP_MSE_SAT0), value0);
+	dm_write_reg(ctx, LINK_REG(DP_MSE_SAT1), value1);
+
+	/* --- wait for transaction finish */
+
+	/* send allocation change trigger (ACT) ?
+	 * this step first sends the ACT,
+	 * then double buffers the SAT into the hardware
+	 * making the new allocation active on the DP MST mode link */
+
+	value0 = dm_read_reg(ctx, LINK_REG(DP_MSE_SAT_UPDATE));
+
+	/* DP_MSE_SAT_UPDATE:
+	 * 0 - No Action
+	 * 1 - Update SAT with trigger
+	 * 2 - Update SAT without trigger */
+
+	set_reg_field_value(
+		value0,
+		1,
+		DP_MSE_SAT_UPDATE,
+		DP_MSE_SAT_UPDATE);
+
+	dm_write_reg(ctx, LINK_REG(DP_MSE_SAT_UPDATE), value0);
+
+	/* wait for update to complete
+	 * (i.e. DP_MSE_SAT_UPDATE field is reset to 0)
+	 * then wait for the transmission
+	 * of at least 16 MTP headers on immediate local link.
+	 * i.e. DP_MSE_16_MTP_KEEPOUT field (read only) is reset to 0
+	 * a value of 1 indicates that DP MST mode
+	 * is in the 16 MTP keepout region after a VC has been added.
+	 * MST stream bandwidth (VC rate) can be configured
+	 * after this bit is cleared */
+
+	do {
+		dm_delay_in_microseconds(ctx, 10);
+
+		value0 = dm_read_reg(ctx,
+				LINK_REG(DP_MSE_SAT_UPDATE));
+
+		value1 = get_reg_field_value(
+				value0,
+				DP_MSE_SAT_UPDATE,
+				DP_MSE_SAT_UPDATE);
+		value2 = get_reg_field_value(
+				value0,
+				DP_MSE_SAT_UPDATE,
+				DP_MSE_16_MTP_KEEPOUT);
+
+		/* bit field DP_MSE_SAT_UPDATE is set to 1 already */
+		if (!value1 && !value2)
+			break;
+		++retries;
+	} while (retries < DP_MST_UPDATE_MAX_RETRY);
+}
+
+/* black light programming */
+void dce110_link_encoder_set_lcd_backlight_level(
+	struct link_encoder *enc,
+	uint32_t level)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+
+	const uint32_t backlight_update_pending_max_retry = 1000;
+
+	uint32_t backlight;
+	uint32_t backlight_period;
+	uint32_t backlight_lock;
+
+	uint32_t i;
+	uint32_t backlight_24bit;
+	uint32_t backlight_17bit;
+	uint32_t backlight_16bit;
+	uint32_t masked_pwm_period;
+	uint8_t rounding_bit;
+	uint8_t bit_count;
+	uint64_t active_duty_cycle;
+
+	backlight = dm_read_reg(ctx, BL_REG(BL_PWM_CNTL));
+	backlight_period = dm_read_reg(ctx, BL_REG(BL_PWM_PERIOD_CNTL));
+	backlight_lock = dm_read_reg(ctx, BL_REG(BL_PWM_GRP1_REG_LOCK));
+
+	/*
+	 * 1. Convert 8-bit value to 17 bit U1.16 format
+	 * (1 integer, 16 fractional bits)
+	 */
+
+	/* 1.1 multiply 8 bit value by 0x10101 to get a 24 bit value,
+	 * effectively multiplying value by 256/255
+	 * eg. for a level of 0xEF, backlight_24bit = 0xEF * 0x10101 = 0xEFEFEF
+	 */
+	backlight_24bit = level * 0x10101;
+
+	/* 1.2 The upper 16 bits of the 24 bit value is the fraction, lower 8
+	 * used for rounding, take most significant bit of fraction for
+	 * rounding, e.g. for 0xEFEFEF, rounding bit is 1
+	 */
+	rounding_bit = (backlight_24bit >> 7) & 1;
+
+	/* 1.3 Add the upper 16 bits of the 24 bit value with the rounding bit
+	 * resulting in a 17 bit value e.g. 0xEFF0 = (0xEFEFEF >> 8) + 1
+	 */
+	backlight_17bit = (backlight_24bit >> 8) + rounding_bit;
+
+	/*
+	 * 2. Find  16 bit backlight active duty cycle, where 0 <= backlight
+	 * active duty cycle <= backlight period
+	 */
+
+	/* 2.1 Apply bitmask for backlight period value based on value of BITCNT
+	 */
+	{
+		uint32_t pwm_period_bitcnt = get_reg_field_value(
+			backlight_period,
+			BL_PWM_PERIOD_CNTL,
+			BL_PWM_PERIOD_BITCNT);
+		if (pwm_period_bitcnt == 0)
+			bit_count = 16;
+		else
+			bit_count = pwm_period_bitcnt;
+	}
+
+	/* e.g. maskedPwmPeriod = 0x24 when bitCount is 6 */
+	masked_pwm_period =
+	get_reg_field_value(
+		backlight_period,
+		BL_PWM_PERIOD_CNTL,
+		BL_PWM_PERIOD) & ((1 << bit_count) - 1);
+
+	/* 2.2 Calculate integer active duty cycle required upper 16 bits
+	 * contain integer component, lower 16 bits contain fractional component
+	 * of active duty cycle e.g. 0x21BDC0 = 0xEFF0 * 0x24
+	 */
+	active_duty_cycle = backlight_17bit * masked_pwm_period;
+
+	/* 2.3 Calculate 16 bit active duty cycle from integer and fractional
+	 * components shift by bitCount then mask 16 bits and add rounding bit
+	 * from MSB of fraction e.g. 0x86F7 = ((0x21BDC0 >> 6) & 0xFFF) + 0
+	 */
+	backlight_16bit = active_duty_cycle >> bit_count;
+	backlight_16bit &= 0xFFFF;
+	backlight_16bit += (active_duty_cycle >> (bit_count - 1)) & 0x1;
+	set_reg_field_value(
+		backlight,
+		backlight_16bit,
+		BL_PWM_CNTL,
+		BL_ACTIVE_INT_FRAC_CNT);
+
+	/*
+	 * 3. Program register with updated value
+	 */
+
+	/* 3.1 Lock group 2 backlight registers */
+	set_reg_field_value(
+		backlight_lock,
+		1,
+		BL_PWM_GRP1_REG_LOCK,
+		BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN);
+	set_reg_field_value(
+		backlight_lock,
+		1,
+		BL_PWM_GRP1_REG_LOCK,
+		BL_PWM_GRP1_REG_LOCK);
+	dm_write_reg(ctx, BL_REG(BL_PWM_GRP1_REG_LOCK), backlight_lock);
+
+	/* 3.2 Write new active duty cycle */
+	dm_write_reg(ctx, BL_REG(BL_PWM_CNTL), backlight);
+
+	/* 3.3 Unlock group 2 backlight registers */
+	set_reg_field_value(
+		backlight_lock,
+		0,
+		BL_PWM_GRP1_REG_LOCK,
+		BL_PWM_GRP1_REG_LOCK);
+	dm_write_reg(ctx, BL_REG(BL_PWM_GRP1_REG_LOCK), backlight_lock);
+
+	/* 5.4.4 Wait for pending bit to be cleared */
+	for (i = 0; i < backlight_update_pending_max_retry; ++i) {
+		backlight_lock = dm_read_reg(ctx, BL_REG(BL_PWM_GRP1_REG_LOCK));
+		if (!get_reg_field_value(
+			backlight_lock,
+			BL_PWM_GRP1_REG_LOCK,
+			BL_PWM_GRP1_REG_UPDATE_PENDING))
+			break;
+
+		dm_delay_in_microseconds(ctx, 10);
+	}
+}
+
+void dce110_link_encoder_connect_dig_be_to_fe(
+	struct link_encoder *enc,
+	enum engine_id engine,
+	bool connect)
+{
+	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t value;
+	uint32_t field;
+
+	if (engine != ENGINE_ID_UNKNOWN) {
+		addr = LINK_REG(DIG_BE_CNTL);
+		value = dm_read_reg(ctx, addr);
+
+		field = get_reg_field_value(
+				value,
+				DIG_BE_CNTL,
+				DIG_FE_SOURCE_SELECT);
+
+		if (connect)
+			field |= get_frontend_source(engine);
+		else
+			field &= ~get_frontend_source(engine);
+
+		set_reg_field_value(
+			value,
+			field,
+			DIG_BE_CNTL,
+			DIG_FE_SOURCE_SELECT);
+		dm_write_reg(ctx, addr, value);
+	}
+}
+
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h
new file mode 100644
index 000000000000..46e297137b19
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_link_encoder.h
@@ -0,0 +1,156 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ *  and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef __DC_LINK_ENCODER__DCE110_H__
+#define __DC_LINK_ENCODER__DCE110_H__
+
+#include "inc/link_encoder.h"
+
+#define TO_DCE110_LINK_ENC(link_encoder)\
+	container_of(link_encoder, struct dce110_link_encoder, base)
+
+struct dce110_link_enc_bl_registers {
+	uint32_t BL_PWM_CNTL;
+	uint32_t BL_PWM_GRP1_REG_LOCK;
+	uint32_t BL_PWM_PERIOD_CNTL;
+	uint32_t LVTMA_PWRSEQ_CNTL;
+	uint32_t LVTMA_PWRSEQ_STATE;
+};
+
+struct dce110_link_enc_aux_registers {
+	uint32_t AUX_CONTROL;
+	uint32_t AUX_DPHY_RX_CONTROL0;
+};
+
+struct dce110_link_enc_registers {
+	uint32_t DIG_BE_CNTL;
+	uint32_t DIG_BE_EN_CNTL;
+	uint32_t DP_CONFIG;
+	uint32_t DP_DPHY_CNTL;
+	uint32_t DP_DPHY_INTERNAL_CTRL;
+	uint32_t DP_DPHY_PRBS_CNTL;
+	uint32_t DP_DPHY_SYM0;
+	uint32_t DP_DPHY_SYM1;
+	uint32_t DP_DPHY_SYM2;
+	uint32_t DP_DPHY_TRAINING_PATTERN_SEL;
+	uint32_t DP_LINK_CNTL;
+	uint32_t DP_LINK_FRAMING_CNTL;
+	uint32_t DP_MSE_SAT0;
+	uint32_t DP_MSE_SAT1;
+	uint32_t DP_MSE_SAT2;
+	uint32_t DP_MSE_SAT_UPDATE;
+	uint32_t DP_SEC_CNTL;
+	uint32_t DP_VID_STREAM_CNTL;
+};
+
+struct dce110_link_encoder {
+	struct link_encoder base;
+	const struct dce110_link_enc_registers *link_regs;
+	const struct dce110_link_enc_aux_registers *aux_regs;
+	const struct dce110_link_enc_bl_registers *bl_regs;
+};
+
+bool dce110_link_encoder_construct(
+	struct dce110_link_encoder *enc110,
+	const struct encoder_init_data *init_data,
+	const struct dce110_link_enc_registers *link_regs,
+	const struct dce110_link_enc_aux_registers *aux_regs,
+	const struct dce110_link_enc_bl_registers *bl_regs);
+
+bool dce110_link_encoder_validate_output_with_stream(
+	struct link_encoder *enc,
+	struct core_stream *stream);
+
+/****************** HW programming ************************/
+
+/* initialize HW */  /* why do we initialze aux in here? */
+void dce110_link_encoder_hw_init(struct link_encoder *enc);
+
+/* program DIG_MODE in DIG_BE */
+/* TODO can this be combined with enable_output? */
+void dce110_link_encoder_setup(
+	struct link_encoder *enc,
+	enum signal_type signal);
+
+/* enables TMDS PHY output */
+/* TODO: still need depth or just pass in adjusted pixel clock? */
+void dce110_link_encoder_enable_tmds_output(
+	struct link_encoder *enc,
+	enum clock_source_id clock_source,
+	enum dc_color_depth color_depth,
+	bool hdmi,
+	bool dual_link,
+	uint32_t pixel_clock);
+
+/* enables DP PHY output */
+void dce110_link_encoder_enable_dp_output(
+	struct link_encoder *enc,
+	const struct link_settings *link_settings,
+	enum clock_source_id clock_source);
+
+/* enables DP PHY output in MST mode */
+void dce110_link_encoder_enable_dp_mst_output(
+	struct link_encoder *enc,
+	const struct link_settings *link_settings,
+	enum clock_source_id clock_source);
+
+/* disable PHY output */
+void dce110_link_encoder_disable_output(
+	struct link_encoder *link_enc,
+	enum signal_type signal);
+
+/* set DP lane settings */
+void dce110_link_encoder_dp_set_lane_settings(
+	struct link_encoder *enc,
+	const struct link_training_settings *link_settings);
+
+void dce110_link_encoder_dp_set_phy_pattern(
+	struct link_encoder *enc,
+	const struct encoder_set_dp_phy_pattern_param *param);
+
+/* programs DP MST VC payload allocation */
+void dce110_link_encoder_update_mst_stream_allocation_table(
+	struct link_encoder *enc,
+	const struct link_mst_stream_allocation_table *table);
+
+void dce110_link_encoder_set_lcd_backlight_level(
+	struct link_encoder *enc,
+	uint32_t level);
+
+void dce110_link_encoder_edp_backlight_control(
+	struct link_encoder *enc,
+	bool enable);
+
+void dce110_link_encoder_edp_power_control(
+	struct link_encoder *enc,
+	bool power_up);
+
+void dce110_link_encoder_connect_dig_be_to_fe(
+	struct link_encoder *enc,
+	enum engine_id engine,
+	bool connect);
+
+
+#endif /* __DC_LINK_ENCODER__DCE110_H__ */
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c
new file mode 100644
index 000000000000..210730972c08
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.c
@@ -0,0 +1,1123 @@ 
+/*
+ * 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_bios_types.h"
+#include "dce110_stream_encoder.h"
+
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+#include "dce/dce_11_0_enum.h"
+
+#define LINK_REG(reg)\
+	(enc110->regs->reg)
+
+#define VBI_LINE_0 0
+#define DP_BLANK_MAX_RETRY 20
+#define HDMI_CLOCK_CHANNEL_RATE_MORE_340M 340000
+
+#ifndef HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN_MASK
+	#define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN_MASK 0x2
+	#define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN__SHIFT 0x1
+#endif
+
+enum {
+	DP_MST_UPDATE_MAX_RETRY = 50
+};
+
+static struct stream_encoder_funcs dce110_str_enc_funcs = {
+	.dp_set_stream_attribute =
+		dce110_stream_encoder_dp_set_stream_attribute,
+	.hdmi_set_stream_attribute =
+		dce110_stream_encoder_hdmi_set_stream_attribute,
+	.dvi_set_stream_attribute =
+		dce110_stream_encoder_dvi_set_stream_attribute,
+	.set_mst_bandwidth =
+		dce110_stream_encoder_set_mst_bandwidth,
+	.update_hdmi_info_packets =
+		dce110_stream_encoder_update_hdmi_info_packets,
+	.stop_hdmi_info_packets =
+		dce110_stream_encoder_stop_hdmi_info_packets,
+	.update_dp_info_packets =
+		dce110_stream_encoder_update_dp_info_packets,
+	.stop_dp_info_packets =
+		dce110_stream_encoder_stop_dp_info_packets,
+	.dp_blank =
+		dce110_stream_encoder_dp_blank,
+	.dp_unblank =
+		dce110_stream_encoder_dp_unblank,
+};
+
+static void dce110_update_generic_info_packet(
+	struct dce110_stream_encoder *enc110,
+	uint32_t packet_index,
+	const struct encoder_info_packet *info_packet)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t regval;
+	/* choose which generic packet to use */
+	{
+		addr = LINK_REG(AFMT_VBI_PACKET_CONTROL);
+
+		regval = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(
+			regval,
+			packet_index,
+			AFMT_VBI_PACKET_CONTROL,
+			AFMT_GENERIC_INDEX);
+
+		dm_write_reg(ctx, addr, regval);
+	}
+
+	/* write generic packet header
+	 * (4th byte is for GENERIC0 only) */
+	{
+		addr = LINK_REG(AFMT_GENERIC_HDR);
+
+		regval = 0;
+
+		set_reg_field_value(
+			regval,
+			info_packet->hb0,
+			AFMT_GENERIC_HDR,
+			AFMT_GENERIC_HB0);
+
+		set_reg_field_value(
+			regval,
+			info_packet->hb1,
+			AFMT_GENERIC_HDR,
+			AFMT_GENERIC_HB1);
+
+		set_reg_field_value(
+			regval,
+			info_packet->hb2,
+			AFMT_GENERIC_HDR,
+			AFMT_GENERIC_HB2);
+
+		set_reg_field_value(
+			regval,
+			info_packet->hb3,
+			AFMT_GENERIC_HDR,
+			AFMT_GENERIC_HB3);
+
+		dm_write_reg(ctx, addr, regval);
+	}
+
+	/* write generic packet contents
+	 * (we never use last 4 bytes)
+	 * there are 8 (0-7) mmDIG0_AFMT_GENERIC0_x registers */
+	{
+		const uint32_t *content =
+			(const uint32_t *) &info_packet->sb[0];
+
+		uint32_t counter = 0;
+
+		addr = LINK_REG(AFMT_GENERIC_0);
+
+		do {
+			dm_write_reg(ctx, addr++, *content++);
+
+			++counter;
+		} while (counter < 7);
+	}
+
+	addr = LINK_REG(AFMT_GENERIC_7);
+
+	dm_write_reg(
+		ctx,
+		addr,
+		0);
+
+	/* force double-buffered packet update */
+	{
+		addr = LINK_REG(AFMT_VBI_PACKET_CONTROL);
+
+		regval = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(
+			regval,
+			(packet_index == 0),
+			AFMT_VBI_PACKET_CONTROL,
+			AFMT_GENERIC0_UPDATE);
+
+		set_reg_field_value(
+			regval,
+			(packet_index == 2),
+			AFMT_VBI_PACKET_CONTROL,
+			AFMT_GENERIC2_UPDATE);
+
+		dm_write_reg(ctx, addr, regval);
+	}
+}
+
+static void dce110_update_hdmi_info_packet(
+	struct dce110_stream_encoder *enc110,
+	uint32_t packet_index,
+	const struct encoder_info_packet *info_packet)
+{
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t cont, send, line;
+	uint32_t addr;
+	uint32_t regval;
+
+	if (info_packet->valid) {
+		dce110_update_generic_info_packet(
+			enc110,
+			packet_index,
+			info_packet);
+
+		/* enable transmission of packet(s) -
+		 * packet transmission begins on the next frame */
+		cont = 1;
+		/* send packet(s) every frame */
+		send = 1;
+		/* select line number to send packets on */
+		line = 2;
+	} else {
+		cont = 0;
+		send = 0;
+		line = 0;
+	}
+
+	/* choose which generic packet control to use */
+
+	switch (packet_index) {
+	case 0:
+	case 1:
+		addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL0);
+		break;
+	case 2:
+	case 3:
+		addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL1);
+		break;
+	default:
+		/* invalid HW packet index */
+		dal_logger_write(
+			ctx->logger,
+			LOG_MAJOR_WARNING,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"Invalid HW packet index: %s()\n",
+			__func__);
+		return;
+	}
+
+	regval = dm_read_reg(ctx, addr);
+
+	switch (packet_index) {
+	case 0:
+	case 2:
+		set_reg_field_value(
+			regval,
+			cont,
+			HDMI_GENERIC_PACKET_CONTROL0,
+			HDMI_GENERIC0_CONT);
+		set_reg_field_value(
+			regval,
+			send,
+			HDMI_GENERIC_PACKET_CONTROL0,
+			HDMI_GENERIC0_SEND);
+		set_reg_field_value(
+			regval,
+			line,
+			HDMI_GENERIC_PACKET_CONTROL0,
+			HDMI_GENERIC0_LINE);
+		break;
+	case 1:
+	case 3:
+		set_reg_field_value(
+			regval,
+			cont,
+			HDMI_GENERIC_PACKET_CONTROL0,
+			HDMI_GENERIC1_CONT);
+		set_reg_field_value(
+			regval,
+			send,
+			HDMI_GENERIC_PACKET_CONTROL0,
+			HDMI_GENERIC1_SEND);
+		set_reg_field_value(
+			regval,
+			line,
+			HDMI_GENERIC_PACKET_CONTROL0,
+			HDMI_GENERIC1_LINE);
+		break;
+	default:
+		/* invalid HW packet index */
+		dal_logger_write(
+			ctx->logger,
+			LOG_MAJOR_WARNING,
+			LOG_MINOR_COMPONENT_ENCODER,
+			"Invalid HW packet index: %s()\n",
+			__func__);
+		return;
+	}
+
+	dm_write_reg(ctx, addr, regval);
+}
+
+bool dce110_stream_encoder_construct(
+	struct dce110_stream_encoder *enc110,
+	struct dc_context *ctx,
+	struct dc_bios *bp,
+	enum engine_id eng_id,
+	const struct dce110_stream_enc_registers *regs)
+{
+	if (!enc110)
+		return false;
+	if (!bp)
+		return false;
+
+	enc110->base.funcs = &dce110_str_enc_funcs;
+	enc110->base.ctx = ctx;
+	enc110->base.id = eng_id;
+	enc110->base.bp = bp;
+	enc110->regs = regs;
+
+	return true;
+}
+
+/* setup stream encoder in dp mode */
+void dce110_stream_encoder_dp_set_stream_attribute(
+	struct stream_encoder *enc,
+	struct dc_crtc_timing *crtc_timing)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	const uint32_t addr = LINK_REG(DP_PIXEL_FORMAT);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	/* set pixel encoding */
+	switch (crtc_timing->pixel_encoding) {
+	case PIXEL_ENCODING_YCBCR422:
+		set_reg_field_value(
+			value,
+			DP_PIXEL_ENCODING_YCBCR422,
+			DP_PIXEL_FORMAT,
+			DP_PIXEL_ENCODING);
+		break;
+	case PIXEL_ENCODING_YCBCR444:
+		set_reg_field_value(
+			value,
+			DP_PIXEL_ENCODING_YCBCR444,
+			DP_PIXEL_FORMAT,
+			DP_PIXEL_ENCODING);
+
+		if (crtc_timing->flags.Y_ONLY)
+			if (crtc_timing->display_color_depth != COLOR_DEPTH_666)
+				/* HW testing only, no use case yet.
+				 * Color depth of Y-only could be
+				 * 8, 10, 12, 16 bits */
+				set_reg_field_value(
+					value,
+					DP_PIXEL_ENCODING_Y_ONLY,
+					DP_PIXEL_FORMAT,
+					DP_PIXEL_ENCODING);
+		/* Note: DP_MSA_MISC1 bit 7 is the indicator
+		 * of Y-only mode.
+		 * This bit is set in HW if register
+		 * DP_PIXEL_ENCODING is programmed to 0x4 */
+		break;
+	default:
+		set_reg_field_value(
+			value,
+			DP_PIXEL_ENCODING_RGB444,
+			DP_PIXEL_FORMAT,
+			DP_PIXEL_ENCODING);
+		break;
+	}
+
+	/* set color depth */
+
+	switch (crtc_timing->display_color_depth) {
+	case COLOR_DEPTH_888:
+		set_reg_field_value(
+			value,
+			DP_COMPONENT_DEPTH_8BPC,
+			DP_PIXEL_FORMAT,
+			DP_COMPONENT_DEPTH);
+		break;
+	case COLOR_DEPTH_101010:
+		set_reg_field_value(
+			value,
+			DP_COMPONENT_DEPTH_10BPC,
+			DP_PIXEL_FORMAT,
+			DP_COMPONENT_DEPTH);
+		break;
+	case COLOR_DEPTH_121212:
+		set_reg_field_value(
+			value,
+			DP_COMPONENT_DEPTH_12BPC,
+			DP_PIXEL_FORMAT,
+			DP_COMPONENT_DEPTH);
+		break;
+	default:
+		set_reg_field_value(
+			value,
+			DP_COMPONENT_DEPTH_6BPC,
+			DP_PIXEL_FORMAT,
+			DP_COMPONENT_DEPTH);
+		break;
+	}
+
+	/* set dynamic range and YCbCr range */
+	set_reg_field_value(value, 0, DP_PIXEL_FORMAT, DP_DYN_RANGE);
+	set_reg_field_value(value, 0, DP_PIXEL_FORMAT, DP_YCBCR_RANGE);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+/* setup stream encoder in hdmi mode */
+void dce110_stream_encoder_hdmi_set_stream_attribute(
+	struct stream_encoder *enc,
+	struct dc_crtc_timing *crtc_timing,
+	bool enable_audio)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t output_pixel_clock = crtc_timing->pix_clk_khz;
+	uint32_t value;
+	uint32_t addr;
+	struct bp_encoder_control cntl = {0};
+
+	cntl.action = ENCODER_CONTROL_SETUP;
+	cntl.engine_id = enc110->base.id;
+	cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A;
+	cntl.enable_dp_audio = enable_audio;
+	cntl.pixel_clock = crtc_timing->pix_clk_khz;
+	cntl.lanes_number = LANE_COUNT_FOUR;
+	cntl.color_depth = crtc_timing->display_color_depth;
+
+	if (enc110->base.bp->funcs->encoder_control(
+			enc110->base.bp, &cntl) != BP_RESULT_OK)
+		return;
+
+	addr = LINK_REG(DIG_FE_CNTL);
+	value = dm_read_reg(ctx, addr);
+
+	switch (crtc_timing->pixel_encoding) {
+	case PIXEL_ENCODING_YCBCR422:
+		set_reg_field_value(value, 1, DIG_FE_CNTL, TMDS_PIXEL_ENCODING);
+		break;
+	default:
+		set_reg_field_value(value, 0, DIG_FE_CNTL, TMDS_PIXEL_ENCODING);
+		break;
+	}
+	set_reg_field_value(value, 0, DIG_FE_CNTL, TMDS_COLOR_FORMAT);
+	dm_write_reg(ctx, addr, value);
+
+	/* setup HDMI engine */
+	addr = LINK_REG(HDMI_CONTROL);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 1, HDMI_CONTROL, HDMI_PACKET_GEN_VERSION);
+	set_reg_field_value(value, 1, HDMI_CONTROL, HDMI_KEEPOUT_MODE);
+	set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE);
+	set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN);
+	set_reg_field_value(value, 0, HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE);
+
+	switch (crtc_timing->display_color_depth) {
+	case COLOR_DEPTH_888:
+		set_reg_field_value(
+			value,
+			0,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_DEPTH);
+		break;
+	case COLOR_DEPTH_101010:
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_DEPTH);
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_ENABLE);
+		output_pixel_clock = (crtc_timing->pix_clk_khz * 30) / 24;
+		break;
+	case COLOR_DEPTH_121212:
+		set_reg_field_value(
+			value,
+			2,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_DEPTH);
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_ENABLE);
+		output_pixel_clock = (crtc_timing->pix_clk_khz * 36) / 24;
+		break;
+	case COLOR_DEPTH_161616:
+		set_reg_field_value(
+			value,
+			3,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_DEPTH);
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_DEEP_COLOR_ENABLE);
+		output_pixel_clock = (crtc_timing->pix_clk_khz * 48) / 24;
+		break;
+	default:
+		break;
+	}
+
+	if (output_pixel_clock >= HDMI_CLOCK_CHANNEL_RATE_MORE_340M) {
+		/* enable HDMI data scrambler */
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_DATA_SCRAMBLE_EN);
+
+		/* HDMI_CLOCK_CHANNEL_RATE_MORE_340M
+		 * Clock channel frequency is 1/4 of character rate.*/
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_CLOCK_CHANNEL_RATE);
+	} else if (crtc_timing->flags.LTE_340MCSC_SCRAMBLE) {
+
+		/* TODO: New feature for DCE11, still need to implement */
+
+		/* enable HDMI data scrambler */
+		set_reg_field_value(
+			value,
+			1,
+			HDMI_CONTROL,
+			HDMI_DATA_SCRAMBLE_EN);
+
+		/* HDMI_CLOCK_CHANNEL_FREQ_EQUAL_TO_CHAR_RATE
+		 * Clock channel frequency is the same
+		 * as character rate */
+		set_reg_field_value(
+			value,
+			0,
+			HDMI_CONTROL,
+			HDMI_CLOCK_CHANNEL_RATE);
+	}
+
+	dm_write_reg(ctx, addr, value);
+
+	addr = LINK_REG(HDMI_VBI_PACKET_CONTROL);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT);
+	set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND);
+	set_reg_field_value(value, 1, HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND);
+
+	dm_write_reg(ctx, addr, value);
+
+	/* following belongs to audio */
+	addr = LINK_REG(HDMI_INFOFRAME_CONTROL0);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(
+		value,
+		1,
+		HDMI_INFOFRAME_CONTROL0,
+		HDMI_AUDIO_INFO_SEND);
+	dm_write_reg(ctx, addr, value);
+
+	addr = LINK_REG(AFMT_INFOFRAME_CONTROL0);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(
+		value,
+		1,
+		AFMT_INFOFRAME_CONTROL0,
+		AFMT_AUDIO_INFO_UPDATE);
+	dm_write_reg(ctx, addr, value);
+
+	addr = LINK_REG(HDMI_INFOFRAME_CONTROL1);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(
+		value,
+		VBI_LINE_0 + 2,
+		HDMI_INFOFRAME_CONTROL1,
+		HDMI_AUDIO_INFO_LINE);
+	dm_write_reg(ctx, addr, value);
+
+	addr = LINK_REG(HDMI_GC);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 0, HDMI_GC, HDMI_GC_AVMUTE);
+	dm_write_reg(ctx, addr, value);
+}
+
+/* setup stream encoder in dvi mode */
+void dce110_stream_encoder_dvi_set_stream_attribute(
+	struct stream_encoder *enc,
+	struct dc_crtc_timing *crtc_timing,
+	bool is_dual_link)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr = LINK_REG(DIG_FE_CNTL);
+	uint32_t value = dm_read_reg(ctx, addr);
+	struct bp_encoder_control cntl = {0};
+
+	cntl.action = ENCODER_CONTROL_SETUP;
+	cntl.engine_id = enc110->base.id;
+	cntl.signal = is_dual_link ?
+		SIGNAL_TYPE_DVI_DUAL_LINK :
+		SIGNAL_TYPE_DVI_SINGLE_LINK;
+	cntl.enable_dp_audio = false;
+	cntl.pixel_clock = crtc_timing->pix_clk_khz;
+	cntl.lanes_number = (is_dual_link) ?
+				LANE_COUNT_EIGHT : LANE_COUNT_FOUR;
+	cntl.color_depth = crtc_timing->display_color_depth;
+
+	if (enc110->base.bp->funcs->encoder_control(
+			enc110->base.bp, &cntl) != BP_RESULT_OK)
+		return;
+
+	switch (crtc_timing->pixel_encoding) {
+	case PIXEL_ENCODING_YCBCR422:
+		set_reg_field_value(value, 1, DIG_FE_CNTL, TMDS_PIXEL_ENCODING);
+		break;
+	default:
+		set_reg_field_value(value, 0, DIG_FE_CNTL, TMDS_PIXEL_ENCODING);
+		break;
+	}
+
+	switch (crtc_timing->display_color_depth) {
+	case COLOR_DEPTH_101010:
+		if (crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB)
+			set_reg_field_value(
+				value,
+				2,
+				DIG_FE_CNTL,
+				TMDS_COLOR_FORMAT);
+		else
+			set_reg_field_value(
+				value,
+				0,
+				DIG_FE_CNTL,
+				TMDS_COLOR_FORMAT);
+		break;
+	default:
+		set_reg_field_value(value, 0, DIG_FE_CNTL, TMDS_COLOR_FORMAT);
+		break;
+	}
+	dm_write_reg(ctx, addr, value);
+}
+
+void dce110_stream_encoder_set_mst_bandwidth(
+	struct stream_encoder *enc,
+	struct fixed31_32 avg_time_slots_per_mtp)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t field;
+	uint32_t value;
+	uint32_t retries = 0;
+	uint32_t x = dal_fixed31_32_floor(
+		avg_time_slots_per_mtp);
+	uint32_t y = dal_fixed31_32_ceil(
+		dal_fixed31_32_shl(
+			dal_fixed31_32_sub_int(
+				avg_time_slots_per_mtp,
+				x),
+			26));
+
+	{
+		addr = LINK_REG(DP_MSE_RATE_CNTL);
+		value = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(
+			value,
+			x,
+			DP_MSE_RATE_CNTL,
+			DP_MSE_RATE_X);
+
+		set_reg_field_value(
+			value,
+			y,
+			DP_MSE_RATE_CNTL,
+			DP_MSE_RATE_Y);
+
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* wait for update to be completed on the link */
+	/* i.e. DP_MSE_RATE_UPDATE_PENDING field (read only) */
+	/* is reset to 0 (not pending) */
+	{
+		addr = LINK_REG(DP_MSE_RATE_UPDATE);
+
+		do {
+			value = dm_read_reg(ctx, addr);
+
+			field = get_reg_field_value(
+					value,
+					DP_MSE_RATE_UPDATE,
+					DP_MSE_RATE_UPDATE_PENDING);
+
+			if (!(field &
+			DP_MSE_RATE_UPDATE__DP_MSE_RATE_UPDATE_PENDING_MASK))
+				break;
+
+			dm_delay_in_microseconds(ctx, 10);
+
+			++retries;
+		} while (retries < DP_MST_UPDATE_MAX_RETRY);
+	}
+}
+
+void dce110_stream_encoder_update_hdmi_info_packets(
+	struct stream_encoder *enc,
+	const struct encoder_info_frame *info_frame)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t regval;
+	uint32_t addr;
+	uint32_t control0val;
+	uint32_t control1val;
+
+	if (info_frame->avi.valid) {
+		const uint32_t *content =
+			(const uint32_t *) &info_frame->avi.sb[0];
+
+		addr = LINK_REG(AFMT_AVI_INFO0);
+		regval = content[0];
+		dm_write_reg(
+			ctx,
+			addr,
+			regval);
+		regval = content[1];
+
+		addr = LINK_REG(AFMT_AVI_INFO1);
+		dm_write_reg(
+			ctx,
+			addr,
+			regval);
+		regval = content[2];
+
+		addr = LINK_REG(AFMT_AVI_INFO2);
+		dm_write_reg(
+			ctx,
+			addr,
+			regval);
+		regval = content[3];
+
+		/* move version to AVI_INFO3 */
+		addr = LINK_REG(AFMT_AVI_INFO3);
+		set_reg_field_value(
+			regval,
+			info_frame->avi.hb1,
+			AFMT_AVI_INFO3,
+			AFMT_AVI_INFO_VERSION);
+
+		dm_write_reg(
+			ctx,
+			addr,
+			regval);
+
+		addr = LINK_REG(HDMI_INFOFRAME_CONTROL0);
+
+		control0val = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(
+			control0val,
+			1,
+			HDMI_INFOFRAME_CONTROL0,
+			HDMI_AVI_INFO_SEND);
+
+		set_reg_field_value(
+			control0val,
+			1,
+			HDMI_INFOFRAME_CONTROL0,
+			HDMI_AVI_INFO_CONT);
+
+		dm_write_reg(ctx, addr, control0val);
+
+		addr = LINK_REG(HDMI_INFOFRAME_CONTROL1);
+
+		control1val = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(
+			control1val,
+			VBI_LINE_0 + 2,
+			HDMI_INFOFRAME_CONTROL1,
+			HDMI_AVI_INFO_LINE);
+
+		dm_write_reg(ctx, addr, control1val);
+	} else {
+		addr = LINK_REG(HDMI_INFOFRAME_CONTROL0);
+
+		regval = dm_read_reg(ctx, addr);
+
+		set_reg_field_value(
+			regval,
+			0,
+			HDMI_INFOFRAME_CONTROL0,
+			HDMI_AVI_INFO_SEND);
+
+		set_reg_field_value(
+			regval,
+			0,
+			HDMI_INFOFRAME_CONTROL0,
+			HDMI_AVI_INFO_CONT);
+
+		dm_write_reg(ctx, addr, regval);
+	}
+
+	dce110_update_hdmi_info_packet(enc110, 0, &info_frame->vendor);
+	dce110_update_hdmi_info_packet(enc110, 1, &info_frame->gamut);
+	dce110_update_hdmi_info_packet(enc110, 2, &info_frame->spd);
+}
+
+void dce110_stream_encoder_stop_hdmi_info_packets(
+	struct stream_encoder *enc)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr = 0;
+	uint32_t value = 0;
+
+	/* stop generic packets 0 & 1 on HDMI */
+	addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL0);
+
+	value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL0,
+		HDMI_GENERIC1_CONT);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL0,
+		HDMI_GENERIC1_LINE);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL0,
+		HDMI_GENERIC1_SEND);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL0,
+		HDMI_GENERIC0_CONT);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL0,
+		HDMI_GENERIC0_LINE);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL0,
+		HDMI_GENERIC0_SEND);
+
+	dm_write_reg(ctx, addr, value);
+
+	/* stop generic packets 2 & 3 on HDMI */
+	addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL1);
+
+	value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL1,
+		HDMI_GENERIC2_CONT);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL1,
+		HDMI_GENERIC2_LINE);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL1,
+		HDMI_GENERIC2_SEND);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL1,
+		HDMI_GENERIC3_CONT);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL1,
+		HDMI_GENERIC3_LINE);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_GENERIC_PACKET_CONTROL1,
+		HDMI_GENERIC3_SEND);
+
+	dm_write_reg(ctx, addr, value);
+
+	/* stop AVI packet on HDMI */
+	addr = LINK_REG(HDMI_INFOFRAME_CONTROL0);
+
+	value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_INFOFRAME_CONTROL0,
+		HDMI_AVI_INFO_SEND);
+	set_reg_field_value(
+		value,
+		0,
+		HDMI_INFOFRAME_CONTROL0,
+		HDMI_AVI_INFO_CONT);
+
+	dm_write_reg(ctx, addr, value);
+}
+void dce110_stream_encoder_update_dp_info_packets(
+	struct stream_encoder *enc,
+	const struct encoder_info_frame *info_frame)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr = LINK_REG(DP_SEC_CNTL);
+	uint32_t value;
+
+	if (info_frame->vsc.valid)
+		dce110_update_generic_info_packet(
+			enc110,
+			0,
+			&info_frame->vsc);
+
+	/* enable/disable transmission of packet(s).
+	*  If enabled, packet transmission begins on the next frame
+	*/
+
+	value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(
+		value,
+		info_frame->vsc.valid,
+		DP_SEC_CNTL,
+		DP_SEC_GSP0_ENABLE);
+	/* This bit is the master enable bit.
+	* When enabling secondary stream engine,
+	* this master bit must also be set.
+	* This register shared with audio info frame.
+	* Therefore we need to enable master bit
+	* if at least on of the fields is not 0
+	*/
+	if (value)
+		set_reg_field_value(
+			value,
+			1,
+			DP_SEC_CNTL,
+			DP_SEC_STREAM_ENABLE);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+void dce110_stream_encoder_stop_dp_info_packets(
+	struct stream_encoder *enc)
+{
+	/* stop generic packets on DP */
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr = LINK_REG(DP_SEC_CNTL);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP0_ENABLE);
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP1_ENABLE);
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP2_ENABLE);
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_GSP3_ENABLE);
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_AVI_ENABLE);
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_MPG_ENABLE);
+	set_reg_field_value(value, 0, DP_SEC_CNTL, DP_SEC_STREAM_ENABLE);
+
+	/* this register shared with audio info frame.
+	 * therefore we need to keep master enabled
+	 * if at least one of the fields is not 0 */
+
+	if (value)
+		set_reg_field_value(
+			value,
+			1,
+			DP_SEC_CNTL,
+			DP_SEC_STREAM_ENABLE);
+
+	dm_write_reg(ctx, addr, value);
+}
+
+void dce110_stream_encoder_dp_blank(
+	struct stream_encoder *enc)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr = LINK_REG(DP_VID_STREAM_CNTL);
+	uint32_t value = dm_read_reg(ctx, addr);
+	uint32_t retries = 0;
+	uint32_t max_retries = DP_BLANK_MAX_RETRY * 10;
+
+	/* Note: For CZ, we are changing driver default to disable
+	 * stream deferred to next VBLANK. If results are positive, we
+	 * will make the same change to all DCE versions. There are a
+	 * handful of panels that cannot handle disable stream at
+	 * HBLANK and will result in a white line flash across the
+	 * screen on stream disable. */
+
+	/* Specify the video stream disable point
+	 * (2 = start of the next vertical blank) */
+	set_reg_field_value(
+		value,
+		2,
+		DP_VID_STREAM_CNTL,
+		DP_VID_STREAM_DIS_DEFER);
+	/* Larger delay to wait until VBLANK - use max retry of
+	* 10us*3000=30ms. This covers 16.6ms of typical 60 Hz mode +
+	* a little more because we may not trust delay accuracy.
+	*/
+	max_retries = DP_BLANK_MAX_RETRY * 150;
+
+	/* disable DP stream */
+	set_reg_field_value(value, 0, DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE);
+	dm_write_reg(ctx, addr, value);
+
+	/* the encoder stops sending the video stream
+	* at the start of the vertical blanking.
+	* Poll for DP_VID_STREAM_STATUS == 0
+	*/
+
+	do {
+		value = dm_read_reg(ctx, addr);
+
+		if (!get_reg_field_value(
+			value,
+			DP_VID_STREAM_CNTL,
+			DP_VID_STREAM_STATUS))
+			break;
+
+		dm_delay_in_microseconds(ctx, 10);
+
+		++retries;
+	} while (retries < max_retries);
+
+	ASSERT(retries <= max_retries);
+
+	/* Tell the DP encoder to ignore timing from CRTC, must be done after
+	* the polling. If we set DP_STEER_FIFO_RESET before DP stream blank is
+	* complete, stream status will be stuck in video stream enabled state,
+	* i.e. DP_VID_STREAM_STATUS stuck at 1.
+	*/
+	addr = LINK_REG(DP_STEER_FIFO);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, true, DP_STEER_FIFO, DP_STEER_FIFO_RESET);
+	dm_write_reg(ctx, addr, value);
+}
+
+/* output video stream to link encoder */
+void dce110_stream_encoder_dp_unblank(
+	struct stream_encoder *enc,
+	const struct encoder_unblank_param *param)
+{
+	struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc);
+	struct dc_context *ctx = enc110->base.ctx;
+	uint32_t addr;
+	uint32_t value;
+
+	if (param->link_settings.link_rate != LINK_RATE_UNKNOWN) {
+		uint32_t n_vid = 0x8000;
+		uint32_t m_vid;
+
+		/* M / N = Fstream / Flink
+		* m_vid / n_vid = pixel rate / link rate
+		*/
+
+		uint64_t m_vid_l = n_vid;
+
+		m_vid_l *= param->crtc_timing.pixel_clock;
+		m_vid_l = div_u64(m_vid_l,
+			param->link_settings.link_rate
+				* LINK_RATE_REF_FREQ_IN_KHZ);
+
+		m_vid = (uint32_t) m_vid_l;
+
+		/* enable auto measurement */
+		addr = LINK_REG(DP_VID_TIMING);
+		value = dm_read_reg(ctx, addr);
+		set_reg_field_value(value, 0, DP_VID_TIMING, DP_VID_M_N_GEN_EN);
+		dm_write_reg(ctx, addr, value);
+
+		/* auto measurement need 1 full 0x8000 symbol cycle to kick in,
+		* therefore program initial value for Mvid and Nvid
+		*/
+		addr = LINK_REG(DP_VID_N);
+		value = dm_read_reg(ctx, addr);
+		set_reg_field_value(value, n_vid, DP_VID_N, DP_VID_N);
+		dm_write_reg(ctx, addr, value);
+
+		addr = LINK_REG(DP_VID_M);
+		value = dm_read_reg(ctx, addr);
+		set_reg_field_value(value, m_vid, DP_VID_M, DP_VID_M);
+		dm_write_reg(ctx, addr, value);
+
+		addr = LINK_REG(DP_VID_TIMING);
+		value = dm_read_reg(ctx, addr);
+		set_reg_field_value(value, 1, DP_VID_TIMING, DP_VID_M_N_GEN_EN);
+		dm_write_reg(ctx, addr, value);
+	}
+
+	/* set DIG_START to 0x1 to resync FIFO */
+	addr = LINK_REG(DIG_FE_CNTL);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 1, DIG_FE_CNTL, DIG_START);
+	dm_write_reg(ctx, addr, value);
+
+	/* switch DP encoder to CRTC data */
+	addr = LINK_REG(DP_STEER_FIFO);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(value, 0, DP_STEER_FIFO, DP_STEER_FIFO_RESET);
+	dm_write_reg(ctx, addr, value);
+
+	/* wait 100us for DIG/DP logic to prime
+	* (i.e. a few video lines)
+	*/
+	dm_delay_in_microseconds(ctx, 100);
+
+	/* the hardware would start sending video at the start of the next DP
+	* frame (i.e. rising edge of the vblank).
+	* NOTE: We used to program DP_VID_STREAM_DIS_DEFER = 2 here, but this
+	* register has no effect on enable transition! HW always guarantees
+	* VID_STREAM enable at start of next frame, and this is not
+	* programmable
+	*/
+	addr = LINK_REG(DP_VID_STREAM_CNTL);
+	value = dm_read_reg(ctx, addr);
+	set_reg_field_value(
+		value,
+		true,
+		DP_VID_STREAM_CNTL,
+		DP_VID_STREAM_ENABLE);
+	dm_write_reg(ctx, addr, value);
+}
+
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h
new file mode 100644
index 000000000000..5753a1b7614e
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_stream_encoder.h
@@ -0,0 +1,122 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ *  and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef __DC_STREAM_ENCODER_DCE110_H__
+#define __DC_STREAM_ENCODER_DCE110_H__
+
+#include "inc/stream_encoder.h"
+
+#define DCE110STRENC_FROM_STRENC(stream_encoder)\
+	container_of(stream_encoder, struct dce110_stream_encoder, base)
+
+struct dce110_stream_enc_registers {
+	uint32_t AFMT_AVI_INFO0;
+	uint32_t AFMT_AVI_INFO1;
+	uint32_t AFMT_AVI_INFO2;
+	uint32_t AFMT_AVI_INFO3;
+	uint32_t AFMT_GENERIC_0;
+	uint32_t AFMT_GENERIC_7;
+	uint32_t AFMT_GENERIC_HDR;
+	uint32_t AFMT_INFOFRAME_CONTROL0;
+	uint32_t AFMT_VBI_PACKET_CONTROL;
+	uint32_t DIG_FE_CNTL;
+	uint32_t DP_MSE_RATE_CNTL;
+	uint32_t DP_MSE_RATE_UPDATE;
+	uint32_t DP_PIXEL_FORMAT;
+	uint32_t DP_SEC_CNTL;
+	uint32_t DP_STEER_FIFO;
+	uint32_t DP_VID_M;
+	uint32_t DP_VID_N;
+	uint32_t DP_VID_STREAM_CNTL;
+	uint32_t DP_VID_TIMING;
+	uint32_t HDMI_CONTROL;
+	uint32_t HDMI_GC;
+	uint32_t HDMI_GENERIC_PACKET_CONTROL0;
+	uint32_t HDMI_GENERIC_PACKET_CONTROL1;
+	uint32_t HDMI_INFOFRAME_CONTROL0;
+	uint32_t HDMI_INFOFRAME_CONTROL1;
+	uint32_t HDMI_VBI_PACKET_CONTROL;
+	uint32_t TMDS_CNTL;
+};
+
+struct dce110_stream_encoder {
+	struct stream_encoder base;
+	const struct dce110_stream_enc_registers *regs;
+};
+
+bool dce110_stream_encoder_construct(
+	struct dce110_stream_encoder *enc110,
+	struct dc_context *ctx,
+	struct dc_bios *bp,
+	enum engine_id eng_id,
+	const struct dce110_stream_enc_registers *regs);
+
+/***** HW programming ***********/
+/* setup stream encoder in dp mode */
+void dce110_stream_encoder_dp_set_stream_attribute(
+	struct stream_encoder *enc,
+	struct dc_crtc_timing *crtc_timing);
+
+/* setup stream encoder in hdmi mode */
+void dce110_stream_encoder_hdmi_set_stream_attribute(
+	struct stream_encoder *enc,
+	struct dc_crtc_timing *crtc_timing,
+	bool enable_audio);
+
+/* setup stream encoder in dvi mode */
+void dce110_stream_encoder_dvi_set_stream_attribute(
+	struct stream_encoder *enc,
+	struct dc_crtc_timing *crtc_timing,
+	bool is_dual_link);
+
+/* set throttling for DP MST */
+void dce110_stream_encoder_set_mst_bandwidth(
+	struct stream_encoder *enc,
+	struct fixed31_32 avg_time_slots_per_mtp);
+
+void dce110_stream_encoder_update_hdmi_info_packets(
+	struct stream_encoder *enc,
+	const struct encoder_info_frame *info_frame);
+
+void dce110_stream_encoder_stop_hdmi_info_packets(
+	struct stream_encoder *enc);
+
+void dce110_stream_encoder_update_dp_info_packets(
+	struct stream_encoder *enc,
+	const struct encoder_info_frame *info_frame);
+
+void dce110_stream_encoder_stop_dp_info_packets(
+	struct stream_encoder *enc);
+
+/* output blank/idle stream to link encoder */
+void dce110_stream_encoder_dp_blank(
+	struct stream_encoder *enc);
+
+/* output video stream to link encoder */
+void dce110_stream_encoder_dp_unblank(
+	struct stream_encoder *enc,
+	const struct encoder_unblank_param *param);
+
+#endif /* __DC_STREAM_ENCODER_DCE110_H__ */