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

Quelle  intel_dp.c   Sprache: C

 
/*
 * Copyright © 2008 Intel Corporation
 *
 * 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 (including the next
 * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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:
 *    Keith Packard <keithp@keithp.com>
 *
 */


#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/math.h>
#include <linux/notifier.h>
#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/string_helpers.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
#include <asm/byteorder.h>

#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_dp_tunnel.h>
#include <drm/display/drm_dsc_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>

#include "g4x_dp.h"
#include "i915_utils.h"
#include "intel_alpm.h"
#include "intel_atomic.h"
#include "intel_audio.h"
#include "intel_backlight.h"
#include "intel_combo_phy_regs.h"
#include "intel_connector.h"
#include "intel_crtc.h"
#include "intel_crtc_state_dump.h"
#include "intel_cx0_phy.h"
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display_driver.h"
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
#include "intel_dp.h"
#include "intel_dp_aux.h"
#include "intel_dp_hdcp.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
#include "intel_dp_tunnel.h"
#include "intel_dpio_phy.h"
#include "intel_dpll.h"
#include "intel_drrs.h"
#include "intel_encoder.h"
#include "intel_fifo_underrun.h"
#include "intel_hdcp.h"
#include "intel_hdmi.h"
#include "intel_hotplug.h"
#include "intel_hotplug_irq.h"
#include "intel_lspcon.h"
#include "intel_lvds.h"
#include "intel_modeset_lock.h"
#include "intel_panel.h"
#include "intel_pch_display.h"
#include "intel_pfit.h"
#include "intel_pps.h"
#include "intel_psr.h"
#include "intel_quirks.h"
#include "intel_tc.h"
#include "intel_vdsc.h"
#include "intel_vrr.h"

/* DP DSC throughput values used for slice count calculations KPixels/s */
#define DP_DSC_PEAK_PIXEL_RATE   2720000
#define DP_DSC_MAX_ENC_THROUGHPUT_0  340000
#define DP_DSC_MAX_ENC_THROUGHPUT_1  400000

/* Max DSC line buffer depth supported by HW. */
#define INTEL_DP_DSC_MAX_LINE_BUF_DEPTH  13

/* DP DSC FEC Overhead factor in ppm = 1/(0.972261) = 1.028530 */
#define DP_DSC_FEC_OVERHEAD_FACTOR  1028530

/* Constants for DP DSC configurations */
static const u8 valid_dsc_bpp[] = {6, 8, 10, 12, 15};

/*
 * With Single pipe configuration, HW is capable of supporting maximum of:
 * 2 slices per line for ICL, BMG
 * 4 slices per line for other platforms.
 * For now consider a max of 2 slices per line, which works for all platforms.
 * With this we can have max of 4 DSC Slices per pipe.
 *
 * For higher resolutions where 12 slice support is required with
 * ultrajoiner, only then each pipe can support 3 slices.
 *
 * #TODO Split this better to use 4 slices/dsc engine where supported.
 */

static const u8 valid_dsc_slicecount[] = {1, 2, 3, 4};

/**
 * intel_dp_is_edp - is the given port attached to an eDP panel (either CPU or PCH)
 * @intel_dp: DP struct
 *
 * If a CPU or PCH DP output is attached to an eDP panel, this function
 * will return true, and false otherwise.
 *
 * This function is not safe to use prior to encoder type being set.
 */

bool intel_dp_is_edp(struct intel_dp *intel_dp)
{
 struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);

 return dig_port->base.type == INTEL_OUTPUT_EDP;
}

static void intel_dp_unset_edid(struct intel_dp *intel_dp);

/* Is link rate UHBR and thus 128b/132b? */
bool intel_dp_is_uhbr(const struct intel_crtc_state *crtc_state)
{
 return drm_dp_is_uhbr_rate(crtc_state->port_clock);
}

/**
 * intel_dp_link_symbol_size - get the link symbol size for a given link rate
 * @rate: link rate in 10kbit/s units
 *
 * Returns the link symbol size in bits/symbol units depending on the link
 * rate -> channel coding.
 */

int intel_dp_link_symbol_size(int rate)
{
 return drm_dp_is_uhbr_rate(rate) ? 32 : 10;
}

/**
 * intel_dp_link_symbol_clock - convert link rate to link symbol clock
 * @rate: link rate in 10kbit/s units
 *
 * Returns the link symbol clock frequency in kHz units depending on the
 * link rate and channel coding.
 */

int intel_dp_link_symbol_clock(int rate)
{
 return DIV_ROUND_CLOSEST(rate * 10, intel_dp_link_symbol_size(rate));
}

static int max_dprx_rate(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
 int max_rate;

 if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp))
  max_rate = drm_dp_tunnel_max_dprx_rate(intel_dp->tunnel);
 else
  max_rate = drm_dp_bw_code_to_link_rate(intel_dp->dpcd[DP_MAX_LINK_RATE]);

 /*
 * Some broken eDP sinks illegally declare support for
 * HBR3 without TPS4, and are unable to produce a stable
 * output. Reject HBR3 when TPS4 is not available.
 */

 if (max_rate >= 810000 && !drm_dp_tps4_supported(intel_dp->dpcd)) {
  drm_dbg_kms(display->drm,
       "[ENCODER:%d:%s] Rejecting HBR3 due to missing TPS4 support\n",
       encoder->base.base.id, encoder->base.name);
  max_rate = 540000;
 }

 return max_rate;
}

static int max_dprx_lane_count(struct intel_dp *intel_dp)
{
 if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp))
  return drm_dp_tunnel_max_dprx_lane_count(intel_dp->tunnel);

 return drm_dp_max_lane_count(intel_dp->dpcd);
}

static void intel_dp_set_default_sink_rates(struct intel_dp *intel_dp)
{
 intel_dp->sink_rates[0] = 162000;
 intel_dp->num_sink_rates = 1;
}

/* update sink rates from dpcd */
static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp)
{
 static const int dp_rates[] = {
  162000, 270000, 540000, 810000
 };
 int i, max_rate;
 int max_lttpr_rate;

 if (drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS)) {
  /* Needed, e.g., for Apple MBP 2017, 15 inch eDP Retina panel */
  static const int quirk_rates[] = { 162000, 270000, 324000 };

  memcpy(intel_dp->sink_rates, quirk_rates, sizeof(quirk_rates));
  intel_dp->num_sink_rates = ARRAY_SIZE(quirk_rates);

  return;
 }

 /*
 * Sink rates for 8b/10b.
 */

 max_rate = max_dprx_rate(intel_dp);
 max_lttpr_rate = drm_dp_lttpr_max_link_rate(intel_dp->lttpr_common_caps);
 if (max_lttpr_rate)
  max_rate = min(max_rate, max_lttpr_rate);

 for (i = 0; i < ARRAY_SIZE(dp_rates); i++) {
  if (dp_rates[i] > max_rate)
   break;
  intel_dp->sink_rates[i] = dp_rates[i];
 }

 /*
 * Sink rates for 128b/132b. If set, sink should support all 8b/10b
 * rates and 10 Gbps.
 */

 if (drm_dp_128b132b_supported(intel_dp->dpcd)) {
  u8 uhbr_rates = 0;

  BUILD_BUG_ON(ARRAY_SIZE(intel_dp->sink_rates) < ARRAY_SIZE(dp_rates) + 3);

  drm_dp_dpcd_readb(&intel_dp->aux,
      DP_128B132B_SUPPORTED_LINK_RATES, &uhbr_rates);

  if (drm_dp_lttpr_count(intel_dp->lttpr_common_caps)) {
   /* We have a repeater */
   if (intel_dp->lttpr_common_caps[0] >= 0x20 &&
       intel_dp->lttpr_common_caps[DP_MAIN_LINK_CHANNEL_CODING_PHY_REPEATER -
       DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV] &
       DP_PHY_REPEATER_128B132B_SUPPORTED) {
    /* Repeater supports 128b/132b, valid UHBR rates */
    uhbr_rates &= intel_dp->lttpr_common_caps[DP_PHY_REPEATER_128B132B_RATES -
           DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV];
   } else {
    /* Does not support 128b/132b */
    uhbr_rates = 0;
   }
  }

  if (uhbr_rates & DP_UHBR10)
   intel_dp->sink_rates[i++] = 1000000;
  if (uhbr_rates & DP_UHBR13_5)
   intel_dp->sink_rates[i++] = 1350000;
  if (uhbr_rates & DP_UHBR20)
   intel_dp->sink_rates[i++] = 2000000;
 }

 intel_dp->num_sink_rates = i;
}

static void intel_dp_set_sink_rates(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_connector *connector = intel_dp->attached_connector;
 struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 struct intel_encoder *encoder = &intel_dig_port->base;

 intel_dp_set_dpcd_sink_rates(intel_dp);

 if (intel_dp->num_sink_rates)
  return;

 drm_err(display->drm,
  "[CONNECTOR:%d:%s][ENCODER:%d:%s] Invalid DPCD with no link rates, using defaults\n",
  connector->base.base.id, connector->base.name,
  encoder->base.base.id, encoder->base.name);

 intel_dp_set_default_sink_rates(intel_dp);
}

static void intel_dp_set_default_max_sink_lane_count(struct intel_dp *intel_dp)
{
 intel_dp->max_sink_lane_count = 1;
}

static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_connector *connector = intel_dp->attached_connector;
 struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 struct intel_encoder *encoder = &intel_dig_port->base;

 intel_dp->max_sink_lane_count = max_dprx_lane_count(intel_dp);

 switch (intel_dp->max_sink_lane_count) {
 case 1:
 case 2:
 case 4:
  return;
 }

 drm_err(display->drm,
  "[CONNECTOR:%d:%s][ENCODER:%d:%s] Invalid DPCD max lane count (%d), using default\n",
  connector->base.base.id, connector->base.name,
  encoder->base.base.id, encoder->base.name,
  intel_dp->max_sink_lane_count);

 intel_dp_set_default_max_sink_lane_count(intel_dp);
}

/* Get length of rates array potentially limited by max_rate. */
static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
{
 int i;

 /* Limit results by potentially reduced max rate */
 for (i = 0; i < len; i++) {
  if (rates[len - i - 1] <= max_rate)
   return len - i;
 }

 return 0;
}

/* Get length of common rates array potentially limited by max_rate. */
static int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp,
       int max_rate)
{
 return intel_dp_rate_limit_len(intel_dp->common_rates,
           intel_dp->num_common_rates, max_rate);
}

int intel_dp_common_rate(struct intel_dp *intel_dp, int index)
{
 struct intel_display *display = to_intel_display(intel_dp);

 if (drm_WARN_ON(display->drm,
   index < 0 || index >= intel_dp->num_common_rates))
  return 162000;

 return intel_dp->common_rates[index];
}

/* Theoretical max between source and sink */
int intel_dp_max_common_rate(struct intel_dp *intel_dp)
{
 return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1);
}

int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port)
{
 int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata);
 int max_lanes = dig_port->max_lanes;

 if (vbt_max_lanes)
  max_lanes = min(max_lanes, vbt_max_lanes);

 return max_lanes;
}

/* Theoretical max between source and sink */
int intel_dp_max_common_lane_count(struct intel_dp *intel_dp)
{
 struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
 int source_max = intel_dp_max_source_lane_count(dig_port);
 int sink_max = intel_dp->max_sink_lane_count;
 int lane_max = intel_tc_port_max_lane_count(dig_port);
 int lttpr_max = drm_dp_lttpr_max_lane_count(intel_dp->lttpr_common_caps);

 if (lttpr_max)
  sink_max = min(sink_max, lttpr_max);

 return min3(source_max, sink_max, lane_max);
}

static int forced_lane_count(struct intel_dp *intel_dp)
{
 return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp));
}

int intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
 int lane_count;

 if (intel_dp->link.force_lane_count)
  lane_count = forced_lane_count(intel_dp);
 else
  lane_count = intel_dp->link.max_lane_count;

 switch (lane_count) {
 case 1:
 case 2:
 case 4:
  return lane_count;
 default:
  MISSING_CASE(lane_count);
  return 1;
 }
}

static int intel_dp_min_lane_count(struct intel_dp *intel_dp)
{
 if (intel_dp->link.force_lane_count)
  return forced_lane_count(intel_dp);

 return 1;
}

/*
 * The required data bandwidth for a mode with given pixel clock and bpp. This
 * is the required net bandwidth independent of the data bandwidth efficiency.
 *
 * TODO: check if callers of this functions should use
 * intel_dp_effective_data_rate() instead.
 */

int
intel_dp_link_required(int pixel_clock, int bpp)
{
 /* pixel_clock is in kHz, divide bpp by 8 for bit to Byte conversion */
 return DIV_ROUND_UP(pixel_clock * bpp, 8);
}

/**
 * intel_dp_effective_data_rate - Return the pixel data rate accounting for BW allocation overhead
 * @pixel_clock: pixel clock in kHz
 * @bpp_x16: bits per pixel .4 fixed point format
 * @bw_overhead: BW allocation overhead in 1ppm units
 *
 * Return the effective pixel data rate in kB/sec units taking into account
 * the provided SSC, FEC, DSC BW allocation overhead.
 */

int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16,
     int bw_overhead)
{
 return DIV_ROUND_UP_ULL(mul_u32_u32(pixel_clock * bpp_x16, bw_overhead),
    1000000 * 16 * 8);
}

/**
 * intel_dp_max_link_data_rate: Calculate the maximum rate for the given link params
 * @intel_dp: Intel DP object
 * @max_dprx_rate: Maximum data rate of the DPRX
 * @max_dprx_lanes: Maximum lane count of the DPRX
 *
 * Calculate the maximum data rate for the provided link parameters taking into
 * account any BW limitations by a DP tunnel attached to @intel_dp.
 *
 * Returns the maximum data rate in kBps units.
 */

int intel_dp_max_link_data_rate(struct intel_dp *intel_dp,
    int max_dprx_rate, int max_dprx_lanes)
{
 int max_rate = drm_dp_max_dprx_data_rate(max_dprx_rate, max_dprx_lanes);

 if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp))
  max_rate = min(max_rate,
          drm_dp_tunnel_available_bw(intel_dp->tunnel));

 return max_rate;
}

bool intel_dp_has_joiner(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 struct intel_encoder *encoder = &intel_dig_port->base;

 /* eDP MSO is not compatible with joiner */
 if (intel_dp->mso_link_count)
  return false;

 return DISPLAY_VER(display) >= 12 ||
  (DISPLAY_VER(display) == 11 &&
   encoder->port != PORT_A);
}

static int dg2_max_source_rate(struct intel_dp *intel_dp)
{
 return intel_dp_is_edp(intel_dp) ? 810000 : 1350000;
}

static int icl_max_source_rate(struct intel_dp *intel_dp)
{
 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;

 if (intel_encoder_is_combo(encoder) && !intel_dp_is_edp(intel_dp))
  return 540000;

 return 810000;
}

static int ehl_max_source_rate(struct intel_dp *intel_dp)
{
 if (intel_dp_is_edp(intel_dp))
  return 540000;

 return 810000;
}

static int mtl_max_source_rate(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;

 if (intel_encoder_is_c10phy(encoder))
  return 810000;

 if (DISPLAY_VERx100(display) == 1401)
  return 1350000;

 return 2000000;
}

static int vbt_max_link_rate(struct intel_dp *intel_dp)
{
 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
 int max_rate;

 max_rate = intel_bios_dp_max_link_rate(encoder->devdata);

 if (intel_dp_is_edp(intel_dp)) {
  struct intel_connector *connector = intel_dp->attached_connector;
  int edp_max_rate = connector->panel.vbt.edp.max_link_rate;

  if (max_rate && edp_max_rate)
   max_rate = min(max_rate, edp_max_rate);
  else if (edp_max_rate)
   max_rate = edp_max_rate;
 }

 return max_rate;
}

static void
intel_dp_set_source_rates(struct intel_dp *intel_dp)
{
 /* The values must be in increasing order */
 static const int bmg_rates[] = {
  162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000,
  810000, 1000000, 1350000,
 };
 static const int mtl_rates[] = {
  162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000,
  810000, 1000000, 2000000,
 };
 static const int icl_rates[] = {
  162000, 216000, 270000, 324000, 432000, 540000, 648000, 810000,
  1000000, 1350000,
 };
 static const int bxt_rates[] = {
  162000, 216000, 243000, 270000, 324000, 432000, 540000
 };
 static const int skl_rates[] = {
  162000, 216000, 270000, 324000, 432000, 540000
 };
 static const int hsw_rates[] = {
  162000, 270000, 540000
 };
 static const int g4x_rates[] = {
  162000, 270000
 };
 struct intel_display *display = to_intel_display(intel_dp);
 const int *source_rates;
 int size, max_rate = 0, vbt_max_rate;

 /* This should only be done once */
 drm_WARN_ON(display->drm,
      intel_dp->source_rates || intel_dp->num_source_rates);

 if (DISPLAY_VER(display) >= 14) {
  if (display->platform.battlemage) {
   source_rates = bmg_rates;
   size = ARRAY_SIZE(bmg_rates);
  } else {
   source_rates = mtl_rates;
   size = ARRAY_SIZE(mtl_rates);
  }
  max_rate = mtl_max_source_rate(intel_dp);
 } else if (DISPLAY_VER(display) >= 11) {
  source_rates = icl_rates;
  size = ARRAY_SIZE(icl_rates);
  if (display->platform.dg2)
   max_rate = dg2_max_source_rate(intel_dp);
  else if (display->platform.alderlake_p || display->platform.alderlake_s ||
    display->platform.dg1 || display->platform.rocketlake)
   max_rate = 810000;
  else if (display->platform.jasperlake || display->platform.elkhartlake)
   max_rate = ehl_max_source_rate(intel_dp);
  else
   max_rate = icl_max_source_rate(intel_dp);
 } else if (display->platform.geminilake || display->platform.broxton) {
  source_rates = bxt_rates;
  size = ARRAY_SIZE(bxt_rates);
 } else if (DISPLAY_VER(display) == 9) {
  source_rates = skl_rates;
  size = ARRAY_SIZE(skl_rates);
 } else if ((display->platform.haswell && !display->platform.haswell_ulx) ||
     display->platform.broadwell) {
  source_rates = hsw_rates;
  size = ARRAY_SIZE(hsw_rates);
 } else {
  source_rates = g4x_rates;
  size = ARRAY_SIZE(g4x_rates);
 }

 vbt_max_rate = vbt_max_link_rate(intel_dp);
 if (max_rate && vbt_max_rate)
  max_rate = min(max_rate, vbt_max_rate);
 else if (vbt_max_rate)
  max_rate = vbt_max_rate;

 if (max_rate)
  size = intel_dp_rate_limit_len(source_rates, size, max_rate);

 intel_dp->source_rates = source_rates;
 intel_dp->num_source_rates = size;
}

static int intersect_rates(const int *source_rates, int source_len,
      const int *sink_rates, int sink_len,
      int *common_rates)
{
 int i = 0, j = 0, k = 0;

 while (i < source_len && j < sink_len) {
  if (source_rates[i] == sink_rates[j]) {
   if (WARN_ON(k >= DP_MAX_SUPPORTED_RATES))
    return k;
   common_rates[k] = source_rates[i];
   ++k;
   ++i;
   ++j;
  } else if (source_rates[i] < sink_rates[j]) {
   ++i;
  } else {
   ++j;
  }
 }
 return k;
}

/* return index of rate in rates array, or -1 if not found */
int intel_dp_rate_index(const int *rates, int len, int rate)
{
 int i;

 for (i = 0; i < len; i++)
  if (rate == rates[i])
   return i;

 return -1;
}

static int intel_dp_link_config_rate(struct intel_dp *intel_dp,
         const struct intel_dp_link_config *lc)
{
 return intel_dp_common_rate(intel_dp, lc->link_rate_idx);
}

static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc)
{
 return 1 << lc->lane_count_exp;
}

static int intel_dp_link_config_bw(struct intel_dp *intel_dp,
       const struct intel_dp_link_config *lc)
{
 return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc),
      intel_dp_link_config_lane_count(lc));
}

static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
{
 struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */
 const struct intel_dp_link_config *lc_a = a;
 const struct intel_dp_link_config *lc_b = b;
 int bw_a = intel_dp_link_config_bw(intel_dp, lc_a);
 int bw_b = intel_dp_link_config_bw(intel_dp, lc_b);

 if (bw_a != bw_b)
  return bw_a - bw_b;

 return intel_dp_link_config_rate(intel_dp, lc_a) -
        intel_dp_link_config_rate(intel_dp, lc_b);
}

static void intel_dp_link_config_init(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_dp_link_config *lc;
 int num_common_lane_configs;
 int i;
 int j;

 if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp))))
  return;

 num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1;

 if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs >
        ARRAY_SIZE(intel_dp->link.configs)))
  return;

 intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs;

 lc = &intel_dp->link.configs[0];
 for (i = 0; i < intel_dp->num_common_rates; i++) {
  for (j = 0; j < num_common_lane_configs; j++) {
   lc->lane_count_exp = j;
   lc->link_rate_idx = i;

   lc++;
  }
 }

 sort_r(intel_dp->link.configs, intel_dp->link.num_configs,
        sizeof(intel_dp->link.configs[0]),
        link_config_cmp_by_bw, NULL,
        intel_dp);
}

void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count)
{
 struct intel_display *display = to_intel_display(intel_dp);
 const struct intel_dp_link_config *lc;

 if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs))
  idx = 0;

 lc = &intel_dp->link.configs[idx];

 *link_rate = intel_dp_link_config_rate(intel_dp, lc);
 *lane_count = intel_dp_link_config_lane_count(lc);
}

int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count)
{
 int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates,
      link_rate);
 int lane_count_exp = ilog2(lane_count);
 int i;

 for (i = 0; i < intel_dp->link.num_configs; i++) {
  const struct intel_dp_link_config *lc = &intel_dp->link.configs[i];

  if (lc->lane_count_exp == lane_count_exp &&
      lc->link_rate_idx == link_rate_idx)
   return i;
 }

 return -1;
}

static void intel_dp_set_common_rates(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);

 drm_WARN_ON(display->drm,
      !intel_dp->num_source_rates || !intel_dp->num_sink_rates);

 intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates,
           intel_dp->num_source_rates,
           intel_dp->sink_rates,
           intel_dp->num_sink_rates,
           intel_dp->common_rates);

 /* Paranoia, there should always be something in common. */
 if (drm_WARN_ON(display->drm, intel_dp->num_common_rates == 0)) {
  intel_dp->common_rates[0] = 162000;
  intel_dp->num_common_rates = 1;
 }

 intel_dp_link_config_init(intel_dp);
}

bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
    u8 lane_count)
{
 /*
 * FIXME: we need to synchronize the current link parameters with
 * hardware readout. Currently fast link training doesn't work on
 * boot-up.
 */

 if (link_rate == 0 ||
     link_rate > intel_dp->link.max_rate)
  return false;

 if (lane_count == 0 ||
     lane_count > intel_dp_max_lane_count(intel_dp))
  return false;

 return true;
}

u32 intel_dp_mode_to_fec_clock(u32 mode_clock)
{
 return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR),
         1000000U);
}

int intel_dp_bw_fec_overhead(bool fec_enabled)
{
 /*
 * TODO: Calculate the actual overhead for a given mode.
 * The hard-coded 1/0.972261=2.853% overhead factor
 * corresponds (for instance) to the 8b/10b DP FEC 2.4% +
 * 0.453% DSC overhead. This is enough for a 3840 width mode,
 * which has a DSC overhead of up to ~0.2%, but may not be
 * enough for a 1024 width mode where this is ~0.8% (on a 4
 * lane DP link, with 2 DSC slices and 8 bpp color depth).
 */

 return fec_enabled ? DP_DSC_FEC_OVERHEAD_FACTOR : 1000000;
}

static int
small_joiner_ram_size_bits(struct intel_display *display)
{
 if (DISPLAY_VER(display) >= 13)
  return 17280 * 8;
 else if (DISPLAY_VER(display) >= 11)
  return 7680 * 8;
 else
  return 6144 * 8;
}

static u32 intel_dp_dsc_nearest_valid_bpp(struct intel_display *display, u32 bpp, u32 pipe_bpp)
{
 u32 bits_per_pixel = bpp;
 int i;

 /* Error out if the max bpp is less than smallest allowed valid bpp */
 if (bits_per_pixel < valid_dsc_bpp[0]) {
  drm_dbg_kms(display->drm, "Unsupported BPP %u, min %u\n",
       bits_per_pixel, valid_dsc_bpp[0]);
  return 0;
 }

 /* From XE_LPD onwards we support from bpc upto uncompressed bpp-1 BPPs */
 if (DISPLAY_VER(display) >= 13) {
  bits_per_pixel = min(bits_per_pixel, pipe_bpp - 1);

  /*
 * According to BSpec, 27 is the max DSC output bpp,
 * 8 is the min DSC output bpp.
 * While we can still clamp higher bpp values to 27, saving bandwidth,
 * if it is required to oompress up to bpp < 8, means we can't do
 * that and probably means we can't fit the required mode, even with
 * DSC enabled.
 */

  if (bits_per_pixel < 8) {
   drm_dbg_kms(display->drm,
        "Unsupported BPP %u, min 8\n",
        bits_per_pixel);
   return 0;
  }
  bits_per_pixel = min_t(u32, bits_per_pixel, 27);
 } else {
  /* Find the nearest match in the array of known BPPs from VESA */
  for (i = 0; i < ARRAY_SIZE(valid_dsc_bpp) - 1; i++) {
   if (bits_per_pixel < valid_dsc_bpp[i + 1])
    break;
  }
  drm_dbg_kms(display->drm, "Set dsc bpp from %d to VESA %d\n",
       bits_per_pixel, valid_dsc_bpp[i]);

  bits_per_pixel = valid_dsc_bpp[i];
 }

 return bits_per_pixel;
}

static int bigjoiner_interface_bits(struct intel_display *display)
{
 return DISPLAY_VER(display) >= 14 ? 36 : 24;
}

static u32 bigjoiner_bw_max_bpp(struct intel_display *display, u32 mode_clock,
    int num_joined_pipes)
{
 u32 max_bpp;
 /* With bigjoiner multiple dsc engines are used in parallel so PPC is 2 */
 int ppc = 2;
 int num_big_joiners = num_joined_pipes / 2;

 max_bpp = display->cdclk.max_cdclk_freq * ppc * bigjoiner_interface_bits(display) /
    intel_dp_mode_to_fec_clock(mode_clock);

 max_bpp *= num_big_joiners;

 return max_bpp;

}

static u32 small_joiner_ram_max_bpp(struct intel_display *display,
        u32 mode_hdisplay,
        int num_joined_pipes)
{
 u32 max_bpp;

 /* Small Joiner Check: output bpp <= joiner RAM (bits) / Horiz. width */
 max_bpp = small_joiner_ram_size_bits(display) / mode_hdisplay;

 max_bpp *= num_joined_pipes;

 return max_bpp;
}

static int ultrajoiner_ram_bits(void)
{
 return 4 * 72 * 512;
}

static u32 ultrajoiner_ram_max_bpp(u32 mode_hdisplay)
{
 return ultrajoiner_ram_bits() / mode_hdisplay;
}

/* TODO: return a bpp_x16 value */
static
u32 get_max_compressed_bpp_with_joiner(struct intel_display *display,
           u32 mode_clock, u32 mode_hdisplay,
           int num_joined_pipes)
{
 u32 max_bpp = small_joiner_ram_max_bpp(display, mode_hdisplay, num_joined_pipes);

 if (num_joined_pipes > 1)
  max_bpp = min(max_bpp, bigjoiner_bw_max_bpp(display, mode_clock,
           num_joined_pipes));
 if (num_joined_pipes == 4)
  max_bpp = min(max_bpp, ultrajoiner_ram_max_bpp(mode_hdisplay));

 return max_bpp;
}

/* TODO: return a bpp_x16 value */
u16 intel_dp_dsc_get_max_compressed_bpp(struct intel_display *display,
     u32 link_clock, u32 lane_count,
     u32 mode_clock, u32 mode_hdisplay,
     int num_joined_pipes,
     enum intel_output_format output_format,
     u32 pipe_bpp,
     u32 timeslots)
{
 u32 bits_per_pixel, joiner_max_bpp;

 /*
 * Available Link Bandwidth(Kbits/sec) = (NumberOfLanes)*
 * (LinkSymbolClock)* 8 * (TimeSlots / 64)
 * for SST -> TimeSlots is 64(i.e all TimeSlots that are available)
 * for MST -> TimeSlots has to be calculated, based on mode requirements
 *
 * Due to FEC overhead, the available bw is reduced to 97.2261%.
 * To support the given mode:
 * Bandwidth required should be <= Available link Bandwidth * FEC Overhead
 * =>ModeClock * bits_per_pixel <= Available Link Bandwidth * FEC Overhead
 * =>bits_per_pixel <= Available link Bandwidth * FEC Overhead / ModeClock
 * =>bits_per_pixel <= (NumberOfLanes * LinkSymbolClock) * 8 (TimeSlots / 64) /
 *        (ModeClock / FEC Overhead)
 * =>bits_per_pixel <= (NumberOfLanes * LinkSymbolClock * TimeSlots) /
 *        (ModeClock / FEC Overhead * 8)
 */

 bits_per_pixel = ((link_clock * lane_count) * timeslots) /
    (intel_dp_mode_to_fec_clock(mode_clock) * 8);

 /* Bandwidth required for 420 is half, that of 444 format */
 if (output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
  bits_per_pixel *= 2;

 /*
 * According to DSC 1.2a Section 4.1.1 Table 4.1 the maximum
 * supported PPS value can be 63.9375 and with the further
 * mention that for 420, 422 formats, bpp should be programmed double
 * the target bpp restricting our target bpp to be 31.9375 at max.
 */

 if (output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
  bits_per_pixel = min_t(u32, bits_per_pixel, 31);

 drm_dbg_kms(display->drm, "Max link bpp is %u for %u timeslots "
    "total bw %u pixel clock %u\n",
    bits_per_pixel, timeslots,
    (link_clock * lane_count * 8),
    intel_dp_mode_to_fec_clock(mode_clock));

 joiner_max_bpp = get_max_compressed_bpp_with_joiner(display, mode_clock,
           mode_hdisplay, num_joined_pipes);
 bits_per_pixel = min(bits_per_pixel, joiner_max_bpp);

 bits_per_pixel = intel_dp_dsc_nearest_valid_bpp(display, bits_per_pixel, pipe_bpp);

 return bits_per_pixel;
}

u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector,
    int mode_clock, int mode_hdisplay,
    int num_joined_pipes)
{
 struct intel_display *display = to_intel_display(connector);
 u8 min_slice_count, i;
 int max_slice_width;

 if (mode_clock <= DP_DSC_PEAK_PIXEL_RATE)
  min_slice_count = DIV_ROUND_UP(mode_clock,
            DP_DSC_MAX_ENC_THROUGHPUT_0);
 else
  min_slice_count = DIV_ROUND_UP(mode_clock,
            DP_DSC_MAX_ENC_THROUGHPUT_1);

 /*
 * Due to some DSC engine BW limitations, we need to enable second
 * slice and VDSC engine, whenever we approach close enough to max CDCLK
 */

 if (mode_clock >= ((display->cdclk.max_cdclk_freq * 85) / 100))
  min_slice_count = max_t(u8, min_slice_count, 2);

 max_slice_width = drm_dp_dsc_sink_max_slice_width(connector->dp.dsc_dpcd);
 if (max_slice_width < DP_DSC_MIN_SLICE_WIDTH_VALUE) {
  drm_dbg_kms(display->drm,
       "Unsupported slice width %d by DP DSC Sink device\n",
       max_slice_width);
  return 0;
 }
 /* Also take into account max slice width */
 min_slice_count = max_t(u8, min_slice_count,
    DIV_ROUND_UP(mode_hdisplay,
          max_slice_width));

 /* Find the closest match to the valid slice count values */
 for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) {
  u8 test_slice_count = valid_dsc_slicecount[i] * num_joined_pipes;

  /*
 * 3 DSC Slices per pipe need 3 DSC engines, which is supported only
 * with Ultrajoiner only for some platforms.
 */

  if (valid_dsc_slicecount[i] == 3 &&
      (!HAS_DSC_3ENGINES(display) || num_joined_pipes != 4))
   continue;

  if (test_slice_count >
      drm_dp_dsc_sink_max_slice_count(connector->dp.dsc_dpcd, false))
   break;

   /*
  * Bigjoiner needs small joiner to be enabled.
  * So there should be at least 2 dsc slices per pipe,
  * whenever bigjoiner is enabled.
  */

  if (num_joined_pipes > 1 && valid_dsc_slicecount[i] < 2)
   continue;

  if (mode_hdisplay % test_slice_count)
   continue;

  if (min_slice_count <= test_slice_count)
   return test_slice_count;
 }

 drm_dbg_kms(display->drm, "Unsupported Slice Count %d\n",
      min_slice_count);
 return 0;
}

static bool source_can_output(struct intel_dp *intel_dp,
         enum intel_output_format format)
{
 struct intel_display *display = to_intel_display(intel_dp);

 switch (format) {
 case INTEL_OUTPUT_FORMAT_RGB:
  return true;

 case INTEL_OUTPUT_FORMAT_YCBCR444:
  /*
 * No YCbCr output support on gmch platforms.
 * Also, ILK doesn't seem capable of DP YCbCr output.
 * The displayed image is severely corrupted. SNB+ is fine.
 */

  return !HAS_GMCH(display) && !display->platform.ironlake;

 case INTEL_OUTPUT_FORMAT_YCBCR420:
  /* Platform < Gen 11 cannot output YCbCr420 format */
  return DISPLAY_VER(display) >= 11;

 default:
  MISSING_CASE(format);
  return false;
 }
}

static bool
dfp_can_convert_from_rgb(struct intel_dp *intel_dp,
    enum intel_output_format sink_format)
{
 if (!drm_dp_is_branch(intel_dp->dpcd))
  return false;

 if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR444)
  return intel_dp->dfp.rgb_to_ycbcr;

 if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420)
  return intel_dp->dfp.rgb_to_ycbcr &&
   intel_dp->dfp.ycbcr_444_to_420;

 return false;
}

static bool
dfp_can_convert_from_ycbcr444(struct intel_dp *intel_dp,
         enum intel_output_format sink_format)
{
 if (!drm_dp_is_branch(intel_dp->dpcd))
  return false;

 if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420)
  return intel_dp->dfp.ycbcr_444_to_420;

 return false;
}

static bool
dfp_can_convert(struct intel_dp *intel_dp,
  enum intel_output_format output_format,
  enum intel_output_format sink_format)
{
 switch (output_format) {
 case INTEL_OUTPUT_FORMAT_RGB:
  return dfp_can_convert_from_rgb(intel_dp, sink_format);
 case INTEL_OUTPUT_FORMAT_YCBCR444:
  return dfp_can_convert_from_ycbcr444(intel_dp, sink_format);
 default:
  MISSING_CASE(output_format);
  return false;
 }

 return false;
}

static enum intel_output_format
intel_dp_output_format(struct intel_connector *connector,
         enum intel_output_format sink_format)
{
 struct intel_display *display = to_intel_display(connector);
 struct intel_dp *intel_dp = intel_attached_dp(connector);
 enum intel_output_format force_dsc_output_format =
  intel_dp->force_dsc_output_format;
 enum intel_output_format output_format;
 if (force_dsc_output_format) {
  if (source_can_output(intel_dp, force_dsc_output_format) &&
      (!drm_dp_is_branch(intel_dp->dpcd) ||
       sink_format != force_dsc_output_format ||
       dfp_can_convert(intel_dp, force_dsc_output_format, sink_format)))
   return force_dsc_output_format;

  drm_dbg_kms(display->drm, "Cannot force DSC output format\n");
 }

 if (sink_format == INTEL_OUTPUT_FORMAT_RGB ||
     dfp_can_convert_from_rgb(intel_dp, sink_format))
  output_format = INTEL_OUTPUT_FORMAT_RGB;

 else if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR444 ||
   dfp_can_convert_from_ycbcr444(intel_dp, sink_format))
  output_format = INTEL_OUTPUT_FORMAT_YCBCR444;

 else
  output_format = INTEL_OUTPUT_FORMAT_YCBCR420;

 drm_WARN_ON(display->drm, !source_can_output(intel_dp, output_format));

 return output_format;
}

int intel_dp_min_bpp(enum intel_output_format output_format)
{
 if (output_format == INTEL_OUTPUT_FORMAT_RGB)
  return intel_display_min_pipe_bpp();
 else
  return 8 * 3;
}

int intel_dp_output_bpp(enum intel_output_format output_format, int bpp)
{
 /*
 * bpp value was assumed to RGB format. And YCbCr 4:2:0 output
 * format of the number of bytes per pixel will be half the number
 * of bytes of RGB pixel.
 */

 if (output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
  bpp /= 2;

 return bpp;
}

static enum intel_output_format
intel_dp_sink_format(struct intel_connector *connector,
       const struct drm_display_mode *mode)
{
 const struct drm_display_info *info = &connector->base.display_info;

 if (drm_mode_is_420_only(info, mode))
  return INTEL_OUTPUT_FORMAT_YCBCR420;

 return INTEL_OUTPUT_FORMAT_RGB;
}

static int
intel_dp_mode_min_output_bpp(struct intel_connector *connector,
        const struct drm_display_mode *mode)
{
 enum intel_output_format output_format, sink_format;

 sink_format = intel_dp_sink_format(connector, mode);

 output_format = intel_dp_output_format(connector, sink_format);

 return intel_dp_output_bpp(output_format, intel_dp_min_bpp(output_format));
}

static bool intel_dp_hdisplay_bad(struct intel_display *display,
      int hdisplay)
{
 /*
 * Older platforms don't like hdisplay==4096 with DP.
 *
 * On ILK/SNB/IVB the pipe seems to be somewhat running (scanline
 * and frame counter increment), but we don't get vblank interrupts,
 * and the pipe underruns immediately. The link also doesn't seem
 * to get trained properly.
 *
 * On CHV the vblank interrupts don't seem to disappear but
 * otherwise the symptoms are similar.
 *
 * TODO: confirm the behaviour on HSW+
 */

 return hdisplay == 4096 && !HAS_DDI(display);
}

static int intel_dp_max_tmds_clock(struct intel_dp *intel_dp)
{
 struct intel_connector *connector = intel_dp->attached_connector;
 const struct drm_display_info *info = &connector->base.display_info;
 int max_tmds_clock = intel_dp->dfp.max_tmds_clock;

 /* Only consider the sink's max TMDS clock if we know this is a HDMI DFP */
 if (max_tmds_clock && info->max_tmds_clock)
  max_tmds_clock = min(max_tmds_clock, info->max_tmds_clock);

 return max_tmds_clock;
}

static enum drm_mode_status
intel_dp_tmds_clock_valid(struct intel_dp *intel_dp,
     int clock, int bpc,
     enum intel_output_format sink_format,
     bool respect_downstream_limits)
{
 int tmds_clock, min_tmds_clock, max_tmds_clock;

 if (!respect_downstream_limits)
  return MODE_OK;

 tmds_clock = intel_hdmi_tmds_clock(clock, bpc, sink_format);

 min_tmds_clock = intel_dp->dfp.min_tmds_clock;
 max_tmds_clock = intel_dp_max_tmds_clock(intel_dp);

 if (min_tmds_clock && tmds_clock < min_tmds_clock)
  return MODE_CLOCK_LOW;

 if (max_tmds_clock && tmds_clock > max_tmds_clock)
  return MODE_CLOCK_HIGH;

 return MODE_OK;
}

static enum drm_mode_status
intel_dp_mode_valid_downstream(struct intel_connector *connector,
          const struct drm_display_mode *mode,
          int target_clock)
{
 struct intel_dp *intel_dp = intel_attached_dp(connector);
 const struct drm_display_info *info = &connector->base.display_info;
 enum drm_mode_status status;
 enum intel_output_format sink_format;

 /* If PCON supports FRL MODE, check FRL bandwidth constraints */
 if (intel_dp->dfp.pcon_max_frl_bw) {
  int target_bw;
  int max_frl_bw;
  int bpp = intel_dp_mode_min_output_bpp(connector, mode);

  target_bw = bpp * target_clock;

  max_frl_bw = intel_dp->dfp.pcon_max_frl_bw;

  /* converting bw from Gbps to Kbps*/
  max_frl_bw = max_frl_bw * 1000000;

  if (target_bw > max_frl_bw)
   return MODE_CLOCK_HIGH;

  return MODE_OK;
 }

 if (intel_dp->dfp.max_dotclock &&
     target_clock > intel_dp->dfp.max_dotclock)
  return MODE_CLOCK_HIGH;

 sink_format = intel_dp_sink_format(connector, mode);

 /* Assume 8bpc for the DP++/HDMI/DVI TMDS clock check */
 status = intel_dp_tmds_clock_valid(intel_dp, target_clock,
        8, sink_format, true);

 if (status != MODE_OK) {
  if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420 ||
      !connector->base.ycbcr_420_allowed ||
      !drm_mode_is_420_also(info, mode))
   return status;
  sink_format = INTEL_OUTPUT_FORMAT_YCBCR420;
  status = intel_dp_tmds_clock_valid(intel_dp, target_clock,
         8, sink_format, true);
  if (status != MODE_OK)
   return status;
 }

 return MODE_OK;
}

static
bool intel_dp_needs_joiner(struct intel_dp *intel_dp,
      struct intel_connector *connector,
      int hdisplay, int clock,
      int num_joined_pipes)
{
 struct intel_display *display = to_intel_display(intel_dp);
 int hdisplay_limit;

 if (!intel_dp_has_joiner(intel_dp))
  return false;

 num_joined_pipes /= 2;

 hdisplay_limit = DISPLAY_VER(display) >= 30 ? 6144 : 5120;

 return clock > num_joined_pipes * display->cdclk.max_dotclk_freq ||
        hdisplay > num_joined_pipes * hdisplay_limit;
}

int intel_dp_num_joined_pipes(struct intel_dp *intel_dp,
         struct intel_connector *connector,
         int hdisplay, int clock)
{
 struct intel_display *display = to_intel_display(intel_dp);

 if (connector->force_joined_pipes)
  return connector->force_joined_pipes;

 if (HAS_ULTRAJOINER(display) &&
     intel_dp_needs_joiner(intel_dp, connector, hdisplay, clock, 4))
  return 4;

 if ((HAS_BIGJOINER(display) || HAS_UNCOMPRESSED_JOINER(display)) &&
     intel_dp_needs_joiner(intel_dp, connector, hdisplay, clock, 2))
  return 2;

 return 1;
}

bool intel_dp_has_dsc(const struct intel_connector *connector)
{
 struct intel_display *display = to_intel_display(connector);

 if (!HAS_DSC(display))
  return false;

 if (connector->mst.dp && !HAS_DSC_MST(display))
  return false;

 if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP &&
     connector->panel.vbt.edp.dsc_disable)
  return false;

 if (!drm_dp_sink_supports_dsc(connector->dp.dsc_dpcd))
  return false;

 return true;
}

static enum drm_mode_status
intel_dp_mode_valid(struct drm_connector *_connector,
      const struct drm_display_mode *mode)
{
 struct intel_display *display = to_intel_display(_connector->dev);
 struct intel_connector *connector = to_intel_connector(_connector);
 struct intel_dp *intel_dp = intel_attached_dp(connector);
 const struct drm_display_mode *fixed_mode;
 int target_clock = mode->clock;
 int max_rate, mode_rate, max_lanes, max_link_clock;
 int max_dotclk = display->cdclk.max_dotclk_freq;
 u16 dsc_max_compressed_bpp = 0;
 u8 dsc_slice_count = 0;
 enum drm_mode_status status;
 bool dsc = false;
 int num_joined_pipes;

 status = intel_cpu_transcoder_mode_valid(display, mode);
 if (status != MODE_OK)
  return status;

 if (mode->flags & DRM_MODE_FLAG_DBLCLK)
  return MODE_H_ILLEGAL;

 if (mode->clock < 10000)
  return MODE_CLOCK_LOW;

 fixed_mode = intel_panel_fixed_mode(connector, mode);
 if (intel_dp_is_edp(intel_dp) && fixed_mode) {
  status = intel_panel_mode_valid(connector, mode);
  if (status != MODE_OK)
   return status;

  target_clock = fixed_mode->clock;
 }

 num_joined_pipes = intel_dp_num_joined_pipes(intel_dp, connector,
           mode->hdisplay, target_clock);
 max_dotclk *= num_joined_pipes;

 if (target_clock > max_dotclk)
  return MODE_CLOCK_HIGH;

 if (intel_dp_hdisplay_bad(display, mode->hdisplay))
  return MODE_H_ILLEGAL;

 max_link_clock = intel_dp_max_link_rate(intel_dp);
 max_lanes = intel_dp_max_lane_count(intel_dp);

 max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes);

 mode_rate = intel_dp_link_required(target_clock,
        intel_dp_mode_min_output_bpp(connector, mode));

 if (intel_dp_has_dsc(connector)) {
  enum intel_output_format sink_format, output_format;
  int pipe_bpp;

  sink_format = intel_dp_sink_format(connector, mode);
  output_format = intel_dp_output_format(connector, sink_format);
  /*
 * TBD pass the connector BPC,
 * for now U8_MAX so that max BPC on that platform would be picked
 */

  pipe_bpp = intel_dp_dsc_compute_max_bpp(connector, U8_MAX);

  /*
 * Output bpp is stored in 6.4 format so right shift by 4 to get the
 * integer value since we support only integer values of bpp.
 */

  if (intel_dp_is_edp(intel_dp)) {
   dsc_max_compressed_bpp =
    drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd) >> 4;
   dsc_slice_count =
    drm_dp_dsc_sink_max_slice_count(connector->dp.dsc_dpcd,
        true);
  } else if (drm_dp_sink_supports_fec(connector->dp.fec_capability)) {
   dsc_max_compressed_bpp =
    intel_dp_dsc_get_max_compressed_bpp(display,
            max_link_clock,
            max_lanes,
            target_clock,
            mode->hdisplay,
            num_joined_pipes,
            output_format,
            pipe_bpp, 64);
   dsc_slice_count =
    intel_dp_dsc_get_slice_count(connector,
            target_clock,
            mode->hdisplay,
            num_joined_pipes);
  }

  dsc = dsc_max_compressed_bpp && dsc_slice_count;
 }

 if (intel_dp_joiner_needs_dsc(display, num_joined_pipes) && !dsc)
  return MODE_CLOCK_HIGH;

 if (mode_rate > max_rate && !dsc)
  return MODE_CLOCK_HIGH;

 status = intel_dp_mode_valid_downstream(connector, mode, target_clock);
 if (status != MODE_OK)
  return status;

 return intel_mode_valid_max_plane_size(display, mode, num_joined_pipes);
}

bool intel_dp_source_supports_tps3(struct intel_display *display)
{
 return DISPLAY_VER(display) >= 9 ||
  display->platform.broadwell || display->platform.haswell;
}

bool intel_dp_source_supports_tps4(struct intel_display *display)
{
 return DISPLAY_VER(display) >= 10;
}

static void seq_buf_print_array(struct seq_buf *s, const int *array, int nelem)
{
 int i;

 for (i = 0; i < nelem; i++)
  seq_buf_printf(s, "%s%d", i ? ", " : "", array[i]);
}

static void intel_dp_print_rates(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);
 DECLARE_SEQ_BUF(s, 128); /* FIXME: too big for stack? */

 if (!drm_debug_enabled(DRM_UT_KMS))
  return;

 seq_buf_print_array(&s, intel_dp->source_rates, intel_dp->num_source_rates);
 drm_dbg_kms(display->drm, "source rates: %s\n", seq_buf_str(&s));

 seq_buf_clear(&s);
 seq_buf_print_array(&s, intel_dp->sink_rates, intel_dp->num_sink_rates);
 drm_dbg_kms(display->drm, "sink rates: %s\n", seq_buf_str(&s));

 seq_buf_clear(&s);
 seq_buf_print_array(&s, intel_dp->common_rates, intel_dp->num_common_rates);
 drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s));
}

static int forced_link_rate(struct intel_dp *intel_dp)
{
 int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate);

 if (len == 0)
  return intel_dp_common_rate(intel_dp, 0);

 return intel_dp_common_rate(intel_dp, len - 1);
}

int
intel_dp_max_link_rate(struct intel_dp *intel_dp)
{
 int len;

 if (intel_dp->link.force_rate)
  return forced_link_rate(intel_dp);

 len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate);

 return intel_dp_common_rate(intel_dp, len - 1);
}

static int
intel_dp_min_link_rate(struct intel_dp *intel_dp)
{
 if (intel_dp->link.force_rate)
  return forced_link_rate(intel_dp);

 return intel_dp_common_rate(intel_dp, 0);
}

int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
{
 struct intel_display *display = to_intel_display(intel_dp);
 int i = intel_dp_rate_index(intel_dp->sink_rates,
        intel_dp->num_sink_rates, rate);

 if (drm_WARN_ON(display->drm, i < 0))
  i = 0;

 return i;
}

void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
      u8 *link_bw, u8 *rate_select)
{
 struct intel_display *display = to_intel_display(intel_dp);

 /* FIXME g4x can't generate an exact 2.7GHz with the 96MHz non-SSC refclk */
 if (display->platform.g4x && port_clock == 268800)
  port_clock = 270000;

 /* eDP 1.4 rate select method. */
 if (intel_dp->use_rate_select) {
  *link_bw = 0;
  *rate_select =
   intel_dp_rate_select(intel_dp, port_clock);
 } else {
  *link_bw = drm_dp_link_rate_to_bw_code(port_clock);
  *rate_select = 0;
 }
}

bool intel_dp_has_hdmi_sink(struct intel_dp *intel_dp)
{
 struct intel_connector *connector = intel_dp->attached_connector;

 return connector->base.display_info.is_hdmi;
}

static bool intel_dp_source_supports_fec(struct intel_dp *intel_dp,
      const struct intel_crtc_state *pipe_config)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;

 if (DISPLAY_VER(display) >= 12)
  return true;

 if (DISPLAY_VER(display) == 11 && encoder->port != PORT_A &&
     !intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST))
  return true;

 return false;
}

bool intel_dp_supports_fec(struct intel_dp *intel_dp,
      const struct intel_connector *connector,
      const struct intel_crtc_state *pipe_config)
{
 return intel_dp_source_supports_fec(intel_dp, pipe_config) &&
  drm_dp_sink_supports_fec(connector->dp.fec_capability);
}

bool intel_dp_supports_dsc(struct intel_dp *intel_dp,
      const struct intel_connector *connector,
      const struct intel_crtc_state *crtc_state)
{
 if (!intel_dp_has_dsc(connector))
  return false;

 if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP) &&
     !intel_dp_supports_fec(intel_dp, connector, crtc_state))
  return false;

 return intel_dsc_source_support(crtc_state);
}

static int intel_dp_hdmi_compute_bpc(struct intel_dp *intel_dp,
         const struct intel_crtc_state *crtc_state,
         int bpc, bool respect_downstream_limits)
{
 int clock = crtc_state->hw.adjusted_mode.crtc_clock;

 /*
 * Current bpc could already be below 8bpc due to
 * FDI bandwidth constraints or other limits.
 * HDMI minimum is 8bpc however.
 */

 bpc = max(bpc, 8);

 /*
 * We will never exceed downstream TMDS clock limits while
 * attempting deep color. If the user insists on forcing an
 * out of spec mode they will have to be satisfied with 8bpc.
 */

 if (!respect_downstream_limits)
  bpc = 8;

 for (; bpc >= 8; bpc -= 2) {
  if (intel_hdmi_bpc_possible(crtc_state, bpc,
         intel_dp_has_hdmi_sink(intel_dp)) &&
      intel_dp_tmds_clock_valid(intel_dp, clock, bpc, crtc_state->sink_format,
           respect_downstream_limits) == MODE_OK)
   return bpc;
 }

 return -EINVAL;
}

static int intel_dp_max_bpp(struct intel_dp *intel_dp,
       const struct intel_crtc_state *crtc_state,
       bool respect_downstream_limits)
{
 struct intel_display *display = to_intel_display(intel_dp);
 struct intel_connector *connector = intel_dp->attached_connector;
 int bpp, bpc;

 bpc = crtc_state->pipe_bpp / 3;

 if (intel_dp->dfp.max_bpc)
  bpc = min_t(int, bpc, intel_dp->dfp.max_bpc);

 if (intel_dp->dfp.min_tmds_clock) {
  int max_hdmi_bpc;

  max_hdmi_bpc = intel_dp_hdmi_compute_bpc(intel_dp, crtc_state, bpc,
        respect_downstream_limits);
  if (max_hdmi_bpc < 0)
   return 0;

  bpc = min(bpc, max_hdmi_bpc);
 }

 bpp = bpc * 3;
 if (intel_dp_is_edp(intel_dp)) {
  /* Get bpp from vbt only for panels that dont have bpp in edid */
  if (connector->base.display_info.bpc == 0 &&
      connector->panel.vbt.edp.bpp &&
      connector->panel.vbt.edp.bpp < bpp) {
   drm_dbg_kms(display->drm,
        "clamping bpp for eDP panel to BIOS-provided %i\n",
        connector->panel.vbt.edp.bpp);
   bpp = connector->panel.vbt.edp.bpp;
  }
 }

 return bpp;
}

static bool has_seamless_m_n(struct intel_connector *connector)
{
 struct intel_display *display = to_intel_display(connector);

 /*
 * Seamless M/N reprogramming only implemented
 * for BDW+ double buffered M/N registers so far.
 */

 return HAS_DOUBLE_BUFFERED_M_N(display) &&
  intel_panel_drrs_type(connector) == DRRS_TYPE_SEAMLESS;
}

static int intel_dp_mode_clock(const struct intel_crtc_state *crtc_state,
          const struct drm_connector_state *conn_state)
{
 struct intel_connector *connector = to_intel_connector(conn_state->connector);
 const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;

 /* FIXME a bit of a mess wrt clock vs. crtc_clock */
 if (has_seamless_m_n(connector))
  return intel_panel_highest_mode(connector, adjusted_mode)->clock;
 else
  return adjusted_mode->crtc_clock;
}

/* Optimize link config in order: max bpp, min clock, min lanes */
static int
intel_dp_compute_link_config_wide(struct intel_dp *intel_dp,
      struct intel_crtc_state *pipe_config,
      const struct drm_connector_state *conn_state,
      const struct link_config_limits *limits)
{
 int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state);
 int mode_rate, link_rate, link_avail;

 for (bpp = fxp_q4_to_int(limits->link.max_bpp_x16);
      bpp >= fxp_q4_to_int(limits->link.min_bpp_x16);
      bpp -= 2 * 3) {
  int link_bpp = intel_dp_output_bpp(pipe_config->output_format, bpp);

  mode_rate = intel_dp_link_required(clock, link_bpp);

  for (i = 0; i < intel_dp->num_common_rates; i++) {
   link_rate = intel_dp_common_rate(intel_dp, i);
   if (link_rate < limits->min_rate ||
       link_rate > limits->max_rate)
    continue;

   for (lane_count = limits->min_lane_count;
        lane_count <= limits->max_lane_count;
        lane_count <<= 1) {
    link_avail = intel_dp_max_link_data_rate(intel_dp,
          link_rate,
          lane_count);


    if (mode_rate <= link_avail) {
     pipe_config->lane_count = lane_count;
     pipe_config->pipe_bpp = bpp;
     pipe_config->port_clock = link_rate;

     return 0;
    }
   }
  }
 }

 return -EINVAL;
}

int intel_dp_dsc_max_src_input_bpc(struct intel_display *display)
{
 /* Max DSC Input BPC for ICL is 10 and for TGL+ is 12 */
 if (DISPLAY_VER(display) >= 12)
  return 12;
 if (DISPLAY_VER(display) == 11)
  return 10;

 return intel_dp_dsc_min_src_input_bpc();
}

int intel_dp_dsc_compute_max_bpp(const struct intel_connector *connector,
     u8 max_req_bpc)
{
 struct intel_display *display = to_intel_display(connector);
 int i, num_bpc;
 u8 dsc_bpc[3] = {};
 int dsc_max_bpc;

 dsc_max_bpc = intel_dp_dsc_max_src_input_bpc(display);

 if (!dsc_max_bpc)
  return dsc_max_bpc;

 dsc_max_bpc = min(dsc_max_bpc, max_req_bpc);

 num_bpc = drm_dp_dsc_sink_supported_input_bpcs(connector->dp.dsc_dpcd,
             dsc_bpc);
 for (i = 0; i < num_bpc; i++) {
  if (dsc_max_bpc >= dsc_bpc[i])
   return dsc_bpc[i] * 3;
 }

 return 0;
}

static int intel_dp_source_dsc_version_minor(struct intel_display *display)
{
 return DISPLAY_VER(display) >= 14 ? 2 : 1;
}

static int intel_dp_sink_dsc_version_minor(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
{
 return (dsc_dpcd[DP_DSC_REV - DP_DSC_SUPPORT] & DP_DSC_MINOR_MASK) >>
  DP_DSC_MINOR_SHIFT;
}

static int intel_dp_get_slice_height(int vactive)
{
 int slice_height;

 /*
 * VDSC 1.2a spec in Section 3.8 Options for Slices implies that 108
 * lines is an optimal slice height, but any size can be used as long as
 * vertical active integer multiple and maximum vertical slice count
 * requirements are met.
 */

 for (slice_height = 108; slice_height <= vactive; slice_height += 2)
  if (vactive % slice_height == 0)
   return slice_height;

 /*
 * Highly unlikely we reach here as most of the resolutions will end up
 * finding appropriate slice_height in above loop but returning
 * slice_height as 2 here as it should work with all resolutions.
 */

 return 2;
}

static int intel_dp_dsc_compute_params(const struct intel_connector *connector,
           struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(connector);
 struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config;
 int ret;

 /*
 * RC_MODEL_SIZE is currently a constant across all configurations.
 *
 * FIXME: Look into using sink defined DPCD DP_DSC_RC_BUF_BLK_SIZE and
 * DP_DSC_RC_BUF_SIZE for this.
 */

 vdsc_cfg->rc_model_size = DSC_RC_MODEL_SIZE_CONST;
 vdsc_cfg->pic_height = crtc_state->hw.adjusted_mode.crtc_vdisplay;

 vdsc_cfg->slice_height = intel_dp_get_slice_height(vdsc_cfg->pic_height);

 ret = intel_dsc_compute_params(crtc_state);
 if (ret)
  return ret;

 vdsc_cfg->dsc_version_major =
  (connector->dp.dsc_dpcd[DP_DSC_REV - DP_DSC_SUPPORT] &
   DP_DSC_MAJOR_MASK) >> DP_DSC_MAJOR_SHIFT;
 vdsc_cfg->dsc_version_minor =
  min(intel_dp_source_dsc_version_minor(display),
      intel_dp_sink_dsc_version_minor(connector->dp.dsc_dpcd));
 if (vdsc_cfg->convert_rgb)
  vdsc_cfg->convert_rgb =
   connector->dp.dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT] &
   DP_DSC_RGB;

 vdsc_cfg->line_buf_depth = min(INTEL_DP_DSC_MAX_LINE_BUF_DEPTH,
           drm_dp_dsc_sink_line_buf_depth(connector->dp.dsc_dpcd));
 if (!vdsc_cfg->line_buf_depth) {
  drm_dbg_kms(display->drm,
       "DSC Sink Line Buffer Depth invalid\n");
  return -EINVAL;
 }

 vdsc_cfg->block_pred_enable =
  connector->dp.dsc_dpcd[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT] &
  DP_DSC_BLK_PREDICTION_IS_SUPPORTED;

 return drm_dsc_compute_rc_parameters(vdsc_cfg);
}

static bool intel_dp_dsc_supports_format(const struct intel_connector *connector,
      enum intel_output_format output_format)
{
 struct intel_display *display = to_intel_display(connector);
 u8 sink_dsc_format;

 switch (output_format) {
 case INTEL_OUTPUT_FORMAT_RGB:
  sink_dsc_format = DP_DSC_RGB;
  break;
 case INTEL_OUTPUT_FORMAT_YCBCR444:
  sink_dsc_format = DP_DSC_YCbCr444;
  break;
 case INTEL_OUTPUT_FORMAT_YCBCR420:
  if (min(intel_dp_source_dsc_version_minor(display),
   intel_dp_sink_dsc_version_minor(connector->dp.dsc_dpcd)) < 2)
   return false;
  sink_dsc_format = DP_DSC_YCbCr420_Native;
  break;
 default:
  return false;
 }

 return drm_dp_dsc_sink_supports_format(connector->dp.dsc_dpcd, sink_dsc_format);
}

static bool is_bw_sufficient_for_dsc_config(int dsc_bpp_x16, u32 link_clock,
         u32 lane_count, u32 mode_clock,
         enum intel_output_format output_format,
         int timeslots)
{
 u32 available_bw, required_bw;

 available_bw = (link_clock * lane_count * timeslots * 16)  / 8;
 required_bw = dsc_bpp_x16 * (intel_dp_mode_to_fec_clock(mode_clock));

 return available_bw > required_bw;
}

static int dsc_compute_link_config(struct intel_dp *intel_dp,
       struct intel_crtc_state *pipe_config,
       struct drm_connector_state *conn_state,
       const struct link_config_limits *limits,
       int dsc_bpp_x16,
       int timeslots)
{
 const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
 int link_rate, lane_count;
 int i;

 for (i = 0; i < intel_dp->num_common_rates; i++) {
  link_rate = intel_dp_common_rate(intel_dp, i);
  if (link_rate < limits->min_rate || link_rate > limits->max_rate)
   continue;

  for (lane_count = limits->min_lane_count;
       lane_count <= limits->max_lane_count;
       lane_count <<= 1) {

   /*
 * FIXME: intel_dp_mtp_tu_compute_config() requires
 * ->lane_count and ->port_clock set before we know
 * they'll work. If we end up failing altogether,
 * they'll remain in crtc state. This shouldn't matter,
 * as we'd then bail out from compute config, but it's
 * just ugly.
 */

   pipe_config->lane_count = lane_count;
   pipe_config->port_clock = link_rate;

   if (drm_dp_is_uhbr_rate(link_rate)) {
    int ret;

    ret = intel_dp_mtp_tu_compute_config(intel_dp,
             pipe_config,
             conn_state,
             dsc_bpp_x16,
             dsc_bpp_x16,
             0, true);
    if (ret)
     continue;
   } else {
    if (!is_bw_sufficient_for_dsc_config(dsc_bpp_x16, link_rate,
             lane_count, adjusted_mode->clock,
             pipe_config->output_format,
             timeslots))
     continue;
   }

   return 0;
  }
 }

 return -EINVAL;
}

static
u16 intel_dp_dsc_max_sink_compressed_bppx16(const struct intel_connector *connector,
         const struct intel_crtc_state *pipe_config,
         int bpc)
{
 u16 max_bppx16 = drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd);

 if (max_bppx16)
  return max_bppx16;
 /*
 * If support not given in DPCD 67h, 68h use the Maximum Allowed bit rate
 * values as given in spec Table 2-157 DP v2.0
 */

 switch (pipe_config->output_format) {
 case INTEL_OUTPUT_FORMAT_RGB:
 case INTEL_OUTPUT_FORMAT_YCBCR444:
  return (3 * bpc) << 4;
 case INTEL_OUTPUT_FORMAT_YCBCR420:
  return (3 * (bpc / 2)) << 4;
 default:
  MISSING_CASE(pipe_config->output_format);
  break;
 }

 return 0;
}

int intel_dp_dsc_sink_min_compressed_bpp(const struct intel_crtc_state *pipe_config)
{
 /* From Mandatory bit rate range Support Table 2-157 (DP v2.0) */
 switch (pipe_config->output_format) {
 case INTEL_OUTPUT_FORMAT_RGB:
 case INTEL_OUTPUT_FORMAT_YCBCR444:
  return 8;
 case INTEL_OUTPUT_FORMAT_YCBCR420:
  return 6;
 default:
  MISSING_CASE(pipe_config->output_format);
  break;
 }

 return 0;
}

int intel_dp_dsc_sink_max_compressed_bpp(const struct intel_connector *connector,
      const struct intel_crtc_state *pipe_config,
      int bpc)
{
 return intel_dp_dsc_max_sink_compressed_bppx16(connector,
             pipe_config, bpc) >> 4;
}

int intel_dp_dsc_min_src_compressed_bpp(void)
{
 /* Min Compressed bpp supported by source is 8 */
 return 8;
}

static int dsc_src_max_compressed_bpp(struct intel_dp *intel_dp)
{
 struct intel_display *display = to_intel_display(intel_dp);

 /*
 * Forcing DSC and using the platform's max compressed bpp is seen to cause
 * underruns. Since DSC isn't needed in these cases, limit the
 * max compressed bpp to 18, which is a safe value across platforms with different
 * pipe bpps.
 */

 if (intel_dp->force_dsc_en)
  return 18;

 /*
 * Max Compressed bpp for Gen 13+ is 27bpp.
 * For earlier platform is 23bpp. (Bspec:49259).
 */

 if (DISPLAY_VER(display) < 13)
  return 23;
 else
  return 27;
}

/*
 * Note: for pre-13 display you still need to check the validity of each step.
 */

int intel_dp_dsc_bpp_step_x16(const struct intel_connector *connector)
{
 struct intel_display *display = to_intel_display(connector);
 u8 incr = drm_dp_dsc_sink_bpp_incr(connector->dp.dsc_dpcd);

 if (DISPLAY_VER(display) < 14 || !incr)
  return fxp_q4_from_int(1);

 if (connector->mst.dp &&
     !connector->link.force_bpp_x16 && !connector->mst.dp->force_dsc_fractional_bpp_en)
  return fxp_q4_from_int(1);

 /* fxp q4 */
 return fxp_q4_from_int(1) / incr;
}

/*
 * Note: for bpp_x16 to be valid it must be also within the source/sink's
 * min..max bpp capability range.
 */

bool intel_dp_dsc_valid_compressed_bpp(struct intel_dp *intel_dp, int bpp_x16)
{
 struct intel_display *display = to_intel_display(intel_dp);
 int i;

 if (DISPLAY_VER(display) >= 13) {
  if (intel_dp->force_dsc_fractional_bpp_en && !fxp_q4_to_frac(bpp_x16))
   return false;

  return true;
 }

 if (fxp_q4_to_frac(bpp_x16))
  return false;

 for (i = 0; i < ARRAY_SIZE(valid_dsc_bpp); i++) {
  if (fxp_q4_to_int(bpp_x16) == valid_dsc_bpp[i])
   return true;
 }

 return false;
}

/*
 * Find the max compressed BPP we can find a link configuration for. The BPPs to
 * try depend on the source (platform) and sink.
 */

static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp,
          struct intel_crtc_state *pipe_config,
          struct drm_connector_state *conn_state,
          const struct link_config_limits *limits,
          int pipe_bpp,
          int timeslots)
{
 struct intel_display *display = to_intel_display(intel_dp);
 const struct intel_connector *connector = to_intel_connector(conn_state->connector);
 const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
 int output_bpp;
 int min_bpp_x16, max_bpp_x16, bpp_step_x16;
 int dsc_joiner_max_bpp;
 int num_joined_pipes = intel_crtc_num_joined_pipes(pipe_config);
 int bpp_x16;
 int ret;

 dsc_joiner_max_bpp = get_max_compressed_bpp_with_joiner(display, adjusted_mode->clock,
        adjusted_mode->hdisplay,
        num_joined_pipes);
 max_bpp_x16 = min(fxp_q4_from_int(dsc_joiner_max_bpp), limits->link.max_bpp_x16);

 bpp_step_x16 = intel_dp_dsc_bpp_step_x16(connector);

 /* Compressed BPP should be less than the Input DSC bpp */
 output_bpp = intel_dp_output_bpp(pipe_config->output_format, pipe_bpp);
 max_bpp_x16 = min(max_bpp_x16, fxp_q4_from_int(output_bpp) - bpp_step_x16);

 drm_WARN_ON(display->drm, !is_power_of_2(bpp_step_x16));
 min_bpp_x16 = round_up(limits->link.min_bpp_x16, bpp_step_x16);
 max_bpp_x16 = round_down(max_bpp_x16, bpp_step_x16);

 for (bpp_x16 = max_bpp_x16; bpp_x16 >= min_bpp_x16; bpp_x16 -= bpp_step_x16) {
  if (!intel_dp_dsc_valid_compressed_bpp(intel_dp, bpp_x16))
   continue;

  ret = dsc_compute_link_config(intel_dp,
           pipe_config,
           conn_state,
           limits,
           bpp_x16,
           timeslots);
  if (ret == 0) {
   pipe_config->dsc.compressed_bpp_x16 = bpp_x16;
   if (intel_dp->force_dsc_fractional_bpp_en &&
       fxp_q4_to_frac(bpp_x16))
    drm_dbg_kms(display->drm,
         "Forcing DSC fractional bpp\n");

   return 0;
  }
 }

 return -EINVAL;
}

int intel_dp_dsc_min_src_input_bpc(void)
{
 /* Min DSC Input BPC for ICL+ is 8 */
 return 8;
}

static
bool is_dsc_pipe_bpp_sufficient(const struct link_config_limits *limits,
    int pipe_bpp)
{
 return pipe_bpp >= limits->pipe.min_bpp &&
        pipe_bpp <= limits->pipe.max_bpp;
}

static
int intel_dp_force_dsc_pipe_bpp(struct intel_dp *intel_dp,
    const struct link_config_limits *limits)
{
 struct intel_display *display = to_intel_display(intel_dp);
 int forced_bpp;

 if (!intel_dp->force_dsc_bpc)
  return 0;

 forced_bpp = intel_dp->force_dsc_bpc * 3;

 if (is_dsc_pipe_bpp_sufficient(limits, forced_bpp)) {
  drm_dbg_kms(display->drm, "Input DSC BPC forced to %d\n",
       intel_dp->force_dsc_bpc);
  return forced_bpp;
 }

 drm_dbg_kms(display->drm,
      "Cannot force DSC BPC:%d, due to DSC BPC limits\n",
      intel_dp->force_dsc_bpc);

 return 0;
}

static int intel_dp_dsc_compute_pipe_bpp(struct intel_dp *intel_dp,
      struct intel_crtc_state *pipe_config,
      struct drm_connector_state *conn_state,
      const struct link_config_limits *limits,
      int timeslots)
{
 const struct intel_connector *connector =
  to_intel_connector(conn_state->connector);
 u8 dsc_bpc[3] = {};
 int forced_bpp, pipe_bpp;
 int num_bpc, i, ret;

 forced_bpp = intel_dp_force_dsc_pipe_bpp(intel_dp, limits);

 if (forced_bpp) {
  ret = dsc_compute_compressed_bpp(intel_dp, pipe_config, conn_state,
       limits, forced_bpp, timeslots);
  if (ret == 0) {
   pipe_config->pipe_bpp = forced_bpp;
   return 0;
  }
 }

 /*
 * Get the maximum DSC bpc that will be supported by any valid
 * link configuration and compressed bpp.
 */

 num_bpc = drm_dp_dsc_sink_supported_input_bpcs(connector->dp.dsc_dpcd, dsc_bpc);
 for (i = 0; i < num_bpc; i++) {
--> --------------------

--> maximum size reached

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

Messung V0.5
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

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