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 */
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© 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.