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 at amd.com>
Reviewed-by: Alex Deucher <alexander.deucher at 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 --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__ */
-- 
2.1.4

Reply via email to