mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 13:23:02 -04:00
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:
11
Documentation/gpu/intel-display/dp-link-capabilities.rst
Normal file
11
Documentation/gpu/intel-display/dp-link-capabilities.rst
Normal 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:
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
9
drivers/gpu/drm/i915/.kunitconfig
Normal file
9
drivers/gpu/drm/i915/.kunitconfig
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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, ¤t_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 */
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
7
drivers/gpu/drm/i915/display/tests/Makefile
Normal file
7
drivers/gpu/drm/i915/display/tests/Makefile
Normal 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
|
||||
1408
drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c
Normal file
1408
drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
1
drivers/gpu/drm/xe/.gitignore
vendored
1
drivers/gpu/drm/xe/.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
*.hdrtest
|
||||
/generated
|
||||
/xe_gen_wa_oob
|
||||
!.kunitconfig-display
|
||||
|
||||
11
drivers/gpu/drm/xe/.kunitconfig-display
Normal file
11
drivers/gpu/drm/xe/.kunitconfig-display
Normal 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
|
||||
@@ -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
|
||||
|
||||
13
drivers/gpu/drm/xe/display/tests/Makefile
Normal file
13
drivers/gpu/drm/xe/display/tests/Makefile
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user