Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  dc.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 "amdgpu.h"

#include "dc.h"

#include "core_status.h"
#include "core_types.h"
#include "hw_sequencer.h"
#include "dce/dce_hwseq.h"

#include "resource.h"
#include "dc_state.h"
#include "dc_state_priv.h"
#include "dc_plane.h"
#include "dc_plane_priv.h"
#include "dc_stream_priv.h"

#include "gpio_service_interface.h"
#include "clk_mgr.h"
#include "clock_source.h"
#include "dc_bios_types.h"

#include "bios_parser_interface.h"
#include "bios/bios_parser_helper.h"
#include "include/irq_service_interface.h"
#include "transform.h"
#include "dmcu.h"
#include "dpp.h"
#include "timing_generator.h"
#include "abm.h"
#include "virtual/virtual_link_encoder.h"
#include "hubp.h"

#include "link_hwss.h"
#include "link_encoder.h"
#include "link_enc_cfg.h"

#include "link.h"
#include "dm_helpers.h"
#include "mem_input.h"

#include "dc_dmub_srv.h"

#include "dsc.h"

#include "vm_helper.h"

#include "dce/dce_i2c.h"

#include "dmub/dmub_srv.h"

#include "dce/dmub_psr.h"

#include "dce/dmub_hw_lock_mgr.h"

#include "dc_trace.h"

#include "hw_sequencer_private.h"

#if defined(CONFIG_DRM_AMD_DC_FP)
#include "dml2/dml2_internal_types.h"
#endif

#include "dce/dmub_outbox.h"

#define CTX \
 dc->ctx

#define DC_LOGGER \
 dc->ctx->logger

static const char DC_BUILD_ID[] = "production-build";

/**
 * DOC: Overview
 *
 * DC is the OS-agnostic component of the amdgpu DC driver.
 *
 * DC maintains and validates a set of structs representing the state of the
 * driver and writes that state to AMD hardware
 *
 * Main DC HW structs:
 *
 * struct dc - The central struct.  One per driver.  Created on driver load,
 * destroyed on driver unload.
 *
 * struct dc_context - One per driver.
 * Used as a backpointer by most other structs in dc.
 *
 * struct dc_link - One per connector (the physical DP, HDMI, miniDP, or eDP
 * plugpoints).  Created on driver load, destroyed on driver unload.
 *
 * struct dc_sink - One per display.  Created on boot or hotplug.
 * Destroyed on shutdown or hotunplug.  A dc_link can have a local sink
 * (the display directly attached).  It may also have one or more remote
 * sinks (in the Multi-Stream Transport case)
 *
 * struct resource_pool - One per driver.  Represents the hw blocks not in the
 * main pipeline.  Not directly accessible by dm.
 *
 * Main dc state structs:
 *
 * These structs can be created and destroyed as needed.  There is a full set of
 * these structs in dc->current_state representing the currently programmed state.
 *
 * struct dc_state - The global DC state to track global state information,
 * such as bandwidth values.
 *
 * struct dc_stream_state - Represents the hw configuration for the pipeline from
 * a framebuffer to a display.  Maps one-to-one with dc_sink.
 *
 * struct dc_plane_state - Represents a framebuffer.  Each stream has at least one,
 * and may have more in the Multi-Plane Overlay case.
 *
 * struct resource_context - Represents the programmable state of everything in
 * the resource_pool.  Not directly accessible by dm.
 *
 * struct pipe_ctx - A member of struct resource_context.  Represents the
 * internal hardware pipeline components.  Each dc_plane_state has either
 * one or two (in the pipe-split case).
 */


/* Private functions */

static inline void elevate_update_type(enum surface_update_type *original, enum surface_update_type new)
{
 if (new > *original)
  *original = new;
}

static void destroy_links(struct dc *dc)
{
 uint32_t i;

 for (i = 0; i < dc->link_count; i++) {
  if (NULL != dc->links[i])
   dc->link_srv->destroy_link(&dc->links[i]);
 }
}

static uint32_t get_num_of_internal_disp(struct dc_link **links, uint32_t num_links)
{
 int i;
 uint32_t count = 0;

 for (i = 0; i < num_links; i++) {
  if (links[i]->connector_signal == SIGNAL_TYPE_EDP ||
    links[i]->is_internal_display)
   count++;
 }

 return count;
}

static int get_seamless_boot_stream_count(struct dc_state *ctx)
{
 uint8_t i;
 uint8_t seamless_boot_stream_count = 0;

 for (i = 0; i < ctx->stream_count; i++)
  if (ctx->streams[i]->apply_seamless_boot_optimization)
   seamless_boot_stream_count++;

 return seamless_boot_stream_count;
}

static bool create_links(
  struct dc *dc,
  uint32_t num_virtual_links)
{
 int i;
 int connectors_num;
 struct dc_bios *bios = dc->ctx->dc_bios;

 dc->link_count = 0;

 connectors_num = bios->funcs->get_connectors_number(bios);

 DC_LOG_DC("BIOS object table - number of connectors: %d", connectors_num);

 if (connectors_num > ENUM_ID_COUNT) {
  dm_error(
   "DC: Number of connectors %d exceeds maximum of %d!\n",
   connectors_num,
   ENUM_ID_COUNT);
  return false;
 }

 dm_output_to_console(
  "DC: %s: connectors_num: physical:%d, virtual:%d\n",
  __func__,
  connectors_num,
  num_virtual_links);

 /* When getting the number of connectors, the VBIOS reports the number of valid indices,
 * but it doesn't say which indices are valid, and not every index has an actual connector.
 * So, if we don't find a connector on an index, that is not an error.
 *
 * - There is no guarantee that the first N indices will be valid
 * - VBIOS may report a higher amount of valid indices than there are actual connectors
 * - Some VBIOS have valid configurations for more connectors than there actually are
 *   on the card. This may be because the manufacturer used the same VBIOS for different
 *   variants of the same card.
 */

 for (i = 0; dc->link_count < connectors_num && i < MAX_LINKS; i++) {
  struct graphics_object_id connector_id = bios->funcs->get_connector_id(bios, i);
  struct link_init_data link_init_params = {0};
  struct dc_link *link;

  if (connector_id.id == CONNECTOR_ID_UNKNOWN)
   continue;

  DC_LOG_DC("BIOS object table - printing link object info for connector number: %d, link_index: %d", i, dc->link_count);

  link_init_params.ctx = dc->ctx;
  /* next BIOS object table connector */
  link_init_params.connector_index = i;
  link_init_params.link_index = dc->link_count;
  link_init_params.dc = dc;
  link = dc->link_srv->create_link(&link_init_params);

  if (link) {
   dc->links[dc->link_count] = link;
   link->dc = dc;
   ++dc->link_count;
  }
 }

 DC_LOG_DC("BIOS object table - end");

 /* Create a link for each usb4 dpia port */
 dc->lowest_dpia_link_index = MAX_LINKS;
 for (i = 0; i < dc->res_pool->usb4_dpia_count; i++) {
  struct link_init_data link_init_params = {0};
  struct dc_link *link;

  link_init_params.ctx = dc->ctx;
  link_init_params.connector_index = i;
  link_init_params.link_index = dc->link_count;
  link_init_params.dc = dc;
  link_init_params.is_dpia_link = true;

  link = dc->link_srv->create_link(&link_init_params);
  if (link) {
   if (dc->lowest_dpia_link_index > dc->link_count)
    dc->lowest_dpia_link_index = dc->link_count;

   dc->links[dc->link_count] = link;
   link->dc = dc;
   ++dc->link_count;
  }
 }

 for (i = 0; i < num_virtual_links; i++) {
  struct dc_link *link = kzalloc(sizeof(*link), GFP_KERNEL);
  struct encoder_init_data enc_init = {0};

  if (link == NULL) {
   BREAK_TO_DEBUGGER();
   goto failed_alloc;
  }

  link->link_index = dc->link_count;
  dc->links[dc->link_count] = link;
  dc->link_count++;

  link->ctx = dc->ctx;
  link->dc = dc;
  link->connector_signal = SIGNAL_TYPE_VIRTUAL;
  link->link_id.type = OBJECT_TYPE_CONNECTOR;
  link->link_id.id = CONNECTOR_ID_VIRTUAL;
  link->link_id.enum_id = ENUM_ID_1;
  link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
  link->link_enc = kzalloc(sizeof(*link->link_enc), GFP_KERNEL);

  if (!link->link_enc) {
   BREAK_TO_DEBUGGER();
   goto failed_alloc;
  }

  link->link_status.dpcd_caps = &link->dpcd_caps;

  enc_init.ctx = dc->ctx;
  enc_init.channel = CHANNEL_ID_UNKNOWN;
  enc_init.hpd_source = HPD_SOURCEID_UNKNOWN;
  enc_init.transmitter = TRANSMITTER_UNKNOWN;
  enc_init.connector = link->link_id;
  enc_init.encoder.type = OBJECT_TYPE_ENCODER;
  enc_init.encoder.id = ENCODER_ID_INTERNAL_VIRTUAL;
  enc_init.encoder.enum_id = ENUM_ID_1;
  virtual_link_encoder_construct(link->link_enc, &enc_init);
 }

 dc->caps.num_of_internal_disp = get_num_of_internal_disp(dc->links, dc->link_count);

 return true;

failed_alloc:
 return false;
}

/* Create additional DIG link encoder objects if fewer than the platform
 * supports were created during link construction. This can happen if the
 * number of physical connectors is less than the number of DIGs.
 */

static bool create_link_encoders(struct dc *dc)
{
 bool res = true;
 unsigned int num_usb4_dpia = dc->res_pool->res_cap->num_usb4_dpia;
 unsigned int num_dig_link_enc = dc->res_pool->res_cap->num_dig_link_enc;
 int i;

 /* A platform without USB4 DPIA endpoints has a fixed mapping between DIG
 * link encoders and physical display endpoints and does not require
 * additional link encoder objects.
 */

 if (num_usb4_dpia == 0)
  return res;

 /* Create as many link encoder objects as the platform supports. DPIA
 * endpoints can be programmably mapped to any DIG.
 */

 if (num_dig_link_enc > dc->res_pool->dig_link_enc_count) {
  for (i = 0; i < num_dig_link_enc; i++) {
   struct link_encoder *link_enc = dc->res_pool->link_encoders[i];

   if (!link_enc && dc->res_pool->funcs->link_enc_create_minimal) {
    link_enc = dc->res_pool->funcs->link_enc_create_minimal(dc->ctx,
      (enum engine_id)(ENGINE_ID_DIGA + i));
    if (link_enc) {
     dc->res_pool->link_encoders[i] = link_enc;
     dc->res_pool->dig_link_enc_count++;
    } else {
     res = false;
    }
   }
  }
 }

 return res;
}

/* Destroy any additional DIG link encoder objects created by
 * create_link_encoders().
 * NB: Must only be called after destroy_links().
 */

static void destroy_link_encoders(struct dc *dc)
{
 unsigned int num_usb4_dpia;
 unsigned int num_dig_link_enc;
 int i;

 if (!dc->res_pool)
  return;

 num_usb4_dpia = dc->res_pool->res_cap->num_usb4_dpia;
 num_dig_link_enc = dc->res_pool->res_cap->num_dig_link_enc;

 /* A platform without USB4 DPIA endpoints has a fixed mapping between DIG
 * link encoders and physical display endpoints and does not require
 * additional link encoder objects.
 */

 if (num_usb4_dpia == 0)
  return;

 for (i = 0; i < num_dig_link_enc; i++) {
  struct link_encoder *link_enc = dc->res_pool->link_encoders[i];

  if (link_enc) {
   link_enc->funcs->destroy(&link_enc);
   dc->res_pool->link_encoders[i] = NULL;
   dc->res_pool->dig_link_enc_count--;
  }
 }
}

static struct dc_perf_trace *dc_perf_trace_create(void)
{
 return kzalloc(sizeof(struct dc_perf_trace), GFP_KERNEL);
}

static void dc_perf_trace_destroy(struct dc_perf_trace **perf_trace)
{
 kfree(*perf_trace);
 *perf_trace = NULL;
}

static bool set_long_vtotal(struct dc *dc, struct dc_stream_state *stream, struct dc_crtc_timing_adjust *adjust)
{
 if (!dc || !stream || !adjust)
  return false;

 if (!dc->current_state)
  return false;

 int i;

 for (i = 0; i < MAX_PIPES; i++) {
  struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];

  if (pipe->stream == stream && pipe->stream_res.tg) {
   if (dc->hwss.set_long_vtotal)
    dc->hwss.set_long_vtotal(&pipe, 1, adjust->v_total_min, adjust->v_total_max);

   return true;
  }
 }

 return false;
}

/**
 *  dc_stream_adjust_vmin_vmax - look up pipe context & update parts of DRR
 *  @dc:     dc reference
 *  @stream: Initial dc stream state
 *  @adjust: Updated parameters for vertical_total_min and vertical_total_max
 *
 *  Looks up the pipe context of dc_stream_state and updates the
 *  vertical_total_min and vertical_total_max of the DRR, Dynamic Refresh
 *  Rate, which is a power-saving feature that targets reducing panel
 *  refresh rate while the screen is static
 *
 *  Return: %true if the pipe context is found and adjusted;
 *          %false if the pipe context is not found.
 */

bool dc_stream_adjust_vmin_vmax(struct dc *dc,
  struct dc_stream_state *stream,
  struct dc_crtc_timing_adjust *adjust)
{
 int i;

 /*
 * Don't adjust DRR while there's bandwidth optimizations pending to
 * avoid conflicting with firmware updates.
 */

 if (dc->ctx->dce_version > DCE_VERSION_MAX) {
  if ((dc->optimized_required || dc->wm_optimized_required) &&
   (stream->adjust.v_total_max != adjust->v_total_max ||
   stream->adjust.v_total_min != adjust->v_total_min)) {
   stream->adjust.timing_adjust_pending = true;
   return false;
  }
 }

 dc_exit_ips_for_hw_access(dc);

 stream->adjust.v_total_max = adjust->v_total_max;
 stream->adjust.v_total_mid = adjust->v_total_mid;
 stream->adjust.v_total_mid_frame_num = adjust->v_total_mid_frame_num;
 stream->adjust.v_total_min = adjust->v_total_min;
 stream->adjust.allow_otg_v_count_halt = adjust->allow_otg_v_count_halt;

 if (dc->caps.max_v_total != 0 &&
  (adjust->v_total_max > dc->caps.max_v_total || adjust->v_total_min > dc->caps.max_v_total)) {
  stream->adjust.timing_adjust_pending = false;
  if (adjust->allow_otg_v_count_halt)
   return set_long_vtotal(dc, stream, adjust);
  else
   return false;
 }

 for (i = 0; i < MAX_PIPES; i++) {
  struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];

  if (pipe->stream == stream && pipe->stream_res.tg) {
   dc->hwss.set_drr(&pipe,
     1,
     *adjust);
   stream->adjust.timing_adjust_pending = false;
   return true;
  }
 }
 return false;
}

/**
 * dc_stream_get_last_used_drr_vtotal - Looks up the pipe context of
 * dc_stream_state and gets the last VTOTAL used by DRR (Dynamic Refresh Rate)
 *
 * @dc: [in] dc reference
 * @stream: [in] Initial dc stream state
 * @refresh_rate: [in] new refresh_rate
 *
 * Return: %true if the pipe context is found and there is an associated
 *         timing_generator for the DC;
 *         %false if the pipe context is not found or there is no
 *         timing_generator for the DC.
 */

bool dc_stream_get_last_used_drr_vtotal(struct dc *dc,
  struct dc_stream_state *stream,
  uint32_t *refresh_rate)
{
 bool status = false;

 int i = 0;

 dc_exit_ips_for_hw_access(dc);

 for (i = 0; i < MAX_PIPES; i++) {
  struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];

  if (pipe->stream == stream && pipe->stream_res.tg) {
   /* Only execute if a function pointer has been defined for
 * the DC version in question
 */

   if (pipe->stream_res.tg->funcs->get_last_used_drr_vtotal) {
    pipe->stream_res.tg->funcs->get_last_used_drr_vtotal(pipe->stream_res.tg, refresh_rate);

    status = true;

    break;
   }
  }
 }

 return status;
}

#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
static inline void
dc_stream_forward_dmub_crc_window(struct dc_dmub_srv *dmub_srv,
  struct rect *rect, struct otg_phy_mux *mux_mapping, bool is_stop)
{
 union dmub_rb_cmd cmd = {0};

 cmd.secure_display.roi_info.phy_id = mux_mapping->phy_output_num;
 cmd.secure_display.roi_info.otg_id = mux_mapping->otg_output_num;

 if (is_stop) {
  cmd.secure_display.header.type = DMUB_CMD__SECURE_DISPLAY;
  cmd.secure_display.header.sub_type = DMUB_CMD__SECURE_DISPLAY_CRC_STOP_UPDATE;
 } else {
  cmd.secure_display.header.type = DMUB_CMD__SECURE_DISPLAY;
  cmd.secure_display.header.sub_type = DMUB_CMD__SECURE_DISPLAY_CRC_WIN_NOTIFY;
  cmd.secure_display.roi_info.x_start = rect->x;
  cmd.secure_display.roi_info.y_start = rect->y;
  cmd.secure_display.roi_info.x_end = rect->x + rect->width;
  cmd.secure_display.roi_info.y_end = rect->y + rect->height;
 }

 dc_wake_and_execute_dmub_cmd(dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_NO_WAIT);
}

static inline void
dc_stream_forward_dmcu_crc_window(struct dmcu *dmcu,
  struct rect *rect, struct otg_phy_mux *mux_mapping, bool is_stop)
{
 if (is_stop)
  dmcu->funcs->stop_crc_win_update(dmcu, mux_mapping);
 else
  dmcu->funcs->forward_crc_window(dmcu, rect, mux_mapping);
}

bool
dc_stream_forward_crc_window(struct dc_stream_state *stream,
  struct rect *rect, uint8_t phy_id, bool is_stop)
{
 struct dmcu *dmcu;
 struct dc_dmub_srv *dmub_srv;
 struct otg_phy_mux mux_mapping;
 struct pipe_ctx *pipe;
 int i;
 struct dc *dc = stream->ctx->dc;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe = &dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe->stream == stream && !pipe->top_pipe && !pipe->prev_odm_pipe)
   break;
 }

 /* Stream not found */
 if (i == MAX_PIPES)
  return false;

 mux_mapping.phy_output_num = phy_id;
 mux_mapping.otg_output_num = pipe->stream_res.tg->inst;

 dmcu = dc->res_pool->dmcu;
 dmub_srv = dc->ctx->dmub_srv;

 /* forward to dmub */
 if (dmub_srv)
  dc_stream_forward_dmub_crc_window(dmub_srv, rect, &mux_mapping, is_stop);
 /* forward to dmcu */
 else if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu))
  dc_stream_forward_dmcu_crc_window(dmcu, rect, &mux_mapping, is_stop);
 else
  return false;

 return true;
}

static void
dc_stream_forward_dmub_multiple_crc_window(struct dc_dmub_srv *dmub_srv,
  struct crc_window *window, struct otg_phy_mux *mux_mapping, bool stop)
{
 int i;
 union dmub_rb_cmd cmd = {0};

 cmd.secure_display.mul_roi_ctl.phy_id = mux_mapping->phy_output_num;
 cmd.secure_display.mul_roi_ctl.otg_id = mux_mapping->otg_output_num;

 cmd.secure_display.header.type = DMUB_CMD__SECURE_DISPLAY;

 if (stop) {
  cmd.secure_display.header.sub_type = DMUB_CMD__SECURE_DISPLAY_MULTIPLE_CRC_STOP_UPDATE;
 } else {
  cmd.secure_display.header.sub_type = DMUB_CMD__SECURE_DISPLAY_MULTIPLE_CRC_WIN_NOTIFY;
  for (i = 0; i < MAX_CRC_WINDOW_NUM; i++) {
   cmd.secure_display.mul_roi_ctl.roi_ctl[i].x_start = window[i].rect.x;
   cmd.secure_display.mul_roi_ctl.roi_ctl[i].y_start = window[i].rect.y;
   cmd.secure_display.mul_roi_ctl.roi_ctl[i].x_end = window[i].rect.x + window[i].rect.width;
   cmd.secure_display.mul_roi_ctl.roi_ctl[i].y_end = window[i].rect.y + window[i].rect.height;
   cmd.secure_display.mul_roi_ctl.roi_ctl[i].enable = window[i].enable;
  }
 }

 dc_wake_and_execute_dmub_cmd(dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_NO_WAIT);
}

bool
dc_stream_forward_multiple_crc_window(struct dc_stream_state *stream,
  struct crc_window *window, uint8_t phy_id, bool stop)
{
 struct dc_dmub_srv *dmub_srv;
 struct otg_phy_mux mux_mapping;
 struct pipe_ctx *pipe;
 int i;
 struct dc *dc = stream->ctx->dc;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe = &dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe->stream == stream && !pipe->top_pipe && !pipe->prev_odm_pipe)
   break;
 }

 /* Stream not found */
 if (i == MAX_PIPES)
  return false;

 mux_mapping.phy_output_num = phy_id;
 mux_mapping.otg_output_num = pipe->stream_res.tg->inst;

 dmub_srv = dc->ctx->dmub_srv;

 /* forward to dmub only. no dmcu support*/
 if (dmub_srv)
  dc_stream_forward_dmub_multiple_crc_window(dmub_srv, window, &mux_mapping, stop);
 else
  return false;

 return true;
}
#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */

/**
 * dc_stream_configure_crc() - Configure CRC capture for the given stream.
 * @dc: DC Object
 * @stream: The stream to configure CRC on.
 * @crc_window: CRC window (x/y start/end) information
 * @enable: Enable CRC if true, disable otherwise.
 * @continuous: Capture CRC on every frame if true. Otherwise, only capture
 *              once.
 * @idx: Capture CRC on which CRC engine instance
 * @reset: Reset CRC engine before the configuration
 *
 * By default, the entire frame is used to calculate the CRC.
 *
 * Return: %false if the stream is not found or CRC capture is not supported;
 *         %true if the stream has been configured.
 */

bool dc_stream_configure_crc(struct dc *dc, struct dc_stream_state *stream,
        struct crc_params *crc_window, bool enable, bool continuous,
        uint8_t idx, bool reset)
{
 struct pipe_ctx *pipe;
 struct crc_params param;
 struct timing_generator *tg;

 pipe = resource_get_otg_master_for_stream(
   &dc->current_state->res_ctx, stream);

 /* Stream not found */
 if (pipe == NULL)
  return false;

 dc_exit_ips_for_hw_access(dc);

 /* By default, capture the full frame */
 param.windowa_x_start = 0;
 param.windowa_y_start = 0;
 param.windowa_x_end = pipe->stream->timing.h_addressable;
 param.windowa_y_end = pipe->stream->timing.v_addressable;
 param.windowb_x_start = 0;
 param.windowb_y_start = 0;
 param.windowb_x_end = pipe->stream->timing.h_addressable;
 param.windowb_y_end = pipe->stream->timing.v_addressable;

 if (crc_window) {
  param.windowa_x_start = crc_window->windowa_x_start;
  param.windowa_y_start = crc_window->windowa_y_start;
  param.windowa_x_end = crc_window->windowa_x_end;
  param.windowa_y_end = crc_window->windowa_y_end;
  param.windowb_x_start = crc_window->windowb_x_start;
  param.windowb_y_start = crc_window->windowb_y_start;
  param.windowb_x_end = crc_window->windowb_x_end;
  param.windowb_y_end = crc_window->windowb_y_end;
 }

 param.dsc_mode = pipe->stream->timing.flags.DSC ? 1:0;
 param.odm_mode = pipe->next_odm_pipe ? 1:0;

 /* Default to the union of both windows */
 param.selection = UNION_WINDOW_A_B;
 param.continuous_mode = continuous;
 param.enable = enable;

 param.crc_eng_inst = idx;
 param.reset = reset;

 tg = pipe->stream_res.tg;

 /* Only call if supported */
 if (tg->funcs->configure_crc)
  return tg->funcs->configure_crc(tg, ¶m);
 DC_LOG_WARNING("CRC capture not supported.");
 return false;
}

/**
 * dc_stream_get_crc() - Get CRC values for the given stream.
 *
 * @dc: DC object.
 * @stream: The DC stream state of the stream to get CRCs from.
 * @idx: index of crc engine to get CRC from
 * @r_cr: CRC value for the red component.
 * @g_y:  CRC value for the green component.
 * @b_cb: CRC value for the blue component.
 *
 * dc_stream_configure_crc needs to be called beforehand to enable CRCs.
 *
 * Return:
 * %false if stream is not found, or if CRCs are not enabled.
 */

bool dc_stream_get_crc(struct dc *dc, struct dc_stream_state *stream, uint8_t idx,
         uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb)
{
 int i;
 struct pipe_ctx *pipe;
 struct timing_generator *tg;

 dc_exit_ips_for_hw_access(dc);

 for (i = 0; i < MAX_PIPES; i++) {
  pipe = &dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe->stream == stream)
   break;
 }
 /* Stream not found */
 if (i == MAX_PIPES)
  return false;

 tg = pipe->stream_res.tg;

 if (tg->funcs->get_crc)
  return tg->funcs->get_crc(tg, idx, r_cr, g_y, b_cb);
 DC_LOG_WARNING("CRC capture not supported.");
 return false;
}

void dc_stream_set_dyn_expansion(struct dc *dc, struct dc_stream_state *stream,
  enum dc_dynamic_expansion option)
{
 /* OPP FMT dyn expansion updates*/
 int i;
 struct pipe_ctx *pipe_ctx;

 dc_exit_ips_for_hw_access(dc);

 for (i = 0; i < MAX_PIPES; i++) {
  if (dc->current_state->res_ctx.pipe_ctx[i].stream
    == stream) {
   pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
   pipe_ctx->stream_res.opp->dyn_expansion = option;
   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);
  }
 }
}

void dc_stream_set_dither_option(struct dc_stream_state *stream,
  enum dc_dither_option option)
{
 struct bit_depth_reduction_params params;
 struct dc_link *link = stream->link;
 struct pipe_ctx *pipes = NULL;
 int i;

 for (i = 0; i < MAX_PIPES; i++) {
  if (link->dc->current_state->res_ctx.pipe_ctx[i].stream ==
    stream) {
   pipes = &link->dc->current_state->res_ctx.pipe_ctx[i];
   break;
  }
 }

 if (!pipes)
  return;
 if (option > DITHER_OPTION_MAX)
  return;

 dc_exit_ips_for_hw_access(stream->ctx->dc);

 stream->dither_option = option;

 memset(¶ms, 0, sizeof(params));
 resource_build_bit_depth_reduction_params(stream, ¶ms);
 stream->bit_depth_params = params;

 if (pipes->plane_res.xfm &&
     pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth) {
  pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth(
   pipes->plane_res.xfm,
   pipes->plane_res.scl_data.lb_params.depth,
   &stream->bit_depth_params);
 }

 pipes->stream_res.opp->funcs->
  opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms);
}

bool dc_stream_set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream)
{
 int i;
 bool ret = false;
 struct pipe_ctx *pipes;

 dc_exit_ips_for_hw_access(dc);

 for (i = 0; i < MAX_PIPES; i++) {
  if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) {
   pipes = &dc->current_state->res_ctx.pipe_ctx[i];
   dc->hwss.program_gamut_remap(pipes);
   ret = true;
  }
 }

 return ret;
}

bool dc_stream_program_csc_matrix(struct dc *dc, struct dc_stream_state *stream)
{
 int i;
 bool ret = false;
 struct pipe_ctx *pipes;

 dc_exit_ips_for_hw_access(dc);

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

   pipes = &dc->current_state->res_ctx.pipe_ctx[i];
   dc->hwss.program_output_csc(dc,
     pipes,
     stream->output_color_space,
     stream->csc_color_matrix.matrix,
     pipes->stream_res.opp->inst);
   ret = true;
  }
 }

 return ret;
}

void dc_stream_set_static_screen_params(struct dc *dc,
  struct dc_stream_state **streams,
  int num_streams,
  const struct dc_static_screen_params *params)
{
 int i, j;
 struct pipe_ctx *pipes_affected[MAX_PIPES];
 int num_pipes_affected = 0;

 dc_exit_ips_for_hw_access(dc);

 for (i = 0; i < num_streams; i++) {
  struct dc_stream_state *stream = streams[i];

  for (j = 0; j < MAX_PIPES; j++) {
   if (dc->current_state->res_ctx.pipe_ctx[j].stream
     == stream) {
    pipes_affected[num_pipes_affected++] =
      &dc->current_state->res_ctx.pipe_ctx[j];
   }
  }
 }

 dc->hwss.set_static_screen_control(pipes_affected, num_pipes_affected, params);
}

static void dc_destruct(struct dc *dc)
{
 // reset link encoder assignment table on destruct
 if (dc->res_pool && dc->res_pool->funcs->link_encs_assign &&
   !dc->config.unify_link_enc_assignment)
  link_enc_cfg_init(dc, dc->current_state);

 if (dc->current_state) {
  dc_state_release(dc->current_state);
  dc->current_state = NULL;
 }

 destroy_links(dc);

 destroy_link_encoders(dc);

 if (dc->clk_mgr) {
  dc_destroy_clk_mgr(dc->clk_mgr);
  dc->clk_mgr = NULL;
 }

 dc_destroy_resource_pool(dc);

 if (dc->link_srv)
  link_destroy_link_service(&dc->link_srv);

 if (dc->ctx) {
  if (dc->ctx->gpio_service)
   dal_gpio_service_destroy(&dc->ctx->gpio_service);

  if (dc->ctx->created_bios)
   dal_bios_parser_destroy(&dc->ctx->dc_bios);
  kfree(dc->ctx->logger);
  dc_perf_trace_destroy(&dc->ctx->perf_trace);

  kfree(dc->ctx);
  dc->ctx = NULL;
 }

 kfree(dc->bw_vbios);
 dc->bw_vbios = NULL;

 kfree(dc->bw_dceip);
 dc->bw_dceip = NULL;

 kfree(dc->dcn_soc);
 dc->dcn_soc = NULL;

 kfree(dc->dcn_ip);
 dc->dcn_ip = NULL;

 kfree(dc->vm_helper);
 dc->vm_helper = NULL;

}

static bool dc_construct_ctx(struct dc *dc,
  const struct dc_init_data *init_params)
{
 struct dc_context *dc_ctx;

 dc_ctx = kzalloc(sizeof(*dc_ctx), GFP_KERNEL);
 if (!dc_ctx)
  return false;

 dc_stream_init_rmcm_3dlut(dc);

 dc_ctx->cgs_device = init_params->cgs_device;
 dc_ctx->driver_context = init_params->driver;
 dc_ctx->dc = dc;
 dc_ctx->asic_id = init_params->asic_id;
 dc_ctx->dc_sink_id_count = 0;
 dc_ctx->dc_stream_id_count = 0;
 dc_ctx->dce_environment = init_params->dce_environment;
 dc_ctx->dcn_reg_offsets = init_params->dcn_reg_offsets;
 dc_ctx->nbio_reg_offsets = init_params->nbio_reg_offsets;
 dc_ctx->clk_reg_offsets = init_params->clk_reg_offsets;

 /* Create logger */
 dc_ctx->logger = kmalloc(sizeof(*dc_ctx->logger), GFP_KERNEL);

 if (!dc_ctx->logger) {
  kfree(dc_ctx);
  return false;
 }

 dc_ctx->logger->dev = adev_to_drm(init_params->driver);
 dc->dml.logger = dc_ctx->logger;

 dc_ctx->dce_version = resource_parse_asic_id(init_params->asic_id);

 dc_ctx->perf_trace = dc_perf_trace_create();
 if (!dc_ctx->perf_trace) {
  kfree(dc_ctx);
  ASSERT_CRITICAL(false);
  return false;
 }

 dc->ctx = dc_ctx;

 dc->link_srv = link_create_link_service();
 if (!dc->link_srv)
  return false;

 return true;
}

static bool dc_construct(struct dc *dc,
  const struct dc_init_data *init_params)
{
 struct dc_context *dc_ctx;
 struct bw_calcs_dceip *dc_dceip;
 struct bw_calcs_vbios *dc_vbios;
 struct dcn_soc_bounding_box *dcn_soc;
 struct dcn_ip_params *dcn_ip;

 dc->config = init_params->flags;

 // Allocate memory for the vm_helper
 dc->vm_helper = kzalloc(sizeof(struct vm_helper), GFP_KERNEL);
 if (!dc->vm_helper) {
  dm_error("%s: failed to create dc->vm_helper\n", __func__);
  goto fail;
 }

 memcpy(&dc->bb_overrides, &init_params->bb_overrides, sizeof(dc->bb_overrides));

 dc_dceip = kzalloc(sizeof(*dc_dceip), GFP_KERNEL);
 if (!dc_dceip) {
  dm_error("%s: failed to create dceip\n", __func__);
  goto fail;
 }

 dc->bw_dceip = dc_dceip;

 dc_vbios = kzalloc(sizeof(*dc_vbios), GFP_KERNEL);
 if (!dc_vbios) {
  dm_error("%s: failed to create vbios\n", __func__);
  goto fail;
 }

 dc->bw_vbios = dc_vbios;
 dcn_soc = kzalloc(sizeof(*dcn_soc), GFP_KERNEL);
 if (!dcn_soc) {
  dm_error("%s: failed to create dcn_soc\n", __func__);
  goto fail;
 }

 dc->dcn_soc = dcn_soc;

 dcn_ip = kzalloc(sizeof(*dcn_ip), GFP_KERNEL);
 if (!dcn_ip) {
  dm_error("%s: failed to create dcn_ip\n", __func__);
  goto fail;
 }

 dc->dcn_ip = dcn_ip;

 if (init_params->bb_from_dmub)
  dc->dml2_options.bb_from_dmub = init_params->bb_from_dmub;
 else
  dc->dml2_options.bb_from_dmub = NULL;

 if (!dc_construct_ctx(dc, init_params)) {
  dm_error("%s: failed to create ctx\n", __func__);
  goto fail;
 }

 dc_ctx = dc->ctx;

 /* Resource should construct all asic specific resources.
 * This should be the only place where we need to parse the asic id
 */

 if (init_params->vbios_override)
  dc_ctx->dc_bios = init_params->vbios_override;
 else {
  /* Create BIOS parser */
  struct bp_init_data bp_init_data;

  bp_init_data.ctx = dc_ctx;
  bp_init_data.bios = init_params->asic_id.atombios_base_address;

  dc_ctx->dc_bios = dal_bios_parser_create(
    &bp_init_data, dc_ctx->dce_version);

  if (!dc_ctx->dc_bios) {
   ASSERT_CRITICAL(false);
   goto fail;
  }

  dc_ctx->created_bios = true;
 }

 dc->vendor_signature = init_params->vendor_signature;

 /* Create GPIO service */
 dc_ctx->gpio_service = dal_gpio_service_create(
   dc_ctx->dce_version,
   dc_ctx->dce_environment,
   dc_ctx);

 if (!dc_ctx->gpio_service) {
  ASSERT_CRITICAL(false);
  goto fail;
 }

 dc->res_pool = dc_create_resource_pool(dc, init_params, dc_ctx->dce_version);
 if (!dc->res_pool)
  goto fail;

 /* set i2c speed if not done by the respective dcnxxx__resource.c */
 if (dc->caps.i2c_speed_in_khz_hdcp == 0)
  dc->caps.i2c_speed_in_khz_hdcp = dc->caps.i2c_speed_in_khz;
 if (dc->caps.max_optimizable_video_width == 0)
  dc->caps.max_optimizable_video_width = 5120;
 dc->clk_mgr = dc_clk_mgr_create(dc->ctx, dc->res_pool->pp_smu, dc->res_pool->dccg);
 if (!dc->clk_mgr)
  goto fail;
#ifdef CONFIG_DRM_AMD_DC_FP
 dc->clk_mgr->force_smu_not_present = init_params->force_smu_not_present;

 if (dc->res_pool->funcs->update_bw_bounding_box) {
  DC_FP_START();
  dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params);
  DC_FP_END();
 }
#endif

 if (!create_links(dc, init_params->num_virtual_links))
  goto fail;

 /* Create additional DIG link encoder objects if fewer than the platform
 * supports were created during link construction.
 */

 if (!create_link_encoders(dc))
  goto fail;

 /* Creation of current_state must occur after dc->dml
 * is initialized in dc_create_resource_pool because
 * on creation it copies the contents of dc->dml
 */

 dc->current_state = dc_state_create(dc, NULL);

 if (!dc->current_state) {
  dm_error("%s: failed to create validate ctx\n", __func__);
  goto fail;
 }

 return true;

fail:
 return false;
}

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

 for (i = 0; i < stream->num_wb_info; i++)
  stream->writeback_info[i].wb_enabled = false;
}

static void apply_ctx_interdependent_lock(struct dc *dc,
       struct dc_state *context,
       struct dc_stream_state *stream,
       bool lock)
{
 int i;

 /* Checks if interdependent update function pointer is NULL or not, takes care of DCE110 case */
 if (dc->hwss.interdependent_update_lock)
  dc->hwss.interdependent_update_lock(dc, context, lock);
 else {
  for (i = 0; i < dc->res_pool->pipe_count; i++) {
   struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
   struct pipe_ctx *old_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];

   // Copied conditions that were previously in dce110_apply_ctx_for_surface
   if (stream == pipe_ctx->stream) {
    if (resource_is_pipe_type(pipe_ctx, OPP_HEAD) &&
     (pipe_ctx->plane_state || old_pipe_ctx->plane_state))
     dc->hwss.pipe_control_lock(dc, pipe_ctx, lock);
   }
  }
 }
}

static void dc_update_visual_confirm_color(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx)
{
 if (dc->debug.visual_confirm & VISUAL_CONFIRM_EXPLICIT) {
  memcpy(&pipe_ctx->visual_confirm_color, &pipe_ctx->plane_state->visual_confirm_color,
  sizeof(pipe_ctx->visual_confirm_color));
  return;
 }

 if (dc->ctx->dce_version >= DCN_VERSION_1_0) {
  memset(&pipe_ctx->visual_confirm_color, 0, sizeof(struct tg_color));

  if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR)
   get_hdr_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
  else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE)
   get_surface_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
  else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SWIZZLE)
   get_surface_tile_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
  else if (dc->debug.visual_confirm == VISUAL_CONFIRM_HW_CURSOR)
   get_cursor_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
  else if (dc->debug.visual_confirm == VISUAL_CONFIRM_DCC)
   get_dcc_visual_confirm_color(dc, pipe_ctx, &(pipe_ctx->visual_confirm_color));
  else {
   if (dc->ctx->dce_version < DCN_VERSION_2_0)
    color_space_to_black_color(
     dc, pipe_ctx->stream->output_color_space, &(pipe_ctx->visual_confirm_color));
  }
  if (dc->ctx->dce_version >= DCN_VERSION_2_0) {
   if (dc->debug.visual_confirm == VISUAL_CONFIRM_MPCTREE)
    get_mpctree_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
   else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SUBVP)
    get_subvp_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
   else if (dc->debug.visual_confirm == VISUAL_CONFIRM_MCLK_SWITCH)
    get_mclk_switch_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
   else if (dc->debug.visual_confirm == VISUAL_CONFIRM_FAMS2)
    get_fams2_visual_confirm_color(dc, context, pipe_ctx, &(pipe_ctx->visual_confirm_color));
   else if (dc->debug.visual_confirm == VISUAL_CONFIRM_VABC)
    get_vabc_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
  }
 }
}

void dc_get_visual_confirm_for_stream(
 struct dc *dc,
 struct dc_stream_state *stream_state,
 struct tg_color *color)
{
 struct dc_stream_status *stream_status = dc_stream_get_status(stream_state);
 struct pipe_ctx *pipe_ctx;
 int i;
 struct dc_plane_state *plane_state = NULL;

 if (!stream_status)
  return;

 switch (dc->debug.visual_confirm) {
 case VISUAL_CONFIRM_DISABLE:
  return;
 case VISUAL_CONFIRM_PSR:
 case VISUAL_CONFIRM_FAMS:
  pipe_ctx = dc_stream_get_pipe_ctx(stream_state);
  if (!pipe_ctx)
   return;
  dc_dmub_srv_get_visual_confirm_color_cmd(dc, pipe_ctx);
  memcpy(color, &dc->ctx->dmub_srv->dmub->visual_confirm_color, sizeof(struct tg_color));
  return;

 default:
  /* find plane with highest layer_index */
  for (i = 0; i < stream_status->plane_count; i++) {
   if (stream_status->plane_states[i]->visible)
    plane_state = stream_status->plane_states[i];
  }
  if (!plane_state)
   return;
  /* find pipe that contains plane with highest layer index */
  for (i = 0; i < MAX_PIPES; i++) {
   struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];

   if (pipe->plane_state == plane_state) {
    memcpy(color, &pipe->visual_confirm_color, sizeof(struct tg_color));
    return;
   }
  }
 }
}

static void disable_dangling_plane(struct dc *dc, struct dc_state *context)
{
 int i, j;
 struct dc_state *dangling_context = dc_state_create_current_copy(dc);
 struct dc_state *current_ctx;
 struct pipe_ctx *pipe;
 struct timing_generator *tg;

 if (dangling_context == NULL)
  return;

 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct dc_stream_state *old_stream =
    dc->current_state->res_ctx.pipe_ctx[i].stream;
  bool should_disable = true;
  bool pipe_split_change = false;

  if ((context->res_ctx.pipe_ctx[i].top_pipe) &&
   (dc->current_state->res_ctx.pipe_ctx[i].top_pipe))
   pipe_split_change = context->res_ctx.pipe_ctx[i].top_pipe->pipe_idx !=
    dc->current_state->res_ctx.pipe_ctx[i].top_pipe->pipe_idx;
  else
   pipe_split_change = context->res_ctx.pipe_ctx[i].top_pipe !=
    dc->current_state->res_ctx.pipe_ctx[i].top_pipe;

  for (j = 0; j < context->stream_count; j++) {
   if (old_stream == context->streams[j]) {
    should_disable = false;
    break;
   }
  }
  if (!should_disable && pipe_split_change &&
    dc->current_state->stream_count != context->stream_count)
   should_disable = true;

  if (old_stream && !dc->current_state->res_ctx.pipe_ctx[i].top_pipe &&
    !dc->current_state->res_ctx.pipe_ctx[i].prev_odm_pipe) {
   struct pipe_ctx *old_pipe, *new_pipe;

   old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
   new_pipe = &context->res_ctx.pipe_ctx[i];

   if (old_pipe->plane_state && !new_pipe->plane_state)
    should_disable = true;
  }

  if (should_disable && old_stream) {
   bool is_phantom = dc_state_get_stream_subvp_type(dc->current_state, old_stream) == SUBVP_PHANTOM;
   pipe = &dc->current_state->res_ctx.pipe_ctx[i];
   tg = pipe->stream_res.tg;
   /* When disabling plane for a phantom pipe, we must turn on the
 * phantom OTG so the disable programming gets the double buffer
 * update. Otherwise the pipe will be left in a partially disabled
 * state that can result in underflow or hang when enabling it
 * again for different use.
 */

   if (is_phantom) {
    if (tg->funcs->enable_crtc) {
     if (dc->hwseq->funcs.blank_pixel_data)
      dc->hwseq->funcs.blank_pixel_data(dc, pipe, true);
     tg->funcs->enable_crtc(tg);
    }
   }

   if (is_phantom)
    dc_state_rem_all_phantom_planes_for_stream(dc, old_stream, dangling_context, true);
   else
    dc_state_rem_all_planes_for_stream(dc, old_stream, dangling_context);
   disable_all_writeback_pipes_for_stream(dc, old_stream, dangling_context);

   if (pipe->stream && pipe->plane_state) {
    if (!dc->debug.using_dml2)
     set_p_state_switch_method(dc, context, pipe);
    dc_update_visual_confirm_color(dc, context, pipe);
   }

   if (dc->hwss.apply_ctx_for_surface) {
    apply_ctx_interdependent_lock(dc, dc->current_state, old_stream, true);
    dc->hwss.apply_ctx_for_surface(dc, old_stream, 0, dangling_context);
    apply_ctx_interdependent_lock(dc, dc->current_state, old_stream, false);
    dc->hwss.post_unlock_program_front_end(dc, dangling_context);
   }

   if (dc->res_pool->funcs->prepare_mcache_programming)
    dc->res_pool->funcs->prepare_mcache_programming(dc, dangling_context);
   if (dc->hwss.program_front_end_for_ctx) {
    dc->hwss.interdependent_update_lock(dc, dc->current_state, true);
    dc->hwss.program_front_end_for_ctx(dc, dangling_context);
    dc->hwss.interdependent_update_lock(dc, dc->current_state, false);
    dc->hwss.post_unlock_program_front_end(dc, dangling_context);
   }
   /* We need to put the phantom OTG back into it's default (disabled) state or we
 * can get corruption when transition from one SubVP config to a different one.
 * The OTG is set to disable on falling edge of VUPDATE so the plane disable
 * will still get it's double buffer update.
 */

   if (is_phantom) {
    if (tg->funcs->disable_phantom_crtc)
     tg->funcs->disable_phantom_crtc(tg);
   }
  }
 }

 current_ctx = dc->current_state;
 dc->current_state = dangling_context;
 dc_state_release(current_ctx);
}

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

 /* check if timing_changed, disable stream*/
 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  struct dc_stream_state *stream = NULL;
  struct dc_link *link = NULL;
  struct pipe_ctx *pipe = NULL;

  pipe = &context->res_ctx.pipe_ctx[i];
  stream = pipe->stream;
  if (stream == NULL)
   continue;

  if (stream->apply_seamless_boot_optimization)
   continue;

  // only looking for first odm pipe
  if (pipe->prev_odm_pipe)
   continue;

  if (stream->link->local_sink &&
   stream->link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
   link = stream->link;
  }

  if (link != NULL && link->link_enc->funcs->is_dig_enabled(link->link_enc)) {
   unsigned int enc_inst, tg_inst = 0;
   unsigned int pix_clk_100hz = 0;

   enc_inst = link->link_enc->funcs->get_dig_frontend(link->link_enc);
   if (enc_inst != ENGINE_ID_UNKNOWN) {
    for (j = 0; j < dc->res_pool->stream_enc_count; j++) {
     if (dc->res_pool->stream_enc[j]->id == enc_inst) {
      tg_inst = dc->res_pool->stream_enc[j]->funcs->dig_source_otg(
       dc->res_pool->stream_enc[j]);
      break;
     }
    }

    dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
     dc->res_pool->dp_clock_source,
     tg_inst, &pix_clk_100hz);

    if (link->link_status.link_active) {
     uint32_t requested_pix_clk_100hz =
      pipe->stream_res.pix_clk_params.requested_pix_clk_100hz;

     if (pix_clk_100hz != requested_pix_clk_100hz) {
      dc->link_srv->set_dpms_off(pipe);
      pipe->stream->dpms_off = false;
     }
    }
   }
  }
 }
}

/* Public functions */

struct dc *dc_create(const struct dc_init_data *init_params)
{
 struct dc *dc = kzalloc(sizeof(*dc), GFP_KERNEL);
 unsigned int full_pipe_count;

 if (!dc)
  return NULL;

 if (init_params->dce_environment == DCE_ENV_VIRTUAL_HW) {
  dc->caps.linear_pitch_alignment = 64;
  if (!dc_construct_ctx(dc, init_params))
   goto destruct_dc;
 } else {
  if (!dc_construct(dc, init_params))
   goto destruct_dc;

  full_pipe_count = dc->res_pool->pipe_count;
  if (dc->res_pool->underlay_pipe_index != NO_UNDERLAY_PIPE)
   full_pipe_count--;
  dc->caps.max_streams = min(
    full_pipe_count,
    dc->res_pool->stream_enc_count);

  dc->caps.max_links = dc->link_count;
  dc->caps.max_audios = dc->res_pool->audio_count;
  dc->caps.linear_pitch_alignment = 64;

  dc->caps.max_dp_protocol_version = DP_VERSION_1_4;

  dc->caps.max_otg_num = dc->res_pool->res_cap->num_timing_generator;

  if (dc->res_pool->dmcu != NULL)
   dc->versions.dmcu_version = dc->res_pool->dmcu->dmcu_version;
 }

 dc->dcn_reg_offsets = init_params->dcn_reg_offsets;
 dc->nbio_reg_offsets = init_params->nbio_reg_offsets;
 dc->clk_reg_offsets = init_params->clk_reg_offsets;

 /* Populate versioning information */
 dc->versions.dc_ver = DC_VER;

 dc->build_id = DC_BUILD_ID;

 DC_LOG_DC("Display Core initialized\n");

 return dc;

destruct_dc:
 dc_destruct(dc);
 kfree(dc);
 return NULL;
}

static void detect_edp_presence(struct dc *dc)
{
 struct dc_link *edp_links[MAX_NUM_EDP];
 struct dc_link *edp_link = NULL;
 enum dc_connection_type type;
 int i;
 int edp_num;

 dc_get_edp_links(dc, edp_links, &edp_num);
 if (!edp_num)
  return;

 for (i = 0; i < edp_num; i++) {
  edp_link = edp_links[i];
  if (dc->config.edp_not_connected) {
   edp_link->edp_sink_present = false;
  } else {
   dc_link_detect_connection_type(edp_link, &type);
   edp_link->edp_sink_present = (type != dc_connection_none);
  }
 }
}

void dc_hardware_init(struct dc *dc)
{

 detect_edp_presence(dc);
 if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW)
  dc->hwss.init_hw(dc);
 dc_dmub_srv_notify_fw_dc_power_state(dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D0);
}

void dc_init_callbacks(struct dc *dc,
  const struct dc_callback_init *init_params)
{
 dc->ctx->cp_psp = init_params->cp_psp;
}

void dc_deinit_callbacks(struct dc *dc)
{
 memset(&dc->ctx->cp_psp, 0, sizeof(dc->ctx->cp_psp));
}

void dc_destroy(struct dc **dc)
{
 dc_destruct(*dc);
 kfree(*dc);
 *dc = NULL;
}

static void enable_timing_multisync(
  struct dc *dc,
  struct dc_state *ctx)
{
 int i, multisync_count = 0;
 int pipe_count = dc->res_pool->pipe_count;
 struct pipe_ctx *multisync_pipes[MAX_PIPES] = { NULL };

 for (i = 0; i < pipe_count; i++) {
  if (!ctx->res_ctx.pipe_ctx[i].stream ||
    !ctx->res_ctx.pipe_ctx[i].stream->triggered_crtc_reset.enabled)
   continue;
  if (ctx->res_ctx.pipe_ctx[i].stream == ctx->res_ctx.pipe_ctx[i].stream->triggered_crtc_reset.event_source)
   continue;
  multisync_pipes[multisync_count] = &ctx->res_ctx.pipe_ctx[i];
  multisync_count++;
 }

 if (multisync_count > 0) {
  dc->hwss.enable_per_frame_crtc_position_reset(
   dc, multisync_count, multisync_pipes);
 }
}

static void program_timing_sync(
  struct dc *dc,
  struct dc_state *ctx)
{
 int i, j, k;
 int group_index = 0;
 int num_group = 0;
 int pipe_count = dc->res_pool->pipe_count;
 struct pipe_ctx *unsynced_pipes[MAX_PIPES] = { NULL };

 for (i = 0; i < pipe_count; i++) {
  if (!ctx->res_ctx.pipe_ctx[i].stream
    || ctx->res_ctx.pipe_ctx[i].top_pipe
    || ctx->res_ctx.pipe_ctx[i].prev_odm_pipe)
   continue;

  unsynced_pipes[i] = &ctx->res_ctx.pipe_ctx[i];
 }

 for (i = 0; i < pipe_count; i++) {
  int group_size = 1;
  enum timing_synchronization_type sync_type = NOT_SYNCHRONIZABLE;
  struct pipe_ctx *pipe_set[MAX_PIPES];

  if (!unsynced_pipes[i])
   continue;

  pipe_set[0] = unsynced_pipes[i];
  unsynced_pipes[i] = NULL;

  /* Add tg to the set, search rest of the tg's for ones with
 * same timing, add all tgs with same timing to the group
 */

  for (j = i + 1; j < pipe_count; j++) {
   if (!unsynced_pipes[j])
    continue;
   if (sync_type != TIMING_SYNCHRONIZABLE &&
    dc->hwss.enable_vblanks_synchronization &&
    unsynced_pipes[j]->stream_res.tg->funcs->align_vblanks &&
    resource_are_vblanks_synchronizable(
     unsynced_pipes[j]->stream,
     pipe_set[0]->stream)) {
    sync_type = VBLANK_SYNCHRONIZABLE;
    pipe_set[group_size] = unsynced_pipes[j];
    unsynced_pipes[j] = NULL;
    group_size++;
   } else
   if (sync_type != VBLANK_SYNCHRONIZABLE &&
    resource_are_streams_timing_synchronizable(
     unsynced_pipes[j]->stream,
     pipe_set[0]->stream)) {
    sync_type = TIMING_SYNCHRONIZABLE;
    pipe_set[group_size] = unsynced_pipes[j];
    unsynced_pipes[j] = NULL;
    group_size++;
   }
  }

  /* set first unblanked pipe as master */
  for (j = 0; j < group_size; j++) {
   bool is_blanked;

   if (pipe_set[j]->stream_res.opp->funcs->dpg_is_blanked)
    is_blanked =
     pipe_set[j]->stream_res.opp->funcs->dpg_is_blanked(pipe_set[j]->stream_res.opp);
   else
    is_blanked =
     pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg);
   if (!is_blanked) {
    if (j == 0)
     break;

    swap(pipe_set[0], pipe_set[j]);
    break;
   }
  }

  for (k = 0; k < group_size; k++) {
   struct dc_stream_status *status = dc_state_get_stream_status(ctx, pipe_set[k]->stream);

   if (!status)
    continue;

   status->timing_sync_info.group_id = num_group;
   status->timing_sync_info.group_size = group_size;
   if (k == 0)
    status->timing_sync_info.master = true;
   else
    status->timing_sync_info.master = false;

  }

  /* remove any other unblanked pipes as they have already been synced */
  if (dc->config.use_pipe_ctx_sync_logic) {
   /* check pipe's syncd to decide which pipe to be removed */
   for (j = 1; j < group_size; j++) {
    if (pipe_set[j]->pipe_idx_syncd == pipe_set[0]->pipe_idx_syncd) {
     group_size--;
     pipe_set[j] = pipe_set[group_size];
     j--;
    } else
     /* link slave pipe's syncd with master pipe */
     pipe_set[j]->pipe_idx_syncd = pipe_set[0]->pipe_idx_syncd;
   }
  } else {
   /* remove any other pipes by checking valid plane */
   for (j = j + 1; j < group_size; j++) {
    bool is_blanked;

    if (pipe_set[j]->stream_res.opp->funcs->dpg_is_blanked)
     is_blanked =
      pipe_set[j]->stream_res.opp->funcs->dpg_is_blanked(pipe_set[j]->stream_res.opp);
    else
     is_blanked =
      pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg);
    if (!is_blanked) {
     group_size--;
     pipe_set[j] = pipe_set[group_size];
     j--;
    }
   }
  }

  if (group_size > 1) {
   if (sync_type == TIMING_SYNCHRONIZABLE) {
    dc->hwss.enable_timing_synchronization(
     dc, ctx, group_index, group_size, pipe_set);
   } else
    if (sync_type == VBLANK_SYNCHRONIZABLE) {
    dc->hwss.enable_vblanks_synchronization(
     dc, group_index, group_size, pipe_set);
    }
   group_index++;
  }
  num_group++;
 }
}

static bool streams_changed(struct dc *dc,
       struct dc_stream_state *streams[],
       uint8_t stream_count)
{
 uint8_t i;

 if (stream_count != dc->current_state->stream_count)
  return true;

 for (i = 0; i < dc->current_state->stream_count; i++) {
  if (dc->current_state->streams[i] != streams[i])
   return true;
  if (!streams[i]->link->link_state_valid)
   return true;
 }

 return false;
}

bool dc_validate_boot_timing(const struct dc *dc,
    const struct dc_sink *sink,
    struct dc_crtc_timing *crtc_timing)
{
 struct timing_generator *tg;
 struct stream_encoder *se = NULL;

 struct dc_crtc_timing hw_crtc_timing = {0};

 struct dc_link *link = sink->link;
 unsigned int i, enc_inst, tg_inst = 0;

 /* Support seamless boot on EDP displays only */
 if (sink->sink_signal != SIGNAL_TYPE_EDP) {
  return false;
 }

 if (dc->debug.force_odm_combine) {
  DC_LOG_DEBUG("boot timing validation failed due to force_odm_combine\n");
  return false;
 }

 /* Check for enabled DIG to identify enabled display */
 if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) {
  DC_LOG_DEBUG("boot timing validation failed due to disabled DIG\n");
  return false;
 }

 enc_inst = link->link_enc->funcs->get_dig_frontend(link->link_enc);

 if (enc_inst == ENGINE_ID_UNKNOWN) {
  DC_LOG_DEBUG("boot timing validation failed due to unknown DIG engine ID\n");
  return false;
 }

 for (i = 0; i < dc->res_pool->stream_enc_count; i++) {
  if (dc->res_pool->stream_enc[i]->id == enc_inst) {

   se = dc->res_pool->stream_enc[i];

   tg_inst = dc->res_pool->stream_enc[i]->funcs->dig_source_otg(
    dc->res_pool->stream_enc[i]);
   break;
  }
 }

 // tg_inst not found
 if (i == dc->res_pool->stream_enc_count) {
  DC_LOG_DEBUG("boot timing validation failed due to timing generator instance not found\n");
  return false;
 }

 if (tg_inst >= dc->res_pool->timing_generator_count) {
  DC_LOG_DEBUG("boot timing validation failed due to invalid timing generator count\n");
  return false;
 }

 if (tg_inst != link->link_enc->preferred_engine) {
  DC_LOG_DEBUG("boot timing validation failed due to non-preferred timing generator\n");
  return false;
 }

 tg = dc->res_pool->timing_generators[tg_inst];

 if (!tg->funcs->get_hw_timing) {
  DC_LOG_DEBUG("boot timing validation failed due to missing get_hw_timing callback\n");
  return false;
 }

 if (!tg->funcs->get_hw_timing(tg, &hw_crtc_timing)) {
  DC_LOG_DEBUG("boot timing validation failed due to failed get_hw_timing return\n");
  return false;
 }

 if (crtc_timing->h_total != hw_crtc_timing.h_total) {
  DC_LOG_DEBUG("boot timing validation failed due to h_total mismatch\n");
  return false;
 }

 if (crtc_timing->h_border_left != hw_crtc_timing.h_border_left) {
  DC_LOG_DEBUG("boot timing validation failed due to h_border_left mismatch\n");
  return false;
 }

 if (crtc_timing->h_addressable != hw_crtc_timing.h_addressable) {
  DC_LOG_DEBUG("boot timing validation failed due to h_addressable mismatch\n");
  return false;
 }

 if (crtc_timing->h_border_right != hw_crtc_timing.h_border_right) {
  DC_LOG_DEBUG("boot timing validation failed due to h_border_right mismatch\n");
  return false;
 }

 if (crtc_timing->h_front_porch != hw_crtc_timing.h_front_porch) {
  DC_LOG_DEBUG("boot timing validation failed due to h_front_porch mismatch\n");
  return false;
 }

 if (crtc_timing->h_sync_width != hw_crtc_timing.h_sync_width) {
  DC_LOG_DEBUG("boot timing validation failed due to h_sync_width mismatch\n");
  return false;
 }

 if (crtc_timing->v_total != hw_crtc_timing.v_total) {
  DC_LOG_DEBUG("boot timing validation failed due to v_total mismatch\n");
  return false;
 }

 if (crtc_timing->v_border_top != hw_crtc_timing.v_border_top) {
  DC_LOG_DEBUG("boot timing validation failed due to v_border_top mismatch\n");
  return false;
 }

 if (crtc_timing->v_addressable != hw_crtc_timing.v_addressable) {
  DC_LOG_DEBUG("boot timing validation failed due to v_addressable mismatch\n");
  return false;
 }

 if (crtc_timing->v_border_bottom != hw_crtc_timing.v_border_bottom) {
  DC_LOG_DEBUG("boot timing validation failed due to v_border_bottom mismatch\n");
  return false;
 }

 if (crtc_timing->v_front_porch != hw_crtc_timing.v_front_porch) {
  DC_LOG_DEBUG("boot timing validation failed due to v_front_porch mismatch\n");
  return false;
 }

 if (crtc_timing->v_sync_width != hw_crtc_timing.v_sync_width) {
  DC_LOG_DEBUG("boot timing validation failed due to v_sync_width mismatch\n");
  return false;
 }

 /* block DSC for now, as VBIOS does not currently support DSC timings */
 if (crtc_timing->flags.DSC) {
  DC_LOG_DEBUG("boot timing validation failed due to DSC\n");
  return false;
 }

 if (dc_is_dp_signal(link->connector_signal)) {
  unsigned int pix_clk_100hz = 0;
  uint32_t numOdmPipes = 1;
  uint32_t id_src[4] = {0};

  dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
   dc->res_pool->dp_clock_source,
   tg_inst, &pix_clk_100hz);

  if (tg->funcs->get_optc_source)
   tg->funcs->get_optc_source(tg,
      &numOdmPipes, &id_src[0], &id_src[1]);

  if (numOdmPipes == 2) {
   pix_clk_100hz *= 2;
  } else if (numOdmPipes == 4) {
   pix_clk_100hz *= 4;
  } else if (se && se->funcs->get_pixels_per_cycle) {
   uint32_t pixels_per_cycle = se->funcs->get_pixels_per_cycle(se);

   if (pixels_per_cycle != 1 && !dc->debug.enable_dp_dig_pixel_rate_div_policy) {
    DC_LOG_DEBUG("boot timing validation failed due to pixels_per_cycle\n");
    return false;
   }

   pix_clk_100hz *= pixels_per_cycle;
  }

  // Note: In rare cases, HW pixclk may differ from crtc's pixclk
  // slightly due to rounding issues in 10 kHz units.
  if (crtc_timing->pix_clk_100hz != pix_clk_100hz) {
   DC_LOG_DEBUG("boot timing validation failed due to pix_clk_100hz mismatch\n");
   return false;
  }

  if (!se || !se->funcs->dp_get_pixel_format) {
   DC_LOG_DEBUG("boot timing validation failed due to missing dp_get_pixel_format\n");
   return false;
  }

  if (!se->funcs->dp_get_pixel_format(
   se,
   &hw_crtc_timing.pixel_encoding,
   &hw_crtc_timing.display_color_depth)) {
   DC_LOG_DEBUG("boot timing validation failed due to dp_get_pixel_format failure\n");
   return false;
  }

  if (hw_crtc_timing.display_color_depth != crtc_timing->display_color_depth) {
   DC_LOG_DEBUG("boot timing validation failed due to display_color_depth mismatch\n");
   return false;
  }

  if (hw_crtc_timing.pixel_encoding != crtc_timing->pixel_encoding) {
   DC_LOG_DEBUG("boot timing validation failed due to pixel_encoding mismatch\n");
   return false;
  }
 }


 if (link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED) {
  DC_LOG_DEBUG("boot timing validation failed due to VSC SDP colorimetry\n");
  return false;
 }

 if (link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
  DC_LOG_DEBUG("boot timing validation failed due to DP 128b/132b\n");
  return false;
 }

 if (dc->link_srv->edp_is_ilr_optimization_required(link, crtc_timing)) {
  DC_LOG_EVENT_LINK_TRAINING("Seamless boot disabled to optimize eDP link rate\n");
  return false;
 }

 return true;
}

static inline bool should_update_pipe_for_stream(
  struct dc_state *context,
  struct pipe_ctx *pipe_ctx,
  struct dc_stream_state *stream)
{
 return (pipe_ctx->stream && pipe_ctx->stream == stream);
}

static inline bool should_update_pipe_for_plane(
  struct dc_state *context,
  struct pipe_ctx *pipe_ctx,
  struct dc_plane_state *plane_state)
{
 return (pipe_ctx->plane_state == plane_state);
}

void dc_enable_stereo(
 struct dc *dc,
 struct dc_state *context,
 struct dc_stream_state *streams[],
 uint8_t stream_count)
{
 int i, j;
 struct pipe_ctx *pipe;

 dc_exit_ips_for_hw_access(dc);

 for (i = 0; i < MAX_PIPES; i++) {
  if (context != NULL) {
   pipe = &context->res_ctx.pipe_ctx[i];
  } else {
   context = dc->current_state;
   pipe = &dc->current_state->res_ctx.pipe_ctx[i];
  }

  for (j = 0; pipe && j < stream_count; j++)  {
   if (should_update_pipe_for_stream(context, pipe, streams[j]) &&
    dc->hwss.setup_stereo)
    dc->hwss.setup_stereo(pipe, dc);
  }
 }
}

void dc_trigger_sync(struct dc *dc, struct dc_state *context)
{
 if (context->stream_count > 1 && !dc->debug.disable_timing_sync) {
  dc_exit_ips_for_hw_access(dc);

  enable_timing_multisync(dc, context);
  program_timing_sync(dc, context);
 }
}

static uint8_t get_stream_mask(struct dc *dc, struct dc_state *context)
{
 int i;
 unsigned int stream_mask = 0;

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

 return stream_mask;
}

void dc_z10_restore(const struct dc *dc)
{
 if (dc->hwss.z10_restore)
  dc->hwss.z10_restore(dc);
}

void dc_z10_save_init(struct dc *dc)
{
 if (dc->hwss.z10_save_init)
  dc->hwss.z10_save_init(dc);
}

/* Set a pipe unlock order based on the change in DET allocation and stores it in dc scratch memory
 * Prevents over allocation of DET during unlock process
 * e.g. 2 pipe config with different streams with a max of 20 DET segments
 * Before: After:
 * - Pipe0: 10 DET segments - Pipe0: 12 DET segments
 * - Pipe1: 10 DET segments - Pipe1: 8 DET segments
 * If Pipe0 gets updated first, 22 DET segments will be allocated
 */

static void determine_pipe_unlock_order(struct dc *dc, struct dc_state *context)
{
 unsigned int i = 0;
 struct pipe_ctx *pipe = NULL;
 struct timing_generator *tg = NULL;

 if (!dc->config.set_pipe_unlock_order)
  return;

 memset(dc->scratch.pipes_to_unlock_first, 0, sizeof(dc->scratch.pipes_to_unlock_first));
 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  pipe = &context->res_ctx.pipe_ctx[i];
  tg = pipe->stream_res.tg;

  if (!resource_is_pipe_type(pipe, OTG_MASTER) ||
    !tg->funcs->is_tg_enabled(tg) ||
    dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
   continue;
  }

  if (resource_calculate_det_for_stream(context, pipe) <
    resource_calculate_det_for_stream(dc->current_state, &dc->current_state->res_ctx.pipe_ctx[i])) {
   dc->scratch.pipes_to_unlock_first[i] = true;
  }
 }
}

/**
 * dc_commit_state_no_check - Apply context to the hardware
 *
 * @dc: DC object with the current status to be updated
 * @context: New state that will become the current status at the end of this function
 *
 * Applies given context to the hardware and copy it into current context.
 * It's up to the user to release the src context afterwards.
 *
 * Return: an enum dc_status result code for the operation
 */

static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *context)
{
 struct dc_bios *dcb = dc->ctx->dc_bios;
 enum dc_status result = DC_ERROR_UNEXPECTED;
 struct pipe_ctx *pipe;
 int i, k, l;
 struct dc_stream_state *dc_streams[MAX_STREAMS] = {0};
 struct dc_state *old_state;
 bool subvp_prev_use = false;

 dc_z10_restore(dc);
 dc_allow_idle_optimizations(dc, false);

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

  /* Check old context for SubVP */
  subvp_prev_use |= (dc_state_get_pipe_subvp_type(dc->current_state, old_pipe) == SUBVP_PHANTOM);
  if (subvp_prev_use)
   break;
 }

 for (i = 0; i < context->stream_count; i++)
  dc_streams[i] =  context->streams[i];

 if (!dcb->funcs->is_accelerated_mode(dcb)) {
  disable_vbios_mode_if_required(dc, context);
  dc->hwss.enable_accelerated_mode(dc, context);
 }

 if (dc->hwseq->funcs.wait_for_pipe_update_if_needed) {
  for (i = 0; i < dc->res_pool->pipe_count; i++) {
   pipe = &context->res_ctx.pipe_ctx[i];
   //Only delay otg master for a given config
   if (resource_is_pipe_type(pipe, OTG_MASTER)) {
    //dc_commit_state_no_check is always a full update
    dc->hwseq->funcs.wait_for_pipe_update_if_needed(dc, pipe, false);
    break;
   }
  }
 }

 if (context->stream_count > get_seamless_boot_stream_count(context) ||
  context->stream_count == 0)
  dc->hwss.prepare_bandwidth(dc, context);

 /* When SubVP is active, all HW programming must be done while
 * SubVP lock is acquired
 */

 if (dc->hwss.subvp_pipe_control_lock)
  dc->hwss.subvp_pipe_control_lock(dc, context, truetrue, NULL, subvp_prev_use);
 if (dc->hwss.fams2_global_control_lock)
  dc->hwss.fams2_global_control_lock(dc, context, true);

 if (dc->hwss.update_dsc_pg)
  dc->hwss.update_dsc_pg(dc, context, false);

 disable_dangling_plane(dc, context);
 /* re-program planes for existing stream, in case we need to
 * free up plane resource for later use
 */

 if (dc->hwss.apply_ctx_for_surface) {
  for (i = 0; i < context->stream_count; i++) {
   if (context->streams[i]->mode_changed)
    continue;
   apply_ctx_interdependent_lock(dc, context, context->streams[i], true);
   dc->hwss.apply_ctx_for_surface(
    dc, context->streams[i],
    context->stream_status[i].plane_count,
    context); /* use new pipe config in new context */
   apply_ctx_interdependent_lock(dc, context, context->streams[i], false);
   dc->hwss.post_unlock_program_front_end(dc, context);
  }
 }

 /* Program hardware */
 for (i = 0; i < dc->res_pool->pipe_count; i++) {
  pipe = &context->res_ctx.pipe_ctx[i];
  dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe);
 }

 result = dc->hwss.apply_ctx_to_hw(dc, context);

 if (result != DC_OK) {
  /* Application of dc_state to hardware stopped. */
  dc->current_state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
  return result;
 }

 dc_trigger_sync(dc, context);

 /* Full update should unconditionally be triggered when dc_commit_state_no_check is called */
 for (i = 0; i < context->stream_count; i++) {
  uint32_t prev_dsc_changed = context->streams[i]->update_flags.bits.dsc_changed;

  context->streams[i]->update_flags.raw = 0xFFFFFFFF;
  context->streams[i]->update_flags.bits.dsc_changed = prev_dsc_changed;
 }

 determine_pipe_unlock_order(dc, context);
 /* Program all planes within new context*/
 if (dc->res_pool->funcs->prepare_mcache_programming)
  dc->res_pool->funcs->prepare_mcache_programming(dc, context);
 if (dc->hwss.program_front_end_for_ctx) {
  dc->hwss.interdependent_update_lock(dc, context, true);
  dc->hwss.program_front_end_for_ctx(dc, context);

  if (dc->hwseq->funcs.set_wait_for_update_needed_for_pipe) {
   for (i = 0; i < dc->res_pool->pipe_count; i++) {
    pipe = &context->res_ctx.pipe_ctx[i];
    dc->hwseq->funcs.set_wait_for_update_needed_for_pipe(dc, pipe);
   }
  }

  dc->hwss.interdependent_update_lock(dc, context, false);
  dc->hwss.post_unlock_program_front_end(dc, context);
 }

 if (dc->hwss.commit_subvp_config)
  dc->hwss.commit_subvp_config(dc, context);
 if (dc->hwss.subvp_pipe_control_lock)
  dc->hwss.subvp_pipe_control_lock(dc, context, falsetrue, NULL, subvp_prev_use);
 if (dc->hwss.fams2_global_control_lock)
  dc->hwss.fams2_global_control_lock(dc, context, false);

 for (i = 0; i < context->stream_count; i++) {
  const struct dc_link *link = context->streams[i]->link;

  if (!context->streams[i]->mode_changed)
   continue;

  if (dc->hwss.apply_ctx_for_surface) {
   apply_ctx_interdependent_lock(dc, context, context->streams[i], true);
   dc->hwss.apply_ctx_for_surface(
     dc, context->streams[i],
     context->stream_status[i].plane_count,
     context);
   apply_ctx_interdependent_lock(dc, context, context->streams[i], false);
   dc->hwss.post_unlock_program_front_end(dc, context);
  }

  /*
 * enable stereo
 * TODO rework dc_enable_stereo call to work with validation sets?
 */

  for (k = 0; k < MAX_PIPES; k++) {
   pipe = &context->res_ctx.pipe_ctx[k];

   for (l = 0 ; pipe && l < context->stream_count; l++)  {
    if (context->streams[l] &&
     context->streams[l] == pipe->stream &&
     dc->hwss.setup_stereo)
     dc->hwss.setup_stereo(pipe, dc);
   }
  }

  CONN_MSG_MODE(link, "{%dx%d, %dx%d@%dKhz}",
    context->streams[i]->timing.h_addressable,
    context->streams[i]->timing.v_addressable,
    context->streams[i]->timing.h_total,
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=89 G=92

¤ Dauer der Verarbeitung: 0.42 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge