Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/amd/display/dc/hwss/dce110/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 100 kB image not shown  

Quelle  dce110_hwseq.c

  Sprache: C
 

/*
 * Copyright 2015 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: AMD
 *
 */


#include "dm_services.h"
#include "dc.h"
#include "dc_bios_types.h"
#include "core_types.h"
#include "core_status.h"
#include "resource.h"
#include "dm_helpers.h"
#include "dce110_hwseq.h"
#include "dce110/dce110_timing_generator.h"
#include "dce/dce_hwseq.h"
#include "dce100/dce100_hwseq.h"
#include "gpio_service_interface.h"

#include "dce110/dce110_compressor.h"

#include "bios/bios_parser_helper.h"
#include "timing_generator.h"
#include "mem_input.h"
#include "opp.h"
#include "ipp.h"
#include "transform.h"
#include "stream_encoder.h"
#include "link_encoder.h"
#include "link_enc_cfg.h"
#include "link_hwss.h"
#include "link.h"
#include "dccg.h"
#include "clock_source.h"
#include "clk_mgr.h"
#include "abm.h"
#include "audio.h"
#include "reg_helper.h"
#include "panel_cntl.h"
#include "dc_state_priv.h"
#include "dpcd_defs.h"
#include "dsc.h"
/* include DCE11 register header files */
#include "dce/dce_11_0_d.h"
#include "dce/dce_11_0_sh_mask.h"
#include "custom_float.h"

#include "atomfirmware.h"

#include "dcn10/dcn10_hwseq.h"

#define GAMMA_HW_POINTS_NUM 256

/*
 * 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
#define OLED_POST_T7_DELAY 100
#define OLED_PRE_T11_DELAY 150

#define CTX \
 hws->ctx

#define DC_LOGGER \
 ctx->logger
#define DC_LOGGER_INIT() \
 struct dc_context *ctx = dc->ctx

#define REG(reg)\
 hws->regs->reg

#undef FN
#define FN(reg_name, field_name) \
 hws->shifts->field_name, hws->masks->field_name

struct dce110_hw_seq_reg_offsets {
 uint32_t crtc;
};

static const struct dce110_hw_seq_reg_offsets reg_offsets[] = {
{
 .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL),
},
{
 .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL),
},
{
 .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL),
},
{
 .crtc = (mmCRTCV_GSL_CONTROL - mmCRTC_GSL_CONTROL),
}
};

#define HW_REG_BLND(reg, id)\
 (reg + reg_offsets[id].blnd)

#define HW_REG_CRTC(reg, id)\
 (reg + reg_offsets[id].crtc)

#define MAX_WATERMARK 0xFFFF
#define SAFE_NBP_MARK 0x7FFF

/*******************************************************************************
 * Private definitions
 ******************************************************************************/

/***************************PIPE_CONTROL***********************************/
static void dce110_init_pte(struct dc_context *ctx)
{
 uint32_t addr;
 uint32_t value = 0;
 uint32_t chunk_int = 0;
 uint32_t chunk_mul = 0;

 addr = mmUNP_DVMM_PTE_CONTROL;
 value = dm_read_reg(ctx, addr);

 set_reg_field_value(
  value,
  0,
  DVMM_PTE_CONTROL,
  DVMM_USE_SINGLE_PTE);

 set_reg_field_value(
  value,
  1,
  DVMM_PTE_CONTROL,
  DVMM_PTE_BUFFER_MODE0);

 set_reg_field_value(
  value,
  1,
  DVMM_PTE_CONTROL,
  DVMM_PTE_BUFFER_MODE1);

 dm_write_reg(ctx, addr, value);

 addr = mmDVMM_PTE_REQ;
 value = dm_read_reg(ctx, addr);

 chunk_int = get_reg_field_value(
  value,
  DVMM_PTE_REQ,
  HFLIP_PTEREQ_PER_CHUNK_INT);

 chunk_mul = get_reg_field_value(
  value,
  DVMM_PTE_REQ,
  HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER);

 if (chunk_int != 0x4 || chunk_mul != 0x4) {

  set_reg_field_value(
   value,
   255,
   DVMM_PTE_REQ,
   MAX_PTEREQ_TO_ISSUE);

  set_reg_field_value(
   value,
   4,
   DVMM_PTE_REQ,
   HFLIP_PTEREQ_PER_CHUNK_INT);

  set_reg_field_value(
   value,
   4,
   DVMM_PTE_REQ,
   HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER);

  dm_write_reg(ctx, addr, value);
 }
}
/**************************************************************************/

static void enable_display_pipe_clock_gating(
 struct dc_context *ctx,
 bool clock_gating)
{
 /*TODO*/
}

static bool dce110_enable_display_power_gating(
 struct dc *dc,
 uint8_t controller_id,
 struct dc_bios *dcb,
 enum pipe_gating_control power_gating)
{
 enum bp_result bp_result = BP_RESULT_OK;
 enum bp_pipe_control_action cntl;
 struct dc_context *ctx = dc->ctx;
 unsigned int underlay_idx = dc->res_pool->underlay_pipe_index;

 if (power_gating == PIPE_GATING_CONTROL_INIT)
  cntl = ASIC_PIPE_INIT;
 else if (power_gating == PIPE_GATING_CONTROL_ENABLE)
  cntl = ASIC_PIPE_ENABLE;
 else
  cntl = ASIC_PIPE_DISABLE;

 if (controller_id == underlay_idx)
  controller_id = CONTROLLER_ID_UNDERLAY0 - 1;

 if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0) {

  bp_result = dcb->funcs->enable_disp_power_gating(
      dcb, controller_id + 1, cntl);

  /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2
 * by default when command table is called
 *
 * Bios parser accepts controller_id = 6 as indicative of
 * underlay pipe in dce110. But we do not support more
 * than 3.
 */

  if (controller_id < CONTROLLER_ID_MAX - 1)
   dm_write_reg(ctx,
    HW_REG_CRTC(mmCRTC_MASTER_UPDATE_MODE, controller_id),
    0);
 }

 if (power_gating != PIPE_GATING_CONTROL_ENABLE)
  dce110_init_pte(ctx);

 if (bp_result == BP_RESULT_OK)
  return true;
 else
  return false;
}

static void dce110_prescale_params(struct ipp_prescale_params *prescale_params,
  const struct dc_plane_state *plane_state)
{
 prescale_params->mode = IPP_PRESCALE_MODE_FIXED_UNSIGNED;

 switch (plane_state->format) {
 case SURFACE_PIXEL_FORMAT_GRPH_RGB565:
  prescale_params->scale = 0x2082;
  break;
 case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888:
 case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888:
  prescale_params->scale = 0x2020;
  break;
 case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010:
 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010:
  prescale_params->scale = 0x2008;
  break;
 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616:
 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:
  prescale_params->scale = 0x2000;
  break;
 default:
  ASSERT(false);
  break;
 }
}

static bool
dce110_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx,
          const struct dc_plane_state *plane_state)
{
 struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp;
 const struct dc_transfer_func *tf = NULL;
 struct ipp_prescale_params prescale_params = { 0 };
 bool result = true;

 if (ipp == NULL)
  return false;

 tf = &plane_state->in_transfer_func;

 dce110_prescale_params(&prescale_params, plane_state);
 ipp->funcs->ipp_program_prescale(ipp, &prescale_params);

 if (!plane_state->gamma_correction.is_identity &&
   dce_use_lut(plane_state->format))
  ipp->funcs->ipp_program_input_lut(ipp, &plane_state->gamma_correction);

 if (tf->type == TF_TYPE_PREDEFINED) {
  switch (tf->tf) {
  case TRANSFER_FUNCTION_SRGB:
   ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_HW_sRGB);
   break;
  case TRANSFER_FUNCTION_BT709:
   ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_HW_xvYCC);
   break;
  case TRANSFER_FUNCTION_LINEAR:
   ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_BYPASS);
   break;
  case TRANSFER_FUNCTION_PQ:
  default:
   result = false;
   break;
  }
 } else if (tf->type == TF_TYPE_BYPASS) {
  ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_BYPASS);
 } else {
  /*TF_TYPE_DISTRIBUTED_POINTS - Not supported in DCE 11*/
  result = false;
 }

 return result;
}

static bool convert_to_custom_float(struct pwl_result_data *rgb_resulted,
        struct curve_points *arr_points,
        uint32_t hw_points_num)
{
 struct custom_float_format fmt;

 struct pwl_result_data *rgb = rgb_resulted;

 uint32_t i = 0;

 fmt.exponenta_bits = 6;
 fmt.mantissa_bits = 12;
 fmt.sign = true;

 if (!convert_to_custom_float_format(arr_points[0].x, &fmt,
         &arr_points[0].custom_float_x)) {
  BREAK_TO_DEBUGGER();
  return false;
 }

 if (!convert_to_custom_float_format(arr_points[0].offset, &fmt,
         &arr_points[0].custom_float_offset)) {
  BREAK_TO_DEBUGGER();
  return false;
 }

 if (!convert_to_custom_float_format(arr_points[0].slope, &fmt,
         &arr_points[0].custom_float_slope)) {
  BREAK_TO_DEBUGGER();
  return false;
 }

 fmt.mantissa_bits = 10;
 fmt.sign = false;

 if (!convert_to_custom_float_format(arr_points[1].x, &fmt,
         &arr_points[1].custom_float_x)) {
  BREAK_TO_DEBUGGER();
  return false;
 }

 if (!convert_to_custom_float_format(arr_points[1].y, &fmt,
         &arr_points[1].custom_float_y)) {
  BREAK_TO_DEBUGGER();
  return false;
 }

 if (!convert_to_custom_float_format(arr_points[1].slope, &fmt,
         &arr_points[1].custom_float_slope)) {
  BREAK_TO_DEBUGGER();
  return false;
 }

 fmt.mantissa_bits = 12;
 fmt.sign = true;

 while (i != hw_points_num) {
  if (!convert_to_custom_float_format(rgb->red, &fmt,
          &rgb->red_reg)) {
   BREAK_TO_DEBUGGER();
   return false;
  }

  if (!convert_to_custom_float_format(rgb->green, &fmt,
          &rgb->green_reg)) {
   BREAK_TO_DEBUGGER();
   return false;
  }

  if (!convert_to_custom_float_format(rgb->blue, &fmt,
          &rgb->blue_reg)) {
   BREAK_TO_DEBUGGER();
   return false;
  }

  if (!convert_to_custom_float_format(rgb->delta_red, &fmt,
          &rgb->delta_red_reg)) {
   BREAK_TO_DEBUGGER();
   return false;
  }

  if (!convert_to_custom_float_format(rgb->delta_green, &fmt,
          &rgb->delta_green_reg)) {
   BREAK_TO_DEBUGGER();
   return false;
  }

  if (!convert_to_custom_float_format(rgb->delta_blue, &fmt,
          &rgb->delta_blue_reg)) {
   BREAK_TO_DEBUGGER();
   return false;
  }

  ++rgb;
  ++i;
 }

 return true;
}

#define MAX_LOW_POINT      25
#define NUMBER_REGIONS     16
#define NUMBER_SW_SEGMENTS 16

static bool
dce110_translate_regamma_to_hw_format(const struct dc_transfer_func *output_tf,
          struct pwl_params *regamma_params)
{
 struct curve_points *arr_points;
 struct pwl_result_data *rgb_resulted;
 struct pwl_result_data *rgb;
 struct pwl_result_data *rgb_plus_1;
 struct fixed31_32 y_r;
 struct fixed31_32 y_g;
 struct fixed31_32 y_b;
 struct fixed31_32 y1_min;
 struct fixed31_32 y3_max;

 int32_t region_start, region_end;
 uint32_t i, j, k, seg_distr[NUMBER_REGIONS], increment, start_index, hw_points;

 if (output_tf == NULL || regamma_params == NULL || output_tf->type == TF_TYPE_BYPASS)
  return false;

 arr_points = regamma_params->arr_points;
 rgb_resulted = regamma_params->rgb_resulted;
 hw_points = 0;

 memset(regamma_params, 0, sizeof(struct pwl_params));

 if (output_tf->tf == TRANSFER_FUNCTION_PQ) {
  /* 16 segments
 * segments are from 2^-11 to 2^5
 */

  region_start = -11;
  region_end = region_start + NUMBER_REGIONS;

  for (i = 0; i < NUMBER_REGIONS; i++)
   seg_distr[i] = 4;

 } else {
  /* 10 segments
 * segment is from 2^-10 to 2^1
 * We include an extra segment for range [2^0, 2^1). This is to
 * ensure that colors with normalized values of 1 don't miss the
 * LUT.
 */

  region_start = -10;
  region_end = 1;

  seg_distr[0] = 4;
  seg_distr[1] = 4;
  seg_distr[2] = 4;
  seg_distr[3] = 4;
  seg_distr[4] = 4;
  seg_distr[5] = 4;
  seg_distr[6] = 4;
  seg_distr[7] = 4;
  seg_distr[8] = 4;
  seg_distr[9] = 4;
  seg_distr[10] = 0;
  seg_distr[11] = -1;
  seg_distr[12] = -1;
  seg_distr[13] = -1;
  seg_distr[14] = -1;
  seg_distr[15] = -1;
 }

 for (k = 0; k < 16; k++) {
  if (seg_distr[k] != -1)
   hw_points += (1 << seg_distr[k]);
 }

 j = 0;
 for (k = 0; k < (region_end - region_start); k++) {
  increment = NUMBER_SW_SEGMENTS / (1 << seg_distr[k]);
  start_index = (region_start + k + MAX_LOW_POINT) *
    NUMBER_SW_SEGMENTS;
  for (i = start_index; i < start_index + NUMBER_SW_SEGMENTS;
    i += increment) {
   if (j == hw_points - 1)
    break;
   rgb_resulted[j].red = output_tf->tf_pts.red[i];
   rgb_resulted[j].green = output_tf->tf_pts.green[i];
   rgb_resulted[j].blue = output_tf->tf_pts.blue[i];
   j++;
  }
 }

 /* last point */
 start_index = (region_end + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS;
 rgb_resulted[hw_points - 1].red = output_tf->tf_pts.red[start_index];
 rgb_resulted[hw_points - 1].green = output_tf->tf_pts.green[start_index];
 rgb_resulted[hw_points - 1].blue = output_tf->tf_pts.blue[start_index];

 arr_points[0].x = dc_fixpt_pow(dc_fixpt_from_int(2),
          dc_fixpt_from_int(region_start));
 arr_points[1].x = dc_fixpt_pow(dc_fixpt_from_int(2),
          dc_fixpt_from_int(region_end));

 y_r = rgb_resulted[0].red;
 y_g = rgb_resulted[0].green;
 y_b = rgb_resulted[0].blue;

 y1_min = dc_fixpt_min(y_r, dc_fixpt_min(y_g, y_b));

 arr_points[0].y = y1_min;
 arr_points[0].slope = dc_fixpt_div(arr_points[0].y,
       arr_points[0].x);

 y_r = rgb_resulted[hw_points - 1].red;
 y_g = rgb_resulted[hw_points - 1].green;
 y_b = rgb_resulted[hw_points - 1].blue;

 /* see comment above, m_arrPoints[1].y should be the Y value for the
 * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1)
 */

 y3_max = dc_fixpt_max(y_r, dc_fixpt_max(y_g, y_b));

 arr_points[1].y = y3_max;

 arr_points[1].slope = dc_fixpt_zero;

 if (output_tf->tf == TRANSFER_FUNCTION_PQ) {
  /* for PQ, we want to have a straight line from last HW X point,
 * and the slope to be such that we hit 1.0 at 10000 nits.
 */

  const struct fixed31_32 end_value = dc_fixpt_from_int(125);

  arr_points[1].slope = dc_fixpt_div(
    dc_fixpt_sub(dc_fixpt_one, arr_points[1].y),
    dc_fixpt_sub(end_value, arr_points[1].x));
 }

 regamma_params->hw_points_num = hw_points;

 k = 0;
 for (i = 1; i < 16; i++) {
  if (seg_distr[k] != -1) {
   regamma_params->arr_curve_points[k].segments_num = seg_distr[k];
   regamma_params->arr_curve_points[i].offset =
     regamma_params->arr_curve_points[k].offset + (1 << seg_distr[k]);
  }
  k++;
 }

 if (seg_distr[k] != -1)
  regamma_params->arr_curve_points[k].segments_num = seg_distr[k];

 rgb = rgb_resulted;
 rgb_plus_1 = rgb_resulted + 1;

 i = 1;

 while (i != hw_points + 1) {
  if (dc_fixpt_lt(rgb_plus_1->red, rgb->red))
   rgb_plus_1->red = rgb->red;
  if (dc_fixpt_lt(rgb_plus_1->green, rgb->green))
   rgb_plus_1->green = rgb->green;
  if (dc_fixpt_lt(rgb_plus_1->blue, rgb->blue))
   rgb_plus_1->blue = rgb->blue;

  rgb->delta_red = dc_fixpt_sub(rgb_plus_1->red, rgb->red);
  rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green);
  rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue);

  ++rgb_plus_1;
  ++rgb;
  ++i;
 }

 convert_to_custom_float(rgb_resulted, arr_points, hw_points);

 return true;
}

static bool
dce110_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx,
    const struct dc_stream_state *stream)
{
 struct transform *xfm = pipe_ctx->plane_res.xfm;

 xfm->funcs->opp_power_on_regamma_lut(xfm, true);
 xfm->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM;

 if (stream->out_transfer_func.type == TF_TYPE_PREDEFINED &&
     stream->out_transfer_func.tf == TRANSFER_FUNCTION_SRGB) {
  xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_SRGB);
 } else if (dce110_translate_regamma_to_hw_format(&stream->out_transfer_func,
        &xfm->regamma_params)) {
  xfm->funcs->opp_program_regamma_pwl(xfm, &xfm->regamma_params);
  xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_USER);
 } else {
  xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_BYPASS);
 }

 xfm->funcs->opp_power_on_regamma_lut(xfm, false);

 return true;
}

void dce110_update_info_frame(struct pipe_ctx *pipe_ctx)
{
 bool is_hdmi_tmds;
 bool is_dp;

 ASSERT(pipe_ctx->stream);

 if (pipe_ctx->stream_res.stream_enc == NULL)
  return;  /* this is not root pipe */

 is_hdmi_tmds = dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal);
 is_dp = dc_is_dp_signal(pipe_ctx->stream->signal);

 if (!is_hdmi_tmds && !is_dp)
  return;

 if (is_hdmi_tmds)
  pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets(
   pipe_ctx->stream_res.stream_enc,
   &pipe_ctx->stream_res.encoder_info_frame);
 else {
  if (pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets_sdp_line_num)
   pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets_sdp_line_num(
    pipe_ctx->stream_res.stream_enc,
    &pipe_ctx->stream_res.encoder_info_frame);

  pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets(
   pipe_ctx->stream_res.stream_enc,
   &pipe_ctx->stream_res.encoder_info_frame);
 }
}

void dce110_enable_stream(struct pipe_ctx *pipe_ctx)
{
 enum dc_lane_count lane_count =
  pipe_ctx->stream->link->cur_link_settings.lane_count;
 struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;
 struct dc_link *link = pipe_ctx->stream->link;
 const struct dc *dc = link->dc;
 const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
 uint32_t active_total_with_borders;
 uint32_t early_control = 0;
 struct timing_generator *tg = pipe_ctx->stream_res.tg;

 link_hwss->setup_stream_attribute(pipe_ctx);
 link_hwss->setup_stream_encoder(pipe_ctx);

 dc->hwss.update_info_frame(pipe_ctx);

 /* enable early control to avoid corruption on DP monitor*/
 active_total_with_borders =
   timing->h_addressable
    + timing->h_border_left
    + timing->h_border_right;

 if (lane_count != 0)
  early_control = active_total_with_borders % lane_count;

 if (early_control == 0)
  early_control = lane_count;

 tg->funcs->set_early_control(tg, early_control);
}

static enum bp_result link_transmitter_control(
  struct dc_bios *bios,
 struct bp_transmitter_control *cntl)
{
 enum bp_result result;

 result = bios->funcs->transmitter_control(bios, cntl);

 return result;
}

/*
 * @brief
 * eDP only.
 */

void dce110_edp_wait_for_hpd_ready(
  struct dc_link *link,
  bool power_up)
{
 struct dc_context *ctx = link->ctx;
 struct graphics_object_id connector = link->link_enc->connector;
 struct gpio *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)
  /*
 * 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 */
 /* TODO what to do with this? */
 hpd = ctx->dc->link_srv->get_hpd_gpio(ctx->dc_bios, connector, ctx->gpio_service);

 if (!hpd) {
  BREAK_TO_DEBUGGER();
  return;
 }

 if (link->panel_config.pps.extra_t3_ms > 0) {
  int extra_t3_in_ms = link->panel_config.pps.extra_t3_ms;

  msleep(extra_t3_in_ms);
 }

 dal_gpio_open(hpd, GPIO_MODE_INTERRUPT);

 /* wait until timeout or panel detected */

 do {
  uint32_t detected = 0;

  dal_gpio_get_value(hpd, &detected);

  if (!(detected ^ power_up)) {
   edp_hpd_high = true;
   break;
  }

  msleep(HPD_CHECK_INTERVAL);

  time_elapsed += HPD_CHECK_INTERVAL;
 } while (time_elapsed < timeout);

 dal_gpio_close(hpd);

 dal_gpio_destroy_irq(&hpd);

 /* ensure that the panel is detected */
 if (!edp_hpd_high)
  DC_LOG_DC("%s: wait timed out!\n", __func__);
}

void dce110_edp_power_control(
  struct dc_link *link,
  bool power_up)
{
 struct dc_context *ctx = link->ctx;
 struct bp_transmitter_control cntl = { 0 };
 enum bp_result bp_result;
 uint8_t pwrseq_instance;


 if (dal_graphics_object_id_get_connector_id(link->link_enc->connector)
   != CONNECTOR_ID_EDP) {
  BREAK_TO_DEBUGGER();
  return;
 }

 if (!link->panel_cntl)
  return;
 if (power_up !=
  link->panel_cntl->funcs->is_panel_powered_on(link->panel_cntl)) {

  unsigned long long current_ts = dm_get_timestamp(ctx);
  unsigned long long time_since_edp_poweroff_ms =
    div64_u64(dm_get_elapse_time_in_ns(
      ctx,
      current_ts,
      ctx->dc->link_srv->dp_trace_get_edp_poweroff_timestamp(link)), 1000000);
  unsigned long long time_since_edp_poweron_ms =
    div64_u64(dm_get_elapse_time_in_ns(
      ctx,
      current_ts,
      ctx->dc->link_srv->dp_trace_get_edp_poweron_timestamp(link)), 1000000);
  DC_LOG_HW_RESUME_S3(
    "%s: transition: power_up=%d current_ts=%llu edp_poweroff=%llu edp_poweron=%llu time_since_edp_poweroff_ms=%llu time_since_edp_poweron_ms=%llu",
    __func__,
    power_up,
    current_ts,
    ctx->dc->link_srv->dp_trace_get_edp_poweroff_timestamp(link),
    ctx->dc->link_srv->dp_trace_get_edp_poweron_timestamp(link),
    time_since_edp_poweroff_ms,
    time_since_edp_poweron_ms);

  /* Send VBIOS command to prompt eDP panel power */
  if (power_up) {
   /* edp requires a min of 500ms from LCDVDD off to on */
   unsigned long long remaining_min_edp_poweroff_time_ms = 500;

   /* add time defined by a patch, if any (usually patch extra_t12_ms is 0) */
   if (link->local_sink != NULL)
    remaining_min_edp_poweroff_time_ms +=
     link->panel_config.pps.extra_t12_ms;

   /* Adjust remaining_min_edp_poweroff_time_ms if this is not the first time. */
   if (ctx->dc->link_srv->dp_trace_get_edp_poweroff_timestamp(link) != 0) {
    if (time_since_edp_poweroff_ms < remaining_min_edp_poweroff_time_ms)
     remaining_min_edp_poweroff_time_ms =
      remaining_min_edp_poweroff_time_ms - time_since_edp_poweroff_ms;
    else
     remaining_min_edp_poweroff_time_ms = 0;
   }

   if (remaining_min_edp_poweroff_time_ms) {
    DC_LOG_HW_RESUME_S3(
      "%s: remaining_min_edp_poweroff_time_ms=%llu: begin wait.\n",
      __func__, remaining_min_edp_poweroff_time_ms);
    msleep(remaining_min_edp_poweroff_time_ms);
    DC_LOG_HW_RESUME_S3(
      "%s: remaining_min_edp_poweroff_time_ms=%llu: end wait.\n",
      __func__, remaining_min_edp_poweroff_time_ms);
    dm_output_to_console("%s: wait %lld ms to power on eDP.\n",
      __func__, remaining_min_edp_poweroff_time_ms);
   } else {
    DC_LOG_HW_RESUME_S3(
      "%s: remaining_min_edp_poweroff_time_ms=%llu: no wait required.\n",
      __func__, remaining_min_edp_poweroff_time_ms);
   }
  }

  DC_LOG_HW_RESUME_S3(
    "%s: BEGIN: Panel Power action: %s\n",
    __func__, (power_up ? "On":"Off"));

  cntl.action = power_up ?
   TRANSMITTER_CONTROL_POWER_ON :
   TRANSMITTER_CONTROL_POWER_OFF;
  cntl.transmitter = link->link_enc->transmitter;
  cntl.connector_obj_id = link->link_enc->connector;
  cntl.coherent = false;
  cntl.lanes_number = LANE_COUNT_FOUR;
  cntl.hpd_sel = link->link_enc->hpd_source;
  pwrseq_instance = link->panel_cntl->pwrseq_inst;

  if (ctx->dc->ctx->dmub_srv &&
    ctx->dc->debug.dmub_command_table) {

   if (cntl.action == TRANSMITTER_CONTROL_POWER_ON) {
    bp_result = ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios,
      LVTMA_CONTROL_POWER_ON,
      pwrseq_instance, link->link_powered_externally);
   } else {
    bp_result = ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios,
      LVTMA_CONTROL_POWER_OFF,
      pwrseq_instance, link->link_powered_externally);
   }
  }

  bp_result = link_transmitter_control(ctx->dc_bios, &cntl);

  DC_LOG_HW_RESUME_S3(
    "%s: END: Panel Power action: %s bp_result=%u\n",
    __func__, (power_up ? "On":"Off"),
    bp_result);

  ctx->dc->link_srv->dp_trace_set_edp_power_timestamp(link, power_up);

  DC_LOG_HW_RESUME_S3(
    "%s: updated values: edp_poweroff=%llu edp_poweron=%llu\n",
    __func__,
    ctx->dc->link_srv->dp_trace_get_edp_poweroff_timestamp(link),
    ctx->dc->link_srv->dp_trace_get_edp_poweron_timestamp(link));

  if (bp_result != BP_RESULT_OK)
   DC_LOG_ERROR(
     "%s: Panel Power bp_result: %d\n",
     __func__, bp_result);
 } else {
  DC_LOG_HW_RESUME_S3(
    "%s: Skipping Panel Power action: %s\n",
    __func__, (power_up ? "On":"Off"));
 }
}

void dce110_edp_wait_for_T12(
  struct dc_link *link)
{
 struct dc_context *ctx = link->ctx;

 if (dal_graphics_object_id_get_connector_id(link->link_enc->connector)
   != CONNECTOR_ID_EDP) {
  BREAK_TO_DEBUGGER();
  return;
 }

 if (!link->panel_cntl)
  return;

 if (!link->panel_cntl->funcs->is_panel_powered_on(link->panel_cntl) &&
   ctx->dc->link_srv->dp_trace_get_edp_poweroff_timestamp(link) != 0) {
  unsigned int t12_duration = 500; // Default T12 as per spec
  unsigned long long current_ts = dm_get_timestamp(ctx);
  unsigned long long time_since_edp_poweroff_ms =
    div64_u64(dm_get_elapse_time_in_ns(
      ctx,
      current_ts,
      ctx->dc->link_srv->dp_trace_get_edp_poweroff_timestamp(link)), 1000000);

  t12_duration += link->panel_config.pps.extra_t12_ms; // Add extra T12

  if (time_since_edp_poweroff_ms < t12_duration)
   msleep(t12_duration - time_since_edp_poweroff_ms);
 }
}
/*todo: cloned in stream enc, fix*/
/*
 * @brief
 * eDP only. Control the backlight of the eDP panel
 */

void dce110_edp_backlight_control(
  struct dc_link *link,
  bool enable)
{
 struct dc_context *ctx = link->ctx;
 struct bp_transmitter_control cntl = { 0 };
 uint8_t pwrseq_instance = 0;
 unsigned int pre_T11_delay = (link->dpcd_sink_ext_caps.bits.oled ? OLED_PRE_T11_DELAY : 0);
 unsigned int post_T7_delay = (link->dpcd_sink_ext_caps.bits.oled ? OLED_POST_T7_DELAY : 0);

 if (dal_graphics_object_id_get_connector_id(link->link_enc->connector)
  != CONNECTOR_ID_EDP) {
  BREAK_TO_DEBUGGER();
  return;
 }

 if (link->panel_cntl && !(link->dpcd_sink_ext_caps.bits.oled ||
  link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 ||
  link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) {
  bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl);

  if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) {
   DC_LOG_HW_RESUME_S3(
    "%s: panel already powered up/off. Do nothing.\n",
    __func__);
   return;
  }
 }

 /* Send VBIOS command to control eDP panel backlight */

 DC_LOG_HW_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 = link->link_enc->transmitter;
 cntl.connector_obj_id = link->link_enc->connector;
 /*todo: unhardcode*/
 cntl.lanes_number = LANE_COUNT_FOUR;
 cntl.hpd_sel = link->link_enc->hpd_source;
 cntl.signal = SIGNAL_TYPE_EDP;

 /* 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); */
  /*edp 1.2*/
 if (link->panel_cntl)
  pwrseq_instance = link->panel_cntl->pwrseq_inst;

 if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_ON) {
  if (!link->dc->config.edp_no_power_sequencing)
  /*
 * Sometimes, DP receiver chip power-controlled externally by an
 * Embedded Controller could be treated and used as eDP,
 * if it drives mobile display. In this case,
 * we shouldn't be doing power-sequencing, hence we can skip
 * waiting for T7-ready.
 */

   ctx->dc->link_srv->edp_receiver_ready_T7(link);
  else
   DC_LOG_DC("edp_receiver_ready_T7 skipped\n");
 }

 /* Setting link_powered_externally will bypass delays in the backlight
 * as they are not required if the link is being powered by a different
 * source.
 */

 if (ctx->dc->ctx->dmub_srv &&
   ctx->dc->debug.dmub_command_table) {
  if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_ON)
   ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios,
     LVTMA_CONTROL_LCD_BLON,
     pwrseq_instance, link->link_powered_externally);
  else
   ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios,
     LVTMA_CONTROL_LCD_BLOFF,
     pwrseq_instance, link->link_powered_externally);
 }

 link_transmitter_control(ctx->dc_bios, &cntl);

 if (enable && link->dpcd_sink_ext_caps.bits.oled &&
     !link->dc->config.edp_no_power_sequencing &&
     !link->local_sink->edid_caps.panel_patch.oled_optimize_display_on) {
  post_T7_delay += link->panel_config.pps.extra_post_t7_ms;
  msleep(post_T7_delay);
 }

 if (link->dpcd_sink_ext_caps.bits.oled ||
  link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 ||
  link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)
  ctx->dc->link_srv->edp_backlight_enable_aux(link, enable);

 /*edp 1.2*/
 if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_OFF) {
  if (!link->dc->config.edp_no_power_sequencing)
  /*
 * Sometimes, DP receiver chip power-controlled externally by an
 * Embedded Controller could be treated and used as eDP,
 * if it drives mobile display. In this case,
 * we shouldn't be doing power-sequencing, hence we can skip
 * waiting for T9-ready.
 */

   ctx->dc->link_srv->edp_add_delay_for_T9(link);
  else
   DC_LOG_DC("edp_receiver_ready_T9 skipped\n");
 }

 if (!enable) {
  /*follow oem panel config's requirement*/
  pre_T11_delay += link->panel_config.pps.extra_pre_t11_ms;
  if (pre_T11_delay)
   msleep(pre_T11_delay);
 }
}

void dce110_enable_audio_stream(struct pipe_ctx *pipe_ctx)
{
 /* notify audio driver for audio modes of monitor */
 struct dc *dc;
 struct clk_mgr *clk_mgr;
 unsigned int i, num_audio = 1;
 const struct link_hwss *link_hwss;

 if (!pipe_ctx->stream)
  return;

 dc = pipe_ctx->stream->ctx->dc;
 clk_mgr = dc->clk_mgr;
 link_hwss = get_link_hwss(pipe_ctx->stream->link, &pipe_ctx->link_res);

 if (pipe_ctx->stream_res.audio && pipe_ctx->stream_res.audio->enabled == true)
  return;

 if (pipe_ctx->stream_res.audio) {
  for (i = 0; i < MAX_PIPES; i++) {
   /*current_state not updated yet*/
   if (dc->current_state->res_ctx.pipe_ctx[i].stream_res.audio != NULL)
    num_audio++;
  }

  pipe_ctx->stream_res.audio->funcs->az_enable(pipe_ctx->stream_res.audio);

  if (num_audio >= 1 && clk_mgr->funcs->enable_pme_wa)
   /*this is the first audio. apply the PME w/a in order to wake AZ from D3*/
   clk_mgr->funcs->enable_pme_wa(clk_mgr);

  link_hwss->enable_audio_packet(pipe_ctx);

  if (pipe_ctx->stream_res.audio)
   pipe_ctx->stream_res.audio->enabled = true;
 }
}

void dce110_disable_audio_stream(struct pipe_ctx *pipe_ctx)
{
 struct dc *dc;
 struct clk_mgr *clk_mgr;
 const struct link_hwss *link_hwss;

 if (!pipe_ctx || !pipe_ctx->stream)
  return;

 dc = pipe_ctx->stream->ctx->dc;
 clk_mgr = dc->clk_mgr;
 link_hwss = get_link_hwss(pipe_ctx->stream->link, &pipe_ctx->link_res);

 if (pipe_ctx->stream_res.audio && pipe_ctx->stream_res.audio->enabled == false)
  return;

 link_hwss->disable_audio_packet(pipe_ctx);

 if (pipe_ctx->stream_res.audio) {
  pipe_ctx->stream_res.audio->enabled = false;

  if (clk_mgr->funcs->enable_pme_wa)
   /*this is the first audio. apply the PME w/a in order to wake AZ from D3*/
   clk_mgr->funcs->enable_pme_wa(clk_mgr);

  /* TODO: notify audio driver for if audio modes list changed
 * add audio mode list change flag */

  /* dal_audio_disable_azalia_audio_jack_presence(stream->audio,
 * stream->stream_engine_id);
 */

 }
}

void dce110_disable_stream(struct pipe_ctx *pipe_ctx)
{
 struct dc_stream_state *stream = pipe_ctx->stream;
 struct dc_link *link = stream->link;
 struct dc *dc = pipe_ctx->stream->ctx->dc;
 const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
 struct dccg *dccg = dc->res_pool->dccg;
 struct timing_generator *tg = pipe_ctx->stream_res.tg;
 struct dtbclk_dto_params dto_params = {0};
 int dp_hpo_inst;
 struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
 struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;

 if (!dc->config.unify_link_enc_assignment)
  link_enc = link_enc_cfg_get_link_enc(link);

 if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal)) {
  pipe_ctx->stream_res.stream_enc->funcs->stop_hdmi_info_packets(
   pipe_ctx->stream_res.stream_enc);
  pipe_ctx->stream_res.stream_enc->funcs->hdmi_reset_stream_attribute(
   pipe_ctx->stream_res.stream_enc);
 }

 if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {
  pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->stop_dp_info_packets(
     pipe_ctx->stream_res.hpo_dp_stream_enc);
 } else if (dc_is_dp_signal(pipe_ctx->stream->signal))
  pipe_ctx->stream_res.stream_enc->funcs->stop_dp_info_packets(
   pipe_ctx->stream_res.stream_enc);

 dc->hwss.disable_audio_stream(pipe_ctx);

 link_hwss->reset_stream_encoder(pipe_ctx);

 if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx) && dccg) {
  dto_params.otg_inst = tg->inst;
  dto_params.timing = &pipe_ctx->stream->timing;
  dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst;
  if (dccg) {
   dccg->funcs->disable_symclk32_se(dccg, dp_hpo_inst);
   dccg->funcs->set_dpstreamclk(dccg, REFCLK, tg->inst, dp_hpo_inst);
   if (!(dc->ctx->dce_version >= DCN_VERSION_3_5)) {
    if (dccg && dccg->funcs->set_dtbclk_dto)
     dccg->funcs->set_dtbclk_dto(dccg, &dto_params);
   }
  }
 } else if (dccg && dccg->funcs->disable_symclk_se) {
  dccg->funcs->disable_symclk_se(dccg, stream_enc->stream_enc_inst,
            link_enc->transmitter - TRANSMITTER_UNIPHY_A);
 }
}

void dce110_unblank_stream(struct pipe_ctx *pipe_ctx,
  struct dc_link_settings *link_settings)
{
 struct encoder_unblank_param params = { { 0 } };
 struct dc_stream_state *stream = pipe_ctx->stream;
 struct dc_link *link = stream->link;
 struct dce_hwseq *hws = link->dc->hwseq;

 /* only 3 items below are used by unblank */
 params.timing = pipe_ctx->stream->timing;
 params.link_settings.link_rate = link_settings->link_rate;

 if (dc_is_dp_signal(pipe_ctx->stream->signal))
  pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms);

 if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
  hws->funcs.edp_backlight_control(link, true);
 }
}

void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
{
 struct dc_stream_state *stream = pipe_ctx->stream;
 struct dc_link *link = stream->link;
 struct dce_hwseq *hws = link->dc->hwseq;

 if (hws && hws->wa_state.skip_blank_stream)
  return;

 if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
  if (!link->skip_implict_edp_power_control && hws)
   hws->funcs.edp_backlight_control(link, false);
  link->dc->hwss.set_abm_immediate_disable(pipe_ctx);
 }

 if (link->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {
  /* TODO - DP2.0 HW: Set ODM mode in dp hpo encoder here */
  pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_blank(
    pipe_ctx->stream_res.hpo_dp_stream_enc);
 } else if (dc_is_dp_signal(pipe_ctx->stream->signal)) {
  pipe_ctx->stream_res.stream_enc->funcs->dp_blank(link, pipe_ctx->stream_res.stream_enc);

  if (!dc_is_embedded_signal(pipe_ctx->stream->signal)) {
   /*
 * After output is idle pattern some sinks need time to recognize the stream
 * has changed or they enter protection state and hang.
 */

   msleep(60);
  }
 }

 if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP &&
     !link->dc->config.edp_no_power_sequencing) {
   /*
 * Sometimes, DP receiver chip power-controlled externally by an
 * Embedded Controller could be treated and used as eDP,
 * if it drives mobile display. In this case,
 * we shouldn't be doing power-sequencing, hence we can skip
 * waiting for T9-ready.
 */

  link->dc->link_srv->edp_receiver_ready_T9(link);
 }

}


void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
{
 if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL)
  pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable);
}

static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id)
{
 switch (crtc_id) {
 case CONTROLLER_ID_D0:
  return DTO_SOURCE_ID0;
 case CONTROLLER_ID_D1:
  return DTO_SOURCE_ID1;
 case CONTROLLER_ID_D2:
  return DTO_SOURCE_ID2;
 case CONTROLLER_ID_D3:
  return DTO_SOURCE_ID3;
 case CONTROLLER_ID_D4:
  return DTO_SOURCE_ID4;
 case CONTROLLER_ID_D5:
  return DTO_SOURCE_ID5;
 default:
  return DTO_SOURCE_UNKNOWN;
 }
}

static void populate_audio_dp_link_info(
 const struct pipe_ctx *pipe_ctx,
 struct audio_dp_link_info *dp_link_info)
{
 const struct dc_stream_state *stream = pipe_ctx->stream;
 const struct dc_link *link = stream->link;
 struct fixed31_32 link_bw_kbps;

 dp_link_info->encoding = link->dc->link_srv->dp_get_encoding_format(
    &pipe_ctx->link_config.dp_link_settings);
 dp_link_info->is_mst = (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST);
 dp_link_info->lane_count = pipe_ctx->link_config.dp_link_settings.lane_count;
 dp_link_info->link_rate = pipe_ctx->link_config.dp_link_settings.link_rate;

 link_bw_kbps = dc_fixpt_from_int(dc_link_bandwidth_kbps(link,
   &pipe_ctx->link_config.dp_link_settings));

 /* For audio stream calculations, the video stream should not include FEC or SSC
 * in order to get the most pessimistic values.
 */

 if (dp_link_info->encoding == DP_8b_10b_ENCODING &&
   link->dc->link_srv->dp_is_fec_supported(link)) {
  link_bw_kbps = dc_fixpt_mul(link_bw_kbps,
    dc_fixpt_from_fraction(100, DATA_EFFICIENCY_8b_10b_FEC_EFFICIENCY_x100));
 } else if (dp_link_info->encoding == DP_128b_132b_ENCODING) {
  link_bw_kbps = dc_fixpt_mul(link_bw_kbps,
    dc_fixpt_from_fraction(10000, 9975)); /* 99.75% SSC overhead*/
 }

 dp_link_info->link_bandwidth_kbps = dc_fixpt_floor(link_bw_kbps);

 /* Calculates hblank_min_symbol_width for 128b/132b
 * Corresponding HBLANK_MIN_SYMBOL_WIDTH register is calculated as:
 *   floor(h_blank * bits_per_pixel / 128)
 */

 if (dp_link_info->encoding == DP_128b_132b_ENCODING) {
  struct dc_crtc_timing *crtc_timing = &pipe_ctx->stream->timing;

  uint32_t h_active = crtc_timing->h_addressable + crtc_timing->h_border_left
    + crtc_timing->h_border_right;
  uint32_t h_blank = crtc_timing->h_total - h_active;

  uint32_t bpp;

  if (crtc_timing->flags.DSC) {
   bpp = crtc_timing->dsc_cfg.bits_per_pixel;
  } else {
   /* When the timing is using DSC, dsc_cfg.bits_per_pixel is in 16th bits.
 * The bpp in this path is scaled to 16th bits so the final calculation
 * is correct for both cases.
 */

   bpp = 16;
   switch (crtc_timing->display_color_depth) {
   case COLOR_DEPTH_666:
    bpp *= 18;
    break;
   case COLOR_DEPTH_888:
    bpp *= 24;
    break;
   case COLOR_DEPTH_101010:
    bpp *= 30;
    break;
   case COLOR_DEPTH_121212:
    bpp *= 36;
    break;
   default:
    bpp = 0;
    break;
   }

   switch (crtc_timing->pixel_encoding) {
   case PIXEL_ENCODING_YCBCR422:
    bpp = bpp * 2 / 3;
    break;
   case PIXEL_ENCODING_YCBCR420:
    bpp /= 2;
    break;
   default:
    break;
   }
  }

  /* Min symbol width = floor(h_blank * (bpp/16) / 128) */
  dp_link_info->hblank_min_symbol_width = dc_fixpt_floor(
    dc_fixpt_div(dc_fixpt_from_int(h_blank * bpp),
      dc_fixpt_from_int(128 / 16)));

 } else {
  dp_link_info->hblank_min_symbol_width = 0;
 }
}

void build_audio_output(
 struct dc_state *state,
 const struct pipe_ctx *pipe_ctx,
 struct audio_output *audio_output)
{
 const struct dc_stream_state *stream = pipe_ctx->stream;
 audio_output->engine_id = pipe_ctx->stream_res.stream_enc->id;

 audio_output->signal = pipe_ctx->stream->signal;

 /* audio_crtc_info  */

 audio_output->crtc_info.h_total =
  stream->timing.h_total;

 /*
 * Audio packets are sent during actual CRTC blank physical signal, we
 * need to specify actual active signal portion
 */

 audio_output->crtc_info.h_active =
   stream->timing.h_addressable
   + stream->timing.h_border_left
   + stream->timing.h_border_right;

 audio_output->crtc_info.v_active =
   stream->timing.v_addressable
   + stream->timing.v_border_top
   + stream->timing.v_border_bottom;

 audio_output->crtc_info.pixel_repetition = 1;

 audio_output->crtc_info.interlaced =
   stream->timing.flags.INTERLACE;

 audio_output->crtc_info.refresh_rate =
  (stream->timing.pix_clk_100hz*100)/
  (stream->timing.h_total*stream->timing.v_total);

 audio_output->crtc_info.color_depth =
  stream->timing.display_color_depth;

 audio_output->crtc_info.requested_pixel_clock_100Hz =
   pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz;

 audio_output->crtc_info.calculated_pixel_clock_100Hz =
   pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz;

 audio_output->crtc_info.pixel_encoding =
  stream->timing.pixel_encoding;

 audio_output->crtc_info.dsc_bits_per_pixel =
   stream->timing.dsc_cfg.bits_per_pixel;

 audio_output->crtc_info.dsc_num_slices =
   stream->timing.dsc_cfg.num_slices_h;

/*for HDMI, audio ACR is with deep color ratio factor*/
 if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal) &&
  audio_output->crtc_info.requested_pixel_clock_100Hz ==
    (stream->timing.pix_clk_100hz)) {
  if (pipe_ctx->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420) {
   audio_output->crtc_info.requested_pixel_clock_100Hz =
     audio_output->crtc_info.requested_pixel_clock_100Hz/2;
   audio_output->crtc_info.calculated_pixel_clock_100Hz =
     pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz/2;

  }
 }

 if (state->clk_mgr &&
  (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
   pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)) {
  audio_output->pll_info.audio_dto_source_clock_in_khz =
    state->clk_mgr->funcs->get_dp_ref_clk_frequency(
      state->clk_mgr);
 }

 audio_output->pll_info.feed_back_divider =
   pipe_ctx->pll_settings.feedback_divider;

 audio_output->pll_info.dto_source =
  translate_to_dto_source(
   pipe_ctx->stream_res.tg->inst + 1);

 /* TODO hard code to enable for now. Need get from stream */
 audio_output->pll_info.ss_enabled = true;

 audio_output->pll_info.ss_percentage =
   pipe_ctx->pll_settings.ss_percentage;

 if (dc_is_dp_signal(pipe_ctx->stream->signal)) {
  populate_audio_dp_link_info(pipe_ctx, &audio_output->dp_link_info);
 }
}

static void program_scaler(const struct dc *dc,
  const struct pipe_ctx *pipe_ctx)
{
 struct tg_color color = {0};

 /* TOFPGA */
 if (pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth == NULL)
  return;

 if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE)
  get_surface_visual_confirm_color(pipe_ctx, &color);
 else
  color_space_to_black_color(dc,
    pipe_ctx->stream->output_color_space,
    &color);

 pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth(
  pipe_ctx->plane_res.xfm,
  pipe_ctx->plane_res.scl_data.lb_params.depth,
  &pipe_ctx->stream->bit_depth_params);

 if (pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color) {
  /*
 * The way 420 is packed, 2 channels carry Y component, 1 channel
 * alternate between Cb and Cr, so both channels need the pixel
 * value for Y
 */

  if (pipe_ctx->stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)
   color.color_r_cr = color.color_g_y;

  pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color(
    pipe_ctx->stream_res.tg,
    &color);
 }

 pipe_ctx->plane_res.xfm->funcs->transform_set_scaler(pipe_ctx->plane_res.xfm,
  &pipe_ctx->plane_res.scl_data);
}

static enum dc_status dce110_enable_stream_timing(
  struct pipe_ctx *pipe_ctx,
  struct dc_state *context,
  struct dc *dc)
{
 struct dc_stream_state *stream = pipe_ctx->stream;
 struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx.
   pipe_ctx[pipe_ctx->pipe_idx];
 struct tg_color black_color = {0};

 if (!pipe_ctx_old->stream) {

  /* program blank color */
  color_space_to_black_color(dc,
    stream->output_color_space, &black_color);
  pipe_ctx->stream_res.tg->funcs->set_blank_color(
    pipe_ctx->stream_res.tg,
    &black_color);

  /*
 * Must blank CRTC after disabling power gating and before any
 * programming, otherwise CRTC will be hung in bad state
 */

  pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true);

  if (false == pipe_ctx->clock_source->funcs->program_pix_clk(
    pipe_ctx->clock_source,
    &pipe_ctx->stream_res.pix_clk_params,
    dc->link_srv->dp_get_encoding_format(&pipe_ctx->link_config.dp_link_settings),
    &pipe_ctx->pll_settings)) {
   BREAK_TO_DEBUGGER();
   return DC_ERROR_UNEXPECTED;
  }

  if (dc_is_hdmi_tmds_signal(stream->signal)) {
   stream->link->phy_state.symclk_ref_cnts.otg = 1;
   if (stream->link->phy_state.symclk_state == SYMCLK_OFF_TX_OFF)
    stream->link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;
   else
    stream->link->phy_state.symclk_state = SYMCLK_ON_TX_ON;
  }

  pipe_ctx->stream_res.tg->funcs->program_timing(
    pipe_ctx->stream_res.tg,
    &stream->timing,
    0,
    0,
    0,
    0,
    0,
    pipe_ctx->stream->signal,
    true);
 }

 if (!pipe_ctx_old->stream) {
  if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(
    pipe_ctx->stream_res.tg)) {
   BREAK_TO_DEBUGGER();
   return DC_ERROR_UNEXPECTED;
  }
 }

 return DC_OK;
}

enum dc_status dce110_apply_single_controller_ctx_to_hw(
  struct pipe_ctx *pipe_ctx,
  struct dc_state *context,
  struct dc *dc)
{
 struct dc_stream_state *stream = pipe_ctx->stream;
 struct dc_link *link = stream->link;
 struct drr_params params = {0};
 unsigned int event_triggers = 0;
 struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe;
 struct dce_hwseq *hws = dc->hwseq;
 const struct link_hwss *link_hwss = get_link_hwss(
   link, &pipe_ctx->link_res);


 if (hws->funcs.disable_stream_gating) {
  hws->funcs.disable_stream_gating(dc, pipe_ctx);
 }

 if (pipe_ctx->stream_res.audio != NULL) {
  build_audio_output(context, pipe_ctx, &pipe_ctx->stream_res.audio_output);

  link_hwss->setup_audio_output(pipe_ctx, &pipe_ctx->stream_res.audio_output,
    pipe_ctx->stream_res.audio->inst);

  pipe_ctx->stream_res.audio->funcs->az_configure(
    pipe_ctx->stream_res.audio,
    pipe_ctx->stream->signal,
    &pipe_ctx->stream_res.audio_output.crtc_info,
    &pipe_ctx->stream->audio_info,
    &pipe_ctx->stream_res.audio_output.dp_link_info);

  if (dc->config.disable_hbr_audio_dp2)
   if (pipe_ctx->stream_res.audio->funcs->az_disable_hbr_audio &&
     dc->link_srv->dp_is_128b_132b_signal(pipe_ctx))
    pipe_ctx->stream_res.audio->funcs->az_disable_hbr_audio(pipe_ctx->stream_res.audio);
 }

 /* make sure no pipes syncd to the pipe being enabled */
 if (!pipe_ctx->stream->apply_seamless_boot_optimization && dc->config.use_pipe_ctx_sync_logic)
  check_syncd_pipes_for_disabled_master_pipe(dc, context, pipe_ctx->pipe_idx);

 pipe_ctx->stream_res.opp->funcs->opp_program_fmt(
  pipe_ctx->stream_res.opp,
  &stream->bit_depth_params,
  &stream->clamping);

 pipe_ctx->stream_res.opp->funcs->opp_set_dyn_expansion(
   pipe_ctx->stream_res.opp,
   COLOR_SPACE_YCBCR601,
   stream->timing.display_color_depth,
   stream->signal);

 while (odm_pipe) {
  odm_pipe->stream_res.opp->funcs->opp_set_dyn_expansion(
    odm_pipe->stream_res.opp,
    COLOR_SPACE_YCBCR601,
    stream->timing.display_color_depth,
    stream->signal);

  odm_pipe->stream_res.opp->funcs->opp_program_fmt(
    odm_pipe->stream_res.opp,
    &stream->bit_depth_params,
    &stream->clamping);
  odm_pipe = odm_pipe->next_odm_pipe;
 }

 /* DCN3.1 FPGA Workaround
 * Need to enable HPO DP Stream Encoder before setting OTG master enable.
 * To do so, move calling function enable_stream_timing to only be done AFTER calling
 * function core_link_enable_stream
 */

 if (!(hws->wa.dp_hpo_and_otg_sequence && dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)))
  /*  */
  /* Do not touch stream timing on seamless boot optimization. */
  if (!pipe_ctx->stream->apply_seamless_boot_optimization)
   hws->funcs.enable_stream_timing(pipe_ctx, context, dc);

 if (hws->funcs.setup_vupdate_interrupt)
  hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx);

 params.vertical_total_min = stream->adjust.v_total_min;
 params.vertical_total_max = stream->adjust.v_total_max;
 set_drr_and_clear_adjust_pending(pipe_ctx, stream, ¶ms);

 // DRR should set trigger event to monitor surface update event
 if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0)
  event_triggers = 0x80;
 /* Event triggers and num frames initialized for DRR, but can be
 * later updated for PSR use. Note DRR trigger events are generated
 * regardless of whether num frames met.
 */

 if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control)
  pipe_ctx->stream_res.tg->funcs->set_static_screen_control(
    pipe_ctx->stream_res.tg, event_triggers, 2);

 if (!dc_is_virtual_signal(pipe_ctx->stream->signal))
  pipe_ctx->stream_res.stream_enc->funcs->dig_connect_to_otg(
   pipe_ctx->stream_res.stream_enc,
   pipe_ctx->stream_res.tg->inst);

 if (dc_is_dp_signal(pipe_ctx->stream->signal))
  dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_CONNECT_DIG_FE_OTG);

 /* Temporary workaround to perform DSC programming ahead of stream enablement
 * for smartmux/SPRS
 * TODO: Remove SmartMux/SPRS checks once movement of DSC programming is generalized
 */

 if (pipe_ctx->stream->timing.flags.DSC) {
  if ((pipe_ctx->stream->signal == SIGNAL_TYPE_EDP &&
   ((link->dc->config.smart_mux_version && link->dc->is_switch_in_progress_dest)
   || link->is_dds || link->skip_implict_edp_power_control)) &&
   (dc_is_dp_signal(pipe_ctx->stream->signal) ||
   dc_is_virtual_signal(pipe_ctx->stream->signal)))
   dc->link_srv->set_dsc_enable(pipe_ctx, true);
 }

 if (!stream->dpms_off)
  dc->link_srv->set_dpms_on(context, pipe_ctx);

 /* DCN3.1 FPGA Workaround
 * Need to enable HPO DP Stream Encoder before setting OTG master enable.
 * To do so, move calling function enable_stream_timing to only be done AFTER calling
 * function core_link_enable_stream
 */

 if (hws->wa.dp_hpo_and_otg_sequence && dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {
  if (!pipe_ctx->stream->apply_seamless_boot_optimization)
   hws->funcs.enable_stream_timing(pipe_ctx, context, dc);
 }

 pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != NULL;

 /* Phantom and main stream share the same link (because the stream
 * is constructed with the same sink). Make sure not to override
 * and link programming on the main.
 */

 if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) {
  pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false;
  pipe_ctx->stream->link->replay_settings.replay_feature_enabled = false;
 }
 return DC_OK;
}

/******************************************************************************/

static void power_down_encoders(struct dc *dc)
{
 int i;

 for (i = 0; i < dc->link_count; i++) {
  enum signal_type signal = dc->links[i]->connector_signal;

  dc->link_srv->blank_dp_stream(dc->links[i], false);

  if (signal != SIGNAL_TYPE_EDP)
   signal = SIGNAL_TYPE_NONE;

  if (dc->links[i]->ep_type == DISPLAY_ENDPOINT_PHY)
   dc->links[i]->link_enc->funcs->disable_output(
     dc->links[i]->link_enc, signal);

  dc->links[i]->link_status.link_active = false;
  memset(&dc->links[i]->cur_link_settings, 0,
    sizeof(dc->links[i]->cur_link_settings));
 }
}

static void power_down_controllers(struct dc *dc)
{
 int i;

 for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
  dc->res_pool->timing_generators[i]->funcs->disable_crtc(
    dc->res_pool->timing_generators[i]);
 }
}

static void power_down_clock_sources(struct dc *dc)
{
 int i;

 if (dc->res_pool->dp_clock_source->funcs->cs_power_down(
  dc->res_pool->dp_clock_source) == false)
  dm_error("Failed to power down pll! (dp clk src)\n");

 for (i = 0; i < dc->res_pool->clk_src_count; i++) {
  if (dc->res_pool->clock_sources[i]->funcs->cs_power_down(
    dc->res_pool->clock_sources[i]) == false)
   dm_error("Failed to power down pll! (clk src index=%d)\n", i);
 }
}

static void power_down_all_hw_blocks(struct dc *dc)
{
 power_down_encoders(dc);

 power_down_controllers(dc);

 power_down_clock_sources(dc);

 if (dc->fbc_compressor)
  dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor);
}

static void disable_vga_and_power_gate_all_controllers(
  struct dc *dc)
{
 int i;
 struct timing_generator *tg;
 struct dc_context *ctx = dc->ctx;

 for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
  tg = dc->res_pool->timing_generators[i];

  if (tg->funcs->disable_vga)
   tg->funcs->disable_vga(tg);
 }
 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  /* Enable CLOCK gating for each pipe BEFORE controller
 * powergating. */

  enable_display_pipe_clock_gating(ctx,
    true);

  dc->current_state->res_ctx.pipe_ctx[i].pipe_idx = i;
  dc->hwss.disable_plane(dc, dc->current_state,
   &dc->current_state->res_ctx.pipe_ctx[i]);
 }
}


static void get_edp_streams(struct dc_state *context,
  struct dc_stream_state **edp_streams,
  int *edp_stream_num)
{
 int i;

 *edp_stream_num = 0;
 for (i = 0; i < context->stream_count; i++) {
  if (context->streams[i]->signal == SIGNAL_TYPE_EDP) {
   edp_streams[*edp_stream_num] = context->streams[i];
   if (++(*edp_stream_num) == MAX_NUM_EDP)
    return;
  }
 }
}

static void get_edp_links_with_sink(
  struct dc *dc,
  struct dc_link **edp_links_with_sink,
  int *edp_with_sink_num)
{
 int i;

 /* check if there is an eDP panel not in use */
 *edp_with_sink_num = 0;
 for (i = 0; i < dc->link_count; i++) {
  if (dc->links[i]->local_sink &&
   dc->links[i]->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
   edp_links_with_sink[*edp_with_sink_num] = dc->links[i];
   if (++(*edp_with_sink_num) == MAX_NUM_EDP)
    return;
  }
 }
}

static void clean_up_dsc_blocks(struct dc *dc)
{
 struct display_stream_compressor *dsc = NULL;
 struct timing_generator *tg = NULL;
 struct stream_encoder *se = NULL;
 struct dccg *dccg = dc->res_pool->dccg;
 struct pg_cntl *pg_cntl = dc->res_pool->pg_cntl;
 int i;

 if (!dc->caps.is_apu ||
  dc->ctx->dce_version < DCN_VERSION_3_15)
  return;
 /*VBIOS supports dsc starts from dcn315*/
 for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) {
  struct dcn_dsc_state s  = {0};

  dsc = dc->res_pool->dscs[i];
  dsc->funcs->dsc_read_state(dsc, &s);
  if (s.dsc_fw_en) {
   /* disable DSC in OPTC */
   if (i < dc->res_pool->timing_generator_count) {
    tg = dc->res_pool->timing_generators[i];
    tg->funcs->set_dsc_config(tg, OPTC_DSC_DISABLED, 0, 0);
   }
   /* disable DSC in stream encoder */
   if (i < dc->res_pool->stream_enc_count) {
    se = dc->res_pool->stream_enc[i];
    se->funcs->dp_set_dsc_config(se, OPTC_DSC_DISABLED, 0, 0);
    se->funcs->dp_set_dsc_pps_info_packet(se, false, NULL, true);
   }
   /* disable DSC block */
   if (dccg->funcs->set_ref_dscclk)
    dccg->funcs->set_ref_dscclk(dccg, dsc->inst);
   dsc->funcs->dsc_disable(dsc);

   /* power down DSC */
   if (pg_cntl != NULL)
    pg_cntl->funcs->dsc_pg_control(pg_cntl, dsc->inst, false);
  }
 }
}

/*
 * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need:
 *  1. Power down all DC HW blocks
 *  2. Disable VGA engine on all controllers
 *  3. Enable power gating for controller
 *  4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS)
 */

void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context)
{
 struct dc_link *edp_links_with_sink[MAX_NUM_EDP];
 struct dc_link *edp_links[MAX_NUM_EDP];
 struct dc_stream_state *edp_streams[MAX_NUM_EDP];
 struct dc_link *edp_link_with_sink = NULL;
 struct dc_link *edp_link = NULL;
 struct pipe_ctx *pipe_ctx = NULL;
 struct dce_hwseq *hws = dc->hwseq;
 int edp_with_sink_num;
 int edp_num;
 int edp_stream_num;
 int i;
 bool can_apply_edp_fast_boot = false;
 bool can_apply_seamless_boot = false;
 bool keep_edp_vdd_on = false;
 struct dc_bios *dcb = dc->ctx->dc_bios;
 DC_LOGGER_INIT();


 get_edp_links_with_sink(dc, edp_links_with_sink, &edp_with_sink_num);
 dc_get_edp_links(dc, edp_links, &edp_num);

 if (hws->funcs.init_pipes)
  hws->funcs.init_pipes(dc, context);

 get_edp_streams(context, edp_streams, &edp_stream_num);

 /* Check fastboot support, disable on DCE 6-8 because of blank screens */
 if (edp_num && edp_stream_num && dc->ctx->dce_version < DCE_VERSION_10_0) {
  for (i = 0; i < edp_num; i++) {
   edp_link = edp_links[i];
   if (edp_link != edp_streams[0]->link)
    continue;
   // enable fastboot if backend is enabled on eDP
   if (edp_link->link_enc->funcs->is_dig_enabled &&
       edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) &&
       edp_link->link_status.link_active) {
    struct dc_stream_state *edp_stream = edp_streams[0];

    can_apply_edp_fast_boot = dc_validate_boot_timing(dc,
     edp_stream->sink, &edp_stream->timing);

    // For Mux-platform, the default value is false.
    // Disable fast boot during mux switching.
    // The flag would be clean after switching done.
    if (dc->is_switch_in_progress_dest && edp_link->is_dds)
     can_apply_edp_fast_boot = false;

    edp_stream->apply_edp_fast_boot_optimization = can_apply_edp_fast_boot;
    if (can_apply_edp_fast_boot) {
     DC_LOG_EVENT_LINK_TRAINING("eDP fast boot Enable\n");

     // Vbios & Driver support different pixel rate div policy.
     pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, edp_stream);
     if (pipe_ctx &&
      hws->funcs.is_dp_dig_pixel_rate_div_policy &&
      hws->funcs.is_dp_dig_pixel_rate_div_policy(pipe_ctx)) {
      // Get Vbios div factor from register
      dc->res_pool->dccg->funcs->get_pixel_rate_div(
       dc->res_pool->dccg,
       pipe_ctx->stream_res.tg->inst,
       &pipe_ctx->pixel_rate_divider.div_factor1,
       &pipe_ctx->pixel_rate_divider.div_factor2);

      // VBios doesn't support pixel rate div, so force it.
      // If VBios supports it, we check it from reigster or other flags.
      pipe_ctx->stream_res.pix_clk_params.dio_se_pix_per_cycle = 1;
     }
    }
    break;
   }
  }
  // We are trying to enable eDP, don't power down VDD
  if (can_apply_edp_fast_boot)
   keep_edp_vdd_on = true;
 }

 // Check seamless boot support
 for (i = 0; i < context->stream_count; i++) {
  if (context->streams[i]->apply_seamless_boot_optimization) {
   can_apply_seamless_boot = true;
   break;
  }
 }

 /* eDP should not have stream in resume from S4 and so even with VBios post
 * it should get turned off
 */

 if (edp_with_sink_num)
  edp_link_with_sink = edp_links_with_sink[0];

 // During a mux switch, powering down the HW blocks and then enabling
 // the link via a DPCD SET_POWER write causes a brief flash
 keep_edp_vdd_on |= dc->is_switch_in_progress_dest;

 if (!can_apply_edp_fast_boot && !can_apply_seamless_boot) {
  if (edp_link_with_sink && !keep_edp_vdd_on) {
   /*turn off backlight before DP_blank and encoder powered down*/
   hws->funcs.edp_backlight_control(edp_link_with_sink, false);
  }
  /*resume from S3, no vbios posting, no need to power down again*/
  if (dcb && dcb->funcs && !dcb->funcs->is_accelerated_mode(dcb))
   clk_mgr_exit_optimized_pwr_state(dc, dc->clk_mgr);

  power_down_all_hw_blocks(dc);

  /* DSC could be enabled on eDP during VBIOS post.
 * To clean up dsc blocks if eDP is in link but not active.
 */

  if (edp_link_with_sink && (edp_stream_num == 0))
   clean_up_dsc_blocks(dc);

  disable_vga_and_power_gate_all_controllers(dc);
  if (edp_link_with_sink && !keep_edp_vdd_on)
   dc->hwss.edp_power_control(edp_link_with_sink, false);
  if (dcb && dcb->funcs && !dcb->funcs->is_accelerated_mode(dcb))
   clk_mgr_optimize_pwr_state(dc, dc->clk_mgr);
 }
 bios_set_scratch_acc_mode_change(dc->ctx->dc_bios, 1);
}

static uint32_t compute_pstate_blackout_duration(
 struct bw_fixed blackout_duration,
 const struct dc_stream_state *stream)
{
 uint32_t total_dest_line_time_ns;
 uint32_t pstate_blackout_duration_ns;

 pstate_blackout_duration_ns = 1000 * blackout_duration.value >> 24;

 total_dest_line_time_ns = 1000000UL *
  (stream->timing.h_total * 10) /
  stream->timing.pix_clk_100hz +
  pstate_blackout_duration_ns;

 return total_dest_line_time_ns;
}

static void dce110_set_displaymarks(
 const struct dc *dc,
 struct dc_state *context)
{
 uint8_t i, num_pipes;
 unsigned int underlay_idx = dc->res_pool->underlay_pipe_index;

 for (i = 0, num_pipes = 0; i < MAX_PIPES; i++) {
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
  uint32_t total_dest_line_time_ns;

  if (pipe_ctx->stream == NULL)
   continue;

  total_dest_line_time_ns = compute_pstate_blackout_duration(
   dc->bw_vbios->blackout_duration, pipe_ctx->stream);
  pipe_ctx->plane_res.mi->funcs->mem_input_program_display_marks(
   pipe_ctx->plane_res.mi,
   context->bw_ctx.bw.dce.nbp_state_change_wm_ns[num_pipes],
   context->bw_ctx.bw.dce.stutter_exit_wm_ns[num_pipes],
   context->bw_ctx.bw.dce.stutter_entry_wm_ns[num_pipes],
   context->bw_ctx.bw.dce.urgent_wm_ns[num_pipes],
   total_dest_line_time_ns);
  if (i == underlay_idx) {
   num_pipes++;
   pipe_ctx->plane_res.mi->funcs->mem_input_program_chroma_display_marks(
    pipe_ctx->plane_res.mi,
    context->bw_ctx.bw.dce.nbp_state_change_wm_ns[num_pipes],
    context->bw_ctx.bw.dce.stutter_exit_wm_ns[num_pipes],
    context->bw_ctx.bw.dce.urgent_wm_ns[num_pipes],
    total_dest_line_time_ns);
  }
  num_pipes++;
 }
}

void dce110_set_safe_displaymarks(
  struct resource_context *res_ctx,
  const struct resource_pool *pool)
{
 int i;
 int underlay_idx = pool->underlay_pipe_index;
 struct dce_watermarks max_marks = {
  MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK };
 struct dce_watermarks nbp_marks = {
  SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK };
 struct dce_watermarks min_marks = { 0, 0, 0, 0};

 for (i = 0; i < MAX_PIPES; i++) {
  if (res_ctx->pipe_ctx[i].stream == NULL || res_ctx->pipe_ctx[i].plane_res.mi == NULL)
   continue;

  res_ctx->pipe_ctx[i].plane_res.mi->funcs->mem_input_program_display_marks(
    res_ctx->pipe_ctx[i].plane_res.mi,
    nbp_marks,
    max_marks,
    min_marks,
    max_marks,
    MAX_WATERMARK);

  if (i == underlay_idx)
   res_ctx->pipe_ctx[i].plane_res.mi->funcs->mem_input_program_chroma_display_marks(
    res_ctx->pipe_ctx[i].plane_res.mi,
    nbp_marks,
    max_marks,
    max_marks,
    MAX_WATERMARK);

 }
}

/*******************************************************************************
 * Public functions
 ******************************************************************************/


static void set_drr(struct pipe_ctx **pipe_ctx,
  int num_pipes, struct dc_crtc_timing_adjust adjust)
{
 int i = 0;
 struct drr_params params = {0};
 // DRR should set trigger event to monitor surface update event
 unsigned int event_triggers = 0x80;
 // Note DRR trigger events are generated regardless of whether num frames met.
 unsigned int num_frames = 2;

 params.vertical_total_max = adjust.v_total_max;
 params.vertical_total_min = adjust.v_total_min;

 /* TODO: If multiple pipes are to be supported, you need
 * some GSL stuff. Static screen triggers may be programmed differently
 * as well.
 */

 for (i = 0; i < num_pipes; i++) {
  /* dc_state_destruct() might null the stream resources, so fetch tg
 * here first to avoid a race condition. The lifetime of the pointee
 * itself (the timing_generator object) is not a problem here.
 */

  struct timing_generator *tg = pipe_ctx[i]->stream_res.tg;

  if ((tg != NULL) && tg->funcs) {
   set_drr_and_clear_adjust_pending(pipe_ctx[i], pipe_ctx[i]->stream, ¶ms);
   if (adjust.v_total_max != 0 && adjust.v_total_min != 0)
    if (tg->funcs->set_static_screen_control)
     tg->funcs->set_static_screen_control(
      tg, event_triggers, num_frames);
  }
 }
}

static void get_position(struct pipe_ctx **pipe_ctx,
  int num_pipes,
  struct crtc_position *position)
{
 int i = 0;

 /* TODO: handle pipes > 1
 */

 for (i = 0; i < num_pipes; i++)
  pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position);
}

static void set_static_screen_control(struct pipe_ctx **pipe_ctx,
  int num_pipes, const struct dc_static_screen_params *params)
{
 unsigned int i;
 unsigned int triggers = 0;

 if (params->triggers.overlay_update)
  triggers |= 0x100;
 if (params->triggers.surface_update)
  triggers |= 0x80;
 if (params->triggers.cursor_update)
  triggers |= 0x2;
 if (params->triggers.force_trigger)
  triggers |= 0x1;

 if (num_pipes) {
  struct dc *dc = pipe_ctx[0]->stream->ctx->dc;

  if (dc->fbc_compressor)
   triggers |= 0x84;
 }

 for (i = 0; i < num_pipes; i++)
  pipe_ctx[i]->stream_res.tg->funcs->
   set_static_screen_control(pipe_ctx[i]->stream_res.tg,
     triggers, params->num_frames);
}

/*
 *  Check if FBC can be enabled
 */

static bool should_enable_fbc(struct dc *dc,
  struct dc_state *context,
  uint32_t *pipe_idx)
{
 uint32_t i;
 struct pipe_ctx *pipe_ctx = NULL;
 struct resource_context *res_ctx = &context->res_ctx;
 unsigned int underlay_idx = dc->res_pool->underlay_pipe_index;


 ASSERT(dc->fbc_compressor);

 /* FBC memory should be allocated */
 if (!dc->ctx->fbc_gpu_addr)
  return false;

 /* Only supports single display */
 if (context->stream_count != 1)
  return false;

 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  if (res_ctx->pipe_ctx[i].stream) {

   pipe_ctx = &res_ctx->pipe_ctx[i];

   /* fbc not applicable on underlay pipe */
   if (pipe_ctx->pipe_idx != underlay_idx) {
    *pipe_idx = i;
    break;
   }
  }
 }

 if (i == dc->res_pool->pipe_count)
  return false;

 if (!pipe_ctx->stream->link)
  return false;

 /* Only supports eDP */
 if (pipe_ctx->stream->link->connector_signal != SIGNAL_TYPE_EDP)
  return false;

 /* PSR should not be enabled */
 if (pipe_ctx->stream->link->psr_settings.psr_feature_enabled)
  return false;

 /* Replay should not be enabled */
 if (pipe_ctx->stream->link->replay_settings.replay_feature_enabled)
  return false;

 /* Nothing to compress */
 if (!pipe_ctx->plane_state)
  return false;

 /* Only for non-linear tiling */
 if (pipe_ctx->plane_state->tiling_info.gfx8.array_mode == DC_ARRAY_LINEAR_GENERAL)
  return false;

 return true;
}

/*
 *  Enable FBC
 */

static void enable_fbc(
  struct dc *dc,
  struct dc_state *context)
{
 uint32_t pipe_idx = 0;

 if (should_enable_fbc(dc, context, &pipe_idx)) {
  /* Program GRPH COMPRESSED ADDRESS and PITCH */
  struct compr_addr_and_pitch_params params = {0, 0, 0};
  struct compressor *compr = dc->fbc_compressor;
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx];

  params.source_view_width = pipe_ctx->stream->timing.h_addressable;
  params.source_view_height = pipe_ctx->stream->timing.v_addressable;
  params.inst = pipe_ctx->stream_res.tg->inst;
  compr->compr_surface_address.quad_part = dc->ctx->fbc_gpu_addr;

  compr->funcs->surface_address_and_pitch(compr, ¶ms);
  compr->funcs->set_fbc_invalidation_triggers(compr, 1);

  compr->funcs->enable_fbc(compr, ¶ms);
 }
}

static void dce110_reset_hw_ctx_wrap(
  struct dc *dc,
  struct dc_state *context)
{
 int i;

 /* Reset old context */
 /* look up the targets that have been removed since last commit */
 for (i = 0; i < MAX_PIPES; i++) {
  struct pipe_ctx *pipe_ctx_old =
   &dc->current_state->res_ctx.pipe_ctx[i];
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

  /* Note: We need to disable output if clock sources change,
 * since bios does optimization and doesn't apply if changing
 * PHY when not already disabled.
 */


  /* Skip underlay pipe since it will be handled in commit surface*/
  if (!pipe_ctx_old->stream || pipe_ctx_old->top_pipe)
   continue;

  if (!pipe_ctx->stream ||
    pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) {
   struct clock_source *old_clk = pipe_ctx_old->clock_source;

   /* Disable if new stream is null. O/w, if stream is
 * disabled already, no need to disable again.
 */

   if (!pipe_ctx->stream || !pipe_ctx->stream->dpms_off) {
    dc->link_srv->set_dpms_off(pipe_ctx_old);

    /* free acquired resources*/
    if (pipe_ctx_old->stream_res.audio) {
     /*disable az_endpoint*/
     pipe_ctx_old->stream_res.audio->funcs->
       az_disable(pipe_ctx_old->stream_res.audio);

     /*free audio*/
     if (dc->caps.dynamic_audio == true) {
      /*we have to dynamic arbitrate the audio endpoints*/
      /*we free the resource, need reset is_audio_acquired*/
      update_audio_usage(&dc->current_state->res_ctx, dc->res_pool,
        pipe_ctx_old->stream_res.audio, false);
      pipe_ctx_old->stream_res.audio = NULL;
     }
    }
   }

   pipe_ctx_old->stream_res.tg->funcs->set_blank(pipe_ctx_old->stream_res.tg, true);
   if (!hwss_wait_for_blank_complete(pipe_ctx_old->stream_res.tg)) {
    dm_error("DC: failed to blank crtc!\n");
    BREAK_TO_DEBUGGER();
   }
   pipe_ctx_old->stream_res.tg->funcs->disable_crtc(pipe_ctx_old->stream_res.tg);
   if (dc_is_hdmi_tmds_signal(pipe_ctx_old->stream->signal))
    pipe_ctx_old->stream->link->phy_state.symclk_ref_cnts.otg = 0;
   pipe_ctx_old->plane_res.mi->funcs->free_mem_input(
     pipe_ctx_old->plane_res.mi, dc->current_state->stream_count);

   if (old_clk && 0 == resource_get_clock_source_reference(&context->res_ctx,
          dc->res_pool,
          old_clk))
    old_clk->funcs->cs_power_down(old_clk);

   dc->hwss.disable_plane(dc, dc->current_state, pipe_ctx_old);

   pipe_ctx_old->stream = NULL;
  }
 }
}

static void dce110_setup_audio_dto(
  struct dc *dc,
  struct dc_state *context)
{
 unsigned int i;

 /* program audio wall clock. use HDMI as clock source if HDMI
 * audio active. Otherwise, use DP as clock source
 * first, loop to find any HDMI audio, if not, loop find DP audio
 */

 /* Setup audio rate clock source */
 /* Issue:
* Audio lag happened on DP monitor when unplug a HDMI monitor
*
* Cause:
* In case of DP and HDMI connected or HDMI only, DCCG_AUDIO_DTO_SEL
* is set to either dto0 or dto1, audio should work fine.
* In case of DP connected only, DCCG_AUDIO_DTO_SEL should be dto1,
* set to dto0 will cause audio lag.
*
* Solution:
* Not optimized audio wall dto setup. When mode set, iterate pipe_ctx,
* find first available pipe with audio, setup audio wall DTO per topology
* instead of per pipe.
*/

 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

  if (pipe_ctx->stream == NULL)
   continue;

  if (pipe_ctx->top_pipe)
   continue;
  if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A)
   continue;
  if (pipe_ctx->stream_res.audio != NULL) {
   build_audio_output(context, pipe_ctx, &pipe_ctx->stream_res.audio_output);

   if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->set_audio_dtbclk_dto) {
    struct dtbclk_dto_params dto_params = {0};

    dc->res_pool->dccg->funcs->set_audio_dtbclk_dto(
     dc->res_pool->dccg, &dto_params);

    pipe_ctx->stream_res.audio->funcs->wall_dto_setup(
      pipe_ctx->stream_res.audio,
      pipe_ctx->stream->signal,
      &pipe_ctx->stream_res.audio_output.crtc_info,
      &pipe_ctx->stream_res.audio_output.pll_info);
   } else
    pipe_ctx->stream_res.audio->funcs->wall_dto_setup(
     pipe_ctx->stream_res.audio,
     pipe_ctx->stream->signal,
     &pipe_ctx->stream_res.audio_output.crtc_info,
     &pipe_ctx->stream_res.audio_output.pll_info);
   break;
  }
 }

 /* no HDMI audio is found, try DP audio */
 if (i == dc->res_pool->pipe_count) {
  for (i = 0; i < dc->res_pool->pipe_count; i++) {
   struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

   if (pipe_ctx->stream == NULL)
    continue;

   if (pipe_ctx->top_pipe)
    continue;

   if (!dc_is_dp_signal(pipe_ctx->stream->signal))
    continue;

   if (pipe_ctx->stream_res.audio != NULL) {
    build_audio_output(context,
         pipe_ctx,
         &pipe_ctx->stream_res.audio_output);

    pipe_ctx->stream_res.audio->funcs->wall_dto_setup(
     pipe_ctx->stream_res.audio,
     pipe_ctx->stream->signal,
     &pipe_ctx->stream_res.audio_output.crtc_info,
     &pipe_ctx->stream_res.audio_output.pll_info);
    break;
   }
  }
 }
}

enum dc_status dce110_apply_ctx_to_hw(
  struct dc *dc,
  struct dc_state *context)
{
 struct dce_hwseq *hws = dc->hwseq;
 struct dc_bios *dcb = dc->ctx->dc_bios;
 enum dc_status status;
 int i;
 bool was_hpo_acquired = resource_is_hpo_acquired(dc->current_state);
 bool is_hpo_acquired = resource_is_hpo_acquired(context);

 /* reset syncd pipes from disabled pipes */
 if (dc->config.use_pipe_ctx_sync_logic)
  reset_syncd_pipes_from_disabled_pipes(dc, context);

 /* Reset old context */
 /* look up the targets that have been removed since last commit */
 hws->funcs.reset_hw_ctx_wrap(dc, context);

 /* Skip applying if no targets */
 if (context->stream_count <= 0)
  return DC_OK;

 /* Apply new context */
 dcb->funcs->set_scratch_critical_state(dcb, true);

 /* below is for real asic only */
 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct pipe_ctx *pipe_ctx_old =
     &dc->current_state->res_ctx.pipe_ctx[i];
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

  if (pipe_ctx->stream == NULL || pipe_ctx->top_pipe)
   continue;

  if (pipe_ctx->stream == pipe_ctx_old->stream) {
   if (pipe_ctx_old->clock_source != pipe_ctx->clock_source)
    dce_crtc_switch_to_clk_src(dc->hwseq,
      pipe_ctx->clock_source, i);
   continue;
  }

  hws->funcs.enable_display_power_gating(
    dc, i, dc->ctx->dc_bios,
    PIPE_GATING_CONTROL_DISABLE);
 }

 if (dc->fbc_compressor)
  dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor);

 dce110_setup_audio_dto(dc, context);

 if (dc->hwseq->funcs.setup_hpo_hw_control && was_hpo_acquired != is_hpo_acquired) {
  dc->hwseq->funcs.setup_hpo_hw_control(dc->hwseq, is_hpo_acquired);
 }

 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct pipe_ctx *pipe_ctx_old =
     &dc->current_state->res_ctx.pipe_ctx[i];
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

  if (pipe_ctx->stream == NULL)
   continue;

  if (pipe_ctx->stream == pipe_ctx_old->stream &&
   pipe_ctx->stream->link->link_state_valid) {
   continue;
  }

  if (pipe_ctx_old->stream && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx))
   continue;

  if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe)
   continue;

  status = dce110_apply_single_controller_ctx_to_hw(
    pipe_ctx,
    context,
    dc);

  if (DC_OK != status)
   return status;

#ifdef CONFIG_DRM_AMD_DC_FP
  if (hws->funcs.resync_fifo_dccg_dio)
   hws->funcs.resync_fifo_dccg_dio(hws, dc, context, i);
#endif
 }

 if (dc->fbc_compressor)
  enable_fbc(dc, dc->current_state);

 dcb->funcs->set_scratch_critical_state(dcb, false);

 return DC_OK;
}

/*******************************************************************************
 * Front End programming
 ******************************************************************************/

static void set_default_colors(struct pipe_ctx *pipe_ctx)
{
 struct default_adjustment default_adjust = { 0 };

 default_adjust.force_hw_default = false;
 default_adjust.in_color_space = pipe_ctx->plane_state->color_space;
 default_adjust.out_color_space = pipe_ctx->stream->output_color_space;
 default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW;
 default_adjust.surface_pixel_format = pipe_ctx->plane_res.scl_data.format;

 /* display color depth */
 default_adjust.color_depth =
  pipe_ctx->stream->timing.display_color_depth;

 /* Lb color depth */
 default_adjust.lb_color_depth = pipe_ctx->plane_res.scl_data.lb_params.depth;

 pipe_ctx->plane_res.xfm->funcs->opp_set_csc_default(
     pipe_ctx->plane_res.xfm, &default_adjust);
}


/*******************************************************************************
 * In order to turn on/off specific surface we will program
 * Blender + CRTC
 *
 * In case that we have two surfaces and they have a different visibility
 * we can't turn off the CRTC since it will turn off the entire display
 *
 * |----------------------------------------------- |
 * |bottom pipe|curr pipe  |              |         |
 * |Surface    |Surface    | Blender      |  CRCT   |
 * |visibility |visibility | Configuration|         |
 * |------------------------------------------------|
 * |   off     |    off    | CURRENT_PIPE | blank   |
 * |   off     |    on     | CURRENT_PIPE | unblank |
 * |   on      |    off    | OTHER_PIPE   | unblank |
 * |   on      |    on     | BLENDING     | unblank |
 * -------------------------------------------------|
 *
 ******************************************************************************/

static void program_surface_visibility(const struct dc *dc,
  struct pipe_ctx *pipe_ctx)
{
 enum blnd_mode blender_mode = BLND_MODE_CURRENT_PIPE;
 bool blank_target = false;

 if (pipe_ctx->bottom_pipe) {

  /* For now we are supporting only two pipes */
  ASSERT(pipe_ctx->bottom_pipe->bottom_pipe == NULL);

  if (pipe_ctx->bottom_pipe->plane_state->visible) {
   if (pipe_ctx->plane_state->visible)
    blender_mode = BLND_MODE_BLENDING;
   else
    blender_mode = BLND_MODE_OTHER_PIPE;

  } else if (!pipe_ctx->plane_state->visible)
   blank_target = true;

 } else if (!pipe_ctx->plane_state->visible)
  blank_target = true;

 dce_set_blender_mode(dc->hwseq, pipe_ctx->stream_res.tg->inst, blender_mode);
 pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, blank_target);

}

static void program_gamut_remap(struct pipe_ctx *pipe_ctx)
{
 int i = 0;
 struct xfm_grph_csc_adjustment adjust;
 memset(&adjust, 0, sizeof(adjust));
 adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;


 if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {
  adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;

  for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
   adjust.temperature_matrix[i] =
    pipe_ctx->stream->gamut_remap_matrix.matrix[i];
 }

 pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust);
}
static void update_plane_addr(const struct dc *dc,
  struct pipe_ctx *pipe_ctx)
{
 struct dc_plane_state *plane_state = pipe_ctx->plane_state;

 if (plane_state == NULL)
  return;

 pipe_ctx->plane_res.mi->funcs->mem_input_program_surface_flip_and_addr(
   pipe_ctx->plane_res.mi,
   &plane_state->address,
   plane_state->flip_immediate);

 plane_state->status.requested_address = plane_state->address;
}

static void dce110_update_pending_status(struct pipe_ctx *pipe_ctx)
{
 struct dc_plane_state *plane_state = pipe_ctx->plane_state;

 if (plane_state == NULL)
  return;

 plane_state->status.is_flip_pending =
   pipe_ctx->plane_res.mi->funcs->mem_input_is_flip_pending(
     pipe_ctx->plane_res.mi);

 if (plane_state->status.is_flip_pending && !plane_state->visible)
  pipe_ctx->plane_res.mi->current_address = pipe_ctx->plane_res.mi->request_address;

 plane_state->status.current_address = pipe_ctx->plane_res.mi->current_address;
 if (pipe_ctx->plane_res.mi->current_address.type == PLN_ADDR_TYPE_GRPH_STEREO &&
   pipe_ctx->stream_res.tg->funcs->is_stereo_left_eye) {
  plane_state->status.is_right_eye =\
    !pipe_ctx->stream_res.tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg);
 }
}

void dce110_power_down(struct dc *dc)
{
 power_down_all_hw_blocks(dc);
 disable_vga_and_power_gate_all_controllers(dc);
}

static bool wait_for_reset_trigger_to_occur(
 struct dc_context *dc_ctx,
 struct timing_generator *tg)
{
 struct dc_context *ctx = dc_ctx;
 bool rc = false;

 /* To avoid endless loop we wait at most
 * frames_to_wait_on_triggered_reset frames for the reset to occur. */

 const uint32_t frames_to_wait_on_triggered_reset = 10;
 uint32_t i;

 for (i = 0; i < frames_to_wait_on_triggered_reset; i++) {

  if (!tg->funcs->is_counter_moving(tg)) {
   DC_ERROR("TG counter is not moving!\n");
   break;
  }

  if (tg->funcs->did_triggered_reset_occur(tg)) {
   rc = true;
   /* usually occurs at i=1 */
   DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n",
     i);
   break;
  }

  /* Wait for one frame. */
  tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE);
  tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK);
 }

 if (false == rc)
  DC_ERROR("GSL: Timeout on reset trigger!\n");

 return rc;
}

/* Enable timing synchronization for a group of Timing Generators. */
static void dce110_enable_timing_synchronization(
  struct dc *dc,
  struct dc_state *state,
  int group_index,
  int group_size,
  struct pipe_ctx *grouped_pipes[])
{
 struct dc_context *dc_ctx = dc->ctx;
 struct dcp_gsl_params gsl_params = { 0 };
 int i;
 DC_LOGGER_INIT();

 DC_SYNC_INFO("GSL: Setting-up...\n");

 /* Designate a single TG in the group as a master.
 * Since HW doesn't care which one, we always assign
 * the 1st one in the group. */

 gsl_params.gsl_group = 0;
 gsl_params.gsl_master = grouped_pipes[0]->stream_res.tg->inst;

 for (i = 0; i < group_size; i++)
  grouped_pipes[i]->stream_res.tg->funcs->setup_global_swap_lock(
     grouped_pipes[i]->stream_res.tg, &gsl_params);

 /* Reset slave controllers on master VSync */
 DC_SYNC_INFO("GSL: enabling trigger-reset\n");

 for (i = 1 /* skip the master */; i < group_size; i++)
  grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger(
    grouped_pipes[i]->stream_res.tg,
    gsl_params.gsl_group);

 for (i = 1 /* skip the master */; i < group_size; i++) {
  DC_SYNC_INFO("GSL: waiting for reset to occur.\n");
  wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg);
  grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger(
    grouped_pipes[i]->stream_res.tg);
 }

 /* GSL Vblank synchronization is a one time sync mechanism, assumption
 * is that the sync'ed displays will not drift out of sync over time*/

 DC_SYNC_INFO("GSL: Restoring register states.\n");
 for (i = 0; i < group_size; i++)
  grouped_pipes[i]->stream_res.tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->stream_res.tg);

 DC_SYNC_INFO("GSL: Set-up complete.\n");
}

static void dce110_enable_per_frame_crtc_position_reset(
  struct dc *dc,
  int group_size,
  struct pipe_ctx *grouped_pipes[])
{
 struct dc_context *dc_ctx = dc->ctx;
 struct dcp_gsl_params gsl_params = { 0 };
 int i;
 DC_LOGGER_INIT();

 gsl_params.gsl_group = 0;
 gsl_params.gsl_master = 0;

 for (i = 0; i < group_size; i++)
  grouped_pipes[i]->stream_res.tg->funcs->setup_global_swap_lock(
     grouped_pipes[i]->stream_res.tg, &gsl_params);

 DC_SYNC_INFO("GSL: enabling trigger-reset\n");

 for (i = 1; i < group_size; i++)
  grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset(
    grouped_pipes[i]->stream_res.tg,
    gsl_params.gsl_master,
    &grouped_pipes[i]->stream->triggered_crtc_reset);

 DC_SYNC_INFO("GSL: waiting for reset to occur.\n");
 for (i = 1; i < group_size; i++)
  wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg);

 for (i = 0; i < group_size; i++)
  grouped_pipes[i]->stream_res.tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->stream_res.tg);

}

static void dce110_init_pipes(struct dc *dc, struct dc_state *context)
{
 // Do nothing
}

static void dce110_init_hw(struct dc *dc)
{
 int i;
 struct dc_bios *bp;
 struct transform *xfm;
 struct abm *abm;
 struct dmcu *dmcu;
 struct dce_hwseq *hws = dc->hwseq;
 uint32_t backlight = MAX_BACKLIGHT_LEVEL;
 uint32_t user_level = MAX_BACKLIGHT_LEVEL;

 bp = dc->ctx->dc_bios;
 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  xfm = dc->res_pool->transforms[i];
  xfm->funcs->transform_reset(xfm);

  hws->funcs.enable_display_power_gating(
    dc, i, bp,
    PIPE_GATING_CONTROL_INIT);
  hws->funcs.enable_display_power_gating(
    dc, i, bp,
    PIPE_GATING_CONTROL_DISABLE);
  hws->funcs.enable_display_pipe_clock_gating(
   dc->ctx,
   true);
 }

 dce_clock_gating_power_up(dc->hwseq, false);
 /***************************************/

 for (i = 0; i < dc->link_count; i++) {
  /****************************************/
  /* Power up AND update implementation according to the
 * required signal (which may be different from the
 * default signal on connector). */

  struct dc_link *link = dc->links[i];

  link->link_enc->funcs->hw_init(link->link_enc);
 }

 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct timing_generator *tg = dc->res_pool->timing_generators[i];

  tg->funcs->disable_vga(tg);

  /* Blank controller using driver code instead of
 * command table. */

  tg->funcs->set_blank(tg, true);
  hwss_wait_for_blank_complete(tg);
 }

 for (i = 0; i < dc->res_pool->audio_count; i++) {
  struct audio *audio = dc->res_pool->audios[i];
  audio->funcs->hw_init(audio);
 }

 for (i = 0; i < dc->link_count; i++) {
  struct dc_link *link = dc->links[i];

  if (link->panel_cntl) {
   backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl);
   user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL;
  }
 }

 abm = dc->res_pool->abm;
 if (abm != NULL)
  abm->funcs->abm_init(abm, backlight, user_level);

 dmcu = dc->res_pool->dmcu;
 if (dmcu != NULL && abm != NULL)
  abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);

 if (dc->fbc_compressor)
  dc->fbc_compressor->funcs->power_up_fbc(dc->fbc_compressor);

}


void dce110_prepare_bandwidth(
  struct dc *dc,
  struct dc_state *context)
{
 struct clk_mgr *dccg = dc->clk_mgr;

 dce110_set_safe_displaymarks(&context->res_ctx, dc->res_pool);
 if (dccg)
  dccg->funcs->update_clocks(
    dccg,
    context,
    false);
}

void dce110_optimize_bandwidth(
  struct dc *dc,
  struct dc_state *context)
{
 struct clk_mgr *dccg = dc->clk_mgr;

 dce110_set_displaymarks(dc, context);

 if (dccg)
  dccg->funcs->update_clocks(
    dccg,
    context,
    true);
}

static void dce110_program_front_end_for_pipe(
  struct dc *dc, struct pipe_ctx *pipe_ctx)
{
 struct mem_input *mi = pipe_ctx->plane_res.mi;
 struct dc_plane_state *plane_state = pipe_ctx->plane_state;
 struct xfm_grph_csc_adjustment adjust;
 struct out_csc_color_matrix tbl_entry;
 unsigned int i;
 struct dce_hwseq *hws = dc->hwseq;

 memset(&tbl_entry, 0, sizeof(tbl_entry));

 memset(&adjust, 0, sizeof(adjust));
 adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;

 dce_enable_fe_clock(dc->hwseq, mi->inst, true);

 set_default_colors(pipe_ctx);
 if (pipe_ctx->stream->csc_color_matrix.enable_adjustment
   == true) {
  tbl_entry.color_space =
   pipe_ctx->stream->output_color_space;

  for (i = 0; i < 12; i++)
   tbl_entry.regval[i] =
   pipe_ctx->stream->csc_color_matrix.matrix[i];

  pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment
    (pipe_ctx->plane_res.xfm, &tbl_entry);
 }

 if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {
  adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;

  for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
   adjust.temperature_matrix[i] =
    pipe_ctx->stream->gamut_remap_matrix.matrix[i];
 }

 pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust);

 pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != NULL;

 program_scaler(dc, pipe_ctx);

 mi->funcs->mem_input_program_surface_config(
   mi,
   plane_state->format,
   &plane_state->tiling_info,
   &plane_state->plane_size,
   plane_state->rotation,
   NULL,
   false);
 if (mi->funcs->set_blank)
  mi->funcs->set_blank(mi, pipe_ctx->plane_state->visible);

 if (dc->config.gpu_vm_support)
  mi->funcs->mem_input_program_pte_vm(
    pipe_ctx->plane_res.mi,
    plane_state->format,
    &plane_state->tiling_info,
    plane_state->rotation);

 /* Moved programming gamma from dc to hwss */
 if (pipe_ctx->plane_state->update_flags.bits.full_update ||
   pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
   pipe_ctx->plane_state->update_flags.bits.gamma_change)
  hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);

 if (pipe_ctx->plane_state->update_flags.bits.full_update)
  hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);

 DC_LOG_SURFACE(
   "Pipe:%d %p: addr hi:0x%x, "
   "addr low:0x%x, "
   "src: %d, %d, %d,"
   " %d; dst: %d, %d, %d, %d;"
   "clip: %d, %d, %d, %d\n",
   pipe_ctx->pipe_idx,
   (void *) pipe_ctx->plane_state,
   pipe_ctx->plane_state->address.grph.addr.high_part,
   pipe_ctx->plane_state->address.grph.addr.low_part,
   pipe_ctx->plane_state->src_rect.x,
   pipe_ctx->plane_state->src_rect.y,
   pipe_ctx->plane_state->src_rect.width,
   pipe_ctx->plane_state->src_rect.height,
   pipe_ctx->plane_state->dst_rect.x,
   pipe_ctx->plane_state->dst_rect.y,
   pipe_ctx->plane_state->dst_rect.width,
   pipe_ctx->plane_state->dst_rect.height,
   pipe_ctx->plane_state->clip_rect.x,
   pipe_ctx->plane_state->clip_rect.y,
   pipe_ctx->plane_state->clip_rect.width,
   pipe_ctx->plane_state->clip_rect.height);

 DC_LOG_SURFACE(
   "Pipe %d: width, height, x, y\n"
   "viewport:%d, %d, %d, %d\n"
   "recout:  %d, %d, %d, %d\n",
   pipe_ctx->pipe_idx,
   pipe_ctx->plane_res.scl_data.viewport.width,
   pipe_ctx->plane_res.scl_data.viewport.height,
   pipe_ctx->plane_res.scl_data.viewport.x,
   pipe_ctx->plane_res.scl_data.viewport.y,
   pipe_ctx->plane_res.scl_data.recout.width,
   pipe_ctx->plane_res.scl_data.recout.height,
   pipe_ctx->plane_res.scl_data.recout.x,
   pipe_ctx->plane_res.scl_data.recout.y);
}

static void dce110_apply_ctx_for_surface(
  struct dc *dc,
  const struct dc_stream_state *stream,
  int num_planes,
  struct dc_state *context)
{
 int i;

 if (num_planes == 0)
  return;

 if (dc->fbc_compressor)
  dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor);

 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];

  if (pipe_ctx->stream != stream)
   continue;

  /* Need to allocate mem before program front end for Fiji */
  pipe_ctx->plane_res.mi->funcs->allocate_mem_input(
    pipe_ctx->plane_res.mi,
    pipe_ctx->stream->timing.h_total,
    pipe_ctx->stream->timing.v_total,
    pipe_ctx->stream->timing.pix_clk_100hz / 10,
    context->stream_count);

  dce110_program_front_end_for_pipe(dc, pipe_ctx);

  dc->hwss.update_plane_addr(dc, pipe_ctx);

  program_surface_visibility(dc, pipe_ctx);

 }

 if (dc->fbc_compressor)
  enable_fbc(dc, context);
}

static void dce110_post_unlock_program_front_end(
  struct dc *dc,
  struct dc_state *context)
{
}

static void dce110_power_down_fe(struct dc *dc, struct dc_state *state, struct pipe_ctx *pipe_ctx)
{
 struct dce_hwseq *hws = dc->hwseq;
 int fe_idx = pipe_ctx->plane_res.mi ?
  pipe_ctx->plane_res.mi->inst : pipe_ctx->pipe_idx;

 /* Do not power down fe when stream is active on dce*/
 if (dc->current_state->res_ctx.pipe_ctx[fe_idx].stream)
  return;

 hws->funcs.enable_display_power_gating(
  dc, fe_idx, dc->ctx->dc_bios, PIPE_GATING_CONTROL_ENABLE);

 dc->res_pool->transforms[fe_idx]->funcs->transform_reset(
    dc->res_pool->transforms[fe_idx]);
}

static void dce110_wait_for_mpcc_disconnect(
  struct dc *dc,
  struct resource_pool *res_pool,
  struct pipe_ctx *pipe_ctx)
{
 /* do nothing*/
}

static void program_output_csc(struct dc *dc,
  struct pipe_ctx *pipe_ctx,
  enum dc_color_space colorspace,
  uint16_t *matrix,
  int opp_id)
{
 int i;
 struct out_csc_color_matrix tbl_entry;

 if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) {
  enum dc_color_space color_space = pipe_ctx->stream->output_color_space;

  for (i = 0; i < 12; i++)
   tbl_entry.regval[i] = pipe_ctx->stream->csc_color_matrix.matrix[i];

  tbl_entry.color_space = color_space;

  pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment(
    pipe_ctx->plane_res.xfm, &tbl_entry);
 }
}

static void dce110_set_cursor_position(struct pipe_ctx *pipe_ctx)
{
 struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position;
 struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp;
 struct mem_input *mi = pipe_ctx->plane_res.mi;
 struct dc_cursor_mi_param param = {
  .pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10,
  .ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.xtalin_clock_inKhz,
  .viewport = pipe_ctx->plane_res.scl_data.viewport,
  .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz,
  .v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert,
  .rotation = pipe_ctx->plane_state->rotation,
  .mirror = pipe_ctx->plane_state->horizontal_mirror
 };

 /**
 * If the cursor's source viewport is clipped then we need to
 * translate the cursor to appear in the correct position on
 * the screen.
 *
 * This translation isn't affected by scaling so it needs to be
 * done *after* we adjust the position for the scale factor.
 *
 * This is only done by opt-in for now since there are still
 * some usecases like tiled display that might enable the
 * cursor on both streams while expecting dc to clip it.
 */

 if (pos_cpy.translate_by_source) {
  pos_cpy.x += pipe_ctx->plane_state->src_rect.x;
  pos_cpy.y += pipe_ctx->plane_state->src_rect.y;
 }

 if (pipe_ctx->plane_state->address.type
   == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE)
  pos_cpy.enable = false;

 if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state)
  pos_cpy.enable = false;

 if (ipp->funcs->ipp_cursor_set_position)
  ipp->funcs->ipp_cursor_set_position(ipp, &pos_cpy, ¶m);
 if (mi->funcs->set_cursor_position)
  mi->funcs->set_cursor_position(mi, &pos_cpy, ¶m);
}

static void dce110_set_cursor_attribute(struct pipe_ctx *pipe_ctx)
{
 struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes;

 if (pipe_ctx->plane_res.ipp &&
     pipe_ctx->plane_res.ipp->funcs->ipp_cursor_set_attributes)
  pipe_ctx->plane_res.ipp->funcs->ipp_cursor_set_attributes(
    pipe_ctx->plane_res.ipp, attributes);

 if (pipe_ctx->plane_res.mi &&
     pipe_ctx->plane_res.mi->funcs->set_cursor_attributes)
  pipe_ctx->plane_res.mi->funcs->set_cursor_attributes(
    pipe_ctx->plane_res.mi, attributes);

 if (pipe_ctx->plane_res.xfm &&
     pipe_ctx->plane_res.xfm->funcs->set_cursor_attributes)
  pipe_ctx->plane_res.xfm->funcs->set_cursor_attributes(
    pipe_ctx->plane_res.xfm, attributes);
}

bool dce110_set_backlight_level(struct pipe_ctx *pipe_ctx,
 struct set_backlight_level_params *backlight_level_params)
{
 uint32_t backlight_pwm_u16_16 = backlight_level_params->backlight_pwm_u16_16;
 uint32_t frame_ramp = backlight_level_params->frame_ramp;
 struct dc_link *link = pipe_ctx->stream->link;
 struct dc  *dc = link->ctx->dc;
 struct abm *abm = pipe_ctx->stream_res.abm;
 struct panel_cntl *panel_cntl = link->panel_cntl;
 struct dmcu *dmcu = dc->res_pool->dmcu;
 bool fw_set_brightness = true;
 /* DMCU -1 for all controller id values,
 * therefore +1 here
 */

 uint32_t controller_id = pipe_ctx->stream_res.tg->inst + 1;

 if (abm == NULL || panel_cntl == NULL || (abm->funcs->set_backlight_level_pwm == NULL))
  return false;

 if (dmcu)
  fw_set_brightness = dmcu->funcs->is_dmcu_initialized(dmcu);

 if (!fw_set_brightness && panel_cntl->funcs->driver_set_backlight)
  panel_cntl->funcs->driver_set_backlight(panel_cntl, backlight_pwm_u16_16);
 else
  abm->funcs->set_backlight_level_pwm(
    abm,
    backlight_pwm_u16_16,
    frame_ramp,
    controller_id,
    link->panel_cntl->inst);

 return true;
}

void dce110_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx)
{
 struct abm *abm = pipe_ctx->stream_res.abm;
 struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl;

 if (abm)
  abm->funcs->set_abm_immediate_disable(abm,
    pipe_ctx->stream->link->panel_cntl->inst);

 if (panel_cntl)
  panel_cntl->funcs->store_backlight_level(panel_cntl);
}

void dce110_set_pipe(struct pipe_ctx *pipe_ctx)
{
 struct abm *abm = pipe_ctx->stream_res.abm;
 struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl;
 uint32_t otg_inst = pipe_ctx->stream_res.tg->inst + 1;

 if (abm && panel_cntl)
  abm->funcs->set_pipe(abm, otg_inst, panel_cntl->inst);
}

void dce110_enable_lvds_link_output(struct dc_link *link,
  const struct link_resource *link_res,
  enum clock_source_id clock_source,
  uint32_t pixel_clock)
{
 link->link_enc->funcs->enable_lvds_output(
   link->link_enc,
   clock_source,
   pixel_clock);
 link->phy_state.symclk_state = SYMCLK_ON_TX_ON;
}

void dce110_enable_tmds_link_output(struct dc_link *link,
  const struct link_resource *link_res,
  enum signal_type signal,
  enum clock_source_id clock_source,
  enum dc_color_depth color_depth,
  uint32_t pixel_clock)
{
 link->link_enc->funcs->enable_tmds_output(
   link->link_enc,
   clock_source,
   color_depth,
   signal,
   pixel_clock);
 link->phy_state.symclk_state = SYMCLK_ON_TX_ON;
}

void dce110_enable_dp_link_output(
  struct dc_link *link,
  const struct link_resource *link_res,
  enum signal_type signal,
  enum clock_source_id clock_source,
  const struct dc_link_settings *link_settings)
{
 struct dc  *dc = link->ctx->dc;
 struct dmcu *dmcu = dc->res_pool->dmcu;
 struct pipe_ctx *pipes =
   link->dc->current_state->res_ctx.pipe_ctx;
 struct clock_source *dp_cs =
   link->dc->res_pool->dp_clock_source;
 const struct link_hwss *link_hwss = get_link_hwss(link, link_res);
 unsigned int i;

 /*
 * Add the logic to extract BOTH power up and power down sequences
 * from enable/disable link output and only call edp panel control
 * in enable_link_dp and disable_link_dp once.
 */

 if (link->connector_signal == SIGNAL_TYPE_EDP) {
  link->dc->hwss.edp_wait_for_hpd_ready(link, true);
 }

 /* If the current pixel clock source is not DTO(happens after
 * switching from HDMI passive dongle to DP on the same connector),
 * switch the pixel clock source to DTO.
 */


 for (i = 0; i < MAX_PIPES; i++) {
  if (pipes[i].stream != NULL &&
    pipes[i].stream->link == link) {
   if (pipes[i].clock_source != NULL &&
     pipes[i].clock_source->id != CLOCK_SOURCE_ID_DP_DTO) {
    pipes[i].clock_source = dp_cs;
    pipes[i].stream_res.pix_clk_params.requested_pix_clk_100hz =
      pipes[i].stream->timing.pix_clk_100hz;
    pipes[i].clock_source->funcs->program_pix_clk(
      pipes[i].clock_source,
      &pipes[i].stream_res.pix_clk_params,
      dc->link_srv->dp_get_encoding_format(link_settings),
      &pipes[i].pll_settings);
   }
  }
 }

 if (dc->link_srv->dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) {
  if (dc->clk_mgr->funcs->notify_link_rate_change)
   dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link);
 }

 if (dmcu != NULL && dmcu->funcs->lock_phy)
  dmcu->funcs->lock_phy(dmcu);

 if (link_hwss->ext.enable_dp_link_output)
  link_hwss->ext.enable_dp_link_output(link, link_res, signal,
    clock_source, link_settings);

 link->phy_state.symclk_state = SYMCLK_ON_TX_ON;

 if (dmcu != NULL && dmcu->funcs->unlock_phy)
  dmcu->funcs->unlock_phy(dmcu);

 dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_ENABLE_LINK_PHY);
}

void dce110_disable_link_output(struct dc_link *link,
  const struct link_resource *link_res,
  enum signal_type signal)
{
 struct dc *dc = link->ctx->dc;
 const struct link_hwss *link_hwss = get_link_hwss(link, link_res);
 struct dmcu *dmcu = dc->res_pool->dmcu;

 if (signal == SIGNAL_TYPE_EDP &&
   link->dc->hwss.edp_backlight_control &&
   !link->skip_implict_edp_power_control)
  link->dc->hwss.edp_backlight_control(link, false);
 else if (dmcu != NULL && dmcu->funcs->lock_phy)
  dmcu->funcs->lock_phy(dmcu);

 link_hwss->disable_link_output(link, link_res, signal);
 link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
 /*
 * Add the logic to extract BOTH power up and power down sequences
 * from enable/disable link output and only call edp panel control
 * in enable_link_dp and disable_link_dp once.
 */

 if (dmcu != NULL && dmcu->funcs->unlock_phy)
  dmcu->funcs->unlock_phy(dmcu);
 dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY);
}

static const struct hw_sequencer_funcs dce110_funcs = {
 .program_gamut_remap = program_gamut_remap,
 .program_output_csc = program_output_csc,
 .init_hw = dce110_init_hw,
 .apply_ctx_to_hw = dce110_apply_ctx_to_hw,
 .apply_ctx_for_surface = dce110_apply_ctx_for_surface,
 .post_unlock_program_front_end = dce110_post_unlock_program_front_end,
 .update_plane_addr = update_plane_addr,
 .update_pending_status = dce110_update_pending_status,
 .clear_surface_dcc_and_tiling = dce100_reset_surface_dcc_and_tiling,
 .enable_accelerated_mode = dce110_enable_accelerated_mode,
 .enable_timing_synchronization = dce110_enable_timing_synchronization,
 .enable_per_frame_crtc_position_reset = dce110_enable_per_frame_crtc_position_reset,
 .update_info_frame = dce110_update_info_frame,
 .enable_stream = dce110_enable_stream,
 .disable_stream = dce110_disable_stream,
 .unblank_stream = dce110_unblank_stream,
 .blank_stream = dce110_blank_stream,
 .enable_audio_stream = dce110_enable_audio_stream,
 .disable_audio_stream = dce110_disable_audio_stream,
 .disable_plane = dce110_power_down_fe,
 .pipe_control_lock = dce_pipe_control_lock,
 .interdependent_update_lock = NULL,
 .cursor_lock = dce_pipe_control_lock,
 .prepare_bandwidth = dce110_prepare_bandwidth,
 .optimize_bandwidth = dce110_optimize_bandwidth,
 .set_drr = set_drr,
 .get_position = get_position,
 .set_static_screen_control = set_static_screen_control,
 .setup_stereo = NULL,
 .set_avmute = dce110_set_avmute,
 .wait_for_mpcc_disconnect = dce110_wait_for_mpcc_disconnect,
 .edp_backlight_control = dce110_edp_backlight_control,
 .edp_power_control = dce110_edp_power_control,
 .edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready,
 .set_cursor_position = dce110_set_cursor_position,
 .set_cursor_attribute = dce110_set_cursor_attribute,
 .set_backlight_level = dce110_set_backlight_level,
 .set_abm_immediate_disable = dce110_set_abm_immediate_disable,
 .set_pipe = dce110_set_pipe,
 .enable_lvds_link_output = dce110_enable_lvds_link_output,
 .enable_tmds_link_output = dce110_enable_tmds_link_output,
 .enable_dp_link_output = dce110_enable_dp_link_output,
 .disable_link_output = dce110_disable_link_output,
};

static const struct hwseq_private_funcs dce110_private_funcs = {
 .init_pipes = dce110_init_pipes,
 .set_input_transfer_func = dce110_set_input_transfer_func,
 .set_output_transfer_func = dce110_set_output_transfer_func,
 .power_down = dce110_power_down,
 .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating,
 .enable_display_power_gating = dce110_enable_display_power_gating,
 .reset_hw_ctx_wrap = dce110_reset_hw_ctx_wrap,
 .enable_stream_timing = dce110_enable_stream_timing,
 .disable_stream_gating = NULL,
 .enable_stream_gating = NULL,
 .edp_backlight_control = dce110_edp_backlight_control,
};

void dce110_hw_sequencer_construct(struct dc *dc)
{
 dc->hwss = dce110_funcs;
 dc->hwseq->funcs = dce110_private_funcs;
}


Messung V0.5 in Prozent
C=97 H=95 G=95

¤ Dauer der Verarbeitung: 0.41 Sekunden  (vorverarbeitet am  2026-04-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.