Merge tag 'drm-intel-next-2026-07-28' of https://gitlab.freedesktop.org/drm/i915/kernel into drm-next

drm/i915 feature pull #2 for v7.3:

Features and functionality:
- Enable UHBR link rates on Thunderbolt tunneled links (Imre)
- Reduce Xe3+ PM demand peak bandwidth for power savings (Vinod)
- Add the blend mode property to all planes that support alpha blending (Chaitanya)
- Enable pipe DMC error interrupts for display 30+ (Dibin)
- Add KUnit tests for DP link config selection and fallback (Imre)

Refactoring and cleanups:
- Refactor DP link config selection and unify across use cases (Imre)
- Unify i915 and xe display runtime PM calls (Jani)
- Refactor BIOS framebuffer takeover (Ville)

Fixes:
- Fix HD audio on DP UHBR SST (Kai Vehmanen)
- Fixes to xe driver BIOS framebuffer takeover (Ville)
- Fix 2 pixels-per-clock CDCLK calculation to avoid underruns (Ville)
- Fix incorrectly set VSC SDP Main Stream Attribute (Chaitanya)
- Fix BPC and DSC selection for HDMI sinks (Alexander Kaplan)
- Fix PCON max FRL rate selection (Alexander Kaplan)
- Workaround Xe3P PSR2 screen corruption (Dibin)
- Fix NVL A & B stepping vtotal setting (Suraj)
- Fix xe DPT allocation paths (Maarten)
- Prefer system memory instead of stolen for new framebuffers in xe (Maarten)
- Fix transcoder mask sizes (John Harrison)
- Clear stale UV/Y plane DDB entries on plane disable (Vinod)
- Fix some DP AUX backlight control issues, again (Suraj)
- Fix switching between HDCP 1.4 and 2.2 authentication (Suraj)
- Remove unnecessary Xe2_LPD+ FBC plane width and surface size limits (Vinod)
- Ensure non-zero DSB safe window for PTL+ (Ankit)
- Fix bandwidth calculation to account for 16 DRAM channels (Uma)
- Fix NV12 ceiling division for bigjoiner case (Vidya)

DRM core changes:
- Add Thunderbolt UHBR tunneling support (Imre)

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patch.msgid.link/cb1b5a644d75589cbcdcc8ec8160968140426439@intel.com
This commit is contained in:
Dave Airlie
2026-08-01 06:05:00 +10:00
70 changed files with 3739 additions and 716 deletions

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
DisplayPort Link Capabilities
=============================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_caps.c
:doc: DisplayPort link capabilities
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_caps.h
:internal:

View File

@@ -39,6 +39,7 @@ driver. The display driver isn't an independent driver in that sense.
frontbuffer
hotplug
dp-link-training
dp-link-capabilities
plane
psr
snps-phy

View File

@@ -111,6 +111,8 @@
DPTUN_REG(DP_ALLOCATED_BW) | \
DPTUN_REG(DP_TUNNELING_MAX_LINK_RATE) | \
DPTUN_REG(DP_TUNNELING_MAX_LANE_COUNT) | \
DPTUN_REG(DP_TUNNELING_MAIN_LINK_CHANNEL_CODING) | \
DPTUN_REG(DP_TUNNELING_128B132B_LINK_RATE) | \
DPTUN_REG(DP_DPTX_BW_ALLOCATION_MODE_CONTROL))
static const DECLARE_BITMAP(dptun_info_regs, 64) = {
@@ -140,11 +142,14 @@ struct drm_dp_tunnel {
int estimated_bw;
int allocated_bw;
u8 dprx_128b132b_rates;
int max_dprx_rate;
u8 max_dprx_lane_count;
u8 adapter_id;
bool dprx_128b132b_support:1;
bool dprx_128b132b_lane0_mapping_support:1;
bool bw_alloc_supported:1;
bool bw_alloc_enabled:1;
bool has_io_error:1;
@@ -260,9 +265,46 @@ static int tunnel_reg_bw_granularity(const struct drm_dp_tunnel_regs *regs)
return (250000 << gr) / 8;
}
static bool tunnel_reg_dprx_128b132b_support(const struct drm_dp_tunnel_regs *regs)
{
return tunnel_reg(regs, DP_TUNNELING_MAIN_LINK_CHANNEL_CODING) & DP_128B132B_DP_SUPPORTED;
}
static bool tunnel_reg_dprx_128b132b_lane0_mapping_support(const struct drm_dp_tunnel_regs *regs)
{
return tunnel_reg(regs, DP_TUNNELING_128B132B_LINK_RATE) &
DP_TUNNELING_128B132B_LL_LANE0_MAPPING_SUPPORT;
}
static u8 tunnel_reg_dprx_128b132b_rates(const struct drm_dp_tunnel_regs *regs)
{
if (!tunnel_reg_dprx_128b132b_support(regs))
return 0;
return tunnel_reg(regs, DP_TUNNELING_128B132B_LINK_RATE) &
DP_TUNNELING_128B132B_LINK_RATE_MASK;
}
static u8 max_128b132b_rate(u8 rates)
{
if (rates & DP_TUNNELING_20GBPS_PER_LANE_SUPPORT)
return DP_TUNNELING_20GBPS_PER_LANE_SUPPORT;
else if (rates & DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT)
return DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT;
else if (rates & DP_TUNNELING_10GBPS_PER_LANE_SUPPORT)
return DP_TUNNELING_10GBPS_PER_LANE_SUPPORT;
WARN_ON(rates);
return 0;
}
static int tunnel_reg_max_dprx_rate(const struct drm_dp_tunnel_regs *regs)
{
u8 bw_code = tunnel_reg(regs, DP_TUNNELING_MAX_LINK_RATE);
u8 bw_code = max_128b132b_rate(tunnel_reg_dprx_128b132b_rates(regs));
if (!bw_code)
bw_code = tunnel_reg(regs, DP_TUNNELING_MAX_LINK_RATE);
return drm_dp_bw_code_to_link_rate(bw_code);
}
@@ -706,6 +748,23 @@ static bool update_dprx_caps(struct drm_dp_tunnel *tunnel, const struct drm_dp_t
{
bool changed = false;
if (tunnel_reg_dprx_128b132b_support(regs) != tunnel->dprx_128b132b_support) {
tunnel->dprx_128b132b_support = tunnel_reg_dprx_128b132b_support(regs);
changed = true;
}
if (tunnel_reg_dprx_128b132b_lane0_mapping_support(regs) !=
tunnel->dprx_128b132b_lane0_mapping_support) {
tunnel->dprx_128b132b_lane0_mapping_support =
tunnel_reg_dprx_128b132b_lane0_mapping_support(regs);
changed = true;
}
if (tunnel_reg_dprx_128b132b_rates(regs) != tunnel->dprx_128b132b_rates) {
tunnel->dprx_128b132b_rates = tunnel_reg_dprx_128b132b_rates(regs);
changed = true;
}
if (tunnel_reg_max_dprx_rate(regs) != tunnel->max_dprx_rate) {
tunnel->max_dprx_rate = tunnel_reg_max_dprx_rate(regs);
changed = true;
@@ -1331,6 +1390,61 @@ int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, struct drm_dp_aux *a
}
EXPORT_SYMBOL(drm_dp_tunnel_handle_irq);
/**
* drm_dp_tunnel_128b132b_supported - Query if 128b132b is supported by the tunnel's DPRX
* @tunnel: Tunnel object
*
* The function is used to query if 128b132b is supported by the DPRX connected
* to @tunnel.
*
* Returns %true if 128b132b is supported by the DPRX.
*/
bool drm_dp_tunnel_128b132b_supported(const struct drm_dp_tunnel *tunnel)
{
return tunnel->dprx_128b132b_support;
}
EXPORT_SYMBOL(drm_dp_tunnel_128b132b_supported);
/**
* drm_dp_tunnel_128b132b_lane0_mapping_supported - Check 128b/132b lane 0 mapping support
* @tunnel: Tunnel object
*
* Check whether the DP-out adapter always maps lane 0 as expected by the
* DPRX on a tunneled 128b/132b link. If the function returns %true, one- and
* two-lane configurations with UHBR link rates can always be used. If it
* returns %false, using one or two lanes with UHBR link rates may cause a
* lane-count conversion failure in the DPRX, requiring corrective action by
* the source during link training. See DP Standard v2.1b, section
* 3.5.2.16.3, 128b/132b DPRX Lane Count Conversion Failure Indication and
* Corrective Action.
*
* A four-lane configuration can always be used, provided that the DPRX
* supports it, regardless of the function's return value.
*
* Returns %true if the DP-out adapter supports the 128b/132b lane 0 mapping.
*/
bool drm_dp_tunnel_128b132b_lane0_mapping_supported(const struct drm_dp_tunnel *tunnel)
{
return tunnel->dprx_128b132b_lane0_mapping_support;
}
EXPORT_SYMBOL(drm_dp_tunnel_128b132b_lane0_mapping_supported);
/**
* drm_dp_tunnel_128b132b_dprx_rates - Query the supported 128b132b rates of the tunnel's DPRX
* @tunnel: Tunnel object
*
* The function is used to query the supported 128b132b rates of the DPRX connected
* to @tunnel. Note that the related DP_128B132B_SUPPROTED_LINK_RATES DPCD
* register will indicate no supported 128B132B rates for a tunneled DPRX.
*
* Returns the mask of supported 128b132b rates.
*/
u8 drm_dp_tunnel_128b132b_dprx_rates(const struct drm_dp_tunnel *tunnel)
{
return tunnel->dprx_128b132b_rates;
}
EXPORT_SYMBOL(drm_dp_tunnel_128b132b_dprx_rates);
/**
* drm_dp_tunnel_max_dprx_rate - Query the maximum rate of the tunnel's DPRX
* @tunnel: Tunnel object

View File

@@ -0,0 +1,9 @@
CONFIG_EXPERT=y
CONFIG_MODULES=y
CONFIG_KUNIT=y
CONFIG_PCI=y
CONFIG_DEBUG_FS=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DRM=y
CONFIG_DRM_I915=y
CONFIG_DRM_I915_KUNIT_TEST=y

View File

@@ -220,6 +220,18 @@ config DRM_I915_SELFTEST_BROKEN
If in doubt, say "N".
config DRM_I915_KUNIT_TEST
tristate "KUnit tests for the drm i915 driver" if !KUNIT_ALL_TESTS
depends on DRM_I915 && KUNIT && DEBUG_FS
default KUNIT_ALL_TESTS
help
Choose this option to allow the driver to perform selftests under
the kunit framework
Recommended for driver developers only.
If in doubt, say "N".
config DRM_I915_LOW_LEVEL_TRACEPOINTS
bool "Enable low level request tracing events"
depends on DRM_I915

View File

@@ -386,6 +386,8 @@ i915-y += \
i915-$(CONFIG_DRM_I915_DP_TUNNEL) += \
display/intel_dp_tunnel.o
obj-$(CONFIG_DRM_I915_KUNIT_TEST) += display/tests/
i915-$(CONFIG_DRM_I915_GVT) += \
display/intel_gvt_api.o

View File

@@ -207,7 +207,7 @@ static int _hsw_ips_min_cdclk(const struct intel_crtc_state *crtc_state)
struct intel_display *display = to_intel_display(crtc_state);
if (display->platform.broadwell)
return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
return DIV_ROUND_UP(crtc_state->pixel_rate_cdclk * 100, 95);
/* no IPS specific limits to worry about */
return 0;

View File

@@ -418,13 +418,13 @@ static int i9xx_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
unsigned int num, den;
/*
* Note that crtc_state->pixel_rate accounts for both
* Note that crtc_state->pixel_rate_cdclk accounts for both
* horizontal and vertical panel fitter downscaling factors.
* Pre-HSW bspec tells us to only consider the horizontal
* downscaling factor here. We ignore that and just consider
* both for simplicity.
*/
pixel_rate = crtc_state->pixel_rate;
pixel_rate = crtc_state->pixel_rate_cdclk;
i9xx_plane_ratio(crtc_state, plane_state, &num, &den);
@@ -1108,6 +1108,10 @@ intel_primary_plane_create(struct intel_display *display, enum pipe pipe)
DRM_MODE_ROTATE_0,
supported_rotations);
if (display->platform.valleyview || display->platform.cherryview)
drm_plane_create_blend_mode_property(&plane->base,
BIT(DRM_MODE_BLEND_PREMULTI));
zpos = 0;
drm_plane_create_zpos_immutable_property(&plane->base, zpos);

View File

@@ -38,6 +38,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_display_wa.h"
#include "intel_dp.h"
#include "intel_lpe_audio.h"
/**
@@ -696,6 +697,13 @@ static void ibx_audio_codec_enable(struct intel_encoder *encoder,
mutex_unlock(&display->audio.mutex);
}
static
bool intel_audio_needs_cpu_transcoder_id(const struct intel_crtc_state *crtc_state)
{
return intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST) ||
intel_dp_is_uhbr(crtc_state);
}
bool intel_audio_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
@@ -762,6 +770,8 @@ void intel_audio_codec_enable(struct intel_encoder *encoder,
audio_state = &display->audio.state[cpu_transcoder];
audio_state->encoder = encoder;
audio_state->needs_cpu_transcoder_id =
intel_audio_needs_cpu_transcoder_id(crtc_state);
BUILD_BUG_ON(sizeof(audio_state->eld) != sizeof(crtc_state->eld));
memcpy(audio_state->eld, crtc_state->eld, sizeof(audio_state->eld));
@@ -769,8 +779,12 @@ void intel_audio_codec_enable(struct intel_encoder *encoder,
if (acomp && acomp->base.audio_ops &&
acomp->base.audio_ops->pin_eld_notify) {
/* audio drivers expect cpu_transcoder = -1 to indicate Non-MST cases */
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST))
/*
* Audio drivers expect cpu_transcoder = -1 to indicate
* Non-MST/HBR cases. MST and UHBR SST are addressed by
* a real cpu_transcoder.
*/
if (!intel_audio_needs_cpu_transcoder_id(crtc_state))
cpu_transcoder = -1;
acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr,
(int)port, (int)cpu_transcoder);
@@ -819,14 +833,19 @@ void intel_audio_codec_disable(struct intel_encoder *encoder,
audio_state = &display->audio.state[cpu_transcoder];
audio_state->encoder = NULL;
audio_state->needs_cpu_transcoder_id = false;
memset(audio_state->eld, 0, sizeof(audio_state->eld));
mutex_unlock(&display->audio.mutex);
if (acomp && acomp->base.audio_ops &&
acomp->base.audio_ops->pin_eld_notify) {
/* audio drivers expect cpu_transcoder = -1 to indicate Non-MST cases */
if (!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST))
/*
* Audio drivers expect cpu_transcoder = -1 to indicate
* Non-MST/HBR cases. MST and UHBR SST are addressed by
* a real cpu_transcoder.
*/
if (!intel_audio_needs_cpu_transcoder_id(old_crtc_state))
cpu_transcoder = -1;
acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr,
(int)port, (int)cpu_transcoder);
@@ -1118,18 +1137,24 @@ static int intel_audio_component_get_cdclk_freq(struct device *kdev)
}
/*
* get the intel audio state according to the parameter port and cpu_transcoder
* MST & (cpu_transcoder >= 0): return the audio.state[cpu_transcoder].encoder],
* when port is matched
* MST & (cpu_transcoder < 0): this is invalid
* Non-MST & (cpu_transcoder >= 0): only cpu_transcoder = 0 (the first device entry)
* will get the right intel_encoder with port matched
* Non-MST & (cpu_transcoder < 0): get the right intel_encoder with port matched
* Get the intel audio state for a given (port, cpu_transcoder).
*
* Streams are addressed either by a real cpu_transcoder (DP MST and UHBR SST,
* i.e. entries whose stored needs_cpu_transcoder_id is true) or by port alone
* (legacy SST). Both the signalling side (pin_eld_notify()) and the lookup
* side use the same predicate, so the two are symmetric.
*
* cpu_transcoder >= 0 & needs_cpu_transcoder_id: return audio.state[cpu_transcoder]
* when the port matches.
* cpu_transcoder < 0 & !needs_cpu_transcoder_id: return the first port-matching
* entry.
* cpu_transcoder = 0 & !needs_cpu_transcoder_id: falls to the port-only
* loop so the first device entry of a legacy SST port is still found.
*/
static struct intel_audio_state *find_audio_state(struct intel_display *display,
int port, int cpu_transcoder)
{
/* MST */
/* MST, or UHBR SST. */
if (cpu_transcoder >= 0) {
struct intel_audio_state *audio_state;
struct intel_encoder *encoder;
@@ -1142,11 +1167,11 @@ static struct intel_audio_state *find_audio_state(struct intel_display *display,
encoder = audio_state->encoder;
if (encoder && encoder->port == port &&
encoder->type == INTEL_OUTPUT_DP_MST)
audio_state->needs_cpu_transcoder_id)
return audio_state;
}
/* Non-MST */
/* Legacy SST. */
if (cpu_transcoder > 0)
return NULL;
@@ -1158,7 +1183,7 @@ static struct intel_audio_state *find_audio_state(struct intel_display *display,
encoder = audio_state->encoder;
if (encoder && encoder->port == port &&
encoder->type != INTEL_OUTPUT_DP_MST)
!audio_state->needs_cpu_transcoder_id)
return audio_state;
}

View File

@@ -14,6 +14,7 @@
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_display_wa.h"
#include "intel_dram.h"
#include "intel_mchbar.h"
#include "intel_parent.h"
@@ -52,6 +53,8 @@ struct intel_qgv_point {
#define DEPROGBWPCLIMIT 60
#define PEAK_BW_THRESHOLD 20000
struct intel_psf_gv_point {
u8 clk; /* clock in multiples of 16.6666 MHz */
};
@@ -184,7 +187,7 @@ static int icl_pcode_restrict_qgv_points(struct intel_display *display,
{
int ret;
if (DISPLAY_VER(display) >= 14)
if (HAS_PMDEMAND(display))
return 0;
/* bspec says to keep retrying for at least 1 ms */
@@ -246,12 +249,10 @@ static bool is_y_tile(struct intel_display *display)
return !HAS_4TILE(display);
}
static int icl_get_qgv_points(struct intel_display *display,
const struct dram_info *dram_info,
struct intel_qgv_info *qi)
static int icl_init_qgv_info(struct intel_display *display,
const struct dram_info *dram_info,
struct intel_qgv_info *qi)
{
int i, ret;
qi->num_qgv_points = dram_info->num_qgv_points;
qi->num_psf_points = dram_info->num_psf_gv_points;
@@ -272,7 +273,14 @@ static int icl_get_qgv_points(struct intel_display *display,
case INTEL_DRAM_LPDDR4:
case INTEL_DRAM_LPDDR5:
qi->t_bl = 16;
qi->max_numchannels = 8;
/*
* Wa_16030862157
* Xe3p supports a fully-populated 16-channel LPDDR
* config (4 memory controllers x 4 channels); earlier
* D14+ platforms top out at 8.
*/
qi->max_numchannels =
intel_display_wa(display, INTEL_DISPLAY_WA_16030862157) ? 16 : 8;
qi->channel_width = 16;
qi->deinterleave = 4;
break;
@@ -323,6 +331,18 @@ static int icl_get_qgv_points(struct intel_display *display,
qi->max_numchannels = 1;
}
return 0;
}
static int icl_get_qgv_points(struct intel_display *display,
const struct dram_info *dram_info,
struct intel_qgv_info *qi)
{
int i, ret;
if (icl_init_qgv_info(display, dram_info, qi))
return -EINVAL;
if (drm_WARN_ON(display->drm,
qi->num_qgv_points > ARRAY_SIZE(qi->points)))
qi->num_qgv_points = ARRAY_SIZE(qi->points);
@@ -511,6 +531,19 @@ static const struct intel_display_bw_params *get_display_bw_params(struct intel_
return NULL;
}
static void update_sagv_status(struct intel_display *display, int qgv_points)
{
/*
* In case if SAGV is disabled in BIOS, we always get 1
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
if (qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
}
static int icl_get_bw_info(struct intel_display *display,
const struct dram_info *dram_info,
const struct intel_soc_bw_params *soc_bw_params,
@@ -569,15 +602,6 @@ static int icl_get_bw_info(struct intel_display *display,
i, j, bi->num_planes, bi->deratedbw[j]);
}
}
/*
* In case if SAGV is disabled in BIOS, we always get 1
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
if (qi.num_qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
return 0;
}
@@ -587,6 +611,32 @@ static int tgl_peakbw(int num_channels, int channel_width, int dclk)
return num_channels * (channel_width / 8) * dclk;
}
static void xe3_add_peakbw_threshold(struct intel_display *display)
{
u8 qgv_points = display->bw.num_qgv_points;
if (!HAS_PEAK_BW_THRESHOLD(display))
return;
if (qgv_points >= I915_NUM_QGV_POINTS) {
drm_dbg_kms(display->drm, "QGV points maxed out; skipping peak bandwidth threshold.\n");
return;
}
if (qgv_points <= 1)
return;
display->bw.num_qgv_points++;
display->bw.peakbw[qgv_points] = PEAK_BW_THRESHOLD;
for (int i = 0; i < ARRAY_SIZE(display->bw.max); i++)
display->bw.max[i].deratedbw[qgv_points] = PEAK_BW_THRESHOLD;
drm_dbg_kms(display->drm, "An extra QGV point %d added for Peak bw threshold of %d\n",
qgv_points, PEAK_BW_THRESHOLD);
}
static int tgl_get_bw_info(struct intel_display *display,
const struct dram_info *dram_info,
const struct intel_soc_bw_params *soc_bw_params,
@@ -624,10 +674,16 @@ static int tgl_get_bw_info(struct intel_display *display,
ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels);
/*
* Wa_16030862157
* clperchgroup = 4kpagespermempage * clperchperblock,
* clperchperblock = 8 / num_channels * interleave
* clperchperblock = max(8 / num_channels, 1) * interleave
*
* The 8 / num_channels truncating divide collapses to 0 for
* >8-channel configs (16-channel: 8 / 16 = 0); the max(..., 1) floor
* keeps clperchperblock >= 1 there while preserving the literal
* truncating divide for <=8-channel configs.
*/
clperchgroup = 4 * (8 / num_channels) * qi.deinterleave;
clperchgroup = 4 * max(8 / num_channels, 1) * qi.deinterleave;
display->bw.num_qgv_points = qi.num_qgv_points;
display->bw.num_psf_gv_points = qi.num_psf_points;
@@ -681,6 +737,9 @@ static int tgl_get_bw_info(struct intel_display *display,
drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]);
}
/* For xe3 cases add an extra qgv point for Peak bw threshold */
xe3_add_peakbw_threshold(display);
for (i = 0; i < qi.num_psf_points; i++) {
const struct intel_psf_gv_point *sp = &qi.psf_points[i];
@@ -689,16 +748,6 @@ static int tgl_get_bw_info(struct intel_display *display,
drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]);
}
/*
* In case if SAGV is disabled in BIOS, we always get 1
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
if (qi.num_qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
return 0;
}
@@ -718,8 +767,6 @@ static void dg2_get_bw_info(struct intel_display *display)
/* Bandwidth does not depend on # of planes; set all groups the same */
for (i = 1; i < ARRAY_SIZE(display->bw.max); i++)
display->bw.max[i] = display->bw.max[0];
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
}
static int xe2_hpd_get_bw_info(struct intel_display *display,
@@ -767,7 +814,6 @@ static int xe2_hpd_get_bw_info(struct intel_display *display,
* battery and plugged-in operation.
*/
drm_WARN_ON(display->drm, qi.num_qgv_points != 2);
display->sagv.status = I915_SAGV_ENABLED;
return 0;
}
@@ -868,6 +914,8 @@ void intel_bw_init_hw(struct intel_display *display)
} else if (DISPLAY_VER(display) == 11) {
icl_get_bw_info(display, dram_info, soc_bw_params, display_bw_params);
}
update_sagv_status(display, display->bw.num_qgv_points);
}
static unsigned int intel_bw_num_active_planes(struct intel_display *display,
@@ -1238,7 +1286,7 @@ static int intel_bw_check_qgv_points(struct intel_display *display,
data_rate = DIV_ROUND_UP(data_rate, 1000);
if (DISPLAY_VER(display) >= 14)
if (HAS_PMDEMAND(display))
return mtl_find_qgv_points(display, data_rate, num_active_planes,
new_bw_state);
else

View File

@@ -2908,9 +2908,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state)
}
/* pixels per CDCLK */
static int intel_cdclk_ppc(struct intel_display *display, bool double_wide)
int intel_cdclk_ppc(struct intel_display *display, bool double_wide)
{
return DISPLAY_VER(display) >= 10 || double_wide ? 2 : 1;
return HAS_2PPC(display) || double_wide ? 2 : 1;
}
/* max pixel rate as % of CDCLK (not accounting for PPC) */
@@ -2936,7 +2936,7 @@ static int _intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state,
static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state)
{
return _intel_pixel_rate_to_cdclk(crtc_state, crtc_state->pixel_rate);
return _intel_pixel_rate_to_cdclk(crtc_state, crtc_state->pixel_rate_cdclk);
}
static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)

View File

@@ -22,6 +22,7 @@ struct intel_cdclk_config {
bool joined_mbus;
};
int intel_cdclk_ppc(struct intel_display *display, bool double_wide);
void intel_cdclk_init_hw(struct intel_display *display);
void intel_cdclk_uninit_hw(struct intel_display *display);
void intel_init_cdclk_hooks(struct intel_display *display);

View File

@@ -1078,6 +1078,9 @@ intel_cursor_plane_create(struct intel_display *display,
intel_cursor_add_size_hints_property(cursor);
drm_plane_create_blend_mode_property(&cursor->base,
BIT(DRM_MODE_BLEND_PREMULTI));
zpos = DISPLAY_RUNTIME_INFO(display)->num_sprites[pipe] + 1;
drm_plane_create_zpos_immutable_property(&cursor->base, zpos);

View File

@@ -469,8 +469,10 @@ void intel_ddi_set_dp_msa(const struct intel_crtc_state *crtc_state,
* of Color Encoding Format and Content Color Gamut] while sending
* YCBCR 420, HDR BT.2020 signals we should program MSA MISC1 fields
* which indicate VSC SDP for the Pixel Encoding/Colorimetry Format.
* Only set the delegation bit when the content needs it and
* the sink advertises support.
*/
if (intel_dp_needs_vsc_sdp(crtc_state, conn_state))
if (intel_dp_needs_vsc_colorimetry(crtc_state, conn_state))
temp |= DP_MSA_MISC_COLOR_VSC_SDP;
intel_de_write(display, TRANS_MSA_MISC(display, cpu_transcoder),
@@ -4563,7 +4565,7 @@ static bool crtcs_port_sync_compatible(const struct intel_crtc_state *crtc_state
m_n_equal(&crtc_state1->dp_m_n, &crtc_state2->dp_m_n);
}
static u8
static u16
intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state,
int tile_group_id)
{
@@ -4572,7 +4574,7 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state,
const struct drm_connector_state *conn_state;
struct intel_atomic_state *state =
to_intel_atomic_state(ref_crtc_state->uapi.state);
u8 transcoders = 0;
u16 transcoders = 0;
int i;
/*
@@ -4616,7 +4618,7 @@ static int intel_ddi_compute_config_late(struct intel_atomic_state *state,
{
struct intel_display *display = to_intel_display(encoder);
struct drm_connector *connector = conn_state->connector;
u8 port_sync_transcoders = 0;
u16 port_sync_transcoders = 0;
int ret = 0;
if (intel_crtc_has_dp_encoder(crtc_state))

View File

@@ -45,6 +45,7 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_rect.h>
#include <drm/drm_vblank.h>
#include <drm/intel/step.h>
#include "g4x_dp.h"
#include "g4x_hdmi.h"
@@ -2233,6 +2234,29 @@ static u32 ilk_pipe_pixel_rate(const struct intel_crtc_state *crtc_state)
pixel_rate);
}
static u32 ilk_pipe_pixel_rate_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
u32 pixel_rate = crtc_state->hw.pipe_mode.crtc_clock;
unsigned int ppc = HAS_2PPC(display) ? 2 : 1;
struct drm_rect src;
/*
* We only use IF-ID interlacing. If we ever use
* PF-ID we'll need to adjust the pixel_rate here.
*/
if (!crtc_state->pch_pfit.enabled)
return pixel_rate;
drm_rect_init(&src, 0, 0,
drm_rect_width(&crtc_state->pipe_src) << 16,
drm_rect_height(&crtc_state->pipe_src) << 16);
return intel_adjusted_rate_cdclk(&src, &crtc_state->pch_pfit.dst,
pixel_rate, ppc);
}
static void intel_mode_from_crtc_timings(struct drm_display_mode *mode,
const struct drm_display_mode *timings)
{
@@ -2258,13 +2282,17 @@ static void intel_crtc_compute_pixel_rate(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
if (HAS_GMCH(display))
if (HAS_GMCH(display)) {
/* FIXME calculate proper pipe pixel rate for GMCH pfit */
crtc_state->pixel_rate =
crtc_state->hw.pipe_mode.crtc_clock;
else
crtc_state->pixel_rate_cdclk = crtc_state->pixel_rate;
} else {
crtc_state->pixel_rate =
ilk_pipe_pixel_rate(crtc_state);
crtc_state->pixel_rate_cdclk =
ilk_pipe_pixel_rate_cdclk(crtc_state);
}
}
static void intel_joiner_adjust_timings(const struct intel_crtc_state *crtc_state,
@@ -2458,6 +2486,19 @@ static int intel_crtc_set_context_latency(struct intel_crtc_state *crtc_state)
set_context_latency = max(set_context_latency,
intel_psr_min_set_context_latency(crtc_state));
/*
* From PTL onwards, the set context latency can be in the vactive
* region, letting the safe window start some lines before the vblank
* start. With modes that have a smaller vblank region, the computed
* guardband is clamped to the vblank length, making the undelayed and
* delayed vblank coincide. If the SCL is also 0, the 'safe window'
* becomes effectively 0, and the DSB configured to wait for it gets
* stalled, since the hardware never signals the safe window. Keep the
* set context latency at a minimum of 1 to avoid this.
*/
if (DISPLAY_VER(display) >= 30)
set_context_latency = max(1, set_context_latency);
return set_context_latency;
}
@@ -2737,6 +2778,10 @@ void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state,
HSYNC_START(adjusted_mode->crtc_hsync_start - 1) |
HSYNC_END(adjusted_mode->crtc_hsync_end - 1));
if (display->platform.novalake &&
IS_DISPLAY_STEP(display, STEP_A0, STEP_C0))
crtc_vtotal = 1;
intel_de_write(display, TRANS_VTOTAL(display, transcoder),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
@@ -2830,6 +2875,10 @@ void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state,
* The double buffer latch point for TRANS_VTOTAL
* is the transcoder's undelayed vblank.
*/
if (display->platform.novalake &&
IS_DISPLAY_STEP(display, STEP_A0, STEP_C0))
crtc_vtotal = 1;
intel_de_write(display, TRANS_VTOTAL(display, transcoder),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
@@ -3758,9 +3807,9 @@ static void enabled_joiner_pipes(struct intel_display *display,
}
}
static u8 hsw_panel_transcoders(struct intel_display *display)
static u16 hsw_panel_transcoders(struct intel_display *display)
{
u8 panel_transcoder_mask = BIT(TRANSCODER_EDP);
u16 panel_transcoder_mask = BIT(TRANSCODER_EDP);
if (DISPLAY_VER(display) >= 11)
panel_transcoder_mask |= BIT(TRANSCODER_DSI_0) | BIT(TRANSCODER_DSI_1);
@@ -3768,13 +3817,13 @@ static u8 hsw_panel_transcoders(struct intel_display *display)
return panel_transcoder_mask;
}
static u8 hsw_enabled_transcoders(struct intel_crtc *crtc)
static u16 hsw_enabled_transcoders(struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(crtc);
u8 panel_transcoder_mask = hsw_panel_transcoders(display);
u16 panel_transcoder_mask = hsw_panel_transcoders(display);
enum transcoder cpu_transcoder;
u8 primary_pipe, secondary_pipes;
u8 enabled_transcoders = 0;
u16 enabled_transcoders = 0;
/*
* XXX: Do intel_display_power_get_if_enabled before reading this (for
@@ -3835,18 +3884,18 @@ static u8 hsw_enabled_transcoders(struct intel_crtc *crtc)
return enabled_transcoders;
}
static bool has_edp_transcoders(u8 enabled_transcoders)
static bool has_edp_transcoders(u16 enabled_transcoders)
{
return enabled_transcoders & BIT(TRANSCODER_EDP);
}
static bool has_dsi_transcoders(u8 enabled_transcoders)
static bool has_dsi_transcoders(u16 enabled_transcoders)
{
return enabled_transcoders & (BIT(TRANSCODER_DSI_0) |
BIT(TRANSCODER_DSI_1));
}
static bool has_pipe_transcoders(u8 enabled_transcoders)
static bool has_pipe_transcoders(u16 enabled_transcoders)
{
return enabled_transcoders & ~(BIT(TRANSCODER_EDP) |
BIT(TRANSCODER_DSI_0) |
@@ -3854,7 +3903,7 @@ static bool has_pipe_transcoders(u8 enabled_transcoders)
}
static void assert_enabled_transcoders(struct intel_display *display,
u8 enabled_transcoders)
u16 enabled_transcoders)
{
/* Only one type of transcoder please */
drm_WARN_ON(display->drm,
@@ -5373,6 +5422,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(pch_pfit.casf.strength);
PIPE_CONF_CHECK_I(scaler_state.scaler_id);
PIPE_CONF_CHECK_I(pixel_rate_cdclk);
PIPE_CONF_CHECK_I(pixel_rate);
PIPE_CONF_CHECK_X(gamma_mode);
@@ -5871,7 +5921,7 @@ static int intel_atomic_check_crtcs(struct intel_atomic_state *state)
}
static bool intel_cpu_transcoders_need_modeset(struct intel_atomic_state *state,
u8 transcoders)
u16 transcoders)
{
const struct intel_crtc_state *new_crtc_state;
struct intel_crtc *crtc;
@@ -6507,7 +6557,7 @@ int intel_atomic_check(struct drm_device *dev,
}
if (is_trans_port_sync_mode(new_crtc_state)) {
u8 trans = new_crtc_state->sync_mode_slaves_mask;
u16 trans = new_crtc_state->sync_mode_slaves_mask;
if (new_crtc_state->master_transcoder != INVALID_TRANSCODER)
trans |= BIT(new_crtc_state->master_transcoder);

View File

@@ -97,6 +97,8 @@ struct intel_wm_funcs {
struct intel_audio_state {
struct intel_encoder *encoder;
u8 eld[MAX_ELD_BYTES];
/* MST, or SST on UHBR link */
bool needs_cpu_transcoder_id;
};
struct intel_audio {

View File

@@ -146,6 +146,7 @@ struct intel_display_platforms {
func(supports_tv);
#define HAS_128B_Y_TILING(__display) (!(__display)->platform.i915g && !(__display)->platform.i915gm)
#define HAS_2PPC(__display) (DISPLAY_VER(__display) >= 10)
#define HAS_4TILE(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14)
#define HAS_ASYNC_FLIPS(__display) (DISPLAY_VER(__display) >= 5)
#define HAS_AS_SDP(__display) (DISPLAY_VER(__display) >= 13)
@@ -192,8 +193,10 @@ struct intel_display_platforms {
#define HAS_MBUS_JOINING(__display) ((__display)->platform.alderlake_p || DISPLAY_VER(__display) >= 14)
#define HAS_MSO(__display) (DISPLAY_VER(__display) >= 12)
#define HAS_OVERLAY(__display) (DISPLAY_INFO(__display)->has_overlay)
#define HAS_PEAK_BW_THRESHOLD(__display) (DISPLAY_VER(__display) >= 30)
#define HAS_PIPEDMC(__display) (DISPLAY_VER(__display) >= 12)
#define HAS_PIXEL_NORMALIZER(__display) (DISPLAY_VER(__display) >= 35)
#define HAS_PMDEMAND(__display) (DISPLAY_VER(__display) >= 14)
#define HAS_PSR(__display) (DISPLAY_INFO(__display)->has_psr)
#define HAS_PSR_HW_TRACKING(__display) (DISPLAY_INFO(__display)->has_psr_hw_tracking)
#define HAS_PSR2_SEL_FETCH(__display) (DISPLAY_VER(__display) >= 12)

View File

@@ -887,3 +887,74 @@ void intel_display_driver_pm_resume(struct intel_display *display)
intel_display_power_enable(display);
}
void intel_display_driver_runtime_pm_enable(struct intel_display *display)
{
intel_display_power_enable(display);
}
void intel_display_driver_runtime_pm_disable(struct intel_display *display)
{
intel_display_power_disable(display);
}
/* before irq suspend */
void intel_display_driver_pm_runtime_suspend(struct intel_display *display)
{
}
/* after irq suspend */
void intel_display_driver_pm_runtime_suspend_late(struct intel_display *display)
{
intel_display_power_runtime_suspend(display);
/*
* FIXME: We really should find a document that references the arguments
* used below!
*/
if (display->platform.broadwell) {
/*
* On Broadwell, if we use PCI_D1 the PCH DDI ports will stop
* being detected, and the call we do at i915_pm_runtime_resume()
* won't be able to restore them. Since PCI_D3hot matches the
* actual specification and appears to be working, use it.
*/
intel_opregion_notify_adapter(display, PCI_D3hot);
} else {
/*
* current versions of firmware which depend on this opregion
* notification have repurposed the D1 definition to mean
* "runtime suspended" vs. what you would normally expect (D3)
* to distinguish it from notifications that might be sent via
* the suspend path.
*/
intel_opregion_notify_adapter(display, PCI_D1);
}
if (!display->platform.valleyview && !display->platform.cherryview)
intel_hpd_poll_enable(display);
}
/* before irq resume */
void intel_display_driver_pm_runtime_resume_early(struct intel_display *display)
{
intel_opregion_notify_adapter(display, PCI_D0);
intel_display_power_runtime_resume(display);
}
/* after irq resume */
void intel_display_driver_pm_runtime_resume(struct intel_display *display)
{
/*
* On VLV/CHV display interrupts are part of the display
* power well, so hpd is reinitialized from there. For
* everyone else do it here.
*/
if (!display->platform.valleyview && !display->platform.cherryview) {
intel_hpd_init(display);
intel_hpd_poll_disable(display);
}
skl_watermark_ipc_update(display);
}

View File

@@ -43,5 +43,13 @@ void intel_display_driver_suspend_access(struct intel_display *display);
void intel_display_driver_resume_access(struct intel_display *display);
bool intel_display_driver_check_access(struct intel_display *display);
void intel_display_driver_runtime_pm_enable(struct intel_display *display);
void intel_display_driver_runtime_pm_disable(struct intel_display *display);
void intel_display_driver_pm_runtime_suspend(struct intel_display *display);
void intel_display_driver_pm_runtime_suspend_late(struct intel_display *display);
void intel_display_driver_pm_runtime_resume_early(struct intel_display *display);
void intel_display_driver_pm_runtime_resume(struct intel_display *display);
#endif /* __INTEL_DISPLAY_DRIVER_H__ */

View File

@@ -1262,7 +1262,7 @@ gen8_de_misc_irq_handler(struct intel_display *display, u32 iir)
}
}
if (DISPLAY_VER(display) >= 14) {
if (HAS_PMDEMAND(display)) {
if (iir & (XELPDP_PMDEMAND_RSP |
XELPDP_PMDEMAND_RSPTOUT_ERR)) {
if (iir & XELPDP_PMDEMAND_RSPTOUT_ERR)

View File

@@ -1290,7 +1290,7 @@ static void gen9_dbuf_enable(struct intel_display *display)
slices_mask = BIT(DBUF_S1) | display->dbuf.enabled_slices;
if (DISPLAY_VER(display) >= 14)
if (HAS_PMDEMAND(display))
intel_pmdemand_program_dbuf(display, slices_mask);
/*
@@ -1304,7 +1304,7 @@ static void gen9_dbuf_disable(struct intel_display *display)
{
gen9_dbuf_slices_update(display, 0);
if (DISPLAY_VER(display) >= 14)
if (HAS_PMDEMAND(display))
intel_pmdemand_program_dbuf(display, 0);
}

View File

@@ -1747,6 +1747,9 @@
#define XELPD_CHICKEN_DCPR_3 _MMIO(0x46438)
#define DMD_RSP_TIMEOUT_DISABLE REG_BIT(19)
#define XE3P_CHICKEN_DCPR_4 _MMIO(0x454a0)
#define DCPR4_BLOCK_DC3CO_ACTIVE_FRAME REG_BIT(24)
#define SKL_DFSM _MMIO(0x51000)
#define SKL_DFSM_DISPLAY_PM_DISABLE (1 << 27)
#define SKL_DFSM_DISPLAY_HDCP_DISABLE (1 << 25)

View File

@@ -1083,6 +1083,13 @@ struct intel_crtc_state {
*/
unsigned int pixel_rate;
/*
* Pipe pixel rate for CDCLK, adjusted for
* panel fitter/pipe scaler downscaling.
* CDCLK use cases need further adjustment.
*/
unsigned int pixel_rate_cdclk;
/* Whether to set up the PCH/FDI. Note that we never allow sharing
* between pch encoders and cpu encoders. */
bool has_pch_encoder;
@@ -1372,7 +1379,7 @@ struct intel_crtc_state {
enum transcoder master_transcoder;
/* Bitmask to indicate slaves attached */
u8 sync_mode_slaves_mask;
u16 sync_mode_slaves_mask;
/* Only valid on TGL+ */
enum transcoder mst_master_transcoder;
@@ -1871,6 +1878,7 @@ struct intel_dp {
struct drm_dp_tunnel *tunnel;
bool tunnel_suspended:1;
u8 disabled_uhbr_lane_mask;
struct {
struct intel_dp_mst_encoder *stream_encoders[I915_MAX_PIPES];

View File

@@ -112,6 +112,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa,
DISPLAY_VERx100(display) == 1401;
case INTEL_DISPLAY_WA_14025769978:
return DISPLAY_VER(display) == 35;
case INTEL_DISPLAY_WA_14026643300:
return DISPLAY_VER(display) == 35;
case INTEL_DISPLAY_WA_15013987218:
return DISPLAY_VER(display) == 20;
case INTEL_DISPLAY_WA_15018326506:
@@ -138,6 +140,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa,
STEP_A0, STEP_B0);
case INTEL_DISPLAY_WA_16029024088:
return DISPLAY_VER(display) >= 35;
case INTEL_DISPLAY_WA_16030862157:
return DISPLAY_VER(display) == 35;
case INTEL_DISPLAY_WA_18034343758:
return DISPLAY_VER(display) == 20 ||
(display->platform.pantherlake &&

View File

@@ -43,6 +43,7 @@ enum intel_display_wa {
INTEL_DISPLAY_WA_14016740474,
INTEL_DISPLAY_WA_14020863754,
INTEL_DISPLAY_WA_14025769978,
INTEL_DISPLAY_WA_14026643300,
INTEL_DISPLAY_WA_15013987218,
INTEL_DISPLAY_WA_15018326506,
INTEL_DISPLAY_WA_16011181250,
@@ -53,6 +54,7 @@ enum intel_display_wa {
INTEL_DISPLAY_WA_16025573575,
INTEL_DISPLAY_WA_16025596647,
INTEL_DISPLAY_WA_16029024088,
INTEL_DISPLAY_WA_16030862157,
INTEL_DISPLAY_WA_18034343758,
INTEL_DISPLAY_WA_22010178259,
INTEL_DISPLAY_WA_22010947358,

View File

@@ -505,14 +505,16 @@ static void pipedmc_clock_gating_wa(struct intel_display *display, bool enable)
static u32 pipedmc_interrupt_mask(struct intel_display *display)
{
/*
* TODO: Check if PIPEDMC_ERROR bit enabling causes errors
* on PTL, enable it if validation passes
*/
if (DISPLAY_VER(display) >= 35)
return PIPEDMC_FLIPQ_PROG_DONE |
PIPEDMC_ERROR;
if (DISPLAY_VER(display) >= 30)
return PIPEDMC_FLIPQ_PROG_DONE |
PIPEDMC_GTT_FAULT |
PIPEDMC_ATS_FAULT |
PIPEDMC_ERROR;
/*
* FIXME PIPEDMC_ERROR not enabled for now due to LNL pipe B
* triggering it during the first DC state transition. Figure

View File

@@ -191,6 +191,29 @@ static void intel_dp_set_default_sink_rates(struct intel_dp *intel_dp)
intel_dp->num_sink_rates = 1;
}
static bool dprx_supports_128b132b(struct intel_dp *intel_dp)
{
if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp))
return drm_dp_tunnel_128b132b_supported(intel_dp->tunnel);
else
return drm_dp_128b132b_supported(intel_dp->dpcd);
}
static u8 dprx_128b132b_link_rates(struct intel_dp *intel_dp, u8 no_bwa_rates)
{
u8 ret;
if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp))
return no_bwa_rates;
ret = drm_dp_tunnel_128b132b_dprx_rates(intel_dp->tunnel);
static_assert(DP_TUNNELING_10GBPS_PER_LANE_SUPPORT == DP_UHBR10 &&
DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT == DP_UHBR13_5 &&
DP_TUNNELING_20GBPS_PER_LANE_SUPPORT == DP_UHBR20);
return ret;
}
/* update sink rates from dpcd */
static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp)
{
@@ -199,6 +222,7 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp)
};
int i, max_rate;
int max_lttpr_rate;
u8 uhbr_rates = 0;
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 */
@@ -224,17 +248,20 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp)
intel_dp->sink_rates[i] = dp_rates[i];
}
/*
* The following register must be read unconditionally for the later
* DP tunnel 128b132b detection to work, see DP Standard v2.1 5.14.3 .
*/
drm_dp_dpcd_read_byte(&intel_dp->aux, DP_128B132B_SUPPORTED_LINK_RATES, &uhbr_rates);
/*
* 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;
if (dprx_supports_128b132b(intel_dp)) {
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);
uhbr_rates = dprx_128b132b_link_rates(intel_dp, uhbr_rates);
if (drm_dp_lttpr_count(intel_dp->lttpr_common_caps)) {
/* We have a repeater */
@@ -354,44 +381,6 @@ static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp)
return min3(source_max, sink_max, lane_max);
}
int intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config max_link_limits;
struct intel_dp_link_config forced_params;
int lane_count;
intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits);
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
if (forced_params.lane_count)
lane_count = forced_params.lane_count;
else
lane_count = max_link_limits.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)
{
struct intel_dp_link_config forced_params;
intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params);
if (forced_params.lane_count)
return forced_params.lane_count;
return 1;
}
int intel_dp_link_bw_overhead(int link_clock, int lane_count, int hdisplay,
int dsc_slice_count, int bpp_x16, unsigned long flags)
{
@@ -701,7 +690,8 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp)
intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates);
if (intel_dp_link_caps_update(intel_dp->link.caps,
common_rates, num_common_rates,
intel_dp_get_max_common_lane_count(intel_dp)))
intel_dp_get_max_common_lane_count(intel_dp),
intel_dp->reset_link_params))
params_changed = true;
return params_changed;
@@ -1330,6 +1320,7 @@ intel_dp_mode_valid_format(struct intel_connector *connector,
struct intel_dp *intel_dp = intel_attached_dp(connector);
enum intel_output_format output_format;
int max_rate, mode_rate, max_lanes, max_link_clock;
struct intel_dp_link_config max_bw_config;
u16 dsc_max_compressed_bpp = 0;
enum drm_mode_status status;
bool dsc = false;
@@ -1342,8 +1333,9 @@ intel_dp_mode_valid_format(struct intel_connector *connector,
output_format = intel_dp_output_format(connector, sink_format);
max_link_clock = intel_dp_max_link_rate(intel_dp);
max_lanes = intel_dp_max_lane_count(intel_dp);
intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config);
max_link_clock = max_bw_config.rate;
max_lanes = max_bw_config.lane_count;
max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes);
@@ -1537,39 +1529,6 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp)
intel_dp_link_caps_print_common_rates(intel_dp->link.caps);
}
int
intel_dp_max_link_rate(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config max_link_limits;
struct intel_dp_link_config forced_params;
int len;
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
if (forced_params.rate)
return forced_params.rate;
intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits);
len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate);
return intel_dp_common_rate(link_caps, len - 1);
}
static int
intel_dp_min_link_rate(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config forced_params;
intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params);
if (forced_params.rate)
return forced_params.rate;
return intel_dp_common_rate(link_caps, 0);
}
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -1751,48 +1710,54 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state,
const struct link_config_limits *limits)
{
struct intel_connector *connector = to_intel_connector(conn_state->connector);
int bpp, clock = intel_dp_mode_clock(pipe_config, conn_state);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state);
int link_rate, link_avail;
struct intel_dp_link_caps_order order =
intel_dp_link_caps_connector_compute_order(connector);
int err = -EINVAL;
int 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_x16 =
intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp);
struct intel_dp_link_config link_config;
struct intel_dp_link_caps_iter iter;
for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) {
link_rate = intel_dp_common_rate(link_caps, 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) {
const struct drm_display_mode *adjusted_mode =
intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter);
for_each_dp_link_config(&iter, &link_config) {
const struct drm_display_mode *adjusted_mode =
&pipe_config->hw.adjusted_mode;
int mode_rate =
intel_dp_link_required(link_rate, lane_count,
clock, adjusted_mode->hdisplay,
link_bpp_x16, 0);
int mode_rate;
link_avail = intel_dp_max_link_data_rate(intel_dp,
link_rate,
lane_count);
mode_rate = intel_dp_link_required(link_config.rate,
link_config.lane_count,
clock, adjusted_mode->hdisplay,
link_bpp_x16, 0);
if (mode_rate <= link_avail) {
pipe_config->lane_count = lane_count;
pipe_config->pipe_bpp = bpp;
pipe_config->port_clock = link_rate;
link_avail = intel_dp_max_link_data_rate(intel_dp,
link_config.rate,
link_config.lane_count);
return 0;
}
if (mode_rate <= link_avail) {
pipe_config->lane_count = link_config.lane_count;
pipe_config->pipe_bpp = bpp;
pipe_config->port_clock = link_config.rate;
err = 0;
break;
}
}
intel_dp_link_caps_iter_end(&iter);
if (!err)
break;
}
return -EINVAL;
return err;
}
int intel_dp_dsc_max_src_input_bpc(struct intel_display *display)
@@ -1986,63 +1951,63 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp,
const struct link_config_limits *limits,
int dsc_bpp_x16)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
int link_rate, lane_count;
int i;
struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_caps_order order =
intel_dp_link_caps_connector_compute_order(connector);
struct intel_dp_link_config link_config;
struct intel_dp_link_caps_iter iter;
int err = -EINVAL;
for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) {
link_rate = intel_dp_common_rate(link_caps, i);
if (link_rate < limits->min_rate || link_rate > limits->max_rate)
continue;
intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter);
for_each_dp_link_config(&iter, &link_config) {
/*
* 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 = link_config.lane_count;
pipe_config->port_clock = link_config.rate;
for (lane_count = limits->min_lane_count;
lane_count <= limits->max_lane_count;
lane_count <<= 1) {
if (drm_dp_is_uhbr_rate(link_config.rate)) {
int ret;
/*
* 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;
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 {
unsigned long bw_overhead_flags =
pipe_config->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0;
int line_slice_count =
intel_dsc_line_slice_count(&pipe_config->dsc.slice_config);
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 {
unsigned long bw_overhead_flags =
pipe_config->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0;
int line_slice_count =
intel_dsc_line_slice_count(&pipe_config->dsc.slice_config);
if (!is_bw_sufficient_for_dsc_config(intel_dp,
link_rate, lane_count,
adjusted_mode->crtc_clock,
adjusted_mode->hdisplay,
line_slice_count,
dsc_bpp_x16,
bw_overhead_flags))
continue;
}
return 0;
if (!is_bw_sufficient_for_dsc_config(intel_dp,
link_config.rate,
link_config.lane_count,
adjusted_mode->crtc_clock,
adjusted_mode->hdisplay,
line_slice_count,
dsc_bpp_x16,
bw_overhead_flags))
continue;
}
}
return -EINVAL;
err = 0;
break;
}
intel_dp_link_caps_iter_end(&iter);
return err;
}
static u16 intel_dp_dsc_max_delta_bppx16(const struct intel_connector *connector,
@@ -2250,7 +2215,7 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp,
int pipe_bpp)
{
struct intel_display *display = to_intel_display(intel_dp);
const struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct intel_connector *connector = to_intel_connector(conn_state->connector);
int min_bpp_x16, max_bpp_x16, bpp_step_x16;
int bpp_x16;
int ret;
@@ -2262,8 +2227,19 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp,
max_bpp_x16 = align_max_compressed_bpp_x16(connector, pipe_config->output_format,
pipe_bpp, max_bpp_x16);
if (intel_dp_is_edp(intel_dp)) {
pipe_config->port_clock = limits->max_rate;
pipe_config->lane_count = limits->max_lane_count;
struct intel_dp_link_config max_link_config;
/*
* FIXME: Clarify why eDP does not use the regular SST BW
* check and instead always uses the maximum link config,
* regardless of intel_dp::use_max_params. Then unify this eDP
* path with the regular DP path.
*/
if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config))
return -EINVAL;
pipe_config->port_clock = max_link_config.rate;
pipe_config->lane_count = max_link_config.lane_count;
pipe_config->dsc.compressed_bpp_x16 = max_bpp_x16;
@@ -2576,6 +2552,20 @@ bool intel_dp_mode_valid_with_dsc(struct intel_connector *connector,
bw_overhead_flags);
}
bool
intel_dp_get_connector_max_link_config(struct intel_connector *connector,
const struct link_config_limits *limits,
struct intel_dp_link_config *max_link_config)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_caps_order order =
intel_dp_link_caps_connector_compute_order(connector);
return intel_dp_link_caps_get_max_config(link_caps, order.key, limits->link_config_filter,
max_link_config);
}
/*
* Calculate the output link min, max bpp values in limits based on the pipe bpp
* range, crtc_state and dsc mode. Return true on success.
@@ -2592,6 +2582,7 @@ intel_dp_compute_config_link_bpp_limits(struct intel_connector *connector,
&crtc_state->hw.adjusted_mode;
const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
const struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
struct intel_dp_link_config max_link_config;
int max_link_bpp_x16;
max_link_bpp_x16 = min(crtc_state->max_link_bpp_x16,
@@ -2621,14 +2612,17 @@ intel_dp_compute_config_link_bpp_limits(struct intel_connector *connector,
limits->link.max_bpp_x16 = max_link_bpp_x16;
if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config))
return false;
drm_dbg_kms(display->drm,
"[ENCODER:%d:%s][CRTC:%d:%s] DP link limits: pixel clock %d kHz DSC %s max lanes %d max rate %d max pipe_bpp %d min link_bpp " FXP_Q4_FMT " max link_bpp " FXP_Q4_FMT "\n",
"[ENCODER:%d:%s][CRTC:%d:%s] DP link limits: pixel clock %d kHz DSC %s max link %dx%d max pipe_bpp %d min link_bpp " FXP_Q4_FMT " max link_bpp " FXP_Q4_FMT "\n",
encoder->base.base.id, encoder->base.name,
crtc->base.base.id, crtc->base.name,
adjusted_mode->crtc_clock,
str_on_off(dsc),
limits->max_lane_count,
limits->max_rate,
max_link_config.lane_count,
max_link_config.rate,
limits->pipe.max_bpp,
FXP_Q4_ARGS(limits->link.min_bpp_x16),
FXP_Q4_ARGS(limits->link.max_bpp_x16));
@@ -2679,17 +2673,12 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp,
struct link_config_limits *limits)
{
struct intel_display *display = to_intel_display(intel_dp);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST);
struct intel_connector *connector =
to_intel_connector(conn_state->connector);
limits->min_rate = intel_dp_min_link_rate(intel_dp);
limits->max_rate = intel_dp_max_link_rate(intel_dp);
limits->min_rate = min(limits->min_rate, limits->max_rate);
limits->min_lane_count = intel_dp_min_lane_count(intel_dp);
limits->max_lane_count = intel_dp_max_lane_count(intel_dp);
limits->link_config_filter = INTEL_DP_LINK_CAPS_FILTER_ALL;
limits->pipe.min_bpp = intel_dp_min_bpp(crtc_state->output_format);
if (is_mst) {
@@ -2720,6 +2709,20 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp,
crtc_state)));
}
/*
* HDMI knows no 6 bpc transport format, and DSC with an at least
* 8 bpc input provides a better output quality than a dithered
* 6 bpc output. Prefer it for HDMI sinks, by failing the
* uncompressed link config for modes which would fit only with a
* 6 bpc pipe BPP. Honor a lower limit set via the max bpc
* connector property.
*/
if (!dsc && intel_dp_has_hdmi_sink(intel_dp) &&
intel_dp_supports_dsc(intel_dp, connector, crtc_state) &&
limits->pipe.max_bpp >= 24 &&
crtc_state->pipe_bpp >= 24)
limits->pipe.min_bpp = max(limits->pipe.min_bpp, 24);
if (limits->pipe.min_bpp <= 0 ||
limits->pipe.min_bpp > limits->pipe.max_bpp) {
drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] Invalid pipe bpp range: %d-%d\n",
@@ -2754,6 +2757,9 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp,
crtc_state->pipe_bpp, limits->pipe.max_bpp);
if (is_mst || intel_dp->use_max_params) {
struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE;
struct intel_dp_link_config max_config;
/*
* For MST we always configure max link bw - the spec doesn't
* seem to suggest we should do otherwise.
@@ -2765,11 +2771,17 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp,
* configuration, and typically on older panels these
* values correspond to the native resolution of the panel.
*/
limits->min_lane_count = limits->max_lane_count;
limits->min_rate = limits->max_rate;
if (!intel_dp_get_connector_max_link_config(connector, limits, &max_config))
return false;
if (!intel_dp_link_caps_filter_add(link_caps, &new_filter, &max_config))
return false;
limits->link_config_filter = new_filter;
}
intel_dp_test_compute_config(intel_dp, crtc_state, limits);
if (!intel_dp_test_compute_config(connector, crtc_state, limits))
return false;
return intel_dp_compute_config_link_bpp_limits(connector,
crtc_state,
@@ -3163,8 +3175,7 @@ static void intel_dp_compute_vsc_sdp(struct intel_dp *intel_dp,
{
struct drm_dp_vsc_sdp *vsc;
if ((!intel_dp->colorimetry_support ||
!intel_dp_needs_vsc_sdp(crtc_state, conn_state)) &&
if (!intel_dp_needs_vsc_colorimetry(crtc_state, conn_state) &&
!crtc_state->has_psr)
return;
@@ -3173,8 +3184,7 @@ static void intel_dp_compute_vsc_sdp(struct intel_dp *intel_dp,
crtc_state->infoframes.enable |= intel_hdmi_infoframe_enable(DP_SDP_VSC);
vsc->sdp_type = DP_SDP_VSC;
/* Needs colorimetry */
if (intel_dp_needs_vsc_sdp(crtc_state, conn_state)) {
if (intel_dp_needs_vsc_colorimetry(crtc_state, conn_state)) {
intel_dp_compute_vsc_colorimetry(crtc_state, conn_state,
vsc);
} else if (crtc_state->has_panel_replay) {
@@ -3652,7 +3662,14 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp,
void intel_dp_reset_link_params(struct intel_dp *intel_dp)
{
/*
* TODO: Remove the following reset of link capabilities, as
* this isn't needed after intel_dp_link_caps_update(reset=true)
* was called.
*/
intel_dp_link_caps_reset(intel_dp->link.caps);
intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp);
intel_dp->link.mst_probed_lane_count = 0;
intel_dp->link.mst_probed_rate = 0;
intel_dp_link_training_reset(intel_dp->link.training);
@@ -4121,7 +4138,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp)
rate_per_lane = info->hdmi.max_frl_rate_per_lane;
max_frl_rate = max_lanes * rate_per_lane;
if (info->hdmi.dsc_cap.v_1p2) {
/*
* The sink's DSC max FRL rate only applies to compressed video
* transport, which requires a DSC 1.2 encoder in the PCON. Without
* one the HDMI link always carries uncompressed video, for which
* the regular max FRL rate is the limit.
*/
if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) &&
info->hdmi.dsc_cap.v_1p2) {
max_dsc_lanes = info->hdmi.dsc_cap.max_lanes;
dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane;
if (max_dsc_lanes && dsc_rate_per_lane)
@@ -5100,14 +5124,22 @@ static bool intel_dp_get_and_ack_sink_irq_esi_sst(struct intel_dp *intel_dp, u8
}
bool
intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state)
intel_dp_needs_vsc_colorimetry(const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state)
{
struct intel_dp *intel_dp =
enc_to_intel_dp(to_intel_encoder(conn_state->best_encoder));
/*
* As per DP 1.4a spec section 2.2.4.3 [MSA Field for Indication
* of Color Encoding Format and Content Color Gamut], in order to
* sending YCBCR 420 or HDR BT.2020 signals we should use DP VSC SDP.
* send YCBCR 420 or HDR BT.2020 signals we should use DP VSC SDP.
* Only signal this when the sink advertises VSC SDP colorimetry
* support.
*/
if (!intel_dp->colorimetry_support)
return false;
if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
return true;
@@ -5590,8 +5622,9 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp)
struct intel_display *display = to_intel_display(intel_dp);
bool force_retrain = intel_dp_link_training_get_force_retrain(intel_dp->link.training);
bool reprobe_needed = false;
int tries = 33;
for (;;) {
while (--tries) {
u8 esi[4] = {};
u8 ack[4] = {};
bool new_irqs;
@@ -5634,6 +5667,11 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp)
break;
}
if (!tries) {
drm_dbg_kms(display->drm, "DPRX ESI not clearing, device may be stuck\n");
reprobe_needed = true;
}
return !reprobe_needed;
}
@@ -6299,6 +6337,8 @@ intel_dp_detect(struct drm_connector *_connector,
intel_dp_tunnel_disconnect(intel_dp);
intel_dp_tunnel_uhbr_lanes_wa_reset(intel_dp);
goto out_unset_edid;
}
@@ -6363,6 +6403,9 @@ intel_dp_detect(struct drm_connector *_connector,
if (intel_dp_is_edp(intel_dp) || connector->detect_edid)
status = connector_status_connected;
if (intel_dp_tunnel_uhbr_lanes_wa_setup(intel_dp))
intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp);
out_unset_edid:
if (status != connector_status_connected && !intel_dp->is_mst)
intel_dp_unset_edid(intel_dp);
@@ -6577,7 +6620,7 @@ static int intel_modeset_tile_group(struct intel_atomic_state *state,
return ret;
}
static int intel_modeset_affected_transcoders(struct intel_atomic_state *state, u8 transcoders)
static int intel_modeset_affected_transcoders(struct intel_atomic_state *state, u16 transcoders)
{
struct intel_display *display = to_intel_display(state);
struct intel_crtc *crtc;
@@ -6625,7 +6668,7 @@ static int intel_modeset_synced_crtcs(struct intel_atomic_state *state,
drm_atomic_get_old_connector_state(&state->base, &connector->base);
const struct intel_crtc_state *old_crtc_state;
struct intel_crtc *crtc;
u8 transcoders;
u16 transcoders;
crtc = to_intel_crtc(old_conn_state->crtc);
if (!crtc)

View File

@@ -8,6 +8,8 @@
#include <linux/types.h>
#include "intel_dp_link_caps.h"
enum intel_output_format;
enum pipe;
enum port;
@@ -22,11 +24,11 @@ struct intel_crtc_state;
struct intel_digital_port;
struct intel_display;
struct intel_dp;
struct intel_dp_link_config;
struct intel_encoder;
struct link_config_limits {
int min_rate, max_rate;
int min_lane_count, max_lane_count;
struct intel_dp_link_caps_filter link_config_filter;
struct {
/* Uncompressed DSC input or link output bpp in 1 bpp units */
int min_bpp, max_bpp;
@@ -103,8 +105,6 @@ void intel_dp_mst_suspend(struct intel_display *display);
void intel_dp_mst_resume(struct intel_display *display);
int intel_dp_rate_limit_len(const int *rates, int len, int max_rate);
int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port);
int intel_dp_max_link_rate(struct intel_dp *intel_dp);
int intel_dp_max_lane_count(struct intel_dp *intel_dp);
int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state);
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate);
int intel_dp_rate_index(const int *rates, int len, int rate);
@@ -128,8 +128,8 @@ int intel_dp_max_link_data_rate(struct intel_dp *intel_dp,
bool intel_dp_joiner_needs_dsc(struct intel_display *display,
int num_joined_pipes);
bool intel_dp_has_joiner(struct intel_dp *intel_dp);
bool intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state);
bool intel_dp_needs_vsc_colorimetry(const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state);
void intel_dp_set_infoframes(struct intel_encoder *encoder, bool enable,
const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state);
@@ -144,6 +144,9 @@ int intel_dp_dsc_compute_max_bpp(const struct intel_connector *connector,
u8 dsc_max_bpc);
int intel_dp_compute_min_compressed_bpp_x16(struct intel_connector *connector,
enum intel_output_format output_format);
bool intel_dp_get_connector_max_link_config(struct intel_connector *connector,
const struct link_config_limits *limits,
struct intel_dp_link_config *max_link_config);
bool intel_dp_mode_valid_with_dsc(struct intel_connector *connector,
int link_clock, int lane_count,
int mode_clock, int mode_hdisplay,

View File

@@ -615,12 +615,7 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp)
int ret;
u8 bit_min, bit_max;
/*
* Since we only support Fully AUX Based VESA Backlight interface make sure
* backlight enable is possible via AUX along with backlight adjustment
*/
if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP &&
intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP))
if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP))
return false;
ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min);

View File

@@ -16,9 +16,91 @@
#include "intel_display_core.h"
#include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dp_link_caps.h"
/**
* DOC: DisplayPort link capabilities
*
* The Intel DP link caps API tracks the supported and allowed
* DisplayPort link configurations for a DP encoder and its attached
* connectors, and provides helpers to iterate over the allowed
* configurations and constrain them by filtering, disabling, or
* limiting them to maximum link parameters.
*
* Locking
* -------
*
* All accesses to this API must be serialized. The only exception
* is intel_dp_link_caps_get_max_limits(), which allow lockless
* lookup. Such lookups may observe an out-of-sync &struct
* intel_dp_link_config tuple, i.e. a rate from one state and a lane
* count from another.
*
* The Intel i915/xe drivers ensure the above serialization by holding
* &drm_mode_config.connection_mutex and, while holding the lock,
* waiting for any pending asynchronous atomic commits. This also allows
* use of the API from the tails of asynchronous atomic commits, which
* cannot hold the lock.
*
* Iterating and restricting link configurations
* ---------------------------------------------
*
* The link configuration iterators can iterate the ``allowed
* configurations`` during modeset configuration selection or link
* training fallback handling in a configurable order.
*
* The iteration order can depend on connector type (eDP, DP SST,
* DP MST) and modeset-specific conditions or driver policies, such
* as DSC vs. non-DSC modes, power saving vs. better user experience,
* or policy changes after a link training failure.
*
* The configurations exposed via the iterators can be additionally
* constrained in the following ways:
*
* - Filtered for a given modeset based on modeset-specific conditions.
* Examples for such conditions include driver policies preferring
* power saving or better user experience, post-link training failure
* preference changes, or sink automated test requests limiting the
* usable configurations.
*
* - Disabled permanently for the connected sink. Examples of reasons
* to disable a configuration include a link training failure for a
* given configuration or a driver workaround preventing the use of
* a particular configuration.
*
* - Limited via a maximum link rate and lane count. For example, after
* a link training failure, subsequent modesets may be limited to
* configurations at or below the failed parameters.
*
* This mechanism exists for backward compatibility only. Eventually,
* it will be removed in favor of relying solely on individually
* disabled configurations, as described above.
*
* Terminology
* -----------
*
* ``Common link capabilities`` (or ``common caps``) refer to the link
* rates and maximum lane count supported by both the source and the
* sink, i.e. the intersection of their respective capabilities.
*
* ``Supported configurations`` are all configurations defined by the
* ``Common link capabilities``' link rates and maximum lane count.
*
* ``Disabled configurations`` are ``Supported configurations`` disabled
* via this API.
*
* ``Enabled configurations`` are ``Supported configurations`` that are
* not disabled.
*
* ``Forced configurations`` are ``Enabled configurations`` forced via
* forced link parameter debugfs entries.
*
* ``Allowed configurations`` are the ``Enabled configurations``, or if
* forcing is in effect the ``Forced configurations``, constrained by a
* maximum rate and lane count set via the API.
*/
struct intel_dp_link_caps {
struct intel_dp *dp;
@@ -41,6 +123,44 @@ struct intel_dp_link_caps {
u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS;
} configs[INTEL_DP_MAX_LINK_CONFIGS];
/*
* Indices to intel_dp_link_caps::configs[] in rate/lane count,
* lane_count/rate order.
*/
u8 rate_lane_map[INTEL_DP_MAX_LINK_CONFIGS];
u8 lane_rate_map[INTEL_DP_MAX_LINK_CONFIGS];
/*
* Filter of configurations enabled for the current sink
* connection.
*
* Each bit in the filter's configuration mask corresponds to a
* configuration index in the intel_dp_link_caps::configs[] array.
*
* All configurations start out enabled in the filter after a
* new sink is connected. Users disable configurations afterwards
* via the link caps API. All configurations get re-enabled
* internally in the following cases:
* - when forcing a link rate or lane count
* - when intel_dp_link_caps_update(reset=true) is called after
* a new sink is connected
* - when intel_dp_link_caps_update(reset=false) with changed
* link capabilities is called
* - when intel_dp_link_caps_reset() is called after a new sink
* is connected
*/
struct intel_dp_link_caps_filter enabled_configs;
/*
* Allowed configurations are the supported configurations defined by
* config_table.rates and config_table.max_lane_count, constrained by
* config_table.enabled_configs and the forced_params and
* max_limits values below.
*
* See get_allowed_config_filter() for the filter of these
* configurations.
*/
/*
* Forced parameters requested via debugfs. Remains set across sink
* disconnects.
@@ -58,16 +178,78 @@ struct intel_dp_link_caps {
*/
struct intel_dp_link_config max_limits;
};
static_assert(BITS_PER_TYPE(((struct intel_dp_link_caps_filter *)NULL)->config_mask) >=
ARRAY_SIZE(((struct intel_dp_link_caps *)NULL)->configs));
static struct intel_dp_link_caps_order bw_desc_config_order(void)
{
struct intel_dp_link_caps_order order = {
.key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW,
.dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC,
};
return order;
}
static enum intel_dp_link_caps_order_key
connector_compute_order_key(bool is_mst)
{
if (is_mst)
return INTEL_DP_LINK_CAPS_ORDER_KEY_BW;
else
return INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE;
}
static enum intel_dp_link_caps_order_key
connector_fallback_order_key(bool is_mst)
{
if (is_mst)
return INTEL_DP_LINK_CAPS_ORDER_KEY_BW;
else
return INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE;
}
static enum intel_dp_link_caps_order_direction
connector_compute_order_dir(bool is_mst, bool use_max_params)
{
if (is_mst || use_max_params)
return INTEL_DP_LINK_CAPS_ORDER_DIR_DESC;
else
return INTEL_DP_LINK_CAPS_ORDER_DIR_ASC;
}
struct intel_dp_link_caps_order
intel_dp_link_caps_connector_compute_order(struct intel_connector *connector)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps_order order = {
.key = connector_compute_order_key(connector->mst.dp),
.dir = connector_compute_order_dir(connector->mst.dp, intel_dp->use_max_params)
};
return order;
}
struct intel_dp_link_caps_order
intel_dp_link_caps_connector_fallback_order(bool is_mst)
{
struct intel_dp_link_caps_order order = {
.key = connector_fallback_order_key(is_mst),
.dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC,
};
return order;
}
/* Get length of common rates array potentially limited by max_rate. */
int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps,
int max_rate)
static int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps,
int max_rate)
{
return intel_dp_rate_limit_len(link_caps->rates,
link_caps->num_rates, max_rate);
}
int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index)
static int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index)
{
struct intel_display *display = to_intel_display(link_caps->dp);
@@ -78,24 +260,12 @@ int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index)
return link_caps->rates[index];
}
int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate)
{
return intel_dp_rate_index(link_caps->rates,
link_caps->num_rates,
rate);
}
/* Theoretical max between source and sink */
int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps)
static int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps)
{
return intel_dp_common_rate(link_caps, link_caps->num_rates - 1);
}
int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps)
{
return link_caps->num_rates;
}
void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps)
{
struct intel_display *display = to_intel_display(link_caps->dp);
@@ -108,7 +278,7 @@ void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps)
drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s));
}
int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps)
static int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps)
{
return link_caps->max_lane_count;
}
@@ -154,20 +324,353 @@ static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_ent
return 1 << lce->lane_count_exp;
}
static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
static void
to_intel_dp_link_config(struct intel_dp_link_caps *link_caps,
int config_idx, struct intel_dp_link_config *config)
{
const struct intel_dp_link_config_entry *lce = &link_caps->configs[config_idx];
config->rate = intel_dp_link_config_rate(link_caps, lce);
config->lane_count = intel_dp_link_config_lane_count(lce);
}
static int
iter_pos_to_idx(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_order config_order,
int iter_pos)
{
int config_idx;
if (!in_range(iter_pos, 0, link_caps->num_configs))
return -1;
switch (config_order.dir) {
case INTEL_DP_LINK_CAPS_ORDER_DIR_ASC:
break;
case INTEL_DP_LINK_CAPS_ORDER_DIR_DESC:
iter_pos = link_caps->num_configs - 1 - iter_pos;
break;
default:
MISSING_CASE(config_order.dir);
return -1;
}
switch (config_order.key) {
case INTEL_DP_LINK_CAPS_ORDER_KEY_BW:
config_idx = iter_pos;
break;
case INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE:
config_idx = link_caps->rate_lane_map[iter_pos];
break;
case INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE:
config_idx = link_caps->lane_rate_map[iter_pos];
break;
default:
MISSING_CASE(config_order.key);
return -1;
}
return config_idx;
}
static bool iter_get_next_config(struct intel_dp_link_caps_iter *iter,
struct intel_dp_link_config *config)
{
while (true) {
int config_idx;
iter->pos++;
config_idx = iter_pos_to_idx(iter->link_caps, iter->order, iter->pos);
if (config_idx < 0) {
iter->pos = -1;
*config = INTEL_DP_LINK_CONFIG_NULL;
break;
}
if (!(BIT(config_idx) & iter->filter.config_mask))
continue;
to_intel_dp_link_config(iter->link_caps, config_idx, config);
break;
}
return iter->pos >= 0;
}
static void iter_start(struct intel_dp_link_caps_iter *iter,
struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_order order,
struct intel_dp_link_caps_filter filter)
{
iter->link_caps = link_caps;
iter->pos = -1;
iter->order = order;
iter->filter = filter;
iter->get_next_config = iter_get_next_config;
}
static struct intel_dp_link_caps_filter
calc_allowed_config_filter(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_filter enabled_configs,
const struct intel_dp_link_config *max_limits,
const struct intel_dp_link_config *forced_params)
{
struct intel_dp_link_caps_filter allowed_configs = INTEL_DP_LINK_CAPS_FILTER_NONE;
struct intel_dp_link_caps_order order = bw_desc_config_order();
struct intel_dp_link_caps_iter iter;
struct intel_dp_link_config config;
iter_start(&iter, link_caps, order, enabled_configs);
for_each_dp_link_config(&iter, &config) {
if (forced_params->rate &&
forced_params->rate != config.rate)
continue;
if (forced_params->lane_count &&
forced_params->lane_count != config.lane_count)
continue;
if (config.rate > max_limits->rate)
continue;
if (config.lane_count > max_limits->lane_count)
continue;
allowed_configs.config_mask |= BIT(iter_pos_to_idx(link_caps, order, iter.pos));
}
intel_dp_link_caps_iter_end(&iter);
return allowed_configs;
}
/*
* get_allowed_config_filter - get filter for the currently allowed configs
* @link_caps: link capabilities state
*
* Return:
* Filter of link configurations allowed after applying the current
* maximum link limits, and further narrowing them by removing any disabled
* configuration and limiting to forced link parameters.
*
* See also:
* - intel_dp_link_caps_get_max_limits()
* - intel_dp_link_caps_get_forced_params()
*/
static struct intel_dp_link_caps_filter
get_allowed_config_filter(struct intel_dp_link_caps *link_caps)
{
struct intel_dp_link_config forced_params;
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
return calc_allowed_config_filter(link_caps, link_caps->enabled_configs,
&link_caps->max_limits, &forced_params);
}
void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter,
struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_order order,
struct intel_dp_link_caps_filter filter)
{
filter.config_mask &= get_allowed_config_filter(link_caps).config_mask;
iter_start(iter, link_caps, order, filter);
}
void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter)
{
memset(iter, 0, sizeof(*iter));
}
/**
* intel_dp_link_caps_get_max_config - get the maximum config in a given order
* @link_caps: link capabilities state
* @order_key: ordering key used to rank candidate configurations
* @filter: filter for candidate configurations
* @max_config: returned maximum link configuration
*
* Find the last configuration among the currently allowed
* configurations filtered by @filter in the iteration order
* selected by @order_key, and store it in @max_config.
*
* See also:
* - &enum intel_dp_link_caps_order_key
*
* Returns:
* %true if a maximum config is returned
* %false otherwise.
*/
bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps,
enum intel_dp_link_caps_order_key order_key,
struct intel_dp_link_caps_filter filter,
struct intel_dp_link_config *max_config)
{
struct intel_dp_link_caps_order order = {
.key = order_key,
.dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC
};
struct intel_dp_link_config iter_config;
struct intel_dp_link_caps_iter iter;
bool found = false;
intel_dp_link_caps_iter_start(&iter, link_caps, order, filter);
for_each_dp_link_config(&iter, &iter_config) {
found = true;
break;
}
intel_dp_link_caps_iter_end(&iter);
if (!found)
return false;
*max_config = iter_config;
return true;
}
/**
* intel_dp_link_caps_get_max_bw_config - get maximum BW link configuration
* @link_caps: link capabilities state
* @max_config: returned maximum link configuration
*
* Return the maximum BW link configuration among the currently
* allowed configurations.
*/
void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *max_config)
{
if (!intel_dp_link_caps_get_max_config(link_caps,
bw_desc_config_order().key, INTEL_DP_LINK_CAPS_FILTER_ALL,
max_config))
*max_config = INTEL_DP_LINK_CONFIG_NULL;
}
static int find_config_idx(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_filter filter,
const struct intel_dp_link_config *link_config)
{
struct intel_dp_link_caps_order order = bw_desc_config_order();
struct intel_dp_link_config iter_config;
struct intel_dp_link_caps_iter iter;
int pos = -1;
intel_dp_link_caps_iter_start(&iter, link_caps, order, filter);
for_each_dp_link_config(&iter, &iter_config) {
if (iter_config.rate == link_config->rate &&
iter_config.lane_count == link_config->lane_count) {
pos = iter.pos;
break;
}
}
intel_dp_link_caps_iter_end(&iter);
if (pos < 0)
return pos;
return iter_pos_to_idx(link_caps, order, pos);
}
bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_filter *filter,
const struct intel_dp_link_config *config)
{
int idx;
idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config);
if (idx < 0)
return false;
filter->config_mask |= BIT(idx);
return true;
}
static bool intel_dp_link_caps_filter_remove(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_filter *filter,
const struct intel_dp_link_config *config)
{
int idx;
idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config);
if (idx < 0)
return false;
filter->config_mask &= ~BIT(idx);
return true;
}
static void set_max_link_limits(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
{
link_caps->max_limits = *max_link_limits;
}
static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps)
static void reset_max_link_limits(struct intel_dp_link_caps *link_caps)
{
struct intel_dp_link_config max_link_limits = {
.rate = intel_dp_max_common_rate(link_caps),
.lane_count = intel_dp_link_caps_max_common_lane_count(link_caps),
};
set_max_link_limits_no_update(link_caps, &max_link_limits);
set_max_link_limits(link_caps, &max_link_limits);
}
static void reset_max_link_limits_reenable_all(struct intel_dp_link_caps *link_caps)
{
link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL;
reset_max_link_limits(link_caps);
}
/**
* intel_dp_link_caps_disable_config - disable a configuration
* @link_caps: link capabilities state
* @config: configuration to disable
*
* Disable the configuration identified by @config. This removes the
* configuration from the set of allowed configurations. The disabling
* shouldn't leave the remaining configuration set empty.
*
* The configuration remains disallowed until intel_dp_link_caps() with
* reset=%true or changed sink capabilities is called, or
* intel_dp_link_caps_reset() is called. Each of these happens after a
* new sink is connected or the currently connected sink changes its
* capabilities.
*
* Return:
* - %true if @config was valid and the derived state was updated.
* - %false if @config was invalid or the remaining configuration set
* would remain empty.
*/
bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *config)
{
struct intel_dp_link_caps_filter enabled_configs = link_caps->enabled_configs;
struct intel_dp_link_config forced_params;
if (!intel_dp_link_caps_filter_remove(link_caps, &enabled_configs, config))
return false;
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
if (!calc_allowed_config_filter(link_caps, enabled_configs,
&link_caps->max_limits, &forced_params).config_mask)
return false;
link_caps->enabled_configs = enabled_configs;
return true;
}
/**
@@ -197,6 +700,25 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
*max_link_limits = link_caps->max_limits;
}
static bool max_link_limits_valid(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
{
struct intel_dp_link_caps_filter allowed_configs;
struct intel_dp_link_config forced_params;
if (max_link_limits->lane_count > INTEL_DP_MAX_LANE_COUNT ||
!is_power_of_2(max_link_limits->lane_count))
return false;
/* TODO: Validate max_link_limits->rate against the source supported rates. */
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
allowed_configs = calc_allowed_config_filter(link_caps, link_caps->enabled_configs,
max_link_limits, &forced_params);
return allowed_configs.config_mask != 0;
}
/**
* intel_dp_link_caps_set_max_limits - set the current maximum link limits
* @link_caps: link capabilities state
@@ -205,6 +727,10 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
* Set the current maximum rate and lane count limits to @max_link_limits,
* constraining the set of allowed configurations.
*
* The new limits must leave at least one configuration allowed: the limits
* must not be below the currently active forced parameters or below all the
* configurations that remain after disabled configurations are excluded.
*
* Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize
* this call against concurrent queries and updates to @link_caps, in line
* with the rest of the API.
@@ -217,9 +743,11 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
{
set_max_link_limits_no_update(link_caps, max_link_limits);
if (!max_link_limits_valid(link_caps, max_link_limits))
return false;
set_max_link_limits(link_caps, max_link_limits);
/* TODO: validate max_link_limits */
return true;
}
@@ -232,7 +760,7 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
*/
void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps)
{
reset_max_link_limits_no_update(link_caps);
reset_max_link_limits(link_caps);
}
static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps,
@@ -259,14 +787,70 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
intel_dp_link_config_rate(link_caps, lce_b);
}
/* Return %true if the supported link parameters have changed. */
static int link_config_cmp_by_rate_lane(const void *a, const void *b, const void *p)
{
const struct intel_dp_link_caps *link_caps = p;
u8 *lce_a_idx = (u8 *)a;
u8 *lce_b_idx = (u8 *)b;
const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx];
const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx];
if (lce_a->link_rate_idx != lce_b->link_rate_idx)
return lce_a->link_rate_idx - lce_b->link_rate_idx;
return lce_a->lane_count_exp - lce_b->lane_count_exp;
}
static int link_config_cmp_by_lane_rate(const void *a, const void *b, const void *p)
{
const struct intel_dp_link_caps *link_caps = p;
u8 *lce_a_idx = (u8 *)a;
u8 *lce_b_idx = (u8 *)b;
const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx];
const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx];
if (lce_a->lane_count_exp != lce_b->lane_count_exp)
return lce_a->lane_count_exp - lce_b->lane_count_exp;
return lce_a->link_rate_idx - lce_b->link_rate_idx;
}
/**
* intel_dp_link_caps_update - rebuild the supported link configuration state
* @link_caps: link capabilities state
* @rates: supported common link rates
* @num_rates: number of entries in @rates
* @max_lane_count: supported maximum lane count
* @reset: reset limits and disabled configs
*
* Rebuild the supported link configuration state from @rates and
* @max_lane_count.
*
* If @reset is %true, reset the maximum link limits to the maximum
* supported rate and lane count, and re-enable all configurations.
*
* This function is called regularly, at least after a sink is connected,
* but it may also be called later whenever the sink capabilities may have
* changed, for example in response to HPD IRQ / RX_CAP_CHANGED signaling.
*
* In the Intel driver this function is currently called whenever the
* connector detect handler runs, after reading the sink capabilities. This
* may change if those capabilities are cached until the sink is
* disconnected, or until RX_CAP_CHANGED is signaled. In any case, this
* function should be called whenever the sink capabilities were read out
* and may have changed.
*
* Returns:
* - %true if the link capabilities have changed, %false otherwise.
*/
bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
const int *rates, int num_rates, int max_lane_count)
const int *rates, int num_rates, int max_lane_count,
bool reset)
{
struct intel_dp *intel_dp = link_caps->dp;
struct intel_display *display = to_intel_display(intel_dp);
struct intel_dp_link_config_entry *lce;
bool link_params_changed = false;
bool link_params_changed = reset;
int num_common_lane_configs;
int i;
int j;
@@ -313,41 +897,25 @@ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
link_config_cmp_by_bw, NULL,
intel_dp);
return link_params_changed;
}
void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps,
int idx, int *link_rate, int *lane_count)
{
struct intel_display *display = to_intel_display(link_caps->dp);
const struct intel_dp_link_config_entry *lce;
if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs))
idx = 0;
lce = &link_caps->configs[idx];
*link_rate = intel_dp_link_config_rate(link_caps, lce);
*lane_count = intel_dp_link_config_lane_count(lce);
}
int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps,
int link_rate, int lane_count)
{
int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates,
link_rate);
int lane_count_exp = ilog2(lane_count);
int i;
for (i = 0; i < link_caps->num_configs; i++) {
const struct intel_dp_link_config_entry *lce = &link_caps->configs[i];
if (lce->lane_count_exp == lane_count_exp &&
lce->link_rate_idx == link_rate_idx)
return i;
link_caps->rate_lane_map[i] = i;
link_caps->lane_rate_map[i] = i;
}
return -1;
sort_r(link_caps->rate_lane_map, link_caps->num_configs,
sizeof(link_caps->rate_lane_map[0]),
link_config_cmp_by_rate_lane, NULL,
link_caps);
sort_r(link_caps->lane_rate_map, link_caps->num_configs,
sizeof(link_caps->lane_rate_map[0]),
link_config_cmp_by_lane_rate, NULL,
link_caps);
if (link_params_changed)
reset_max_link_limits_reenable_all(link_caps);
return link_params_changed;
}
/**
@@ -364,8 +932,7 @@ int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps,
*/
void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps)
{
/* TODO: Update the maximum link information. */
reset_max_link_limits_no_update(link_caps);
reset_max_link_limits_reenable_all(link_caps);
}
static int i915_dp_force_link_rate_show(struct seq_file *m, void *data)
@@ -628,6 +1195,43 @@ static int i915_dp_max_lane_count_show(void *data, u64 *val)
}
DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n");
static int intel_dp_allowed_link_configs_show(struct seq_file *m, void *data)
{
struct intel_connector *connector = to_intel_connector(m->private);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config link_config;
struct intel_dp_link_caps_iter iter;
int err;
int i;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
i = 0;
intel_dp_link_caps_iter_start(&iter,
link_caps,
intel_dp_link_caps_connector_compute_order(connector),
INTEL_DP_LINK_CAPS_FILTER_ALL);
for_each_dp_link_config(&iter, &link_config) {
seq_printf(m, "%s%dx%d",
i ? " " : "",
link_config.lane_count, link_config.rate);
i++;
}
intel_dp_link_caps_iter_end(&iter);
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
seq_putc(m, '\n');
return 0;
}
DEFINE_SHOW_ATTRIBUTE(intel_dp_allowed_link_configs);
/**
* intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector
@@ -654,6 +1258,9 @@ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector)
debugfs_create_file("i915_dp_max_lane_count", 0444, root,
connector, &i915_dp_max_lane_count_fops);
debugfs_create_file("intel_dp_allowed_link_configs", 0444, root,
connector, &intel_dp_allowed_link_configs_fops);
}
struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp)
@@ -665,6 +1272,7 @@ struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp)
return NULL;
link_caps->dp = intel_dp;
link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL;
return link_caps;
}
@@ -673,3 +1281,32 @@ void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps)
{
kfree(link_caps);
}
#if IS_ENABLED(CONFIG_KUNIT)
#define __INIT_MEMBER(__name, __fn) \
.__name = __fn,
#define INTEL_DP_LINK_CAPS_TEST_OPS_INIT \
INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__INIT_MEMBER)
#ifdef I915
const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops = {
INTEL_DP_LINK_CAPS_TEST_OPS_INIT
};
EXPORT_SYMBOL(i915_display_dp_link_caps_test_ops);
#else
const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops = {
INTEL_DP_LINK_CAPS_TEST_OPS_INIT
};
EXPORT_SYMBOL(intel_display_dp_link_caps_test_ops);
#endif /* I915 */
#undef INTEL_DP_LINK_CAPS_TEST_OPS_INIT
#undef __INIT_MEMBER
#endif /* CONFIG_KUNIT */

View File

@@ -4,6 +4,7 @@
#ifndef __INTEL_DP_LINK_CAPS_H__
#define __INTEL_DP_LINK_CAPS_H__
#include <linux/bitops.h>
#include <linux/types.h>
struct intel_connector;
@@ -11,23 +12,132 @@ struct intel_dp;
struct intel_dp_link_caps;
struct intel_dp_link_config;
int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps,
int max_rate);
int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index);
int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate);
int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps);
int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps);
int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps);
/**
* enum intel_dp_link_caps_order_key - key used to order configurations
* @INTEL_DP_LINK_CAPS_ORDER_KEY_BW:
* Order configurations by bandwidth, then by link rate.
* @INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE:
* Order configurations by link rate, then by lane count.
* @INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE:
* Order configurations by lane count, then by link rate.
* @INTEL_DP_LINK_CAPS_ORDER_KEY_NUM:
* Number of ordering keys.
*
* Selects how a caller wants the configuration table to be ordered,
* together with an &enum intel_dp_link_caps_order_direction, for
* iteration queries.
*
* See also:
* - &struct intel_dp_link_caps_order
* - intel_dp_link_caps_get_max_config()
*/
enum intel_dp_link_caps_order_key {
INTEL_DP_LINK_CAPS_ORDER_KEY_BW,
INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE,
INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE,
INTEL_DP_LINK_CAPS_ORDER_KEY_NUM
};
/**
* enum intel_dp_link_caps_order_direction - iteration direction
* @INTEL_DP_LINK_CAPS_ORDER_DIR_ASC:
* Iterate in ascending order according to the selected ordering key.
* @INTEL_DP_LINK_CAPS_ORDER_DIR_DESC:
* Iterate in descending order according to the selected ordering key.
* @INTEL_DP_LINK_CAPS_ORDER_DIR_NUM:
* Number of ordering directions.
*
* Selects the direction associated with an
* &enum intel_dp_link_caps_order_key for iteration queries.
*
* See also:
* - &struct intel_dp_link_caps_order
*/
enum intel_dp_link_caps_order_direction {
INTEL_DP_LINK_CAPS_ORDER_DIR_ASC,
INTEL_DP_LINK_CAPS_ORDER_DIR_DESC,
INTEL_DP_LINK_CAPS_ORDER_DIR_NUM
};
/**
* struct intel_dp_link_caps_order - configuration ordering
* @key:
* Key used to order configurations.
* @dir:
* Direction of the selected ordering.
*
* Describes an iteration order for link configurations.
*
* See also:
* - for_each_dp_link_config()
*/
struct intel_dp_link_caps_order {
enum intel_dp_link_caps_order_key key;
enum intel_dp_link_caps_order_direction dir;
};
struct intel_dp_link_caps_filter {
u32 config_mask;
};
#define INTEL_DP_LINK_CAPS_FILTER_NONE \
((struct intel_dp_link_caps_filter){ .config_mask = 0 })
#define INTEL_DP_LINK_CAPS_FILTER_ALL \
((struct intel_dp_link_caps_filter){ .config_mask = (u32)-1 })
struct intel_dp_link_caps_iter {
struct intel_dp_link_caps *link_caps;
int pos;
struct intel_dp_link_caps_order order;
struct intel_dp_link_caps_filter filter;
bool (*get_next_config)(struct intel_dp_link_caps_iter *iter,
struct intel_dp_link_config *config);
};
/**
* for_each_dp_link_config - iterate allowed link configurations
* @__iter:
* &struct intel_dp_link_caps_iter being iterated
* @__config:
* pointer to &struct intel_dp_link_config filled for each match
*/
#define for_each_dp_link_config(__iter, __config) \
while ((__iter)->get_next_config((__iter), (__config)))
void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter,
struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_order order,
struct intel_dp_link_caps_filter filter);
void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter);
struct intel_dp_link_caps_order
intel_dp_link_caps_connector_compute_order(struct intel_connector *connector);
struct intel_dp_link_caps_order
intel_dp_link_caps_connector_fallback_order(bool is_mst);
void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps);
void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *forced_params);
int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps,
int link_rate, int lane_count);
void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps,
int idx, int *link_rate, int *lane_count);
bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_caps_filter *filter,
const struct intel_dp_link_config *config);
bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps,
enum intel_dp_link_caps_order_key order_key,
struct intel_dp_link_caps_filter filter,
struct intel_dp_link_config *max_config);
void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *max_config);
bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *config);
void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *max_link_limits);
@@ -36,7 +146,8 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps);
bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
const int *rates, int num_rates, int max_lane_count);
const int *rates, int num_rates, int max_lane_count,
bool reset);
void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps);
void intel_dp_link_caps_debugfs_add(struct intel_connector *connector);
@@ -44,4 +155,41 @@ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector);
struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp);
void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps);
#if IS_ENABLED(CONFIG_KUNIT)
#define INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__X) \
__X(connector_compute_order, intel_dp_link_caps_connector_compute_order) \
__X(connector_fallback_order, intel_dp_link_caps_connector_fallback_order) \
__X(iter_start, intel_dp_link_caps_iter_start) \
__X(iter_end, intel_dp_link_caps_iter_end) \
__X(set_max_limits, intel_dp_link_caps_set_max_limits) \
__X(get_max_limits, intel_dp_link_caps_get_max_limits) \
__X(get_max_bw_config, intel_dp_link_caps_get_max_bw_config) \
__X(reset_max_limits, intel_dp_link_caps_reset_max_limits) \
__X(disable_config, intel_dp_link_caps_disable_config) \
__X(update, intel_dp_link_caps_update) \
__X(init, intel_dp_link_caps_init) \
__X(cleanup, intel_dp_link_caps_cleanup)
#define __DECLARE_MEMBER(__name, __fn) \
typeof(__fn) *__name;
#define INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE \
INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__DECLARE_MEMBER)
struct intel_dp_link_caps_test_ops {
INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE
};
#undef INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE
#undef __DECLARE_MEMBER
#ifdef I915
extern const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops;
#else
extern const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops;
#endif /* I915 */
#endif /* CONFIG_KUNIT */
#endif /* __INTEL_DP_LINK_CAPS_H__ */

View File

@@ -21,6 +21,8 @@
* IN THE SOFTWARE.
*/
#include <kunit/visibility.h>
#include <linux/debugfs.h>
#include <linux/iopoll.h>
@@ -1846,124 +1848,57 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp,
return true;
}
static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config forced_params;
int link_rate;
int lane_count;
int i;
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
i = intel_dp_link_config_index(intel_dp->link.caps,
crtc_state->port_clock, crtc_state->lane_count);
for (i--; i >= 0; i--) {
intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count);
if ((forced_params.rate &&
forced_params.rate != link_rate) ||
(forced_params.lane_count &&
forced_params.lane_count != lane_count))
continue;
break;
}
if (i < 0)
return false;
*new_link_rate = link_rate;
*new_lane_count = lane_count;
return true;
}
static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config forced_params;
int rate_index;
int new_rate;
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
if (forced_params.rate)
return -1;
rate_index = intel_dp_link_caps_common_rate_idx(link_caps,
current_rate);
if (rate_index <= 0)
return -1;
new_rate = intel_dp_common_rate(link_caps, rate_index - 1);
/* TODO: Make switching from UHBR to non-UHBR rates work. */
if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate))
return -1;
return new_rate;
}
static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count)
{
struct intel_dp_link_config forced_params;
intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params);
if (forced_params.lane_count)
return -1;
if (current_lane_count == 1)
return -1;
return current_lane_count >> 1;
}
static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int link_rate;
int lane_count;
lane_count = crtc_state->lane_count;
link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock);
if (link_rate < 0) {
lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count);
link_rate = intel_dp_max_common_rate(link_caps);
}
if (lane_count < 0)
return false;
*new_link_rate = link_rate;
*new_lane_count = lane_count;
return true;
}
static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
/* TODO: Use the same fallback logic on SST as on MST. */
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST))
return reduce_link_params_in_bw_order(intel_dp, crtc_state,
new_link_rate, new_lane_count);
else
return reduce_link_params_in_rate_lane_order(intel_dp, crtc_state,
new_link_rate, new_lane_count);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST);
struct intel_dp_link_caps_order order =
intel_dp_link_caps_connector_fallback_order(is_mst);
struct intel_dp_link_config old_config = {
.rate = crtc_state->port_clock,
.lane_count = crtc_state->lane_count,
};
struct intel_dp_link_caps_iter iter;
struct intel_dp_link_config config;
bool old_found = false;
bool new_found = false;
intel_dp_link_caps_iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL);
for_each_dp_link_config(&iter, &config) {
if (!old_found) {
if (config.rate == old_config.rate &&
config.lane_count == old_config.lane_count)
old_found = true;
continue;
}
*new_link_rate = config.rate;
*new_lane_count = config.lane_count;
new_found = true;
break;
}
intel_dp_link_caps_iter_end(&iter);
return new_found;
}
static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
VISIBLE_IF_KUNIT
int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(intel_dp);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config max_link_limits;
struct intel_dp_link_config current_config = {
.rate = crtc_state->port_clock,
.lane_count = crtc_state->lane_count,
};
int new_link_rate;
int new_lane_count;
int err = -1;
if (intel_dp_is_edp(intel_dp) && !intel_dp->use_max_params) {
lt_dbg(intel_dp, DP_PHY_DPRX,
@@ -1972,16 +1907,49 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
return 0;
}
/*
* Temporarily reset the max link limit before selecting the fallback
* config.
*
* After fallback, the current logic narrows the allowed configurations
* to the selected config's rate and lane count. That can make a later
* fallback candidate fall outside the current max_limit, so reset it
* before searching.
*
* TODO: Constrain the allowed configurations by only disabling individual
* configurations and remove setting maximum link parameters.
*/
intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits);
intel_dp_link_caps_reset_max_limits(link_caps);
/*
* TODO: Make fallback depend only on disabling the current config,
* once max_limit no longer constrains the allowed config set. Then
* disabling the current config will define the allowed configs for
* the subsequent modeset, so there will be no need to select a
* reduced config separately here.
*/
if (!reduce_link_params(intel_dp, crtc_state, &new_link_rate, &new_lane_count))
return -1;
goto out_restore_max_limits;
if (intel_dp_is_edp(intel_dp) &&
!intel_dp_can_link_train_fallback_for_edp(intel_dp, new_link_rate, new_lane_count)) {
lt_dbg(intel_dp, DP_PHY_DPRX,
"Retrying Link training for eDP with same parameters\n");
return 0;
err = 0;
goto out_restore_max_limits;
}
/*
* Shouldn't fail: the current config was enabled, and reducing the
* link parameters should still leave the fallback config allowed.
*/
if (drm_WARN_ON(display->drm,
!intel_dp_link_caps_disable_config(link_caps, &current_config)))
return -1;
lt_dbg(intel_dp, DP_PHY_DPRX,
"Reducing link parameters from %dx%d to %dx%d\n",
crtc_state->lane_count, crtc_state->port_clock,
@@ -1990,10 +1958,19 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
max_link_limits.rate = new_link_rate;
max_link_limits.lane_count = new_lane_count;
/* TODO: handle an update failure */
intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits);
err = 0;
return 0;
out_restore_max_limits:
/*
* Shouldn't fail: setting max_limits can only fail if they drop below
* the optionally forced rate/lane-count parameters, but the reduced
* config was chosen to satisfy those constraints.
*/
if (drm_WARN_ON(display->drm,
!intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits)))
err = -1;
return err;
}
static bool intel_dp_schedule_fallback_link_training(struct intel_atomic_state *state,
@@ -2834,3 +2811,32 @@ void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training
{
kfree(link_training);
}
#if IS_ENABLED(CONFIG_KUNIT)
#define __INIT_MEMBER(__name, __fn) \
.__name = __fn,
#define INTEL_DP_LINK_TRAINING_TEST_OPS_INIT \
INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__INIT_MEMBER)
#ifdef I915
const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops = {
INTEL_DP_LINK_TRAINING_TEST_OPS_INIT
};
EXPORT_SYMBOL(i915_display_dp_link_training_test_ops);
#else
const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops = {
INTEL_DP_LINK_TRAINING_TEST_OPS_INIT
};
EXPORT_SYMBOL(intel_display_dp_link_training_test_ops);
#endif /* I915 */
#undef INTEL_DP_LINK_TRAINING_TEST_OPS_INIT
#undef __INIT_MEMBER
#endif /* CONFIG_KUNIT */

View File

@@ -8,6 +8,8 @@
#include <drm/display/drm_dp_helper.h>
#include "intel_dp_link_caps.h"
struct intel_atomic_state;
struct intel_connector;
struct intel_crtc_state;
@@ -71,4 +73,33 @@ void intel_dp_link_training_reset(struct intel_dp_link_training *link_training);
struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp);
void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training);
#if IS_ENABLED(CONFIG_KUNIT)
int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
#define INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__X) \
__X(get_fallback_values, intel_dp_get_link_train_fallback_values)
#define __DECLARE_MEMBER(__name, __fn) \
typeof(__fn) *__name;
#define INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE \
INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__DECLARE_MEMBER)
struct intel_dp_link_training_test_ops {
INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE
};
#undef INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE
#undef __DECLARE_MEMBER
#ifdef I915
extern const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops;
#else
extern const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops;
#endif /* I915 */
#endif /* CONFIG_KUNIT */
#endif /* __INTEL_DP_LINK_TRAINING_H__ */

View File

@@ -47,6 +47,7 @@
#include "intel_display_wa.h"
#include "intel_dp.h"
#include "intel_dp_hdcp.h"
#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -444,8 +445,20 @@ static int mst_stream_compute_link_config(struct intel_dp *intel_dp,
struct drm_connector_state *conn_state,
const struct link_config_limits *limits)
{
crtc_state->lane_count = limits->max_lane_count;
crtc_state->port_clock = limits->max_rate;
struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct intel_dp_link_config max_link_config;
/*
* FIXME: Use a proper iteration over the link configurations, instead
* of using only the max BW config. For instance UHBR rate configs may
* have additional limitations over non-UHBR ones, due to the DSC DPT
* bpp maximum limit.
*/
if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config))
return -EINVAL;
crtc_state->port_clock = max_link_config.rate;
crtc_state->lane_count = max_link_config.lane_count;
/*
* FIXME: allocate the BW according to link_bpp, which in the case of
@@ -464,6 +477,7 @@ static int mst_stream_dsc_compute_link_config(struct intel_dp *intel_dp,
{
struct intel_display *display = to_intel_display(intel_dp);
struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct intel_dp_link_config max_link_config;
crtc_state->pipe_bpp = limits->pipe.max_bpp;
@@ -471,8 +485,17 @@ static int mst_stream_dsc_compute_link_config(struct intel_dp *intel_dp,
"DSC Sink supported compressed min bpp " FXP_Q4_FMT " compressed max bpp " FXP_Q4_FMT "\n",
FXP_Q4_ARGS(limits->link.min_bpp_x16), FXP_Q4_ARGS(limits->link.max_bpp_x16));
crtc_state->lane_count = limits->max_lane_count;
crtc_state->port_clock = limits->max_rate;
/*
* FIXME: Use a proper iteration over the link configurations, instead
* of using only the max BW config. For instance UHBR rate configs may
* have additional limitations over non-UHBR ones, due to the DSC DPT
* bpp maximum limit.
*/
if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config))
return -EINVAL;
crtc_state->port_clock = max_link_config.rate;
crtc_state->lane_count = max_link_config.lane_count;
return intel_dp_mtp_tu_compute_config(intel_dp, crtc_state, conn_state,
limits->link.min_bpp_x16,
@@ -488,6 +511,20 @@ static int mode_hblank_period_ns(const struct drm_display_mode *mode)
mode->crtc_clock);
}
static int get_connector_max_rate(const struct intel_connector *connector,
const struct link_config_limits *limits)
{
struct intel_dp *intel_dp = intel_attached_dp((struct intel_connector *)connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config max_link_config;
intel_dp_link_caps_get_max_config(link_caps,
INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE,
limits->link_config_filter, &max_link_config);
return max_link_config.rate;
}
static bool
hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector,
const struct intel_crtc_state *crtc_state,
@@ -498,11 +535,13 @@ hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector,
bool is_uhbr_sink = connector->mst.dp &&
drm_dp_128b132b_supported(connector->mst.dp->dpcd);
int hblank_limit = is_uhbr_sink ? 500 : 300;
int max_rate;
if (!connector->dp.dsc_hblank_expansion_quirk)
return false;
if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate))
max_rate = get_connector_max_rate(connector, limits);
if (is_uhbr_sink && !drm_dp_is_uhbr_rate(max_rate))
return false;
if (mode_hblank_period_ns(adjusted_mode) > hblank_limit)
@@ -524,6 +563,7 @@ adjust_limits_for_dsc_hblank_expansion_quirk(struct intel_dp *intel_dp,
struct intel_display *display = to_intel_display(connector);
const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
int min_bpp_x16 = limits->link.min_bpp_x16;
int max_rate;
if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits))
return true;
@@ -550,11 +590,10 @@ adjust_limits_for_dsc_hblank_expansion_quirk(struct intel_dp *intel_dp,
return true;
}
drm_WARN_ON(display->drm, limits->min_rate != limits->max_rate);
if (limits->max_rate < 540000)
max_rate = get_connector_max_rate(connector, limits);
if (max_rate < 540000)
min_bpp_x16 = fxp_q4_from_int(13);
else if (limits->max_rate < 810000)
else if (max_rate < 810000)
min_bpp_x16 = fxp_q4_from_int(10);
if (limits->link.min_bpp_x16 >= min_bpp_x16)
@@ -776,7 +815,7 @@ intel_dp_mst_transcoder_mask(struct intel_atomic_state *state,
struct intel_display *display = to_intel_display(state);
const struct intel_digital_connector_state *conn_state;
struct intel_connector *connector;
u8 transcoders = 0;
u16 transcoders = 0;
int i;
if (DISPLAY_VER(display) < 12)
@@ -1414,6 +1453,11 @@ static int mst_connector_get_ddc_modes(struct drm_connector *_connector)
drm_edid_free(drm_edid);
if (intel_dp_tunnel_uhbr_lanes_wa_setup(intel_dp)) {
intel_dp_flush_connector_commits(connector);
intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp);
}
return ret;
}
@@ -1476,6 +1520,7 @@ mst_connector_mode_valid_ctx(struct drm_connector *_connector,
unsigned long bw_overhead_flags =
DRM_DP_BW_OVERHEAD_MST | DRM_DP_BW_OVERHEAD_SSC_REF_CLK;
int min_link_bpp_x16 = fxp_q4_from_int(18);
struct intel_dp_link_config max_bw_config;
static bool supports_dsc;
int ret;
bool dsc = false;
@@ -1508,8 +1553,9 @@ mst_connector_mode_valid_ctx(struct drm_connector *_connector,
min_link_bpp_x16 = intel_dp_compute_min_compressed_bpp_x16(connector,
INTEL_OUTPUT_FORMAT_RGB);
max_link_clock = intel_dp_max_link_rate(intel_dp);
max_lanes = intel_dp_max_lane_count(intel_dp);
intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config);
max_link_clock = max_bw_config.rate;
max_lanes = max_bw_config.lane_count;
max_rate = intel_dp_max_link_data_rate(intel_dp,
max_link_clock, max_lanes);
@@ -2135,14 +2181,19 @@ bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
*/
void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp)
{
int link_rate = intel_dp_max_link_rate(intel_dp);
int lane_count = intel_dp_max_lane_count(intel_dp);
struct intel_dp_link_config max_bw_config;
int link_rate;
int lane_count;
u8 rate_select;
u8 link_bw;
if (intel_dp->link.active)
return;
intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config);
link_rate = max_bw_config.rate;
lane_count = max_bw_config.lane_count;
if (intel_mst_probed_link_params_valid(intel_dp, link_rate, lane_count))
return;

View File

@@ -28,12 +28,92 @@ void intel_dp_test_reset(struct intel_dp *intel_dp)
memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance));
}
static bool set_filter_for_lane_count(struct intel_connector *connector,
struct intel_dp_link_caps *link_caps,
int lane_count,
struct link_config_limits *limits)
{
struct intel_dp_link_config link_config;
struct intel_dp_link_caps_order order =
intel_dp_link_caps_connector_compute_order(connector);
struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE;
struct intel_dp_link_caps_iter iter;
bool found = false;
intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter);
for_each_dp_link_config(&iter, &link_config) {
if (link_config.lane_count != lane_count)
continue;
intel_dp_link_caps_filter_add(link_caps, &new_filter, &link_config);
found = true;
}
intel_dp_link_caps_iter_end(&iter);
if (!found)
return false;
limits->link_config_filter = new_filter;
return true;
}
static bool set_filter_for_link_config(struct intel_connector *connector,
struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *link_params,
struct link_config_limits *limits)
{
struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE;
if (!intel_dp_link_caps_filter_add(link_caps, &new_filter, link_params))
return false;
limits->link_config_filter = new_filter;
return true;
}
static bool set_filter_for_link_params(struct intel_connector *connector,
int link_rate, int lane_count,
struct link_config_limits *limits)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_display *display = to_intel_display(intel_dp);
struct intel_dp_link_config requested_config;
requested_config.rate = link_rate;
requested_config.lane_count = lane_count;
if (set_filter_for_link_config(connector, link_caps, &requested_config, limits))
return true;
/*
* Preserve the legacy behavior: if the requested (rate, lane_count)
* combination is not an allowed config, fall back to all configs
* matching the requested lane count.
*
* TODO: Recheck whether this behavior is actually correct.
*/
if (set_filter_for_lane_count(connector, link_caps, lane_count, limits))
return true;
drm_dbg_kms(display->drm,
"[ENCODER:%d:%s] Invalid autotest link config parameters: %dx%d\n",
encoder->base.base.id, encoder->base.name,
requested_config.lane_count,
requested_config.rate);
return false;
}
/* Adjust link config limits based on compliance test requests. */
void intel_dp_test_compute_config(struct intel_dp *intel_dp,
bool intel_dp_test_compute_config(struct intel_connector *connector,
struct intel_crtc_state *pipe_config,
struct link_config_limits *limits)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_display *display = to_intel_display(intel_dp);
/* For DP Compliance we override the computed bpp for the pipe */
@@ -49,23 +129,14 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp,
/* Use values requested by Compliance Test Request */
if (intel_dp->compliance.test_type == DP_TEST_LINK_TRAINING) {
int index;
/* Validate the compliance test data since max values
* might have changed due to link train fallback.
*/
if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate,
intel_dp->compliance.test_lane_count)) {
index = intel_dp_link_caps_common_rate_idx(link_caps,
intel_dp->compliance.test_link_rate);
if (index >= 0) {
limits->min_rate = intel_dp->compliance.test_link_rate;
limits->max_rate = intel_dp->compliance.test_link_rate;
}
limits->min_lane_count = intel_dp->compliance.test_lane_count;
limits->max_lane_count = intel_dp->compliance.test_lane_count;
}
if (!set_filter_for_link_params(connector,
intel_dp->compliance.test_link_rate,
intel_dp->compliance.test_lane_count,
limits))
return false;
}
return true;
}
/* Compliance test status bits */

View File

@@ -6,6 +6,7 @@
#include <linux/types.h>
struct intel_connector;
struct intel_crtc_state;
struct intel_display;
struct intel_dp;
@@ -13,7 +14,7 @@ struct link_config_limits;
void intel_dp_test_reset(struct intel_dp *intel_dp);
void intel_dp_test_request(struct intel_dp *intel_dp);
void intel_dp_test_compute_config(struct intel_dp *intel_dp,
bool intel_dp_test_compute_config(struct intel_connector *connector,
struct intel_crtc_state *pipe_config,
struct link_config_limits *limits);
bool intel_dp_test_phy(struct intel_dp *intel_dp);

View File

@@ -58,10 +58,12 @@ static int kbytes_to_mbits(int kbytes)
static int get_current_link_bw(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int rate = intel_dp_max_common_rate(link_caps);
int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps);
struct intel_dp_link_config max_bw_config;
return intel_dp_max_link_data_rate(intel_dp, rate, lane_count);
intel_dp_link_caps_get_max_bw_config(link_caps, &max_bw_config);
return intel_dp_max_link_data_rate(intel_dp, max_bw_config.rate,
max_bw_config.lane_count);
}
static int __update_tunnel_state(struct intel_dp *intel_dp, bool force_sink_update)
@@ -829,6 +831,51 @@ void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state)
atomic_increase_bw(state);
}
static u8 lane_count_mask(int lane_count)
{
return BIT(ilog2(lane_count));
}
void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp)
{
struct intel_connector *connector = intel_dp->attached_connector;
struct intel_dp_link_caps_order order =
intel_dp_link_caps_connector_compute_order(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config link_config;
struct intel_dp_link_caps_iter iter;
if (!intel_dp->disabled_uhbr_lane_mask)
return;
intel_dp_link_caps_iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL);
for_each_dp_link_config(&iter, &link_config) {
if (drm_dp_is_uhbr_rate(link_config.rate) &&
lane_count_mask(link_config.lane_count) & intel_dp->disabled_uhbr_lane_mask)
intel_dp_link_caps_disable_config(link_caps, &link_config);
}
intel_dp_link_caps_iter_end(&iter);
}
bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp)
{
u8 old_mask = intel_dp->disabled_uhbr_lane_mask;
if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp) ||
drm_dp_tunnel_128b132b_lane0_mapping_supported(intel_dp->tunnel))
intel_dp->disabled_uhbr_lane_mask = 0;
else
/* TODO: Add support for keeping 2 lanes enabled as well. */
intel_dp->disabled_uhbr_lane_mask = lane_count_mask(1) | lane_count_mask(2);
return intel_dp->disabled_uhbr_lane_mask != old_mask;
}
void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp)
{
intel_dp->disabled_uhbr_lane_mask = 0;
}
/**
* intel_dp_tunnel_mgr_init - Initialize the DP tunnel manager
* @display: display device

View File

@@ -54,6 +54,10 @@ int intel_dp_tunnel_atomic_check_state(struct intel_atomic_state *state,
void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state);
void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp);
bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp);
void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp);
int intel_dp_tunnel_mgr_init(struct intel_display *display);
void intel_dp_tunnel_mgr_cleanup(struct intel_display *display);
@@ -129,6 +133,19 @@ intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state)
return 0;
}
static inline void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp)
{
}
static inline bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp)
{
return false;
}
static inline void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp)
{
}
static inline int
intel_dp_tunnel_mgr_init(struct intel_display *display)
{

View File

@@ -13,6 +13,7 @@
#include "intel_display_core.h"
#include "intel_display_utils.h"
#include "intel_display_regs.h"
#include "intel_display_wa.h"
#include "intel_dram.h"
#include "intel_mchbar.h"
#include "intel_parent.h"
@@ -794,7 +795,18 @@ static int xelpdp_get_dram_info(struct intel_display *display, struct dram_info
dram_info->num_channels = REG_FIELD_GET(MTL_N_OF_POPULATED_CH_MASK, val);
dram_info->num_qgv_points = REG_FIELD_GET(MTL_N_OF_ENABLED_QGV_POINTS_MASK, val);
/* PSF GV points not supported in D14+ */
/*
* Wa_16030862157
* MEM_SS_INFO_GLOBAL populated-channel field is only 4 bits and
* cannot encode 16, so on Xe3p the BIOS programs the saturated field
* value (0xf) to indicate the fully-populated 16-channel config (4
* memory controllers x 4 channels). Interpret it as 16.
*/
if (intel_display_wa(display, INTEL_DISPLAY_WA_16030862157) &&
dram_info->num_channels == REG_FIELD_MAX(MTL_N_OF_POPULATED_CH_MASK))
dram_info->num_channels = 16;
if (DISPLAY_VER(display) >= 35)
dram_info->ecc_impacting_de_bw = REG_FIELD_GET(XE3P_ECC_IMPACTING_DE, val);

View File

@@ -59,6 +59,7 @@
#include "intel_fbc_regs.h"
#include "intel_frontbuffer.h"
#include "intel_parent.h"
#include "skl_universal_plane.h"
#define for_each_fbc_id(__display, __fbc_id) \
for ((__fbc_id) = INTEL_FBC_A; (__fbc_id) < I915_MAX_FBCS; (__fbc_id)++) \
@@ -1305,6 +1306,9 @@ static bool intel_fbc_surface_size_ok(const struct intel_plane_state *plane_stat
struct intel_display *display = to_intel_display(plane_state);
unsigned int effective_w, effective_h, max_w, max_h;
if (DISPLAY_VER(display) >= 20)
return true;
intel_fbc_max_surface_size(display, &max_w, &max_h);
effective_w = plane_state->view.color_plane[0].x +
@@ -1315,10 +1319,18 @@ static bool intel_fbc_surface_size_ok(const struct intel_plane_state *plane_stat
return effective_w <= max_w && effective_h <= max_h;
}
static void intel_fbc_max_plane_size(struct intel_display *display,
static void intel_fbc_max_plane_size(const struct intel_plane_state *plane_state,
unsigned int *w, unsigned int *h)
{
if (DISPLAY_VER(display) >= 10) {
struct intel_display *display = to_intel_display(plane_state);
struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
const struct drm_framebuffer *fb = plane_state->hw.fb;
unsigned int rotation = plane_state->hw.rotation;
if (DISPLAY_VER(display) >= 20) {
*w = intel_plane_max_width(plane, fb, 0, rotation);
*h = 4096;
} else if (DISPLAY_VER(display) >= 10) {
*w = 5120;
*h = 4096;
} else if (DISPLAY_VER(display) >= 8 || display->platform.haswell) {
@@ -1335,10 +1347,9 @@ static void intel_fbc_max_plane_size(struct intel_display *display,
static bool intel_fbc_plane_size_valid(const struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(plane_state);
unsigned int w, h, max_w, max_h;
intel_fbc_max_plane_size(display, &max_w, &max_h);
intel_fbc_max_plane_size(plane_state, &max_w, &max_h);
w = drm_rect_width(&plane_state->uapi.src) >> 16;
h = drm_rect_height(&plane_state->uapi.src) >> 16;
@@ -1567,7 +1578,7 @@ static int _intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state)
/* WaFbcExceedCdClockThreshold:hsw,bdw */
if (display->platform.haswell || display->platform.broadwell)
return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
return DIV_ROUND_UP(crtc_state->pixel_rate_cdclk * 100, 95);
/* no FBC specific limits to worry about */
return 0;

View File

@@ -262,6 +262,32 @@ __intel_fbdev_fb_alloc(struct intel_display *display,
}
static bool bios_fb_ok(const struct intel_framebuffer *fb,
const struct drm_fb_helper_surface_size *sizes)
{
struct intel_display *display = to_intel_display(fb->base.dev);
int width = fb->base.width;
int height = fb->base.height;
int depth = fb->base.format->depth;
int bpp = fb->base.format->cpp[0] * 8;
if (sizes->fb_width > width || sizes->fb_height > height) {
drm_dbg_kms(display->drm,
"BIOS fb too small (%dx%d), we require (%dx%d), releasing it\n",
width, height, sizes->fb_width, sizes->fb_height);
return false;
}
if (sizes->surface_depth != depth || sizes->surface_bpp != bpp) {
drm_dbg_kms(display->drm,
"BIOS fb using wrong depth/bpp (%d/%d), we require (%d/%d), releasing it\n",
depth, bpp, sizes->surface_depth, sizes->surface_bpp);
return false;
}
return true;
}
int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -279,14 +305,7 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
ifbdev->fb = NULL;
if (fb &&
(sizes->fb_width > fb->base.width ||
sizes->fb_height > fb->base.height)) {
drm_dbg_kms(display->drm,
"BIOS fb too small (%dx%d), we require (%dx%d),"
" releasing it\n",
fb->base.width, fb->base.height,
sizes->fb_width, sizes->fb_height);
if (fb && !bios_fb_ok(fb, sizes)) {
drm_framebuffer_put(&fb->base);
fb = NULL;
}

View File

@@ -1075,6 +1075,7 @@ static int intel_hdcp1_enable(struct intel_connector *connector)
ret = intel_hdcp_auth(connector);
if (!ret) {
hdcp->hdcp_encrypted = true;
hdcp->hdcp2_encrypted = false;
return 0;
}
@@ -2109,6 +2110,7 @@ static int _intel_hdcp2_enable(struct intel_atomic_state *state,
hdcp->content_type);
hdcp->hdcp2_encrypted = true;
hdcp->hdcp_encrypted = false;
return 0;
}

View File

@@ -128,11 +128,10 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
/*
* TODO:
* Disable planes if get_initial_plane_config() failed.
* Make sure things work if the surface base is not page aligned.
*/
if (!plane_config->fb)
return;
goto nofb;
if (intel_alloc_initial_plane_obj(display, plane_config)) {
fb = plane_config->fb;
@@ -184,7 +183,8 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
* simplest solution is to just disable the primary plane now and
* pretend the BIOS never had it enabled.
*/
intel_plane_disable_noatomic(crtc, plane);
if (plane_state->uapi.visible)
intel_plane_disable_noatomic(crtc, plane);
}
static void plane_config_fini(struct intel_display *display,

View File

@@ -186,11 +186,11 @@ static void intel_crtc_disable_noatomic_complete(struct intel_crtc *crtc)
* Return all the pipes using a transcoder in @transcoder_mask.
* For joiner configs return only the joiner primary.
*/
static u8 get_transcoder_pipes(struct intel_display *display,
u8 transcoder_mask)
static u16 get_transcoder_pipes(struct intel_display *display,
u16 transcoder_mask)
{
struct intel_crtc *temp_crtc;
u8 pipes = 0;
u16 pipes = 0;
for_each_intel_crtc(display, temp_crtc) {
struct intel_crtc_state *temp_crtc_state =
@@ -214,7 +214,7 @@ static u8 get_transcoder_pipes(struct intel_display *display,
* For joiner configs return only the joiner primary pipes.
*/
static void get_portsync_pipes(struct intel_crtc *crtc,
u8 *master_pipe_mask, u8 *slave_pipes_mask)
u16 *master_pipe_mask, u16 *slave_pipes_mask)
{
struct intel_display *display = to_intel_display(crtc);
struct intel_crtc_state *crtc_state =
@@ -243,10 +243,10 @@ static void get_portsync_pipes(struct intel_crtc *crtc,
*slave_pipes_mask = get_transcoder_pipes(display, master_crtc_state->sync_mode_slaves_mask);
}
static u8 get_joiner_secondary_pipes(struct intel_display *display, u8 primary_pipes_mask)
static u16 get_joiner_secondary_pipes(struct intel_display *display, u16 primary_pipes_mask)
{
struct intel_crtc *primary_crtc;
u8 pipes = 0;
u16 pipes = 0;
for_each_intel_crtc_in_pipe_mask(display, primary_crtc, primary_pipes_mask) {
struct intel_crtc_state *primary_crtc_state =
@@ -263,9 +263,9 @@ static void intel_crtc_disable_noatomic(struct intel_crtc *crtc,
{
struct intel_display *display = to_intel_display(crtc);
struct intel_crtc *temp_crtc;
u8 portsync_master_mask;
u8 portsync_slaves_mask;
u8 joiner_secondaries_mask;
u16 portsync_master_mask;
u16 portsync_slaves_mask;
u16 joiner_secondaries_mask;
/* TODO: Add support for MST */
get_portsync_pipes(crtc, &portsync_master_mask, &portsync_slaves_mask);
@@ -854,14 +854,10 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
* FIXME don't have the fb yet, so can't
* use plane->min_cdclk() :(
*/
if (plane_state->uapi.visible && plane->min_cdclk) {
if (crtc_state->double_wide || DISPLAY_VER(display) >= 10)
crtc_state->plane_min_cdclk[plane->id] =
DIV_ROUND_UP(crtc_state->pixel_rate, 2);
else
crtc_state->plane_min_cdclk[plane->id] =
crtc_state->pixel_rate;
}
if (plane_state->uapi.visible && plane->min_cdclk)
crtc_state->plane_min_cdclk[plane->id] =
DIV_ROUND_UP(crtc_state->pixel_rate_cdclk,
intel_cdclk_ppc(display, crtc_state->double_wide));
drm_dbg_kms(display->drm,
"[PLANE:%d:%s] min_cdclk %d kHz\n",
plane->base.base.id, plane->base.name,

View File

@@ -264,6 +264,50 @@ unsigned int intel_adjusted_rate(const struct drm_rect *src,
dst_w * dst_h);
}
static unsigned int hscale_cdclk(const struct drm_rect *src,
const struct drm_rect *dst,
unsigned int ppc)
{
unsigned int hscale;
hscale = drm_rect_calc_hscale(src, dst, 0, INT_MAX);
hscale = max(hscale, 0x10000);
/*
* Double the fractional part due to some 2 PPC granularity issue
*
* FIXME: BSpec calls for doubling only the <0.5 fractional part,
* and rounding it down to a unit fraction. In practice that is
* not sufficient, and we need a more aggressive CDCLK bump in
* many cases. The updated formula was derived empirically.
* This may need to be updated once we have better undestading
* of what's happening in the hardware...
*/
return (hscale & ~0xffff) + ppc * (hscale & 0xffff);
}
static unsigned int vscale_cdclk(const struct drm_rect *src,
const struct drm_rect *dst)
{
unsigned int vscale;
vscale = drm_rect_calc_vscale(src, dst, 0, INT_MAX);
vscale = max(vscale, 0x10000);
return vscale;
}
unsigned int intel_adjusted_rate_cdclk(const struct drm_rect *src,
const struct drm_rect *dst,
unsigned int rate,
unsigned int ppc)
{
unsigned int hscale = hscale_cdclk(src, dst, ppc);
unsigned int vscale = vscale_cdclk(src, dst);
return DIV64_U64_ROUND_UP((u64)rate * hscale * vscale, 1ull << 32);
}
unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
@@ -284,6 +328,17 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
crtc_state->pixel_rate);
}
unsigned int intel_plane_pixel_rate_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(crtc_state);
unsigned int ppc = HAS_2PPC(display) ? 2 : 1;
return intel_adjusted_rate_cdclk(&plane_state->uapi.src,
&plane_state->uapi.dst,
crtc_state->pixel_rate_cdclk, ppc);
}
unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state,
int color_plane)

View File

@@ -29,8 +29,13 @@ bool intel_plane_can_async_flip(struct intel_plane *plane,
unsigned int intel_adjusted_rate(const struct drm_rect *src,
const struct drm_rect *dst,
unsigned int rate);
unsigned int intel_adjusted_rate_cdclk(const struct drm_rect *src,
const struct drm_rect *dst,
unsigned int rate, unsigned int ppc);
unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
unsigned int intel_plane_pixel_rate_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state,

View File

@@ -152,7 +152,7 @@ intel_pmdemand_update_phys_mask(struct intel_display *display,
{
enum phy phy;
if (DISPLAY_VER(display) < 14)
if (!HAS_PMDEMAND(display))
return;
if (!encoder)
@@ -174,7 +174,7 @@ intel_pmdemand_update_port_clock(struct intel_display *display,
struct intel_pmdemand_state *pmdemand_state,
enum pipe pipe, int port_clock)
{
if (DISPLAY_VER(display) < 14)
if (!HAS_PMDEMAND(display))
return;
pmdemand_state->ddi_clocks[pipe] = port_clock;
@@ -324,7 +324,7 @@ int intel_pmdemand_atomic_check(struct intel_atomic_state *state)
const struct intel_dbuf_state *new_dbuf_state;
struct intel_pmdemand_state *new_pmdemand_state;
if (DISPLAY_VER(display) < 14)
if (!HAS_PMDEMAND(display))
return 0;
if (!intel_pmdemand_needs_update(state))
@@ -404,7 +404,7 @@ intel_pmdemand_init_pmdemand_params(struct intel_display *display,
{
u32 reg1, reg2;
if (DISPLAY_VER(display) < 14)
if (!HAS_PMDEMAND(display))
return;
mutex_lock(&display->pmdemand.lock);
@@ -637,7 +637,7 @@ void intel_pmdemand_pre_plane_update(struct intel_atomic_state *state)
const struct intel_pmdemand_state *old_pmdemand_state =
intel_atomic_get_old_pmdemand_state(state);
if (DISPLAY_VER(display) < 14)
if (!HAS_PMDEMAND(display))
return;
if (!new_pmdemand_state ||
@@ -660,7 +660,7 @@ void intel_pmdemand_post_plane_update(struct intel_atomic_state *state)
const struct intel_pmdemand_state *old_pmdemand_state =
intel_atomic_get_old_pmdemand_state(state);
if (DISPLAY_VER(display) < 14)
if (!HAS_PMDEMAND(display))
return;
if (!new_pmdemand_state ||

View File

@@ -2100,6 +2100,18 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp,
else if (display->platform.alderlake_p)
intel_de_rmw(display, CLKGATE_DIS_MISC, 0,
CLKGATE_DIS_MISC_DMASC_GATING_DIS);
/*
* Wa_14026643300
* On Xe3P, restrict DC3CO entry during active frame when PSR2 is
* enabled without panel Early Transport; required to avoid pipe bad state.
* DMC honours CHICKEN_DCPR_4 bit 24 to block DC3CO entry during active frame.
*/
if (intel_display_wa(display, INTEL_DISPLAY_WA_14026643300) &&
!intel_dp->psr.panel_replay_enabled &&
!intel_dp->psr.su_region_et_enabled)
intel_de_rmw(display, XE3P_CHICKEN_DCPR_4,
0, DCPR4_BLOCK_DC3CO_ACTIVE_FRAME);
}
/* Wa_16025596647 */
@@ -2341,6 +2353,10 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp)
else if (display->platform.alderlake_p)
intel_de_rmw(display, CLKGATE_DIS_MISC,
CLKGATE_DIS_MISC_DMASC_GATING_DIS, 0);
if (intel_display_wa(display, INTEL_DISPLAY_WA_14026643300))
intel_de_rmw(display, XE3P_CHICKEN_DCPR_4,
DCPR4_BLOCK_DC3CO_ACTIVE_FRAME, 0);
}
if (intel_dp_is_edp(intel_dp))

View File

@@ -241,13 +241,13 @@ int vlv_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
unsigned int num, den;
/*
* Note that crtc_state->pixel_rate accounts for both
* Note that crtc_state->pixel_rate_cdclk accounts for both
* horizontal and vertical panel fitter downscaling factors.
* Pre-HSW bspec tells us to only consider the horizontal
* downscaling factor here. We ignore that and just consider
* both for simplicity.
*/
pixel_rate = crtc_state->pixel_rate;
pixel_rate = crtc_state->pixel_rate_cdclk;
vlv_plane_ratio(crtc_state, plane_state, &num, &den);
@@ -550,13 +550,13 @@ int ivb_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
unsigned int num, den;
/*
* Note that crtc_state->pixel_rate accounts for both
* Note that crtc_state->pixel_rate_cdclk accounts for both
* horizontal and vertical panel fitter downscaling factors.
* Pre-HSW bspec tells us to only consider the horizontal
* downscaling factor here. We ignore that and just consider
* both for simplicity.
*/
pixel_rate = crtc_state->pixel_rate;
pixel_rate = crtc_state->pixel_rate_cdclk;
ivb_plane_ratio(crtc_state, plane_state, &num, &den);
@@ -570,13 +570,13 @@ static int ivb_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
unsigned int num, den;
/*
* Note that crtc_state->pixel_rate accounts for both
* Note that crtc_state->pixel_rate_cdclk accounts for both
* horizontal and vertical panel fitter downscaling factors.
* Pre-HSW bspec tells us to only consider the horizontal
* downscaling factor here. We ignore that and just consider
* both for simplicity.
*/
pixel_rate = crtc_state->pixel_rate;
pixel_rate = crtc_state->pixel_rate_cdclk;
src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
dst_w = drm_rect_width(&plane_state->uapi.dst);
@@ -629,7 +629,7 @@ static void hsw_plane_ratio(const struct intel_crtc_state *crtc_state,
int hsw_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
unsigned int pixel_rate = crtc_state->pixel_rate;
unsigned int pixel_rate = crtc_state->pixel_rate_cdclk;
unsigned int num, den;
hsw_plane_ratio(crtc_state, plane_state, &num, &den);
@@ -918,13 +918,13 @@ static int g4x_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
unsigned int limit, decimate;
/*
* Note that crtc_state->pixel_rate accounts for both
* Note that crtc_state->pixel_rate_cdclk accounts for both
* horizontal and vertical panel fitter downscaling factors.
* Pre-HSW bspec tells us to only consider the horizontal
* downscaling factor here. We ignore that and just consider
* both for simplicity.
*/
pixel_rate = crtc_state->pixel_rate;
pixel_rate = crtc_state->pixel_rate_cdclk;
/* Horizontal downscaling limits the maximum pixel rate */
hscale = drm_rect_calc_hscale(&plane_state->uapi.src,
@@ -1722,6 +1722,10 @@ intel_sprite_plane_create(struct intel_display *display,
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
if (display->platform.valleyview || display->platform.cherryview)
drm_plane_create_blend_mode_property(&plane->base,
BIT(DRM_MODE_BLEND_PREMULTI));
zpos = sprite + 1;
drm_plane_create_zpos_immutable_property(&plane->base, zpos);

View File

@@ -5,6 +5,7 @@
*/
#include <drm/drm_print.h>
#include <drm/intel/step.h>
#include "intel_alpm.h"
#include "intel_cmtg.h"
@@ -1106,6 +1107,11 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display);
}
if (display->platform.novalake &&
IS_DISPLAY_STEP(display, STEP_A0, STEP_C0))
crtc_state->hw.adjusted_mode.crtc_vtotal =
intel_vrr_vmin_vtotal(crtc_state);
if (HAS_AS_SDP(display)) {
trans_vrr_vsync =
intel_de_read(display,

View File

@@ -266,7 +266,7 @@ bool icl_is_hdr_plane(struct intel_display *display, enum plane_id plane_id)
static int icl_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state);
unsigned int pixel_rate = intel_plane_pixel_rate_cdclk(crtc_state, plane_state);
/* two pixels per clock */
return DIV_ROUND_UP(pixel_rate, 2);
@@ -290,7 +290,7 @@ glk_plane_ratio(const struct intel_plane_state *plane_state,
static int glk_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state);
unsigned int pixel_rate = intel_plane_pixel_rate_cdclk(crtc_state, plane_state);
unsigned int num, den;
glk_plane_ratio(plane_state, &num, &den);
@@ -317,7 +317,7 @@ skl_plane_ratio(const struct intel_plane_state *plane_state,
static int skl_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state);
unsigned int pixel_rate = intel_plane_pixel_rate_cdclk(crtc_state, plane_state);
unsigned int num, den;
skl_plane_ratio(plane_state, &num, &den);
@@ -1932,10 +1932,10 @@ static int intel_plane_min_height(struct intel_plane *plane,
return 1;
}
static int intel_plane_max_width(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation)
int intel_plane_max_width(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation)
{
if (plane->max_width)
return plane->max_width(fb, color_plane, rotation);
@@ -1943,10 +1943,10 @@ static int intel_plane_max_width(struct intel_plane *plane,
return INT_MAX;
}
static int intel_plane_max_height(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation)
int intel_plane_max_height(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation)
{
if (plane->max_height)
return plane->max_height(fb, color_plane, rotation);
@@ -2126,19 +2126,6 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
return 0;
}
/* Divide a U16.16 fixed-point value by 2, staying in fixed-point domain */
static inline u32 fp_16_16_div2(u32 fp)
{
return fp >> 1;
}
/* Convert a U16.16 fixed-point value to integer, rounding up */
static inline int fp_16_16_to_int_ceil(u32 fp)
{
return DIV_ROUND_UP(fp, 1 << 16);
}
static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(plane_state);
@@ -2154,14 +2141,20 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation);
/*
* LNL+ UV surface start/size =
* ceiling(half of Y plane start/size). Use ceiling division
* unconditionally; it is a no-op for even values.
* UV (chroma) start/size = ceiling(half of the *integer* Y plane
* start/size), i.e. the value the luma surface programs (src >> 16),
* not the raw U16.16. A bigjoiner seam mapped through the scaler can
* give a fractional luma src; ceiling that directly would round the
* chroma one column too far and read past the chroma surface.
*/
int x = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.x1));
int y = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.y1));
int w = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_width(&plane_state->uapi.src)));
int h = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_height(&plane_state->uapi.src)));
int luma_x = plane_state->uapi.src.x1 >> 16;
int luma_y = plane_state->uapi.src.y1 >> 16;
int luma_w = drm_rect_width(&plane_state->uapi.src) >> 16;
int luma_h = drm_rect_height(&plane_state->uapi.src) >> 16;
int x = DIV_ROUND_UP(luma_x, 2);
int y = DIV_ROUND_UP(luma_y, 2);
int w = DIV_ROUND_UP(luma_x + luma_w, 2) - x;
int h = DIV_ROUND_UP(luma_y + luma_h, 2) - y;
u32 offset;
/* FIXME not quite sure how/if these apply to the chroma plane */

View File

@@ -8,6 +8,7 @@
#include <linux/types.h>
struct drm_framebuffer;
struct intel_crtc;
struct intel_display;
struct intel_initial_plane_config;
@@ -42,5 +43,13 @@ bool icl_is_hdr_plane(struct intel_display *display, enum plane_id plane_id);
u32 skl_plane_aux_dist(const struct intel_plane_state *plane_state,
int color_plane);
int intel_plane_max_width(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation);
int intel_plane_max_height(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation);
#endif

View File

@@ -275,6 +275,9 @@ void intel_sagv_pre_plane_update(struct intel_atomic_state *state)
if (!intel_has_sagv(display))
return;
if (HAS_PMDEMAND(display))
return;
if (DISPLAY_VER(display) >= 11)
icl_sagv_pre_plane_update(state);
else
@@ -295,6 +298,9 @@ void intel_sagv_post_plane_update(struct intel_atomic_state *state)
if (!intel_has_sagv(display))
return;
if (HAS_PMDEMAND(display))
return;
if (DISPLAY_VER(display) >= 11)
icl_sagv_post_plane_update(state);
else
@@ -3856,7 +3862,7 @@ void skl_wm_plane_disable_noatomic(struct intel_crtc *crtc,
return;
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb_y[plane->id], 0, 0);
crtc_state->wm.skl.plane_min_ddb[plane->id] = 0;
crtc_state->wm.skl.plane_interim_ddb[plane->id] = 0;

View File

@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
subdir-ccflags-y += -I$(srctree)/drivers/gpu/drm/i915/display/
obj-$(CONFIG_DRM_I915_KUNIT_TEST) += i915_display_test.o
i915_display_test-y = \
intel_dp_link_test.o

File diff suppressed because it is too large Load Diff

View File

@@ -638,7 +638,7 @@ static int i915_driver_register(struct drm_i915_private *dev_priv)
intel_display_driver_register(display);
intel_display_power_enable(display);
intel_display_driver_runtime_pm_enable(display);
intel_runtime_pm_enable(&dev_priv->runtime_pm);
if (i915_switcheroo_register(dev_priv))
@@ -660,7 +660,7 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
i915_switcheroo_unregister(dev_priv);
intel_runtime_pm_disable(&dev_priv->runtime_pm);
intel_display_power_disable(display);
intel_display_driver_runtime_pm_disable(display);
intel_display_driver_unregister(display);
@@ -1511,24 +1511,32 @@ static int i915_pm_runtime_suspend(struct device *kdev)
for_each_gt(gt, dev_priv, i)
intel_gt_runtime_suspend(gt);
intel_display_driver_pm_runtime_suspend(display);
intel_irq_suspend(dev_priv);
for_each_gt(gt, dev_priv, i)
intel_uncore_suspend(gt->uncore);
intel_display_power_runtime_suspend(display);
intel_display_driver_pm_runtime_suspend_late(display);
ret = vlv_suspend_complete(dev_priv);
if (ret) {
drm_err(&dev_priv->drm,
"Runtime suspend failed, disabling it (%d)\n", ret);
intel_uncore_runtime_resume(&dev_priv->uncore);
intel_display_driver_pm_runtime_resume_early(display);
for_each_gt(gt, dev_priv, i)
intel_uncore_runtime_resume(gt->uncore);
intel_irq_resume(dev_priv);
for_each_gt(gt, dev_priv, i)
intel_gt_runtime_resume(gt);
intel_display_driver_pm_runtime_resume(display);
enable_rpm_wakeref_asserts(rpm);
return ret;
@@ -1550,34 +1558,8 @@ static int i915_pm_runtime_suspend(struct device *kdev)
if (root_pdev)
pci_d3cold_disable(root_pdev);
/*
* FIXME: We really should find a document that references the arguments
* used below!
*/
if (IS_BROADWELL(dev_priv)) {
/*
* On Broadwell, if we use PCI_D1 the PCH DDI ports will stop
* being detected, and the call we do at i915_pm_runtime_resume()
* won't be able to restore them. Since PCI_D3hot matches the
* actual specification and appears to be working, use it.
*/
intel_opregion_notify_adapter(display, PCI_D3hot);
} else {
/*
* current versions of firmware which depend on this opregion
* notification have repurposed the D1 definition to mean
* "runtime suspended" vs. what you would normally expect (D3)
* to distinguish it from notifications that might be sent via
* the suspend path.
*/
intel_opregion_notify_adapter(display, PCI_D1);
}
assert_forcewakes_inactive(&dev_priv->uncore);
if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
intel_hpd_poll_enable(display);
drm_dbg(&dev_priv->drm, "Device suspended\n");
return 0;
}
@@ -1600,8 +1582,6 @@ static int i915_pm_runtime_resume(struct device *kdev)
drm_WARN_ON_ONCE(&dev_priv->drm, atomic_read(&rpm->wakeref_count));
disable_rpm_wakeref_asserts(rpm);
intel_opregion_notify_adapter(display, PCI_D0);
root_pdev = pcie_find_root_port(pdev);
if (root_pdev)
pci_d3cold_enable(root_pdev);
@@ -1610,7 +1590,7 @@ static int i915_pm_runtime_resume(struct device *kdev)
drm_dbg(&dev_priv->drm,
"Unclaimed access during suspend, bios?\n");
intel_display_power_runtime_resume(display);
intel_display_driver_pm_runtime_resume_early(display);
ret = vlv_resume_prepare(dev_priv, true);
@@ -1628,17 +1608,7 @@ static int i915_pm_runtime_resume(struct device *kdev)
intel_pxp_runtime_resume(dev_priv->pxp);
/*
* On VLV/CHV display interrupts are part of the display
* power well, so hpd is reinitialized from there. For
* everyone else do it here.
*/
if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) {
intel_hpd_init(display);
intel_hpd_poll_disable(display);
}
skl_watermark_ipc_update(display);
intel_display_driver_pm_runtime_resume(display);
enable_rpm_wakeref_asserts(rpm);

View File

@@ -58,23 +58,23 @@ initial_plane_phys(struct drm_i915_private *i915,
if (intel_memory_type_is_local(mem->type) != is_local) {
drm_err(&i915->drm, "Initial plane FB PTE unsuitable for %s\n",
mem->region.name);
mem->name);
return false;
}
if (dma_addr < mem->region.start || dma_addr > mem->region.end) {
drm_err(&i915->drm,
"Initial plane programming using invalid range, dma_addr=%pa (%s [%pa-%pa])\n",
&dma_addr, mem->region.name, &mem->region.start, &mem->region.end);
&dma_addr, mem->name, &mem->region.start, &mem->region.end);
return false;
}
drm_dbg(&i915->drm, "Using dma_addr=%pa, based on initial plane programming\n",
&dma_addr);
*out_phys_base = dma_addr - mem->region.start;
*out_mem = mem;
drm_dbg_kms(&i915->drm, "Initial plane dma_addr=%pa phys_base=%pa mem=%s\n",
&dma_addr, out_phys_base, mem->name);
return true;
}
@@ -120,7 +120,7 @@ initial_plane_vma(struct drm_i915_private *i915,
I915_BO_PREALLOC);
if (IS_ERR(obj)) {
drm_dbg_kms(&i915->drm, "Failed to preallocate initial FB in %s\n",
mem->region.name);
mem->name);
return NULL;
}

View File

@@ -2,3 +2,4 @@
*.hdrtest
/generated
/xe_gen_wa_oob
!.kunitconfig-display

View File

@@ -0,0 +1,11 @@
CONFIG_EXPERT=y
CONFIG_MODULES=y
CONFIG_KUNIT=y
CONFIG_PCI=y
CONFIG_DEBUG_FS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DRM=m
CONFIG_DRM_XE=m
CONFIG_DRM_XE_DISPLAY=y
CONFIG_DRM_XE_KUNIT_TEST=m

View File

@@ -374,6 +374,9 @@ xe-$(CONFIG_DRM_XE_DP_TUNNEL) += \
obj-$(CONFIG_DRM_XE) += xe.o
obj-$(CONFIG_DRM_XE_KUNIT_TEST) += tests/
ifeq ($(CONFIG_DRM_XE_DISPLAY),y)
obj-$(CONFIG_DRM_XE_KUNIT_TEST) += display/tests/
endif
# header test
hdrtest_find_args := -not -path xe_rtp_helpers.h

View File

@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
subdir-ccflags-$(CONFIG_DRM_XE_DISPLAY) += \
-I$(srctree)/drivers/gpu/drm/i915/display/
# Rule to build display code shared with i915
$(obj)/i915-display/tests/%.o: $(srctree)/drivers/gpu/drm/i915/display/tests/%.c FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
obj-$(CONFIG_DRM_XE_KUNIT_TEST) += xe_display_test.o
xe_display_test-y = \
i915-display/tests/intel_dp_link_test.o

View File

@@ -158,7 +158,7 @@ void xe_display_register(struct xe_device *xe)
return;
intel_display_driver_register(display);
intel_display_power_enable(display);
intel_display_driver_runtime_pm_enable(display);
}
void xe_display_unregister(struct xe_device *xe)
@@ -168,7 +168,7 @@ void xe_display_unregister(struct xe_device *xe)
if (!xe->info.probe_display)
return;
intel_display_power_disable(display);
intel_display_driver_runtime_pm_disable(display);
intel_display_driver_unregister(display);
}
@@ -313,7 +313,7 @@ static void xe_display_enable_d3cold(struct xe_device *xe)
* We do a lot of poking in a lot of registers, make sure they work
* properly.
*/
intel_display_power_disable(display);
intel_display_driver_runtime_pm_disable(display);
intel_display_flush_cleanup_work(display);
@@ -346,9 +346,10 @@ static void xe_display_disable_d3cold(struct xe_device *xe)
intel_opregion_resume(display);
intel_display_power_enable(display);
intel_display_driver_runtime_pm_enable(display);
}
/* before irq suspend */
void xe_display_pm_runtime_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -361,9 +362,10 @@ void xe_display_pm_runtime_suspend(struct xe_device *xe)
return;
}
intel_hpd_poll_enable(display);
intel_display_driver_pm_runtime_suspend(display);
}
/* after irq suspend */
void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -371,17 +373,31 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
if (!xe->info.probe_display)
return;
if (xe->d3cold.allowed)
if (xe->d3cold.allowed) {
xe_display_pm_suspend_late(xe);
/* Ensure the wakelock release work gets flushed */
intel_dmc_wl_flush_release_work(display);
return;
}
/*
* If xe_display_pm_suspend_late() is not called, it is likely
* that we will be on dynamic DC states with DMC wakelock enabled. We
* need to flush the release work in that case.
*/
intel_dmc_wl_flush_release_work(display);
intel_display_driver_pm_runtime_suspend_late(display);
}
/* before irq resume */
void xe_display_pm_runtime_resume_early(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
if (xe->d3cold.allowed)
return;
intel_display_driver_pm_runtime_resume_early(display);
}
/* after irq resume */
void xe_display_pm_runtime_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -394,9 +410,7 @@ void xe_display_pm_runtime_resume(struct xe_device *xe)
return;
}
intel_hpd_init(display);
intel_hpd_poll_disable(display);
skl_watermark_ipc_update(display);
intel_display_driver_pm_runtime_resume(display);
}

View File

@@ -43,6 +43,7 @@ void xe_display_pm_resume_early(struct xe_device *xe);
void xe_display_pm_resume(struct xe_device *xe);
void xe_display_pm_runtime_suspend(struct xe_device *xe);
void xe_display_pm_runtime_suspend_late(struct xe_device *xe);
void xe_display_pm_runtime_resume_early(struct xe_device *xe);
void xe_display_pm_runtime_resume(struct xe_device *xe);
#define XE_DISPLAY_DRIVER_FEATURES (DRIVER_MODESET | DRIVER_ATOMIC)
@@ -80,6 +81,7 @@ static inline void xe_display_pm_resume_early(struct xe_device *xe) {}
static inline void xe_display_pm_resume(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_suspend_late(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_resume_early(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_resume(struct xe_device *xe) {}
#endif /* CONFIG_DRM_XE_DISPLAY */

View File

@@ -147,33 +147,12 @@ static struct drm_gem_object *xe_display_bo_fbdev_create(struct drm_device *drm,
struct xe_device *xe = to_xe_device(drm);
struct xe_bo *obj;
obj = ERR_PTR(-ENODEV);
if (xe_display_bo_fbdev_prefer_stolen(xe, size)) {
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe),
size,
ttm_bo_type_kernel,
XE_BO_FLAG_FORCE_WC |
XE_BO_FLAG_STOLEN |
XE_BO_FLAG_GGTT,
false);
if (!IS_ERR(obj))
drm_info(&xe->drm, "Allocated fbdev into stolen\n");
else
drm_info(&xe->drm, "Allocated fbdev into stolen failed: %li\n", PTR_ERR(obj));
} else {
drm_info(&xe->drm, "Allocating fbdev: Stolen memory not preferred.\n");
}
if (IS_ERR(obj)) {
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size,
ttm_bo_type_kernel,
XE_BO_FLAG_FORCE_WC |
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_GGTT,
false);
}
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size,
ttm_bo_type_kernel,
XE_BO_FLAG_FORCE_WC |
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_GGTT,
false);
if (IS_ERR(obj)) {
drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj);
return ERR_PTR(-ENOMEM);

View File

@@ -164,31 +164,14 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj,
dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8,
XE_PAGE_SIZE);
if (IS_DGFX(xe))
dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
dpt_size, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_VRAM0 |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_PAGETABLE,
pin_params->alignment, false);
else
dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
dpt_size, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_STOLEN |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_PAGETABLE,
pin_params->alignment, false);
if (IS_ERR(dpt))
dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
dpt_size, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_PAGETABLE |
XE_BO_FLAG_FORCE_WC,
pin_params->alignment, false);
dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
dpt_size, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_VRAM_IF_DGFX(tile0) |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_PAGETABLE |
XE_BO_FLAG_FORCE_WC,
pin_params->alignment, false);
if (IS_ERR(dpt))
return PTR_ERR(dpt);

View File

@@ -19,8 +19,24 @@
#include "xe_fb_pin.h"
#include "xe_ggtt.h"
#include "xe_mmio.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_vram_types.h"
static bool is_pte_local(u64 pte)
{
return pte & XE_GGTT_PTE_DM;
}
static bool has_lmembar(struct xe_device *xe)
{
return GRAPHICS_VERx100(xe) >= 1270;
}
static bool need_pte_local(struct xe_device *xe)
{
return IS_DGFX(xe) || has_lmembar(xe);
}
static struct xe_bo *
initial_plane_bo(struct xe_device *xe,
struct intel_initial_plane_config *plane_config)
@@ -37,16 +53,20 @@ initial_plane_bo(struct xe_device *xe,
flags = XE_BO_FLAG_FORCE_WC | XE_BO_FLAG_GGTT;
base = round_down(plane_config->base, page_size);
size = round_up(plane_config->base + plane_config->size,
page_size);
size -= base;
if (IS_DGFX(xe)) {
u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base);
if (!(pte & XE_GGTT_PTE_DM)) {
drm_err(&xe->drm,
"Initial plane programming missing DM bit\n");
if (is_pte_local(pte) != need_pte_local(xe)) {
drm_err(&xe->drm, "Initial plane PTE has bad local memory bit\n");
return NULL;
}
phys_base = pte & ~(page_size - 1);
flags |= XE_BO_FLAG_VRAM0;
/*
@@ -60,14 +80,26 @@ initial_plane_bo(struct xe_device *xe,
return NULL;
}
drm_dbg(&xe->drm,
"Using phys_base=%pa, based on initial plane programming\n",
&phys_base);
drm_dbg_kms(&xe->drm,
"Using phys_base=%pa, based on initial plane programming\n",
&phys_base);
} else {
struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
struct ttm_resource_manager *stolen;
u64 pte;
if (!stolen)
stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
if (!stolen) {
drm_dbg_kms(&xe->drm, "No stolen for initial FB\n");
return NULL;
}
pte = xe_ggtt_read_pte(tile0->mem.ggtt, base);
if (is_pte_local(pte) != need_pte_local(xe)) {
drm_err(&xe->drm, "Initial plane PTE has bad local memory bit\n");
return NULL;
}
phys_base = base;
flags |= XE_BO_FLAG_STOLEN;
@@ -79,16 +111,12 @@ initial_plane_bo(struct xe_device *xe,
}
}
size = round_up(plane_config->base + plane_config->size,
page_size);
size -= base;
bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base,
ttm_bo_type_kernel, flags, 0, false);
if (IS_ERR(bo)) {
drm_dbg(&xe->drm,
"Failed to create bo phys_base=%pa size %u with flags %x: %li\n",
&phys_base, size, flags, PTR_ERR(bo));
drm_dbg_kms(&xe->drm,
"Failed to create bo phys_base=%pa size %u with flags %x: %li\n",
&phys_base, size, flags, PTR_ERR(bo));
return NULL;
}

View File

@@ -700,6 +700,8 @@ int xe_pm_runtime_resume(struct xe_device *xe)
if (xe->d3cold.allowed)
xe_sysctrl_pm_resume(xe);
xe_display_pm_runtime_resume_early(xe);
xe_irq_resume(xe);
for_each_gt(gt, xe, id) {

View File

@@ -61,7 +61,7 @@ static u32 get_wopcm_size(struct xe_device *xe)
return wopcm_size;
}
static u64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
static u64 detect_lmembar_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
{
struct xe_vram_region *tile_vram = xe_device_get_root_tile(xe)->mem.vram;
resource_size_t tile_io_start = xe_vram_region_io_start(tile_vram);
@@ -102,7 +102,7 @@ static u64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
return ALIGN_DOWN(stolen_size, SZ_1M);
}
static u32 detect_bar2_integrated(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
static u32 detect_lmembar_integrated(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
struct xe_gt *media_gt = xe_device_get_root_tile(xe)->media_gt;
@@ -212,9 +212,9 @@ int xe_ttm_stolen_mgr_init(struct xe_device *xe)
if (IS_SRIOV_VF(xe))
stolen_size = 0;
else if (IS_DGFX(xe))
stolen_size = detect_bar2_dgfx(xe, mgr);
stolen_size = detect_lmembar_dgfx(xe, mgr);
else if (GRAPHICS_VERx100(xe) >= 1270)
stolen_size = detect_bar2_integrated(xe, mgr);
stolen_size = detect_lmembar_integrated(xe, mgr);
else
stolen_size = detect_stolen(xe, mgr);
@@ -262,9 +262,9 @@ u64 xe_ttm_stolen_io_offset(struct xe_bo *bo, u32 offset)
return mgr->io_base + (bo->ttm.resource->start << PAGE_SHIFT) + offset;
}
static int __xe_ttm_stolen_io_mem_reserve_bar2(struct xe_device *xe,
struct xe_ttm_stolen_mgr *mgr,
struct ttm_resource *mem)
static int __xe_ttm_stolen_io_mem_reserve_lmembar(struct xe_device *xe,
struct xe_ttm_stolen_mgr *mgr,
struct ttm_resource *mem)
{
if (!mgr->io_base)
return -EIO;
@@ -321,7 +321,7 @@ int xe_ttm_stolen_io_mem_reserve(struct xe_device *xe, struct ttm_resource *mem)
if (xe_ttm_stolen_cpu_access_needs_ggtt(xe))
return __xe_ttm_stolen_io_mem_reserve_stolen(xe, mgr, mem);
else
return __xe_ttm_stolen_io_mem_reserve_bar2(xe, mgr, mem);
return __xe_ttm_stolen_io_mem_reserve_lmembar(xe, mgr, mem);
}
u64 xe_ttm_stolen_gpu_offset(struct xe_device *xe)

View File

@@ -1506,6 +1506,18 @@
#define DP_TUNNELING_MAX_LANE_COUNT 0xe0029
#define DP_TUNNELING_MAX_LANE_COUNT_MASK 0x1f
#define DP_TUNNELING_MAIN_LINK_CHANNEL_CODING 0xe002b
#define DP_128B132B_DP_SUPPORTED (1 << 0)
#define DP_TUNNELING_128B132B_LINK_RATE 0xe002c
#define DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT (1 << 2)
#define DP_TUNNELING_20GBPS_PER_LANE_SUPPORT (1 << 1)
#define DP_TUNNELING_10GBPS_PER_LANE_SUPPORT (1 << 0)
#define DP_TUNNELING_128B132B_LINK_RATE_MASK (DP_TUNNELING_10GBPS_PER_LANE_SUPPORT | \
DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT | \
DP_TUNNELING_20GBPS_PER_LANE_SUPPORT)
#define DP_TUNNELING_128B132B_LL_LANE0_MAPPING_SUPPORT (1 << 7)
#define DP_DPTX_BW_ALLOCATION_MODE_CONTROL 0xe0030
#define DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE (1 << 7)
#define DP_UNMASK_BW_ALLOCATION_IRQ (1 << 6)

View File

@@ -63,6 +63,9 @@ void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel);
int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr,
struct drm_dp_aux *aux);
bool drm_dp_tunnel_128b132b_supported(const struct drm_dp_tunnel *tunnel);
bool drm_dp_tunnel_128b132b_lane0_mapping_supported(const struct drm_dp_tunnel *tunnel);
u8 drm_dp_tunnel_128b132b_dprx_rates(const struct drm_dp_tunnel *tunnel);
int drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel);
int drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel);
int drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel);
@@ -173,6 +176,24 @@ drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr,
return -EOPNOTSUPP;
}
static inline bool
drm_dp_tunnel_128b132b_supported(const struct drm_dp_tunnel *tunnel)
{
return false;
}
static inline bool
drm_dp_tunnel_128b132b_lane0_mapping_supported(const struct drm_dp_tunnel *tunnel)
{
return false;
}
static inline u8
drm_dp_tunnel_128b132b_dprx_rates(const struct drm_dp_tunnel *tunnel)
{
return 0;
}
static inline int
drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel)
{