Merge tag 'drm-intel-next-2025-11-04' of https://gitlab.freedesktop.org/drm/i915/kernel into drm-next

drm/i915 feature pull for v6.19:

Features and functionality:
- Enable LNL+ content adaptive sharpness filter (CASF) (Nemesa)
- Use optimized VRR guardband (Ankit, Ville)
- Enable Xe3p LT PHY (Suraj)
- Enable FBC support for Xe3p_LPD display (Sai Teja, Vinod)
- Specify DMC firmware for display version 30.02 (Dnyaneshwar)
- Report reason for disabling PSR to debugfs (Michał)
- Extend i915_display_info with Type-C port details (Khaled)
- Log DSI send packet sequence errors and contents

Refactoring and cleanups:
- Refactoring to prepare for VRR guardband optimization (Ankit)
- Abstract VRR live status wait (Ankit)
- Refactor VRR and DSB timing to handle Set Context Latency explicitly (Ankit)
- Helpers for prefill latency calculations (Ville)
- Refactor SKL+ watermark latency setup (Ville)
- VRR refactoring and cleanups (Ville)
- SKL+ universal plane cleanups (Ville)
- Decouple CDCLK from state->modeset refactor (Ville)
- Refactor VLV/CHV clock functions (Jani)
- Refactor fbdev handling (Jani)
- Call i915 and xe runtime PM from display via function pointers (Jouni)
- IRQ code refactoring  (Jani)
- Drop display dependency on i915 feature check macros (Jani)
- Refactor and unify i915 and xe stolen memory interfaces towards display (Jani)
- Switch to driver agnostic drm to display pointer chase (Jani)
- Use display version over graphics version in display code (Matt A)
- GVT cleanups (Jonathan, Andi)
- Rename a VLV clock function to unify (Michał)
- Explicitly sanitize DMC package header num entries (Luca)
- Remove redundant port clock check from ALPM (Jouni)
- Use sysfs_emit() instead of sprintf() in PMU sysfs (Madhur Kumar)
- Clean up C20 PHY PLL register macros (Imre, Mika))
- Abstract "address in MMIO table" helper for general use (Matt A)
- Improve VRR platform abstractions (Ville)
- Move towards more standard PCI PM code usage (Ville)
- Framebuffer refactoring (Ville)
- Drop display dependency on i915_utils.h (Jani)
- Include cleanups (Jani)

Fixes:
- Workaround docking station DSC issues with high pixel clock and bpp (Imre)
- Fix Panel Replay in DSC mode (Imre)
- Disable tracepoints for PREEMPT_RT as a workaround (Maarten)
- Fix intel_crtc_get_vblank_counter() on PREEMPT_RT (Maarten)
- Fix C10 PHY identification on PTL/WCL (Dnyaneshwar)
- Take AS SDP into account with optimized guardband (Jouni)
- Fix panic structure allocation memory leak (Jani)
- Adjust an FBC workaround platforms (Vinod)
- Add fallback for CDCLK selection (Naladala)
- Avoid using invalid transcoder in MST transport select (Suraj)
- Don't use cursor size reduction on display version 14+ (Nemesa)
- Fix C20 PHY PLL register programming (Imre, Mika)
- Fix PSR frontbuffer flush handling (Jouni)
- Store ALPM parameters in crtc state (Jouni)
- Defeature DRRS on LNL+ (Ville)
- Fix the scope of the large DRAM DIMM workaround (Ville)
- Fix PICA vs. AUX power ordering issue (Gustavo)
- Fix pixel rate for computing watermark line time (Ville)
- Fix framebuffer set_tiling vs. addfb race (Ville)
- DMC event handler fixes (Ville)

DRM Core:
- CRTC sharpness strength property (Nemesa)
- DPCD DSC quirk for Synaptics Panamera devices (Imre)
- Helpers to query the branch DSC max throughput/line-width (Imre)

Merges:
- Backmerge drm-next for v6.18-rc and to sync with drm-xe-next (Jani)

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Jani Nikula <jani.nikula@intel.com>
Link: https://patch.msgid.link/ec5a05f2df6d597a62033ee2d57225cce707b320@intel.com
pull/1354/merge
Dave Airlie 2025-11-07 09:45:38 +10:00
commit 8f037e11d0
182 changed files with 7571 additions and 2272 deletions

View File

@ -2552,6 +2552,10 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
{ OUI(0x00, 0x0C, 0xE7), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC) }, { OUI(0x00, 0x0C, 0xE7), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC) },
/* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */ /* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */
{ OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) }, { OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) },
/* Synaptics Panamera supports only a compressed bpp of 12 above 50% of its max DSC pixel throughput */
{ OUI(0x90, 0xCC, 0x24), DEVICE_ID('S', 'Y', 'N', 'A', 0x53, 0x22), true, BIT(DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) },
{ OUI(0x90, 0xCC, 0x24), DEVICE_ID('S', 'Y', 'N', 'A', 0x53, 0x31), true, BIT(DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) },
{ OUI(0x90, 0xCC, 0x24), DEVICE_ID('S', 'Y', 'N', 'A', 0x53, 0x33), true, BIT(DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) },
}; };
#undef OUI #undef OUI
@ -2841,6 +2845,158 @@ int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_S
} }
EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs); EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
/*
* See DP Standard v2.1a 2.8.4 Minimum Slices/Display, Table 2-159 and
* Appendix L.1 Derivation of Slice Count Requirements.
*/
static int dsc_sink_min_slice_throughput(int peak_pixel_rate)
{
if (peak_pixel_rate >= 4800000)
return 600000;
else if (peak_pixel_rate >= 2700000)
return 400000;
else
return 340000;
}
/**
* drm_dp_dsc_sink_max_slice_throughput() - Get a DSC sink's maximum pixel throughput per slice
* @dsc_dpcd: DSC sink's capabilities from DPCD
* @peak_pixel_rate: Cumulative peak pixel rate in kHz
* @is_rgb_yuv444: The mode is either RGB or YUV444
*
* Return the DSC sink device's maximum pixel throughput per slice, based on
* the device's @dsc_dpcd capabilities, the @peak_pixel_rate of the transferred
* stream(s) and whether the output format @is_rgb_yuv444 or yuv422/yuv420.
*
* Note that @peak_pixel_rate is the total pixel rate transferred to the same
* DSC/display sink. For instance to calculate a tile's slice count of an MST
* multi-tiled display sink (not considering here the required
* rounding/alignment of slice count)::
*
* @peak_pixel_rate = tile_pixel_rate * tile_count
* total_slice_count = @peak_pixel_rate / drm_dp_dsc_sink_max_slice_throughput(@peak_pixel_rate)
* tile_slice_count = total_slice_count / tile_count
*
* Returns:
* The maximum pixel throughput per slice supported by the DSC sink device
* in kPixels/sec.
*/
int drm_dp_dsc_sink_max_slice_throughput(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
int peak_pixel_rate, bool is_rgb_yuv444)
{
int throughput;
int delta = 0;
int base;
throughput = dsc_dpcd[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT];
if (is_rgb_yuv444) {
throughput = (throughput & DP_DSC_THROUGHPUT_MODE_0_MASK) >>
DP_DSC_THROUGHPUT_MODE_0_SHIFT;
delta = ((dsc_dpcd[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT]) &
DP_DSC_THROUGHPUT_MODE_0_DELTA_MASK) >>
DP_DSC_THROUGHPUT_MODE_0_DELTA_SHIFT; /* in units of 2 MPixels/sec */
delta *= 2000;
} else {
throughput = (throughput & DP_DSC_THROUGHPUT_MODE_1_MASK) >>
DP_DSC_THROUGHPUT_MODE_1_SHIFT;
}
switch (throughput) {
case 0:
return dsc_sink_min_slice_throughput(peak_pixel_rate);
case 1:
base = 340000;
break;
case 2 ... 14:
base = 400000 + 50000 * (throughput - 2);
break;
case 15:
base = 170000;
break;
}
return base + delta;
}
EXPORT_SYMBOL(drm_dp_dsc_sink_max_slice_throughput);
static u8 dsc_branch_dpcd_cap(const u8 dpcd[DP_DSC_BRANCH_CAP_SIZE], int reg)
{
return dpcd[reg - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0];
}
/**
* drm_dp_dsc_branch_max_overall_throughput() - Branch device's max overall DSC pixel throughput
* @dsc_branch_dpcd: DSC branch capabilities from DPCD
* @is_rgb_yuv444: The mode is either RGB or YUV444
*
* Return the branch device's maximum overall DSC pixel throughput, based on
* the device's DPCD DSC branch capabilities, and whether the output
* format @is_rgb_yuv444 or yuv422/yuv420.
*
* Returns:
* - 0: The maximum overall throughput capability is not indicated by
* the device separately and it must be determined from the per-slice
* max throughput (see @drm_dp_dsc_branch_slice_max_throughput())
* and the maximum slice count supported by the device.
* - > 0: The maximum overall DSC pixel throughput supported by the branch
* device in kPixels/sec.
*/
int drm_dp_dsc_branch_max_overall_throughput(const u8 dsc_branch_dpcd[DP_DSC_BRANCH_CAP_SIZE],
bool is_rgb_yuv444)
{
int throughput;
if (is_rgb_yuv444)
throughput = dsc_branch_dpcd_cap(dsc_branch_dpcd,
DP_DSC_BRANCH_OVERALL_THROUGHPUT_0);
else
throughput = dsc_branch_dpcd_cap(dsc_branch_dpcd,
DP_DSC_BRANCH_OVERALL_THROUGHPUT_1);
switch (throughput) {
case 0:
return 0;
case 1:
return 680000;
default:
return 600000 + 50000 * throughput;
}
}
EXPORT_SYMBOL(drm_dp_dsc_branch_max_overall_throughput);
/**
* drm_dp_dsc_branch_max_line_width() - Branch device's max DSC line width
* @dsc_branch_dpcd: DSC branch capabilities from DPCD
*
* Return the branch device's maximum overall DSC line width, based on
* the device's @dsc_branch_dpcd capabilities.
*
* Returns:
* - 0: The maximum line width is not indicated by the device
* separately and it must be determined from the maximum
* slice count and slice-width supported by the device.
* - %-EINVAL: The device indicates an invalid maximum line width
* (< 5120 pixels).
* - >= 5120: The maximum line width in pixels.
*/
int drm_dp_dsc_branch_max_line_width(const u8 dsc_branch_dpcd[DP_DSC_BRANCH_CAP_SIZE])
{
int line_width = dsc_branch_dpcd_cap(dsc_branch_dpcd, DP_DSC_BRANCH_MAX_LINE_WIDTH);
switch (line_width) {
case 0:
return 0;
case 1 ... 15:
return -EINVAL;
default:
return line_width * 320;
}
}
EXPORT_SYMBOL(drm_dp_dsc_branch_max_line_width);
static int drm_dp_read_lttpr_regs(struct drm_dp_aux *aux, static int drm_dp_read_lttpr_regs(struct drm_dp_aux *aux,
const u8 dpcd[DP_RECEIVER_CAP_SIZE], int address, const u8 dpcd[DP_RECEIVER_CAP_SIZE], int address,
u8 *buf, int buf_size) u8 *buf, int buf_size)

View File

@ -419,6 +419,8 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
set_out_fence_for_crtc(state->state, crtc, fence_ptr); set_out_fence_for_crtc(state->state, crtc, fence_ptr);
} else if (property == crtc->scaling_filter_property) { } else if (property == crtc->scaling_filter_property) {
state->scaling_filter = val; state->scaling_filter = val;
} else if (property == crtc->sharpness_strength_property) {
state->sharpness_strength = val;
} else if (crtc->funcs->atomic_set_property) { } else if (crtc->funcs->atomic_set_property) {
return crtc->funcs->atomic_set_property(crtc, state, property, val); return crtc->funcs->atomic_set_property(crtc, state, property, val);
} else { } else {
@ -456,6 +458,8 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc,
*val = 0; *val = 0;
else if (property == crtc->scaling_filter_property) else if (property == crtc->scaling_filter_property)
*val = state->scaling_filter; *val = state->scaling_filter;
else if (property == crtc->sharpness_strength_property)
*val = state->sharpness_strength;
else if (crtc->funcs->atomic_get_property) else if (crtc->funcs->atomic_get_property)
return crtc->funcs->atomic_get_property(crtc, state, property, val); return crtc->funcs->atomic_get_property(crtc, state, property, val);
else { else {

View File

@ -229,6 +229,25 @@ struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc)
* Driver's default scaling filter * Driver's default scaling filter
* Nearest Neighbor: * Nearest Neighbor:
* Nearest Neighbor scaling filter * Nearest Neighbor scaling filter
* SHARPNESS_STRENGTH:
* Atomic property for setting the sharpness strength/intensity by userspace.
*
* The value of this property is set as an integer value ranging
* from 0 - 255 where:
*
* 0: Sharpness feature is disabled(default value).
*
* 1: Minimum sharpness.
*
* 255: Maximum sharpness.
*
* User can gradually increase or decrease the sharpness level and can
* set the optimum value depending on content.
* This value will be passed to kernel through the UAPI.
* The setting of this property does not require modeset.
* The sharpness effect takes place post blending on the final composed output.
* If the feature is disabled, the content remains same without any sharpening effect
* and when this feature is applied, it enhances the clarity of the content.
*/ */
__printf(6, 0) __printf(6, 0)
@ -940,6 +959,22 @@ int drm_crtc_create_scaling_filter_property(struct drm_crtc *crtc,
} }
EXPORT_SYMBOL(drm_crtc_create_scaling_filter_property); EXPORT_SYMBOL(drm_crtc_create_scaling_filter_property);
int drm_crtc_create_sharpness_strength_property(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_property *prop =
drm_property_create_range(dev, 0, "SHARPNESS_STRENGTH", 0, 255);
if (!prop)
return -ENOMEM;
crtc->sharpness_strength_property = prop;
drm_object_attach_property(&crtc->base, prop, 0);
return 0;
}
EXPORT_SYMBOL(drm_crtc_create_sharpness_strength_property);
/** /**
* drm_crtc_in_clone_mode - check if the given CRTC state is in clone mode * drm_crtc_in_clone_mode - check if the given CRTC state is in clone mode
* *

View File

@ -13,6 +13,11 @@ subdir-ccflags-$(CONFIG_DRM_I915_WERROR) += -Werror
# drivers. Define I915 when building i915. # drivers. Define I915 when building i915.
subdir-ccflags-y += -DI915 subdir-ccflags-y += -DI915
# FIXME: Disable tracepoints on i915 for PREEMPT_RT, unfortunately
# it's an all or nothing flag. You cannot selectively disable
# only some tracepoints.
subdir-ccflags-$(CONFIG_PREEMPT_RT) += -DNOTRACE
subdir-ccflags-y += -I$(src) subdir-ccflags-y += -I$(src)
# Please keep these build lists sorted! # Please keep these build lists sorted!
@ -26,6 +31,7 @@ i915-y += \
i915_ioctl.o \ i915_ioctl.o \
i915_irq.o \ i915_irq.o \
i915_mitigations.o \ i915_mitigations.o \
i915_mmio_range.o \
i915_module.o \ i915_module.o \
i915_params.o \ i915_params.o \
i915_pci.o \ i915_pci.o \
@ -228,6 +234,7 @@ i915-y += \
display/intel_bios.o \ display/intel_bios.o \
display/intel_bo.o \ display/intel_bo.o \
display/intel_bw.o \ display/intel_bw.o \
display/intel_casf.o \
display/intel_cdclk.o \ display/intel_cdclk.o \
display/intel_cmtg.o \ display/intel_cmtg.o \
display/intel_color.o \ display/intel_color.o \
@ -236,6 +243,7 @@ i915-y += \
display/intel_crtc.o \ display/intel_crtc.o \
display/intel_crtc_state_dump.o \ display/intel_crtc_state_dump.o \
display/intel_cursor.o \ display/intel_cursor.o \
display/intel_dbuf_bw.o \
display/intel_display.o \ display/intel_display.o \
display/intel_display_conversion.o \ display/intel_display_conversion.o \
display/intel_display_driver.o \ display/intel_display_driver.o \
@ -248,6 +256,7 @@ i915-y += \
display/intel_display_rpm.o \ display/intel_display_rpm.o \
display/intel_display_rps.o \ display/intel_display_rps.o \
display/intel_display_snapshot.o \ display/intel_display_snapshot.o \
display/intel_display_utils.o \
display/intel_display_wa.o \ display/intel_display_wa.o \
display/intel_dmc.o \ display/intel_dmc.o \
display/intel_dmc_wl.o \ display/intel_dmc_wl.o \
@ -297,9 +306,11 @@ i915-y += \
display/intel_vblank.o \ display/intel_vblank.o \
display/intel_vga.o \ display/intel_vga.o \
display/intel_wm.o \ display/intel_wm.o \
display/skl_prefill.o \
display/skl_scaler.o \ display/skl_scaler.o \
display/skl_universal_plane.o \ display/skl_universal_plane.o \
display/skl_watermark.o \ display/skl_watermark.o \
display/vlv_clock.o \
display/vlv_sideband.o display/vlv_sideband.o
i915-$(CONFIG_ACPI) += \ i915-$(CONFIG_ACPI) += \
display/intel_acpi.o \ display/intel_acpi.o \
@ -346,6 +357,7 @@ i915-y += \
display/intel_gmbus.o \ display/intel_gmbus.o \
display/intel_hdmi.o \ display/intel_hdmi.o \
display/intel_lspcon.o \ display/intel_lspcon.o \
display/intel_lt_phy.o \
display/intel_lvds.o \ display/intel_lvds.o \
display/intel_panel.o \ display/intel_panel.o \
display/intel_pfit.o \ display/intel_pfit.o \

View File

@ -11,7 +11,6 @@
#include "g4x_dp.h" #include "g4x_dp.h"
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_audio.h" #include "intel_audio.h"
#include "intel_backlight.h" #include "intel_backlight.h"
#include "intel_connector.h" #include "intel_connector.h"
@ -20,6 +19,7 @@
#include "intel_display_power.h" #include "intel_display_power.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_dp_aux.h" #include "intel_dp_aux.h"
#include "intel_dp_link_training.h" #include "intel_dp_link_training.h"

View File

@ -191,45 +191,46 @@ bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
static bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) static bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
/* IPS only exists on ULT machines and is tied to pipe A. */
if (!hsw_crtc_supports_ips(crtc)) if (!hsw_crtc_supports_ips(crtc))
return false; return false;
if (!display->params.enable_ips)
return false;
if (crtc_state->pipe_bpp > 24) if (crtc_state->pipe_bpp > 24)
return false; return false;
/*
* We compare against max which means we must take
* the increased cdclk requirement into account when
* calculating the new cdclk.
*
* Should measure whether using a lower cdclk w/o IPS
*/
if (display->platform.broadwell &&
crtc_state->pixel_rate > display->cdclk.max_cdclk_freq * 95 / 100)
return false;
return true; return true;
} }
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);
/* no IPS specific limits to worry about */
return 0;
}
int hsw_ips_min_cdclk(const struct intel_crtc_state *crtc_state) int hsw_ips_min_cdclk(const struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
int min_cdclk;
if (!display->platform.broadwell)
return 0;
if (!hsw_crtc_state_ips_capable(crtc_state)) if (!hsw_crtc_state_ips_capable(crtc_state))
return 0; return 0;
/* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */ min_cdclk = _hsw_ips_min_cdclk(crtc_state);
return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
/*
* Do not ask for more than the max CDCLK frequency,
* if that is not enough IPS will simply not be used.
*/
if (min_cdclk > display->cdclk.max_cdclk_freq)
return 0;
return min_cdclk;
} }
int hsw_ips_compute_config(struct intel_atomic_state *state, int hsw_ips_compute_config(struct intel_atomic_state *state,
@ -244,6 +245,12 @@ int hsw_ips_compute_config(struct intel_atomic_state *state,
if (!hsw_crtc_state_ips_capable(crtc_state)) if (!hsw_crtc_state_ips_capable(crtc_state))
return 0; return 0;
if (_hsw_ips_min_cdclk(crtc_state) > display->cdclk.max_cdclk_freq)
return 0;
if (!display->params.enable_ips)
return 0;
/* /*
* When IPS gets enabled, the pipe CRC changes. Since IPS gets * When IPS gets enabled, the pipe CRC changes. Since IPS gets
* enabled and disabled dynamically based on package C states, * enabled and disabled dynamically based on package C states,
@ -257,18 +264,6 @@ int hsw_ips_compute_config(struct intel_atomic_state *state,
if (!(crtc_state->active_planes & ~BIT(PLANE_CURSOR))) if (!(crtc_state->active_planes & ~BIT(PLANE_CURSOR)))
return 0; return 0;
if (display->platform.broadwell) {
const struct intel_cdclk_state *cdclk_state;
cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
/* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */
if (crtc_state->pixel_rate > intel_cdclk_logical(cdclk_state) * 95 / 100)
return 0;
}
crtc_state->ips_enabled = true; crtc_state->ips_enabled = true;
return 0; return 0;

View File

@ -11,7 +11,6 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "i9xx_plane.h" #include "i9xx_plane.h"
#include "i9xx_plane_regs.h" #include "i9xx_plane_regs.h"
#include "intel_atomic.h" #include "intel_atomic.h"
@ -19,6 +18,7 @@
#include "intel_display_irq.h" #include "intel_display_irq.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_fb.h" #include "intel_fb.h"
#include "intel_fbc.h" #include "intel_fbc.h"
#include "intel_frontbuffer.h" #include "intel_frontbuffer.h"

View File

@ -2295,12 +2295,11 @@ static void i9xx_update_wm(struct intel_display *display)
crtc = single_enabled_crtc(display); crtc = single_enabled_crtc(display);
if (display->platform.i915gm && crtc) { if (display->platform.i915gm && crtc) {
struct drm_gem_object *obj; const struct drm_framebuffer *fb =
crtc->base.primary->state->fb;
obj = intel_fb_bo(crtc->base.primary->state->fb);
/* self-refresh seems busted with untiled */ /* self-refresh seems busted with untiled */
if (!intel_bo_is_tiled(obj)) if (fb->modifier == DRM_FORMAT_MOD_LINEAR)
crtc = NULL; crtc = NULL;
} }

View File

@ -35,7 +35,6 @@
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "icl_dsi.h" #include "icl_dsi.h"
#include "icl_dsi_regs.h" #include "icl_dsi_regs.h"
#include "intel_atomic.h" #include "intel_atomic.h"
@ -48,6 +47,7 @@
#include "intel_ddi.h" #include "intel_ddi.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_utils.h"
#include "intel_dsi.h" #include "intel_dsi.h"
#include "intel_dsi_vbt.h" #include "intel_dsi_vbt.h"
#include "intel_panel.h" #include "intel_panel.h"
@ -1655,7 +1655,7 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder,
if (ret) if (ret)
return ret; return ret;
crtc_state->dsc.compression_enable = true; intel_dsc_enable_on_crtc(crtc_state);
return 0; return 0;
} }

View File

@ -11,10 +11,10 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_acpi.h" #include "intel_acpi.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */ #define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */ #define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */

View File

@ -49,7 +49,7 @@ void intel_alpm_init(struct intel_dp *intel_dp)
return; return;
intel_dp->alpm_dpcd = dpcd; intel_dp->alpm_dpcd = dpcd;
mutex_init(&intel_dp->alpm_parameters.lock); mutex_init(&intel_dp->alpm.lock);
} }
static int get_silence_period_symbols(const struct intel_crtc_state *crtc_state) static int get_silence_period_symbols(const struct intel_crtc_state *crtc_state)
@ -58,43 +58,32 @@ static int get_silence_period_symbols(const struct intel_crtc_state *crtc_state)
1000 / 1000; 1000 / 1000;
} }
static int get_lfps_cycle_min_max_time(const struct intel_crtc_state *crtc_state, static void get_lfps_cycle_min_max_time(const struct intel_crtc_state *crtc_state,
int *min, int *max) int *min, int *max)
{ {
if (crtc_state->port_clock < 540000) { if (crtc_state->port_clock < 540000) {
*min = 65 * LFPS_CYCLE_COUNT; *min = 65 * LFPS_CYCLE_COUNT;
*max = 75 * LFPS_CYCLE_COUNT; *max = 75 * LFPS_CYCLE_COUNT;
} else if (crtc_state->port_clock <= 810000) { } else {
*min = 140; *min = 140;
*max = 800; *max = 800;
} else {
*min = *max = -1;
return -1;
} }
return 0;
} }
static int get_lfps_cycle_time(const struct intel_crtc_state *crtc_state) static int get_lfps_cycle_time(const struct intel_crtc_state *crtc_state)
{ {
int tlfps_cycle_min, tlfps_cycle_max, ret; int tlfps_cycle_min, tlfps_cycle_max;
ret = get_lfps_cycle_min_max_time(crtc_state, &tlfps_cycle_min, get_lfps_cycle_min_max_time(crtc_state, &tlfps_cycle_min,
&tlfps_cycle_max); &tlfps_cycle_max);
if (ret)
return ret;
return tlfps_cycle_min + (tlfps_cycle_max - tlfps_cycle_min) / 2; return tlfps_cycle_min + (tlfps_cycle_max - tlfps_cycle_min) / 2;
} }
static int get_lfps_half_cycle_clocks(const struct intel_crtc_state *crtc_state) static int get_lfps_half_cycle_clocks(const struct intel_crtc_state *crtc_state)
{ {
int lfps_cycle_time = get_lfps_cycle_time(crtc_state); return get_lfps_cycle_time(crtc_state) * crtc_state->port_clock / 1000 /
1000 / (2 * LFPS_CYCLE_COUNT);
if (lfps_cycle_time < 0)
return -1;
return lfps_cycle_time * crtc_state->port_clock / 1000 / 1000 / (2 * LFPS_CYCLE_COUNT);
} }
/* /*
@ -133,7 +122,7 @@ static int _lnl_compute_aux_less_wake_time(const struct intel_crtc_state *crtc_s
static int static int
_lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp, _lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state) struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(intel_dp); struct intel_display *display = to_intel_display(intel_dp);
int aux_less_wake_time, aux_less_wake_lines, silence_period, int aux_less_wake_time, aux_less_wake_lines, silence_period,
@ -146,8 +135,6 @@ _lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp,
silence_period = get_silence_period_symbols(crtc_state); silence_period = get_silence_period_symbols(crtc_state);
lfps_half_cycle = get_lfps_half_cycle_clocks(crtc_state); lfps_half_cycle = get_lfps_half_cycle_clocks(crtc_state);
if (lfps_half_cycle < 0)
return false;
if (aux_less_wake_lines > ALPM_CTL_AUX_LESS_WAKE_TIME_MASK || if (aux_less_wake_lines > ALPM_CTL_AUX_LESS_WAKE_TIME_MASK ||
silence_period > PORT_ALPM_CTL_SILENCE_PERIOD_MASK || silence_period > PORT_ALPM_CTL_SILENCE_PERIOD_MASK ||
@ -157,15 +144,15 @@ _lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp,
if (display->params.psr_safest_params) if (display->params.psr_safest_params)
aux_less_wake_lines = ALPM_CTL_AUX_LESS_WAKE_TIME_MASK; aux_less_wake_lines = ALPM_CTL_AUX_LESS_WAKE_TIME_MASK;
intel_dp->alpm_parameters.aux_less_wake_lines = aux_less_wake_lines; crtc_state->alpm_state.aux_less_wake_lines = aux_less_wake_lines;
intel_dp->alpm_parameters.silence_period_sym_clocks = silence_period; crtc_state->alpm_state.silence_period_sym_clocks = silence_period;
intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms = lfps_half_cycle; crtc_state->alpm_state.lfps_half_cycle_num_of_syms = lfps_half_cycle;
return true; return true;
} }
static bool _lnl_compute_alpm_params(struct intel_dp *intel_dp, static bool _lnl_compute_alpm_params(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state) struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(intel_dp); struct intel_display *display = to_intel_display(intel_dp);
int check_entry_lines; int check_entry_lines;
@ -186,7 +173,7 @@ static bool _lnl_compute_alpm_params(struct intel_dp *intel_dp,
if (display->params.psr_safest_params) if (display->params.psr_safest_params)
check_entry_lines = 15; check_entry_lines = 15;
intel_dp->alpm_parameters.check_entry_lines = check_entry_lines; crtc_state->alpm_state.check_entry_lines = check_entry_lines;
return true; return true;
} }
@ -217,7 +204,7 @@ static int io_buffer_wake_time(const struct intel_crtc_state *crtc_state)
} }
bool intel_alpm_compute_params(struct intel_dp *intel_dp, bool intel_alpm_compute_params(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state) struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(intel_dp); struct intel_display *display = to_intel_display(intel_dp);
int io_wake_lines, io_wake_time, fast_wake_lines, fast_wake_time; int io_wake_lines, io_wake_time, fast_wake_lines, fast_wake_time;
@ -255,8 +242,8 @@ bool intel_alpm_compute_params(struct intel_dp *intel_dp,
io_wake_lines = fast_wake_lines = max_wake_lines; io_wake_lines = fast_wake_lines = max_wake_lines;
/* According to Bspec lower limit should be set as 7 lines. */ /* According to Bspec lower limit should be set as 7 lines. */
intel_dp->alpm_parameters.io_wake_lines = max(io_wake_lines, 7); crtc_state->alpm_state.io_wake_lines = max(io_wake_lines, 7);
intel_dp->alpm_parameters.fast_wake_lines = max(fast_wake_lines, 7); crtc_state->alpm_state.fast_wake_lines = max(fast_wake_lines, 7);
return true; return true;
} }
@ -270,12 +257,12 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
int waketime_in_lines, first_sdp_position; int waketime_in_lines, first_sdp_position;
int context_latency, guardband; int context_latency, guardband;
if (intel_dp->alpm_parameters.lobf_disable_debug) { if (intel_dp->alpm.lobf_disable_debug) {
drm_dbg_kms(display->drm, "LOBF is disabled by debug flag\n"); drm_dbg_kms(display->drm, "LOBF is disabled by debug flag\n");
return; return;
} }
if (intel_dp->alpm_parameters.sink_alpm_error) if (intel_dp->alpm.sink_alpm_error)
return; return;
if (!intel_dp_is_edp(intel_dp)) if (!intel_dp_is_edp(intel_dp))
@ -306,9 +293,9 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
adjusted_mode->crtc_vdisplay - context_latency; adjusted_mode->crtc_vdisplay - context_latency;
first_sdp_position = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start; first_sdp_position = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start;
if (intel_alpm_aux_less_wake_supported(intel_dp)) if (intel_alpm_aux_less_wake_supported(intel_dp))
waketime_in_lines = intel_dp->alpm_parameters.io_wake_lines; waketime_in_lines = crtc_state->alpm_state.io_wake_lines;
else else
waketime_in_lines = intel_dp->alpm_parameters.aux_less_wake_lines; waketime_in_lines = crtc_state->alpm_state.aux_less_wake_lines;
crtc_state->has_lobf = (context_latency + guardband) > crtc_state->has_lobf = (context_latency + guardband) >
(first_sdp_position + waketime_in_lines); (first_sdp_position + waketime_in_lines);
@ -325,7 +312,7 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
!crtc_state->has_lobf)) !crtc_state->has_lobf))
return; return;
mutex_lock(&intel_dp->alpm_parameters.lock); mutex_lock(&intel_dp->alpm.lock);
/* /*
* Panel Replay on eDP is always using ALPM aux less. I.e. no need to * Panel Replay on eDP is always using ALPM aux less. I.e. no need to
* check panel support at this point. * check panel support at this point.
@ -334,7 +321,7 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
alpm_ctl = ALPM_CTL_ALPM_ENABLE | alpm_ctl = ALPM_CTL_ALPM_ENABLE |
ALPM_CTL_ALPM_AUX_LESS_ENABLE | ALPM_CTL_ALPM_AUX_LESS_ENABLE |
ALPM_CTL_AUX_LESS_SLEEP_HOLD_TIME_50_SYMBOLS | ALPM_CTL_AUX_LESS_SLEEP_HOLD_TIME_50_SYMBOLS |
ALPM_CTL_AUX_LESS_WAKE_TIME(intel_dp->alpm_parameters.aux_less_wake_lines); ALPM_CTL_AUX_LESS_WAKE_TIME(crtc_state->alpm_state.aux_less_wake_lines);
if (intel_dp->as_sdp_supported) { if (intel_dp->as_sdp_supported) {
u32 pr_alpm_ctl = PR_ALPM_CTL_ADAPTIVE_SYNC_SDP_POSITION_T1; u32 pr_alpm_ctl = PR_ALPM_CTL_ADAPTIVE_SYNC_SDP_POSITION_T1;
@ -352,7 +339,7 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
} else { } else {
alpm_ctl = ALPM_CTL_EXTENDED_FAST_WAKE_ENABLE | alpm_ctl = ALPM_CTL_EXTENDED_FAST_WAKE_ENABLE |
ALPM_CTL_EXTENDED_FAST_WAKE_TIME(intel_dp->alpm_parameters.fast_wake_lines); ALPM_CTL_EXTENDED_FAST_WAKE_TIME(crtc_state->alpm_state.fast_wake_lines);
} }
if (crtc_state->has_lobf) { if (crtc_state->has_lobf) {
@ -360,17 +347,17 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
drm_dbg_kms(display->drm, "Link off between frames (LOBF) enabled\n"); drm_dbg_kms(display->drm, "Link off between frames (LOBF) enabled\n");
} }
alpm_ctl |= ALPM_CTL_ALPM_ENTRY_CHECK(intel_dp->alpm_parameters.check_entry_lines); alpm_ctl |= ALPM_CTL_ALPM_ENTRY_CHECK(crtc_state->alpm_state.check_entry_lines);
intel_de_write(display, ALPM_CTL(display, cpu_transcoder), alpm_ctl); intel_de_write(display, ALPM_CTL(display, cpu_transcoder), alpm_ctl);
mutex_unlock(&intel_dp->alpm_parameters.lock); mutex_unlock(&intel_dp->alpm.lock);
} }
void intel_alpm_configure(struct intel_dp *intel_dp, void intel_alpm_configure(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state) const struct intel_crtc_state *crtc_state)
{ {
lnl_alpm_configure(intel_dp, crtc_state); lnl_alpm_configure(intel_dp, crtc_state);
intel_dp->alpm_parameters.transcoder = crtc_state->cpu_transcoder; intel_dp->alpm.transcoder = crtc_state->cpu_transcoder;
} }
void intel_alpm_port_configure(struct intel_dp *intel_dp, void intel_alpm_port_configure(struct intel_dp *intel_dp,
@ -388,14 +375,14 @@ void intel_alpm_port_configure(struct intel_dp *intel_dp,
PORT_ALPM_CTL_MAX_PHY_SWING_SETUP(15) | PORT_ALPM_CTL_MAX_PHY_SWING_SETUP(15) |
PORT_ALPM_CTL_MAX_PHY_SWING_HOLD(0) | PORT_ALPM_CTL_MAX_PHY_SWING_HOLD(0) |
PORT_ALPM_CTL_SILENCE_PERIOD( PORT_ALPM_CTL_SILENCE_PERIOD(
intel_dp->alpm_parameters.silence_period_sym_clocks); crtc_state->alpm_state.silence_period_sym_clocks);
lfps_ctl_val = PORT_ALPM_LFPS_CTL_LFPS_CYCLE_COUNT(LFPS_CYCLE_COUNT) | lfps_ctl_val = PORT_ALPM_LFPS_CTL_LFPS_CYCLE_COUNT(LFPS_CYCLE_COUNT) |
PORT_ALPM_LFPS_CTL_LFPS_HALF_CYCLE_DURATION( PORT_ALPM_LFPS_CTL_LFPS_HALF_CYCLE_DURATION(
intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms) | crtc_state->alpm_state.lfps_half_cycle_num_of_syms) |
PORT_ALPM_LFPS_CTL_FIRST_LFPS_HALF_CYCLE_DURATION( PORT_ALPM_LFPS_CTL_FIRST_LFPS_HALF_CYCLE_DURATION(
intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms) | crtc_state->alpm_state.lfps_half_cycle_num_of_syms) |
PORT_ALPM_LFPS_CTL_LAST_LFPS_HALF_CYCLE_DURATION( PORT_ALPM_LFPS_CTL_LAST_LFPS_HALF_CYCLE_DURATION(
intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms); crtc_state->alpm_state.lfps_half_cycle_num_of_syms);
} }
intel_de_write(display, PORT_ALPM_CTL(port), alpm_ctl_val); intel_de_write(display, PORT_ALPM_CTL(port), alpm_ctl_val);
@ -433,10 +420,10 @@ void intel_alpm_pre_plane_update(struct intel_atomic_state *state,
continue; continue;
if (old_crtc_state->has_lobf) { if (old_crtc_state->has_lobf) {
mutex_lock(&intel_dp->alpm_parameters.lock); mutex_lock(&intel_dp->alpm.lock);
intel_de_write(display, ALPM_CTL(display, cpu_transcoder), 0); intel_de_write(display, ALPM_CTL(display, cpu_transcoder), 0);
drm_dbg_kms(display->drm, "Link off between frames (LOBF) disabled\n"); drm_dbg_kms(display->drm, "Link off between frames (LOBF) disabled\n");
mutex_unlock(&intel_dp->alpm_parameters.lock); mutex_unlock(&intel_dp->alpm.lock);
} }
} }
} }
@ -530,7 +517,7 @@ i915_edp_lobf_debug_get(void *data, u64 *val)
struct intel_connector *connector = data; struct intel_connector *connector = data;
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
*val = intel_dp->alpm_parameters.lobf_disable_debug; *val = intel_dp->alpm.lobf_disable_debug;
return 0; return 0;
} }
@ -541,7 +528,7 @@ i915_edp_lobf_debug_set(void *data, u64 val)
struct intel_connector *connector = data; struct intel_connector *connector = data;
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
intel_dp->alpm_parameters.lobf_disable_debug = val; intel_dp->alpm.lobf_disable_debug = val;
return 0; return 0;
} }
@ -569,12 +556,12 @@ void intel_alpm_lobf_debugfs_add(struct intel_connector *connector)
void intel_alpm_disable(struct intel_dp *intel_dp) void intel_alpm_disable(struct intel_dp *intel_dp)
{ {
struct intel_display *display = to_intel_display(intel_dp); struct intel_display *display = to_intel_display(intel_dp);
enum transcoder cpu_transcoder = intel_dp->alpm_parameters.transcoder; enum transcoder cpu_transcoder = intel_dp->alpm.transcoder;
if (DISPLAY_VER(display) < 20 || !intel_dp->alpm_dpcd) if (DISPLAY_VER(display) < 20 || !intel_dp->alpm_dpcd)
return; return;
mutex_lock(&intel_dp->alpm_parameters.lock); mutex_lock(&intel_dp->alpm.lock);
intel_de_rmw(display, ALPM_CTL(display, cpu_transcoder), intel_de_rmw(display, ALPM_CTL(display, cpu_transcoder),
ALPM_CTL_ALPM_ENABLE | ALPM_CTL_LOBF_ENABLE | ALPM_CTL_ALPM_ENABLE | ALPM_CTL_LOBF_ENABLE |
@ -585,7 +572,7 @@ void intel_alpm_disable(struct intel_dp *intel_dp)
PORT_ALPM_CTL_ALPM_AUX_LESS_ENABLE, 0); PORT_ALPM_CTL_ALPM_AUX_LESS_ENABLE, 0);
drm_dbg_kms(display->drm, "Disabling ALPM\n"); drm_dbg_kms(display->drm, "Disabling ALPM\n");
mutex_unlock(&intel_dp->alpm_parameters.lock); mutex_unlock(&intel_dp->alpm.lock);
} }
bool intel_alpm_get_error(struct intel_dp *intel_dp) bool intel_alpm_get_error(struct intel_dp *intel_dp)

View File

@ -17,7 +17,7 @@ struct intel_crtc;
void intel_alpm_init(struct intel_dp *intel_dp); void intel_alpm_init(struct intel_dp *intel_dp);
bool intel_alpm_compute_params(struct intel_dp *intel_dp, bool intel_alpm_compute_params(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state); struct intel_crtc_state *crtc_state);
void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp, void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state, struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state); struct drm_connector_state *conn_state);

View File

@ -13,7 +13,6 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_backlight.h" #include "intel_backlight.h"
#include "intel_backlight_regs.h" #include "intel_backlight_regs.h"
#include "intel_connector.h" #include "intel_connector.h"
@ -21,6 +20,7 @@
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp_aux_backlight.h" #include "intel_dp_aux_backlight.h"
#include "intel_dsi_dcs_backlight.h" #include "intel_dsi_dcs_backlight.h"
#include "intel_panel.h" #include "intel_panel.h"

View File

@ -36,11 +36,11 @@
#include "soc/intel_rom.h" #include "soc/intel_rom.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_utils.h"
#include "intel_display.h" #include "intel_display.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_gmbus.h" #include "intel_gmbus.h"
#define _INTEL_BIOS_PRIVATE #define _INTEL_BIOS_PRIVATE

View File

@ -3,32 +3,23 @@
* Copyright © 2019 Intel Corporation * Copyright © 2019 Intel Corporation
*/ */
#include <drm/drm_atomic_state_helper.h>
#include "soc/intel_dram.h" #include "soc/intel_dram.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_bw.h" #include "intel_bw.h"
#include "intel_cdclk.h" #include "intel_crtc.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_mchbar_regs.h" #include "intel_mchbar_regs.h"
#include "intel_pcode.h" #include "intel_pcode.h"
#include "intel_uncore.h" #include "intel_uncore.h"
#include "skl_watermark.h" #include "skl_watermark.h"
struct intel_dbuf_bw {
unsigned int max_bw[I915_MAX_DBUF_SLICES];
u8 active_planes[I915_MAX_DBUF_SLICES];
};
struct intel_bw_state { struct intel_bw_state {
struct intel_global_state base; struct intel_global_state base;
struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES];
/* /*
* Contains a bit mask, used to determine, whether correspondent * Contains a bit mask, used to determine, whether correspondent
@ -836,49 +827,6 @@ void intel_bw_init_hw(struct intel_display *display)
icl_get_bw_info(display, dram_info, &icl_sa_info); icl_get_bw_info(display, dram_info, &icl_sa_info);
} }
static unsigned int intel_bw_crtc_num_active_planes(const struct intel_crtc_state *crtc_state)
{
/*
* We assume cursors are small enough
* to not not cause bandwidth problems.
*/
return hweight8(crtc_state->active_planes & ~BIT(PLANE_CURSOR));
}
static unsigned int intel_bw_crtc_data_rate(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
unsigned int data_rate = 0;
enum plane_id plane_id;
for_each_plane_id_on_crtc(crtc, plane_id) {
/*
* We assume cursors are small enough
* to not not cause bandwidth problems.
*/
if (plane_id == PLANE_CURSOR)
continue;
data_rate += crtc_state->data_rate[plane_id];
if (DISPLAY_VER(display) < 11)
data_rate += crtc_state->data_rate_y[plane_id];
}
return data_rate;
}
/* "Maximum Pipe Read Bandwidth" */
static int intel_bw_crtc_min_cdclk(struct intel_display *display,
unsigned int data_rate)
{
if (DISPLAY_VER(display) < 12)
return 0;
return DIV_ROUND_UP_ULL(mul_u32_u32(data_rate, 10), 512);
}
static unsigned int intel_bw_num_active_planes(struct intel_display *display, static unsigned int intel_bw_num_active_planes(struct intel_display *display,
const struct intel_bw_state *bw_state) const struct intel_bw_state *bw_state)
{ {
@ -894,14 +842,13 @@ static unsigned int intel_bw_num_active_planes(struct intel_display *display,
static unsigned int intel_bw_data_rate(struct intel_display *display, static unsigned int intel_bw_data_rate(struct intel_display *display,
const struct intel_bw_state *bw_state) const struct intel_bw_state *bw_state)
{ {
struct drm_i915_private *i915 = to_i915(display->drm);
unsigned int data_rate = 0; unsigned int data_rate = 0;
enum pipe pipe; enum pipe pipe;
for_each_pipe(display, pipe) for_each_pipe(display, pipe)
data_rate += bw_state->data_rate[pipe]; data_rate += bw_state->data_rate[pipe];
if (DISPLAY_VER(display) >= 13 && i915_vtd_active(i915)) if (DISPLAY_VER(display) >= 13 && intel_display_vtd_active(display))
data_rate = DIV_ROUND_UP(data_rate * 105, 100); data_rate = DIV_ROUND_UP(data_rate * 105, 100);
return data_rate; return data_rate;
@ -1262,223 +1209,6 @@ static int intel_bw_check_qgv_points(struct intel_display *display,
old_bw_state, new_bw_state); old_bw_state, new_bw_state);
} }
static bool intel_dbuf_bw_changed(struct intel_display *display,
const struct intel_dbuf_bw *old_dbuf_bw,
const struct intel_dbuf_bw *new_dbuf_bw)
{
enum dbuf_slice slice;
for_each_dbuf_slice(display, slice) {
if (old_dbuf_bw->max_bw[slice] != new_dbuf_bw->max_bw[slice] ||
old_dbuf_bw->active_planes[slice] != new_dbuf_bw->active_planes[slice])
return true;
}
return false;
}
static bool intel_bw_state_changed(struct intel_display *display,
const struct intel_bw_state *old_bw_state,
const struct intel_bw_state *new_bw_state)
{
enum pipe pipe;
for_each_pipe(display, pipe) {
const struct intel_dbuf_bw *old_dbuf_bw =
&old_bw_state->dbuf_bw[pipe];
const struct intel_dbuf_bw *new_dbuf_bw =
&new_bw_state->dbuf_bw[pipe];
if (intel_dbuf_bw_changed(display, old_dbuf_bw, new_dbuf_bw))
return true;
if (intel_bw_crtc_min_cdclk(display, old_bw_state->data_rate[pipe]) !=
intel_bw_crtc_min_cdclk(display, new_bw_state->data_rate[pipe]))
return true;
}
return false;
}
static void skl_plane_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
struct intel_crtc *crtc,
enum plane_id plane_id,
const struct skl_ddb_entry *ddb,
unsigned int data_rate)
{
struct intel_display *display = to_intel_display(crtc);
unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(display, ddb);
enum dbuf_slice slice;
/*
* The arbiter can only really guarantee an
* equal share of the total bw to each plane.
*/
for_each_dbuf_slice_in_mask(display, slice, dbuf_mask) {
dbuf_bw->max_bw[slice] = max(dbuf_bw->max_bw[slice], data_rate);
dbuf_bw->active_planes[slice] |= BIT(plane_id);
}
}
static void skl_crtc_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
enum plane_id plane_id;
memset(dbuf_bw, 0, sizeof(*dbuf_bw));
if (!crtc_state->hw.active)
return;
for_each_plane_id_on_crtc(crtc, plane_id) {
/*
* We assume cursors are small enough
* to not cause bandwidth problems.
*/
if (plane_id == PLANE_CURSOR)
continue;
skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
&crtc_state->wm.skl.plane_ddb[plane_id],
crtc_state->data_rate[plane_id]);
if (DISPLAY_VER(display) < 11)
skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
&crtc_state->wm.skl.plane_ddb_y[plane_id],
crtc_state->data_rate[plane_id]);
}
}
/* "Maximum Data Buffer Bandwidth" */
static int
intel_bw_dbuf_min_cdclk(struct intel_display *display,
const struct intel_bw_state *bw_state)
{
unsigned int total_max_bw = 0;
enum dbuf_slice slice;
for_each_dbuf_slice(display, slice) {
int num_active_planes = 0;
unsigned int max_bw = 0;
enum pipe pipe;
/*
* The arbiter can only really guarantee an
* equal share of the total bw to each plane.
*/
for_each_pipe(display, pipe) {
const struct intel_dbuf_bw *dbuf_bw = &bw_state->dbuf_bw[pipe];
max_bw = max(dbuf_bw->max_bw[slice], max_bw);
num_active_planes += hweight8(dbuf_bw->active_planes[slice]);
}
max_bw *= num_active_planes;
total_max_bw = max(total_max_bw, max_bw);
}
return DIV_ROUND_UP(total_max_bw, 64);
}
int intel_bw_min_cdclk(struct intel_display *display,
const struct intel_bw_state *bw_state)
{
enum pipe pipe;
int min_cdclk;
min_cdclk = intel_bw_dbuf_min_cdclk(display, bw_state);
for_each_pipe(display, pipe)
min_cdclk = max(min_cdclk,
intel_bw_crtc_min_cdclk(display,
bw_state->data_rate[pipe]));
return min_cdclk;
}
int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
bool *need_cdclk_calc)
{
struct intel_display *display = to_intel_display(state);
struct intel_bw_state *new_bw_state = NULL;
const struct intel_bw_state *old_bw_state = NULL;
const struct intel_cdclk_state *cdclk_state;
const struct intel_crtc_state *old_crtc_state;
const struct intel_crtc_state *new_crtc_state;
int old_min_cdclk, new_min_cdclk;
struct intel_crtc *crtc;
int i;
if (DISPLAY_VER(display) < 9)
return 0;
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
struct intel_dbuf_bw old_dbuf_bw, new_dbuf_bw;
skl_crtc_calc_dbuf_bw(&old_dbuf_bw, old_crtc_state);
skl_crtc_calc_dbuf_bw(&new_dbuf_bw, new_crtc_state);
if (!intel_dbuf_bw_changed(display, &old_dbuf_bw, &new_dbuf_bw))
continue;
new_bw_state = intel_atomic_get_bw_state(state);
if (IS_ERR(new_bw_state))
return PTR_ERR(new_bw_state);
old_bw_state = intel_atomic_get_old_bw_state(state);
new_bw_state->dbuf_bw[crtc->pipe] = new_dbuf_bw;
}
if (!old_bw_state)
return 0;
if (intel_bw_state_changed(display, old_bw_state, new_bw_state)) {
int ret = intel_atomic_lock_global_state(&new_bw_state->base);
if (ret)
return ret;
}
old_min_cdclk = intel_bw_min_cdclk(display, old_bw_state);
new_min_cdclk = intel_bw_min_cdclk(display, new_bw_state);
/*
* No need to check against the cdclk state if
* the min cdclk doesn't increase.
*
* Ie. we only ever increase the cdclk due to bandwidth
* requirements. This can reduce back and forth
* display blinking due to constant cdclk changes.
*/
if (new_min_cdclk <= old_min_cdclk)
return 0;
cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
/*
* No need to recalculate the cdclk state if
* the min cdclk doesn't increase.
*
* Ie. we only ever increase the cdclk due to bandwidth
* requirements. This can reduce back and forth
* display blinking due to constant cdclk changes.
*/
if (new_min_cdclk <= intel_cdclk_bw_min_cdclk(cdclk_state))
return 0;
drm_dbg_kms(display->drm,
"new bandwidth min cdclk (%d kHz) > old min cdclk (%d kHz)\n",
new_min_cdclk, intel_cdclk_bw_min_cdclk(cdclk_state));
*need_cdclk_calc = true;
return 0;
}
static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *changed) static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *changed)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
@ -1489,13 +1219,13 @@ static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *chan
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) { new_crtc_state, i) {
unsigned int old_data_rate = unsigned int old_data_rate =
intel_bw_crtc_data_rate(old_crtc_state); intel_crtc_bw_data_rate(old_crtc_state);
unsigned int new_data_rate = unsigned int new_data_rate =
intel_bw_crtc_data_rate(new_crtc_state); intel_crtc_bw_data_rate(new_crtc_state);
unsigned int old_active_planes = unsigned int old_active_planes =
intel_bw_crtc_num_active_planes(old_crtc_state); intel_crtc_bw_num_active_planes(old_crtc_state);
unsigned int new_active_planes = unsigned int new_active_planes =
intel_bw_crtc_num_active_planes(new_crtc_state); intel_crtc_bw_num_active_planes(new_crtc_state);
struct intel_bw_state *new_bw_state; struct intel_bw_state *new_bw_state;
/* /*
@ -1527,11 +1257,11 @@ static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *chan
static int intel_bw_modeset_checks(struct intel_atomic_state *state) static int intel_bw_modeset_checks(struct intel_atomic_state *state)
{ {
struct intel_display *display = to_intel_display(state);
const struct intel_bw_state *old_bw_state; const struct intel_bw_state *old_bw_state;
struct intel_bw_state *new_bw_state; struct intel_bw_state *new_bw_state;
int ret;
if (DISPLAY_VER(display) < 9) if (!intel_any_crtc_active_changed(state))
return 0; return 0;
new_bw_state = intel_atomic_get_bw_state(state); new_bw_state = intel_atomic_get_bw_state(state);
@ -1543,13 +1273,9 @@ static int intel_bw_modeset_checks(struct intel_atomic_state *state)
new_bw_state->active_pipes = new_bw_state->active_pipes =
intel_calc_active_pipes(state, old_bw_state->active_pipes); intel_calc_active_pipes(state, old_bw_state->active_pipes);
if (new_bw_state->active_pipes != old_bw_state->active_pipes) { ret = intel_atomic_lock_global_state(&new_bw_state->base);
int ret; if (ret)
return ret;
ret = intel_atomic_lock_global_state(&new_bw_state->base);
if (ret)
return ret;
}
return 0; return 0;
} }
@ -1599,7 +1325,7 @@ static int intel_bw_check_sagv_mask(struct intel_atomic_state *state)
return 0; return 0;
} }
int intel_bw_atomic_check(struct intel_atomic_state *state, bool any_ms) int intel_bw_atomic_check(struct intel_atomic_state *state)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
bool changed = false; bool changed = false;
@ -1610,11 +1336,9 @@ int intel_bw_atomic_check(struct intel_atomic_state *state, bool any_ms)
if (DISPLAY_VER(display) < 9) if (DISPLAY_VER(display) < 9)
return 0; return 0;
if (any_ms) { ret = intel_bw_modeset_checks(state);
ret = intel_bw_modeset_checks(state); if (ret)
if (ret) return ret;
return ret;
}
ret = intel_bw_check_sagv_mask(state); ret = intel_bw_check_sagv_mask(state);
if (ret) if (ret)
@ -1657,9 +1381,9 @@ static void intel_bw_crtc_update(struct intel_bw_state *bw_state,
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
bw_state->data_rate[crtc->pipe] = bw_state->data_rate[crtc->pipe] =
intel_bw_crtc_data_rate(crtc_state); intel_crtc_bw_data_rate(crtc_state);
bw_state->num_active_planes[crtc->pipe] = bw_state->num_active_planes[crtc->pipe] =
intel_bw_crtc_num_active_planes(crtc_state); intel_crtc_bw_num_active_planes(crtc_state);
drm_dbg_kms(display->drm, "pipe %c data rate %u num active planes %u\n", drm_dbg_kms(display->drm, "pipe %c data rate %u num active planes %u\n",
pipe_name(crtc->pipe), pipe_name(crtc->pipe),
@ -1690,8 +1414,6 @@ void intel_bw_update_hw_state(struct intel_display *display)
if (DISPLAY_VER(display) >= 11) if (DISPLAY_VER(display) >= 11)
intel_bw_crtc_update(bw_state, crtc_state); intel_bw_crtc_update(bw_state, crtc_state);
skl_crtc_calc_dbuf_bw(&bw_state->dbuf_bw[pipe], crtc_state);
/* initially SAGV has been forced off */ /* initially SAGV has been forced off */
bw_state->pipe_sagv_reject |= BIT(pipe); bw_state->pipe_sagv_reject |= BIT(pipe);
} }
@ -1709,7 +1431,6 @@ void intel_bw_crtc_disable_noatomic(struct intel_crtc *crtc)
bw_state->data_rate[pipe] = 0; bw_state->data_rate[pipe] = 0;
bw_state->num_active_planes[pipe] = 0; bw_state->num_active_planes[pipe] = 0;
memset(&bw_state->dbuf_bw[pipe], 0, sizeof(bw_state->dbuf_bw[pipe]));
} }
static struct intel_global_state * static struct intel_global_state *

View File

@ -28,11 +28,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state);
void intel_bw_init_hw(struct intel_display *display); void intel_bw_init_hw(struct intel_display *display);
int intel_bw_init(struct intel_display *display); int intel_bw_init(struct intel_display *display);
int intel_bw_atomic_check(struct intel_atomic_state *state, bool any_ms); int intel_bw_atomic_check(struct intel_atomic_state *state);
int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
bool *need_cdclk_calc);
int intel_bw_min_cdclk(struct intel_display *display,
const struct intel_bw_state *bw_state);
void intel_bw_update_hw_state(struct intel_display *display); void intel_bw_update_hw_state(struct intel_display *display);
void intel_bw_crtc_disable_noatomic(struct intel_crtc *crtc); void intel_bw_crtc_disable_noatomic(struct intel_crtc *crtc);

View File

@ -0,0 +1,290 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
#include <drm/drm_print.h>
#include "i915_reg.h"
#include "intel_casf.h"
#include "intel_casf_regs.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "skl_scaler.h"
#define MAX_PIXELS_FOR_3_TAP_FILTER (1920 * 1080)
#define MAX_PIXELS_FOR_5_TAP_FILTER (3840 * 2160)
#define FILTER_COEFF_0_125 125
#define FILTER_COEFF_0_25 250
#define FILTER_COEFF_0_5 500
#define FILTER_COEFF_1_0 1000
#define FILTER_COEFF_0_0 0
#define SET_POSITIVE_SIGN(x) ((x) & (~SIGN))
/**
* DOC: Content Adaptive Sharpness Filter (CASF)
*
* Starting from LNL the display engine supports an
* adaptive sharpening filter, enhancing the image
* quality. The display hardware utilizes the second
* pipe scaler for implementing CASF.
* If sharpness is being enabled then pipe scaling
* cannot be used.
* This filter operates on a region of pixels based
* on the tap size. Coefficients are used to generate
* an alpha value which blends the sharpened image to
* original image.
*/
/* Default LUT values to be loaded one time. */
static const u16 sharpness_lut[] = {
4095, 2047, 1364, 1022, 816, 678, 579,
504, 444, 397, 357, 323, 293, 268, 244, 224,
204, 187, 170, 154, 139, 125, 111, 98, 85,
73, 60, 48, 36, 24, 12, 0
};
const u16 filtercoeff_1[] = {
FILTER_COEFF_0_0, FILTER_COEFF_0_0, FILTER_COEFF_0_5,
FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_0,
FILTER_COEFF_0_0,
};
const u16 filtercoeff_2[] = {
FILTER_COEFF_0_0, FILTER_COEFF_0_25, FILTER_COEFF_0_5,
FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_25,
FILTER_COEFF_0_0,
};
const u16 filtercoeff_3[] = {
FILTER_COEFF_0_125, FILTER_COEFF_0_25, FILTER_COEFF_0_5,
FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_25,
FILTER_COEFF_0_125,
};
static void intel_casf_filter_lut_load(struct intel_crtc *crtc,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
int i;
intel_de_write(display, SHRPLUT_INDEX(crtc->pipe),
INDEX_AUTO_INCR | INDEX_VALUE(0));
for (i = 0; i < ARRAY_SIZE(sharpness_lut); i++)
intel_de_write(display, SHRPLUT_DATA(crtc->pipe),
sharpness_lut[i]);
}
void intel_casf_update_strength(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
int win_size;
intel_de_rmw(display, SHARPNESS_CTL(crtc->pipe), FILTER_STRENGTH_MASK,
FILTER_STRENGTH(crtc_state->hw.casf_params.strength));
win_size = intel_de_read(display, SKL_PS_WIN_SZ(crtc->pipe, 1));
intel_de_write_fw(display, SKL_PS_WIN_SZ(crtc->pipe, 1), win_size);
}
static void intel_casf_compute_win_size(struct intel_crtc_state *crtc_state)
{
const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode;
u32 total_pixels = mode->hdisplay * mode->vdisplay;
if (total_pixels <= MAX_PIXELS_FOR_3_TAP_FILTER)
crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_3X3;
else if (total_pixels <= MAX_PIXELS_FOR_5_TAP_FILTER)
crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_5X5;
else
crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_7X7;
}
int intel_casf_compute_config(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
if (!HAS_CASF(display))
return 0;
if (crtc_state->uapi.sharpness_strength == 0) {
crtc_state->hw.casf_params.casf_enable = false;
crtc_state->hw.casf_params.strength = 0;
return 0;
}
crtc_state->hw.casf_params.casf_enable = true;
/*
* HW takes a value in form (1.0 + strength) in 4.4 fixed format.
* Strength is from 0.0-14.9375 ie from 0-239.
* User can give value from 0-255 but is clamped to 239.
* Ex. User gives 85 which is 5.3125 and adding 1.0 gives 6.3125.
* 6.3125 in 4.4 format is b01100101 which is equal to 101.
* Also 85 + 16 = 101.
*/
crtc_state->hw.casf_params.strength =
min(crtc_state->uapi.sharpness_strength, 0xEF) + 0x10;
intel_casf_compute_win_size(crtc_state);
intel_casf_scaler_compute_config(crtc_state);
return 0;
}
void intel_casf_sharpness_get_config(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
u32 sharp;
sharp = intel_de_read(display, SHARPNESS_CTL(crtc->pipe));
if (sharp & FILTER_EN) {
if (drm_WARN_ON(display->drm,
REG_FIELD_GET(FILTER_STRENGTH_MASK, sharp) < 16))
crtc_state->hw.casf_params.strength = 0;
else
crtc_state->hw.casf_params.strength =
REG_FIELD_GET(FILTER_STRENGTH_MASK, sharp);
crtc_state->hw.casf_params.casf_enable = true;
crtc_state->hw.casf_params.win_size =
REG_FIELD_GET(FILTER_SIZE_MASK, sharp);
}
}
bool intel_casf_needs_scaler(const struct intel_crtc_state *crtc_state)
{
if (crtc_state->hw.casf_params.casf_enable)
return true;
return false;
}
static int casf_coeff_tap(int i)
{
return i % SCALER_FILTER_NUM_TAPS;
}
static u32 casf_coeff(struct intel_crtc_state *crtc_state, int t)
{
struct scaler_filter_coeff value;
u32 coeff;
value = crtc_state->hw.casf_params.coeff[t];
value.sign = 0;
coeff = value.sign << 15 | value.exp << 12 | value.mantissa << 3;
return coeff;
}
/*
* 17 phase of 7 taps requires 119 coefficients in 60 dwords per set.
* To enable casf: program scaler coefficients with the coeffients
* that are calculated and stored in hw.casf_params.coeff as per
* SCALER_COEFFICIENT_FORMAT
*/
static void intel_casf_write_coeff(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
int id = crtc_state->scaler_state.scaler_id;
int i;
if (id != 1) {
drm_WARN(display->drm, 0, "Second scaler not enabled\n");
return;
}
intel_de_write_fw(display, GLK_PS_COEF_INDEX_SET(crtc->pipe, id, 0),
PS_COEF_INDEX_AUTO_INC);
for (i = 0; i < 17 * SCALER_FILTER_NUM_TAPS; i += 2) {
u32 tmp;
int t;
t = casf_coeff_tap(i);
tmp = casf_coeff(crtc_state, t);
t = casf_coeff_tap(i + 1);
tmp |= casf_coeff(crtc_state, t) << 16;
intel_de_write_fw(display, GLK_PS_COEF_DATA_SET(crtc->pipe, id, 0),
tmp);
}
}
static void convert_sharpness_coef_binary(struct scaler_filter_coeff *coeff,
u16 coefficient)
{
if (coefficient < 25) {
coeff->mantissa = (coefficient * 2048) / 100;
coeff->exp = 3;
} else if (coefficient < 50) {
coeff->mantissa = (coefficient * 1024) / 100;
coeff->exp = 2;
} else if (coefficient < 100) {
coeff->mantissa = (coefficient * 512) / 100;
coeff->exp = 1;
} else {
coeff->mantissa = (coefficient * 256) / 100;
coeff->exp = 0;
}
}
void intel_casf_scaler_compute_config(struct intel_crtc_state *crtc_state)
{
const u16 *filtercoeff;
u16 filter_coeff[SCALER_FILTER_NUM_TAPS];
u16 sumcoeff = 0;
int i;
if (crtc_state->hw.casf_params.win_size == 0)
filtercoeff = filtercoeff_1;
else if (crtc_state->hw.casf_params.win_size == 1)
filtercoeff = filtercoeff_2;
else
filtercoeff = filtercoeff_3;
for (i = 0; i < SCALER_FILTER_NUM_TAPS; i++)
sumcoeff += *(filtercoeff + i);
for (i = 0; i < SCALER_FILTER_NUM_TAPS; i++) {
filter_coeff[i] = (*(filtercoeff + i) * 100 / sumcoeff);
convert_sharpness_coef_binary(&crtc_state->hw.casf_params.coeff[i],
filter_coeff[i]);
}
}
void intel_casf_enable(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
u32 sharpness_ctl;
intel_casf_filter_lut_load(crtc, crtc_state);
intel_casf_write_coeff(crtc_state);
sharpness_ctl = FILTER_EN | FILTER_STRENGTH(crtc_state->hw.casf_params.strength);
sharpness_ctl |= crtc_state->hw.casf_params.win_size;
intel_de_write(display, SHARPNESS_CTL(crtc->pipe), sharpness_ctl);
skl_scaler_setup_casf(crtc_state);
}
void intel_casf_disable(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
intel_de_write(display, SKL_PS_CTRL(crtc->pipe, 1), 0);
intel_de_write(display, SKL_PS_WIN_POS(crtc->pipe, 1), 0);
intel_de_write(display, SHARPNESS_CTL(crtc->pipe), 0);
intel_de_write(display, SKL_PS_WIN_SZ(crtc->pipe, 1), 0);
}

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2025 Intel Corporation
*/
#ifndef __INTEL_CASF_H__
#define __INTEL_CASF_H__
#include <linux/types.h>
struct intel_crtc_state;
int intel_casf_compute_config(struct intel_crtc_state *crtc_state);
void intel_casf_update_strength(struct intel_crtc_state *new_crtc_state);
void intel_casf_sharpness_get_config(struct intel_crtc_state *crtc_state);
void intel_casf_enable(struct intel_crtc_state *crtc_state);
void intel_casf_disable(const struct intel_crtc_state *crtc_state);
void intel_casf_scaler_compute_config(struct intel_crtc_state *crtc_state);
bool intel_casf_needs_scaler(const struct intel_crtc_state *crtc_state);
#endif /* __INTEL_CASF_H__ */

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2025 Intel Corporation
*/
#ifndef __INTEL_CASF_REGS_H__
#define __INTEL_CASF_REGS_H__
#include "intel_display_reg_defs.h"
#define _SHARPNESS_CTL_A 0x682B0
#define _SHARPNESS_CTL_B 0x68AB0
#define SHARPNESS_CTL(pipe) _MMIO_PIPE(pipe, _SHARPNESS_CTL_A, _SHARPNESS_CTL_B)
#define FILTER_EN REG_BIT(31)
#define FILTER_STRENGTH_MASK REG_GENMASK(15, 8)
#define FILTER_STRENGTH(x) REG_FIELD_PREP(FILTER_STRENGTH_MASK, (x))
#define FILTER_SIZE_MASK REG_GENMASK(1, 0)
#define SHARPNESS_FILTER_SIZE_3X3 REG_FIELD_PREP(FILTER_SIZE_MASK, 0)
#define SHARPNESS_FILTER_SIZE_5X5 REG_FIELD_PREP(FILTER_SIZE_MASK, 1)
#define SHARPNESS_FILTER_SIZE_7X7 REG_FIELD_PREP(FILTER_SIZE_MASK, 2)
#define _SHRPLUT_DATA_A 0x682B8
#define _SHRPLUT_DATA_B 0x68AB8
#define SHRPLUT_DATA(pipe) _MMIO_PIPE(pipe, _SHRPLUT_DATA_A, _SHRPLUT_DATA_B)
#define _SHRPLUT_INDEX_A 0x682B4
#define _SHRPLUT_INDEX_B 0x68AB4
#define SHRPLUT_INDEX(pipe) _MMIO_PIPE(pipe, _SHRPLUT_INDEX_A, _SHRPLUT_INDEX_B)
#define INDEX_AUTO_INCR REG_BIT(10)
#define INDEX_VALUE_MASK REG_GENMASK(4, 0)
#define INDEX_VALUE(x) REG_FIELD_PREP(INDEX_VALUE_MASK, (x))
#endif /* __INTEL_CASF_REGS__ */

View File

@ -32,15 +32,15 @@
#include "hsw_ips.h" #include "hsw_ips.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_audio.h" #include "intel_audio.h"
#include "intel_bw.h"
#include "intel_cdclk.h" #include "intel_cdclk.h"
#include "intel_crtc.h" #include "intel_crtc.h"
#include "intel_dbuf_bw.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_mchbar_regs.h" #include "intel_mchbar_regs.h"
#include "intel_pci_config.h" #include "intel_pci_config.h"
#include "intel_pcode.h" #include "intel_pcode.h"
@ -49,6 +49,7 @@
#include "intel_vdsc.h" #include "intel_vdsc.h"
#include "skl_watermark.h" #include "skl_watermark.h"
#include "skl_watermark_regs.h" #include "skl_watermark_regs.h"
#include "vlv_clock.h"
#include "vlv_dsi.h" #include "vlv_dsi.h"
#include "vlv_sideband.h" #include "vlv_sideband.h"
@ -132,8 +133,8 @@ struct intel_cdclk_state {
*/ */
struct intel_cdclk_config actual; struct intel_cdclk_config actual;
/* minimum acceptable cdclk to satisfy bandwidth requirements */ /* minimum acceptable cdclk to satisfy DBUF bandwidth requirements */
int bw_min_cdclk; int dbuf_bw_min_cdclk;
/* minimum acceptable cdclk for each pipe */ /* minimum acceptable cdclk for each pipe */
int min_cdclk[I915_MAX_PIPES]; int min_cdclk[I915_MAX_PIPES];
/* minimum acceptable voltage level for each pipe */ /* minimum acceptable voltage level for each pipe */
@ -145,6 +146,9 @@ struct intel_cdclk_state {
/* forced minimum cdclk for glk+ audio w/a */ /* forced minimum cdclk for glk+ audio w/a */
int force_min_cdclk; int force_min_cdclk;
/* bitmask of enabled pipes */
u8 enabled_pipes;
/* bitmask of active pipes */ /* bitmask of active pipes */
u8 active_pipes; u8 active_pipes;
@ -563,8 +567,7 @@ static void hsw_get_cdclk(struct intel_display *display,
static int vlv_calc_cdclk(struct intel_display *display, int min_cdclk) static int vlv_calc_cdclk(struct intel_display *display, int min_cdclk)
{ {
struct drm_i915_private *dev_priv = to_i915(display->drm); int freq_320 = (vlv_clock_get_hpll_vco(display->drm) << 1) % 320000 != 0 ?
int freq_320 = (dev_priv->hpll_freq << 1) % 320000 != 0 ?
333333 : 320000; 333333 : 320000;
/* /*
@ -584,8 +587,6 @@ static int vlv_calc_cdclk(struct intel_display *display, int min_cdclk)
static u8 vlv_calc_voltage_level(struct intel_display *display, int cdclk) static u8 vlv_calc_voltage_level(struct intel_display *display, int cdclk)
{ {
struct drm_i915_private *dev_priv = to_i915(display->drm);
if (display->platform.valleyview) { if (display->platform.valleyview) {
if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */ if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */
return 2; return 2;
@ -599,7 +600,7 @@ static u8 vlv_calc_voltage_level(struct intel_display *display, int cdclk)
* hardware has shown that we just need to write the desired * hardware has shown that we just need to write the desired
* CCK divider into the Punit register. * CCK divider into the Punit register.
*/ */
return DIV_ROUND_CLOSEST(dev_priv->hpll_freq << 1, cdclk) - 1; return DIV_ROUND_CLOSEST(vlv_clock_get_hpll_vco(display->drm) << 1, cdclk) - 1;
} }
} }
@ -608,17 +609,12 @@ static void vlv_get_cdclk(struct intel_display *display,
{ {
u32 val; u32 val;
vlv_iosf_sb_get(display->drm, BIT(VLV_IOSF_SB_CCK) | BIT(VLV_IOSF_SB_PUNIT)); cdclk_config->vco = vlv_clock_get_hpll_vco(display->drm);
cdclk_config->cdclk = vlv_clock_get_cdclk(display->drm);
cdclk_config->vco = vlv_get_hpll_vco(display->drm);
cdclk_config->cdclk = vlv_get_cck_clock(display->drm, "cdclk",
CCK_DISPLAY_CLOCK_CONTROL,
cdclk_config->vco);
vlv_punit_get(display->drm);
val = vlv_punit_read(display->drm, PUNIT_REG_DSPSSPM); val = vlv_punit_read(display->drm, PUNIT_REG_DSPSSPM);
vlv_punit_put(display->drm);
vlv_iosf_sb_put(display->drm,
BIT(VLV_IOSF_SB_CCK) | BIT(VLV_IOSF_SB_PUNIT));
if (display->platform.valleyview) if (display->platform.valleyview)
cdclk_config->voltage_level = (val & DSPFREQGUAR_MASK) >> cdclk_config->voltage_level = (val & DSPFREQGUAR_MASK) >>
@ -630,7 +626,6 @@ static void vlv_get_cdclk(struct intel_display *display,
static void vlv_program_pfi_credits(struct intel_display *display) static void vlv_program_pfi_credits(struct intel_display *display)
{ {
struct drm_i915_private *dev_priv = to_i915(display->drm);
unsigned int credits, default_credits; unsigned int credits, default_credits;
if (display->platform.cherryview) if (display->platform.cherryview)
@ -638,7 +633,7 @@ static void vlv_program_pfi_credits(struct intel_display *display)
else else
default_credits = PFI_CREDIT(8); default_credits = PFI_CREDIT(8);
if (display->cdclk.hw.cdclk >= dev_priv->czclk_freq) { if (display->cdclk.hw.cdclk >= vlv_clock_get_czclk(display->drm)) {
/* CHV suggested value is 31 or 63 */ /* CHV suggested value is 31 or 63 */
if (display->platform.cherryview) if (display->platform.cherryview)
credits = PFI_CREDIT_63; credits = PFI_CREDIT_63;
@ -670,7 +665,6 @@ static void vlv_set_cdclk(struct intel_display *display,
const struct intel_cdclk_config *cdclk_config, const struct intel_cdclk_config *cdclk_config,
enum pipe pipe) enum pipe pipe)
{ {
struct drm_i915_private *dev_priv = to_i915(display->drm);
int cdclk = cdclk_config->cdclk; int cdclk = cdclk_config->cdclk;
u32 val, cmd = cdclk_config->voltage_level; u32 val, cmd = cdclk_config->voltage_level;
intel_wakeref_t wakeref; intel_wakeref_t wakeref;
@ -715,7 +709,7 @@ static void vlv_set_cdclk(struct intel_display *display,
if (cdclk == 400000) { if (cdclk == 400000) {
u32 divider; u32 divider;
divider = DIV_ROUND_CLOSEST(dev_priv->hpll_freq << 1, divider = DIV_ROUND_CLOSEST(vlv_clock_get_hpll_vco(display->drm) << 1,
cdclk) - 1; cdclk) - 1;
/* adjust cdclk divider */ /* adjust cdclk divider */
@ -1567,7 +1561,7 @@ static int bxt_calc_cdclk(struct intel_display *display, int min_cdclk)
drm_WARN(display->drm, 1, drm_WARN(display->drm, 1,
"Cannot satisfy minimum cdclk %d with refclk %u\n", "Cannot satisfy minimum cdclk %d with refclk %u\n",
min_cdclk, display->cdclk.hw.ref); min_cdclk, display->cdclk.hw.ref);
return 0; return display->cdclk.max_cdclk_freq;
} }
static int bxt_calc_cdclk_pll_vco(struct intel_display *display, int cdclk) static int bxt_calc_cdclk_pll_vco(struct intel_display *display, int cdclk)
@ -2600,6 +2594,12 @@ static void intel_set_cdclk(struct intel_display *display,
} }
} }
static bool dg2_power_well_count(struct intel_display *display,
const struct intel_cdclk_state *cdclk_state)
{
return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0;
}
static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
@ -2612,16 +2612,16 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state)
if (!intel_cdclk_changed(&old_cdclk_state->actual, if (!intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual) && &new_cdclk_state->actual) &&
new_cdclk_state->active_pipes == dg2_power_well_count(display, old_cdclk_state) ==
old_cdclk_state->active_pipes) dg2_power_well_count(display, new_cdclk_state))
return; return;
/* According to "Sequence Before Frequency Change", voltage level set to 0x3 */ /* According to "Sequence Before Frequency Change", voltage level set to 0x3 */
voltage_level = DISPLAY_TO_PCODE_VOLTAGE_MAX; voltage_level = DISPLAY_TO_PCODE_VOLTAGE_MAX;
change_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk; change_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk;
update_pipe_count = hweight8(new_cdclk_state->active_pipes) > update_pipe_count = dg2_power_well_count(display, new_cdclk_state) >
hweight8(old_cdclk_state->active_pipes); dg2_power_well_count(display, old_cdclk_state);
/* /*
* According to "Sequence Before Frequency Change", * According to "Sequence Before Frequency Change",
@ -2639,7 +2639,7 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state)
* no action if it is decreasing, before the change * no action if it is decreasing, before the change
*/ */
if (update_pipe_count) if (update_pipe_count)
num_active_pipes = hweight8(new_cdclk_state->active_pipes); num_active_pipes = dg2_power_well_count(display, new_cdclk_state);
intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk, intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk,
change_cdclk, update_pipe_count); change_cdclk, update_pipe_count);
@ -2659,8 +2659,8 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state)
voltage_level = new_cdclk_state->actual.voltage_level; voltage_level = new_cdclk_state->actual.voltage_level;
update_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk; update_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk;
update_pipe_count = hweight8(new_cdclk_state->active_pipes) < update_pipe_count = dg2_power_well_count(display, new_cdclk_state) <
hweight8(old_cdclk_state->active_pipes); dg2_power_well_count(display, old_cdclk_state);
/* /*
* According to "Sequence After Frequency Change", * According to "Sequence After Frequency Change",
@ -2676,7 +2676,7 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state)
* no action if it is increasing, after the change * no action if it is increasing, after the change
*/ */
if (update_pipe_count) if (update_pipe_count)
num_active_pipes = hweight8(new_cdclk_state->active_pipes); num_active_pipes = dg2_power_well_count(display, new_cdclk_state);
intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk, intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk,
update_cdclk, update_pipe_count); update_cdclk, update_pipe_count);
@ -2711,6 +2711,9 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state)
struct intel_cdclk_config cdclk_config; struct intel_cdclk_config cdclk_config;
enum pipe pipe; enum pipe pipe;
if (!new_cdclk_state)
return;
if (!intel_cdclk_changed(&old_cdclk_state->actual, if (!intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual)) &new_cdclk_state->actual))
return; return;
@ -2763,6 +2766,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state)
intel_atomic_get_new_cdclk_state(state); intel_atomic_get_new_cdclk_state(state);
enum pipe pipe; enum pipe pipe;
if (!new_cdclk_state)
return;
if (!intel_cdclk_changed(&old_cdclk_state->actual, if (!intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual)) &new_cdclk_state->actual))
return; return;
@ -2800,16 +2806,20 @@ static int intel_cdclk_guardband(struct intel_display *display)
return 90; return 90;
} }
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, int pixel_rate)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
int ppc = intel_cdclk_ppc(display, crtc_state->double_wide); int ppc = intel_cdclk_ppc(display, crtc_state->double_wide);
int guardband = intel_cdclk_guardband(display); int guardband = intel_cdclk_guardband(display);
int pixel_rate = crtc_state->pixel_rate;
return DIV_ROUND_UP(pixel_rate * 100, guardband * ppc); return DIV_ROUND_UP(pixel_rate * 100, guardband * ppc);
} }
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);
}
static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state) static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)
{ {
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
@ -2818,12 +2828,12 @@ static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)
int min_cdclk = 0; int min_cdclk = 0;
for_each_intel_plane_on_crtc(display->drm, crtc, plane) for_each_intel_plane_on_crtc(display->drm, crtc, plane)
min_cdclk = max(min_cdclk, crtc_state->min_cdclk[plane->id]); min_cdclk = max(min_cdclk, crtc_state->plane_min_cdclk[plane->id]);
return min_cdclk; return min_cdclk;
} }
static int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state) int intel_crtc_min_cdclk(const struct intel_crtc_state *crtc_state)
{ {
int min_cdclk; int min_cdclk;
@ -2831,6 +2841,8 @@ static int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_stat
return 0; return 0;
min_cdclk = intel_pixel_rate_to_cdclk(crtc_state); min_cdclk = intel_pixel_rate_to_cdclk(crtc_state);
min_cdclk = max(min_cdclk, intel_crtc_bw_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, intel_fbc_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, hsw_ips_min_cdclk(crtc_state)); min_cdclk = max(min_cdclk, hsw_ips_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, intel_audio_min_cdclk(crtc_state)); min_cdclk = max(min_cdclk, intel_audio_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, vlv_dsi_min_cdclk(crtc_state)); min_cdclk = max(min_cdclk, vlv_dsi_min_cdclk(crtc_state));
@ -2840,51 +2852,110 @@ static int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_stat
return min_cdclk; return min_cdclk;
} }
static int intel_cdclk_update_crtc_min_cdclk(struct intel_atomic_state *state,
struct intel_crtc *crtc,
int old_min_cdclk, int new_min_cdclk,
bool *need_cdclk_calc)
{
struct intel_display *display = to_intel_display(state);
struct intel_cdclk_state *cdclk_state;
bool allow_cdclk_decrease = intel_any_crtc_needs_modeset(state);
int ret;
if (new_min_cdclk == old_min_cdclk)
return 0;
if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
return 0;
cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
old_min_cdclk = cdclk_state->min_cdclk[crtc->pipe];
if (new_min_cdclk == old_min_cdclk)
return 0;
if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
return 0;
cdclk_state->min_cdclk[crtc->pipe] = new_min_cdclk;
ret = intel_atomic_lock_global_state(&cdclk_state->base);
if (ret)
return ret;
*need_cdclk_calc = true;
drm_dbg_kms(display->drm,
"[CRTC:%d:%s] min cdclk: %d kHz -> %d kHz\n",
crtc->base.base.id, crtc->base.name,
old_min_cdclk, new_min_cdclk);
return 0;
}
int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
int old_min_cdclk, int new_min_cdclk,
bool *need_cdclk_calc)
{
struct intel_display *display = to_intel_display(state);
struct intel_cdclk_state *cdclk_state;
bool allow_cdclk_decrease = intel_any_crtc_needs_modeset(state);
int ret;
if (new_min_cdclk == old_min_cdclk)
return 0;
if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
return 0;
cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
old_min_cdclk = cdclk_state->dbuf_bw_min_cdclk;
if (new_min_cdclk == old_min_cdclk)
return 0;
if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
return 0;
cdclk_state->dbuf_bw_min_cdclk = new_min_cdclk;
ret = intel_atomic_lock_global_state(&cdclk_state->base);
if (ret)
return ret;
*need_cdclk_calc = true;
drm_dbg_kms(display->drm,
"dbuf bandwidth min cdclk: %d kHz -> %d kHz\n",
old_min_cdclk, new_min_cdclk);
return 0;
}
static bool glk_cdclk_audio_wa_needed(struct intel_display *display,
const struct intel_cdclk_state *cdclk_state)
{
return display->platform.geminilake &&
cdclk_state->enabled_pipes &&
!is_power_of_2(cdclk_state->enabled_pipes);
}
static int intel_compute_min_cdclk(struct intel_atomic_state *state) static int intel_compute_min_cdclk(struct intel_atomic_state *state)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
struct intel_cdclk_state *cdclk_state = struct intel_cdclk_state *cdclk_state =
intel_atomic_get_new_cdclk_state(state); intel_atomic_get_new_cdclk_state(state);
const struct intel_bw_state *bw_state;
struct intel_crtc *crtc;
struct intel_crtc_state *crtc_state;
int min_cdclk, i;
enum pipe pipe; enum pipe pipe;
int min_cdclk;
for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { min_cdclk = cdclk_state->force_min_cdclk;
int ret; min_cdclk = max(min_cdclk, cdclk_state->dbuf_bw_min_cdclk);
min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
if (min_cdclk < 0)
return min_cdclk;
if (cdclk_state->min_cdclk[crtc->pipe] == min_cdclk)
continue;
cdclk_state->min_cdclk[crtc->pipe] = min_cdclk;
ret = intel_atomic_lock_global_state(&cdclk_state->base);
if (ret)
return ret;
}
bw_state = intel_atomic_get_new_bw_state(state);
if (bw_state) {
min_cdclk = intel_bw_min_cdclk(display, bw_state);
if (cdclk_state->bw_min_cdclk != min_cdclk) {
int ret;
cdclk_state->bw_min_cdclk = min_cdclk;
ret = intel_atomic_lock_global_state(&cdclk_state->base);
if (ret)
return ret;
}
}
min_cdclk = max(cdclk_state->force_min_cdclk,
cdclk_state->bw_min_cdclk);
for_each_pipe(display, pipe) for_each_pipe(display, pipe)
min_cdclk = max(min_cdclk, cdclk_state->min_cdclk[pipe]); min_cdclk = max(min_cdclk, cdclk_state->min_cdclk[pipe]);
@ -2896,8 +2967,7 @@ static int intel_compute_min_cdclk(struct intel_atomic_state *state)
* by changing the cd2x divider (see glk_cdclk_table[]) and * by changing the cd2x divider (see glk_cdclk_table[]) and
* thus a full modeset won't be needed then. * thus a full modeset won't be needed then.
*/ */
if (display->platform.geminilake && cdclk_state->active_pipes && if (glk_cdclk_audio_wa_needed(display, cdclk_state))
!is_power_of_2(cdclk_state->active_pipes))
min_cdclk = max(min_cdclk, 2 * 96000); min_cdclk = max(min_cdclk, 2 * 96000);
if (min_cdclk > display->cdclk.max_cdclk_freq) { if (min_cdclk > display->cdclk.max_cdclk_freq) {
@ -3183,38 +3253,66 @@ intel_atomic_get_cdclk_state(struct intel_atomic_state *state)
return to_intel_cdclk_state(cdclk_state); return to_intel_cdclk_state(cdclk_state);
} }
int intel_cdclk_atomic_check(struct intel_atomic_state *state, static int intel_cdclk_modeset_checks(struct intel_atomic_state *state,
bool *need_cdclk_calc) bool *need_cdclk_calc)
{ {
struct intel_display *display = to_intel_display(state);
const struct intel_cdclk_state *old_cdclk_state; const struct intel_cdclk_state *old_cdclk_state;
const struct intel_cdclk_state *new_cdclk_state; struct intel_cdclk_state *new_cdclk_state;
struct intel_plane_state __maybe_unused *plane_state;
struct intel_plane *plane;
int ret; int ret;
int i;
/* if (!intel_any_crtc_enable_changed(state) &&
* active_planes bitmask has been updated, and potentially affected !intel_any_crtc_active_changed(state))
* planes are part of the state. We can now compute the minimum cdclk return 0;
* for each plane.
*/
for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
ret = intel_plane_calc_min_cdclk(state, plane, need_cdclk_calc);
if (ret)
return ret;
}
ret = intel_bw_calc_min_cdclk(state, need_cdclk_calc); new_cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(new_cdclk_state))
return PTR_ERR(new_cdclk_state);
old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
new_cdclk_state->enabled_pipes =
intel_calc_enabled_pipes(state, old_cdclk_state->enabled_pipes);
new_cdclk_state->active_pipes =
intel_calc_active_pipes(state, old_cdclk_state->active_pipes);
ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
if (ret) if (ret)
return ret; return ret;
old_cdclk_state = intel_atomic_get_old_cdclk_state(state); if (!old_cdclk_state->active_pipes != !new_cdclk_state->active_pipes)
new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
if (new_cdclk_state &&
old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk)
*need_cdclk_calc = true; *need_cdclk_calc = true;
if (glk_cdclk_audio_wa_needed(display, old_cdclk_state) !=
glk_cdclk_audio_wa_needed(display, new_cdclk_state))
*need_cdclk_calc = true;
if (dg2_power_well_count(display, old_cdclk_state) !=
dg2_power_well_count(display, new_cdclk_state))
*need_cdclk_calc = true;
return 0;
}
static int intel_crtcs_calc_min_cdclk(struct intel_atomic_state *state,
bool *need_cdclk_calc)
{
const struct intel_crtc_state *old_crtc_state;
const struct intel_crtc_state *new_crtc_state;
struct intel_crtc *crtc;
int i, ret;
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
ret = intel_cdclk_update_crtc_min_cdclk(state, crtc,
old_crtc_state->min_cdclk,
new_crtc_state->min_cdclk,
need_cdclk_calc);
if (ret)
return ret;
}
return 0; return 0;
} }
@ -3250,18 +3348,17 @@ static bool intel_cdclk_need_serialize(struct intel_display *display,
const struct intel_cdclk_state *old_cdclk_state, const struct intel_cdclk_state *old_cdclk_state,
const struct intel_cdclk_state *new_cdclk_state) const struct intel_cdclk_state *new_cdclk_state)
{ {
bool power_well_cnt_changed = hweight8(old_cdclk_state->active_pipes) !=
hweight8(new_cdclk_state->active_pipes);
bool cdclk_changed = intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual);
/* /*
* We need to poke hw for gen >= 12, because we notify PCode if * We need to poke hw for DG2, because we notify PCode if
* pipe power well count changes. * pipe power well count changes.
*/ */
return cdclk_changed || (display->platform.dg2 && power_well_cnt_changed); return intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual) ||
dg2_power_well_count(display, old_cdclk_state) !=
dg2_power_well_count(display, new_cdclk_state);
} }
int intel_modeset_calc_cdclk(struct intel_atomic_state *state) static int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
const struct intel_cdclk_state *old_cdclk_state; const struct intel_cdclk_state *old_cdclk_state;
@ -3275,9 +3372,6 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
old_cdclk_state = intel_atomic_get_old_cdclk_state(state); old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
new_cdclk_state->active_pipes =
intel_calc_active_pipes(state, old_cdclk_state->active_pipes);
ret = intel_cdclk_modeset_calc_cdclk(state); ret = intel_cdclk_modeset_calc_cdclk(state);
if (ret) if (ret)
return ret; return ret;
@ -3290,9 +3384,7 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
ret = intel_atomic_serialize_global_state(&new_cdclk_state->base); ret = intel_atomic_serialize_global_state(&new_cdclk_state->base);
if (ret) if (ret)
return ret; return ret;
} else if (old_cdclk_state->active_pipes != new_cdclk_state->active_pipes || } else if (intel_cdclk_changed(&old_cdclk_state->logical,
old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk ||
intel_cdclk_changed(&old_cdclk_state->logical,
&new_cdclk_state->logical)) { &new_cdclk_state->logical)) {
ret = intel_atomic_lock_global_state(&new_cdclk_state->base); ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
if (ret) if (ret)
@ -3374,14 +3466,55 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
return 0; return 0;
} }
int intel_cdclk_atomic_check(struct intel_atomic_state *state)
{
const struct intel_cdclk_state *old_cdclk_state;
struct intel_cdclk_state *new_cdclk_state;
bool need_cdclk_calc = false;
int ret;
ret = intel_cdclk_modeset_checks(state, &need_cdclk_calc);
if (ret)
return ret;
ret = intel_crtcs_calc_min_cdclk(state, &need_cdclk_calc);
if (ret)
return ret;
ret = intel_dbuf_bw_calc_min_cdclk(state, &need_cdclk_calc);
if (ret)
return ret;
old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
if (new_cdclk_state &&
old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk) {
ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
if (ret)
return ret;
need_cdclk_calc = true;
}
if (need_cdclk_calc) {
ret = intel_modeset_calc_cdclk(state);
if (ret)
return ret;
}
return 0;
}
void intel_cdclk_update_hw_state(struct intel_display *display) void intel_cdclk_update_hw_state(struct intel_display *display)
{ {
const struct intel_bw_state *bw_state = const struct intel_dbuf_bw_state *dbuf_bw_state =
to_intel_bw_state(display->bw.obj.state); to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
struct intel_cdclk_state *cdclk_state = struct intel_cdclk_state *cdclk_state =
to_intel_cdclk_state(display->cdclk.obj.state); to_intel_cdclk_state(display->cdclk.obj.state);
struct intel_crtc *crtc; struct intel_crtc *crtc;
cdclk_state->enabled_pipes = 0;
cdclk_state->active_pipes = 0; cdclk_state->active_pipes = 0;
for_each_intel_crtc(display->drm, crtc) { for_each_intel_crtc(display->drm, crtc) {
@ -3389,14 +3522,16 @@ void intel_cdclk_update_hw_state(struct intel_display *display)
to_intel_crtc_state(crtc->base.state); to_intel_crtc_state(crtc->base.state);
enum pipe pipe = crtc->pipe; enum pipe pipe = crtc->pipe;
if (crtc_state->hw.enable)
cdclk_state->enabled_pipes |= BIT(pipe);
if (crtc_state->hw.active) if (crtc_state->hw.active)
cdclk_state->active_pipes |= BIT(pipe); cdclk_state->active_pipes |= BIT(pipe);
cdclk_state->min_cdclk[pipe] = intel_crtc_compute_min_cdclk(crtc_state); cdclk_state->min_cdclk[pipe] = crtc_state->min_cdclk;
cdclk_state->min_voltage_level[pipe] = crtc_state->min_voltage_level; cdclk_state->min_voltage_level[pipe] = crtc_state->min_voltage_level;
} }
cdclk_state->bw_min_cdclk = intel_bw_min_cdclk(display, bw_state); cdclk_state->dbuf_bw_min_cdclk = intel_dbuf_bw_min_cdclk(display, dbuf_bw_state);
} }
void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc) void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc)
@ -3565,13 +3700,6 @@ static int pch_rawclk(struct intel_display *display)
return (intel_de_read(display, PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK) * 1000; return (intel_de_read(display, PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK) * 1000;
} }
static int vlv_hrawclk(struct intel_display *display)
{
/* RAWCLK_FREQ_VLV register updated from power well code */
return vlv_get_cck_clock_hpll(display->drm, "hrawclk",
CCK_DISPLAY_REF_CLOCK_CONTROL);
}
static int i9xx_hrawclk(struct intel_display *display) static int i9xx_hrawclk(struct intel_display *display)
{ {
struct drm_i915_private *i915 = to_i915(display->drm); struct drm_i915_private *i915 = to_i915(display->drm);
@ -3605,7 +3733,7 @@ u32 intel_read_rawclk(struct intel_display *display)
else if (HAS_PCH_SPLIT(display)) else if (HAS_PCH_SPLIT(display))
freq = pch_rawclk(display); freq = pch_rawclk(display);
else if (display->platform.valleyview || display->platform.cherryview) else if (display->platform.valleyview || display->platform.cherryview)
freq = vlv_hrawclk(display); freq = vlv_clock_get_hrawclk(display->drm);
else if (DISPLAY_VER(display) >= 3) else if (DISPLAY_VER(display) >= 3)
freq = i9xx_hrawclk(display); freq = i9xx_hrawclk(display);
else else
@ -3897,11 +4025,6 @@ int intel_cdclk_min_cdclk(const struct intel_cdclk_state *cdclk_state, enum pipe
return cdclk_state->min_cdclk[pipe]; return cdclk_state->min_cdclk[pipe];
} }
int intel_cdclk_bw_min_cdclk(const struct intel_cdclk_state *cdclk_state)
{
return cdclk_state->bw_min_cdclk;
}
bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state) bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state)
{ {
const struct intel_cdclk_state *new_cdclk_state, *old_cdclk_state; const struct intel_cdclk_state *new_cdclk_state, *old_cdclk_state;
@ -3933,3 +4056,75 @@ void intel_cdclk_read_hw(struct intel_display *display)
cdclk_state->actual = display->cdclk.hw; cdclk_state->actual = display->cdclk.hw;
cdclk_state->logical = display->cdclk.hw; cdclk_state->logical = display->cdclk.hw;
} }
static int calc_cdclk(const struct intel_crtc_state *crtc_state, int min_cdclk)
{
struct intel_display *display = to_intel_display(crtc_state);
if (DISPLAY_VER(display) >= 10 || display->platform.broxton) {
return bxt_calc_cdclk(display, min_cdclk);
} else if (DISPLAY_VER(display) == 9) {
int vco;
vco = display->cdclk.skl_preferred_vco_freq;
if (vco == 0)
vco = 8100000;
return skl_calc_cdclk(min_cdclk, vco);
} else if (display->platform.broadwell) {
return bdw_calc_cdclk(min_cdclk);
} else if (display->platform.cherryview || display->platform.valleyview) {
return vlv_calc_cdclk(display, min_cdclk);
} else {
return display->cdclk.max_cdclk_freq;
}
}
static unsigned int _intel_cdclk_prefill_adj(const struct intel_crtc_state *crtc_state,
int clock, int min_cdclk)
{
struct intel_display *display = to_intel_display(crtc_state);
int ppc = intel_cdclk_ppc(display, crtc_state->double_wide);
int cdclk = calc_cdclk(crtc_state, min_cdclk);
return min(0x10000, DIV_ROUND_UP_ULL((u64)clock << 16, ppc * cdclk));
}
unsigned int intel_cdclk_prefill_adjustment(const struct intel_crtc_state *crtc_state)
{
/* FIXME use the actual min_cdclk for the pipe here */
return intel_cdclk_prefill_adjustment_worst(crtc_state);
}
unsigned int intel_cdclk_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state)
{
int clock = crtc_state->hw.pipe_mode.crtc_clock;
int min_cdclk;
/*
* FIXME could perhaps consider a few more of the factors
* that go the per-crtc min_cdclk. Namely anything that
* only changes during full modesets.
*
* FIXME this assumes 1:1 scaling, but the other _worst() stuff
* assumes max downscaling, so the final result will be
* unrealistically bad. Figure out where the actual maximum value
* lies and use that to compute a more realistic worst case
* estimate...
*/
min_cdclk = _intel_pixel_rate_to_cdclk(crtc_state, clock);
return _intel_cdclk_prefill_adj(crtc_state, clock, min_cdclk);
}
int intel_cdclk_min_cdclk_for_prefill(const struct intel_crtc_state *crtc_state,
unsigned int prefill_lines_unadjusted,
unsigned int prefill_lines_available)
{
struct intel_display *display = to_intel_display(crtc_state);
const struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode;
int ppc = intel_cdclk_ppc(display, crtc_state->double_wide);
return DIV_ROUND_UP_ULL(mul_u32_u32(pipe_mode->crtc_clock, prefill_lines_unadjusted),
ppc * prefill_lines_available);
}

View File

@ -38,16 +38,17 @@ void intel_set_cdclk_post_plane_update(struct intel_atomic_state *state);
void intel_cdclk_dump_config(struct intel_display *display, void intel_cdclk_dump_config(struct intel_display *display,
const struct intel_cdclk_config *cdclk_config, const struct intel_cdclk_config *cdclk_config,
const char *context); const char *context);
int intel_modeset_calc_cdclk(struct intel_atomic_state *state);
void intel_cdclk_get_cdclk(struct intel_display *display, void intel_cdclk_get_cdclk(struct intel_display *display,
struct intel_cdclk_config *cdclk_config); struct intel_cdclk_config *cdclk_config);
int intel_cdclk_atomic_check(struct intel_atomic_state *state, int intel_cdclk_atomic_check(struct intel_atomic_state *state);
bool *need_cdclk_calc);
int intel_cdclk_state_set_joined_mbus(struct intel_atomic_state *state, bool joined_mbus); int intel_cdclk_state_set_joined_mbus(struct intel_atomic_state *state, bool joined_mbus);
struct intel_cdclk_state * struct intel_cdclk_state *
intel_atomic_get_cdclk_state(struct intel_atomic_state *state); intel_atomic_get_cdclk_state(struct intel_atomic_state *state);
void intel_cdclk_update_hw_state(struct intel_display *display); void intel_cdclk_update_hw_state(struct intel_display *display);
void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc); void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc);
int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
int old_min_cdclk, int new_min_cdclk,
bool *need_cdclk_calc);
#define to_intel_cdclk_state(global_state) \ #define to_intel_cdclk_state(global_state) \
container_of_const((global_state), struct intel_cdclk_state, base) container_of_const((global_state), struct intel_cdclk_state, base)
@ -64,9 +65,16 @@ int intel_cdclk_logical(const struct intel_cdclk_state *cdclk_state);
int intel_cdclk_actual(const struct intel_cdclk_state *cdclk_state); int intel_cdclk_actual(const struct intel_cdclk_state *cdclk_state);
int intel_cdclk_actual_voltage_level(const struct intel_cdclk_state *cdclk_state); int intel_cdclk_actual_voltage_level(const struct intel_cdclk_state *cdclk_state);
int intel_cdclk_min_cdclk(const struct intel_cdclk_state *cdclk_state, enum pipe pipe); int intel_cdclk_min_cdclk(const struct intel_cdclk_state *cdclk_state, enum pipe pipe);
int intel_cdclk_bw_min_cdclk(const struct intel_cdclk_state *cdclk_state);
bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state); bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state);
void intel_cdclk_force_min_cdclk(struct intel_cdclk_state *cdclk_state, int force_min_cdclk); void intel_cdclk_force_min_cdclk(struct intel_cdclk_state *cdclk_state, int force_min_cdclk);
void intel_cdclk_read_hw(struct intel_display *display); void intel_cdclk_read_hw(struct intel_display *display);
unsigned int intel_cdclk_prefill_adjustment(const struct intel_crtc_state *crtc_state);
unsigned int intel_cdclk_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state);
int intel_cdclk_min_cdclk_for_prefill(const struct intel_crtc_state *crtc_state,
unsigned int prefill_lines_unadjusted,
unsigned int prefill_lines_available);
int intel_crtc_min_cdclk(const struct intel_crtc_state *crtc_state);
#endif /* __INTEL_CDCLK_H__ */ #endif /* __INTEL_CDCLK_H__ */

View File

@ -24,12 +24,12 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "i9xx_plane_regs.h" #include "i9xx_plane_regs.h"
#include "intel_color.h" #include "intel_color.h"
#include "intel_color_regs.h" #include "intel_color_regs.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dsb.h" #include "intel_dsb.h"
#include "intel_vrr.h" #include "intel_vrr.h"
@ -2013,7 +2013,7 @@ void intel_color_prepare_commit(struct intel_atomic_state *state,
if (crtc_state->use_dsb && intel_color_uses_chained_dsb(crtc_state)) { if (crtc_state->use_dsb && intel_color_uses_chained_dsb(crtc_state)) {
intel_vrr_send_push(crtc_state->dsb_color, crtc_state); intel_vrr_send_push(crtc_state->dsb_color, crtc_state);
intel_dsb_wait_vblank_delay(state, crtc_state->dsb_color); intel_dsb_wait_for_delayed_vblank(state, crtc_state->dsb_color);
intel_vrr_check_push_sent(crtc_state->dsb_color, crtc_state); intel_vrr_check_push_sent(crtc_state->dsb_color, crtc_state);
intel_dsb_interrupt(crtc_state->dsb_color); intel_dsb_interrupt(crtc_state->dsb_color);
} }

View File

@ -5,12 +5,12 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_combo_phy.h" #include "intel_combo_phy.h"
#include "intel_combo_phy_regs.h" #include "intel_combo_phy_regs.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#define for_each_combo_phy(__display, __phy) \ #define for_each_combo_phy(__display, __phy) \
for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \ for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \

View File

@ -31,7 +31,7 @@
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_utils.h" #include "i915_utils.h" /* for i915_inject_probe_failure() */
#include "intel_connector.h" #include "intel_connector.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_debugfs.h" #include "intel_display_debugfs.h"

View File

@ -84,8 +84,13 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
if (!crtc->active) if (!crtc->active)
return 0; return 0;
if (!vblank->max_vblank_count) if (!vblank->max_vblank_count) {
return (u32)drm_crtc_accurate_vblank_count(&crtc->base); /* On preempt-rt we cannot take the vblank spinlock since this function is called from tracepoints */
if (IS_ENABLED(CONFIG_PREEMPT_RT))
return (u32)drm_crtc_vblank_count(&crtc->base);
else
return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
}
return crtc->base.funcs->get_vblank_counter(&crtc->base); return crtc->base.funcs->get_vblank_counter(&crtc->base);
} }
@ -390,6 +395,9 @@ int intel_crtc_init(struct intel_display *display, enum pipe pipe)
drm_WARN_ON(display->drm, drm_crtc_index(&crtc->base) != crtc->pipe); drm_WARN_ON(display->drm, drm_crtc_index(&crtc->base) != crtc->pipe);
if (HAS_CASF(display))
drm_crtc_create_sharpness_strength_property(&crtc->base);
return 0; return 0;
fail: fail:
@ -748,3 +756,89 @@ void intel_pipe_update_end(struct intel_atomic_state *state,
out: out:
intel_psr_unlock(new_crtc_state); intel_psr_unlock(new_crtc_state);
} }
bool intel_crtc_enable_changed(const struct intel_crtc_state *old_crtc_state,
const struct intel_crtc_state *new_crtc_state)
{
return old_crtc_state->hw.enable != new_crtc_state->hw.enable;
}
bool intel_any_crtc_enable_changed(struct intel_atomic_state *state)
{
const struct intel_crtc_state *old_crtc_state, *new_crtc_state;
struct intel_crtc *crtc;
int i;
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
if (intel_crtc_enable_changed(old_crtc_state, new_crtc_state))
return true;
}
return false;
}
bool intel_crtc_active_changed(const struct intel_crtc_state *old_crtc_state,
const struct intel_crtc_state *new_crtc_state)
{
return old_crtc_state->hw.active != new_crtc_state->hw.active;
}
bool intel_any_crtc_active_changed(struct intel_atomic_state *state)
{
const struct intel_crtc_state *old_crtc_state, *new_crtc_state;
struct intel_crtc *crtc;
int i;
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
if (intel_crtc_active_changed(old_crtc_state, new_crtc_state))
return true;
}
return false;
}
unsigned int intel_crtc_bw_num_active_planes(const struct intel_crtc_state *crtc_state)
{
/*
* We assume cursors are small enough
* to not cause bandwidth problems.
*/
return hweight8(crtc_state->active_planes & ~BIT(PLANE_CURSOR));
}
unsigned int intel_crtc_bw_data_rate(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
unsigned int data_rate = 0;
enum plane_id plane_id;
for_each_plane_id_on_crtc(crtc, plane_id) {
/*
* We assume cursors are small enough
* to not cause bandwidth problems.
*/
if (plane_id == PLANE_CURSOR)
continue;
data_rate += crtc_state->data_rate[plane_id];
if (DISPLAY_VER(display) < 11)
data_rate += crtc_state->data_rate_y[plane_id];
}
return data_rate;
}
/* "Maximum Pipe Read Bandwidth" */
int intel_crtc_bw_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
if (DISPLAY_VER(display) < 12)
return 0;
return DIV_ROUND_UP_ULL(mul_u32_u32(intel_crtc_bw_data_rate(crtc_state), 10), 512);
}

View File

@ -58,4 +58,15 @@ void intel_wait_for_vblank_if_active(struct intel_display *display,
enum pipe pipe); enum pipe pipe);
void intel_crtc_wait_for_next_vblank(struct intel_crtc *crtc); void intel_crtc_wait_for_next_vblank(struct intel_crtc *crtc);
bool intel_any_crtc_enable_changed(struct intel_atomic_state *state);
bool intel_crtc_enable_changed(const struct intel_crtc_state *old_crtc_state,
const struct intel_crtc_state *new_crtc_state);
bool intel_any_crtc_active_changed(struct intel_atomic_state *state);
bool intel_crtc_active_changed(const struct intel_crtc_state *old_crtc_state,
const struct intel_crtc_state *new_crtc_state);
unsigned int intel_crtc_bw_num_active_planes(const struct intel_crtc_state *crtc_state);
unsigned int intel_crtc_bw_data_rate(const struct intel_crtc_state *crtc_state);
int intel_crtc_bw_min_cdclk(const struct intel_crtc_state *crtc_state);
#endif #endif

View File

@ -289,10 +289,9 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
drm_printf(&p, "scanline offset: %d\n", drm_printf(&p, "scanline offset: %d\n",
intel_crtc_scanline_offset(pipe_config)); intel_crtc_scanline_offset(pipe_config));
drm_printf(&p, "vblank delay: %d, framestart delay: %d, MSA timing delay: %d\n", drm_printf(&p, "framestart delay: %d, MSA timing delay: %d, set context latency: %d\n",
pipe_config->hw.adjusted_mode.crtc_vblank_start - pipe_config->framestart_delay, pipe_config->msa_timing_delay,
pipe_config->hw.adjusted_mode.crtc_vdisplay, pipe_config->set_context_latency);
pipe_config->framestart_delay, pipe_config->msa_timing_delay);
drm_printf(&p, "vrr: %s, fixed rr: %s, vmin: %d, vmax: %d, flipline: %d, pipeline full: %d, guardband: %d vsync start: %d, vsync end: %d\n", drm_printf(&p, "vrr: %s, fixed rr: %s, vmin: %d, vmax: %d, flipline: %d, pipeline full: %d, guardband: %d vsync start: %d, vsync end: %d\n",
str_yes_no(pipe_config->vrr.enable), str_yes_no(pipe_config->vrr.enable),
@ -313,9 +312,9 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
drm_printf(&p, "pipe mode: " DRM_MODE_FMT "\n", drm_printf(&p, "pipe mode: " DRM_MODE_FMT "\n",
DRM_MODE_ARG(&pipe_config->hw.pipe_mode)); DRM_MODE_ARG(&pipe_config->hw.pipe_mode));
intel_dump_crtc_timings(&p, &pipe_config->hw.pipe_mode); intel_dump_crtc_timings(&p, &pipe_config->hw.pipe_mode);
drm_printf(&p, "port clock: %d, pipe src: " DRM_RECT_FMT ", pixel rate %d\n", drm_printf(&p, "port clock: %d, pipe src: " DRM_RECT_FMT ", pixel rate %d, min cdclk %d\n",
pipe_config->port_clock, DRM_RECT_ARG(&pipe_config->pipe_src), pipe_config->port_clock, DRM_RECT_ARG(&pipe_config->pipe_src),
pipe_config->pixel_rate); pipe_config->pixel_rate, pipe_config->min_cdclk);
drm_printf(&p, "linetime: %d, ips linetime: %d\n", drm_printf(&p, "linetime: %d, ips linetime: %d\n",
pipe_config->linetime, pipe_config->ips_linetime); pipe_config->linetime, pipe_config->ips_linetime);
@ -373,6 +372,11 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
intel_vdsc_state_dump(&p, 0, pipe_config); intel_vdsc_state_dump(&p, 0, pipe_config);
drm_printf(&p, "sharpness strength: %d, sharpness tap size: %d, sharpness enable: %d\n",
pipe_config->hw.casf_params.strength,
pipe_config->hw.casf_params.win_size,
pipe_config->hw.casf_params.casf_enable);
dump_planes: dump_planes:
if (!state) if (!state)
return; return;

View File

@ -12,13 +12,13 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_vblank.h> #include <drm/drm_vblank.h>
#include "i915_utils.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_cursor.h" #include "intel_cursor.h"
#include "intel_cursor_regs.h" #include "intel_cursor_regs.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display.h" #include "intel_display.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_fb.h" #include "intel_fb.h"
#include "intel_fb_pin.h" #include "intel_fb_pin.h"
#include "intel_frontbuffer.h" #include "intel_frontbuffer.h"
@ -662,7 +662,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
cntl = plane_state->ctl | cntl = plane_state->ctl |
i9xx_cursor_ctl_crtc(crtc_state); i9xx_cursor_ctl_crtc(crtc_state);
if (width != height) if (DISPLAY_VER(display) < 14 && width != height)
fbc_ctl = CUR_FBC_EN | CUR_FBC_HEIGHT(height - 1); fbc_ctl = CUR_FBC_EN | CUR_FBC_HEIGHT(height - 1);
base = plane_state->surf; base = plane_state->surf;

View File

@ -8,7 +8,6 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_alpm.h" #include "intel_alpm.h"
#include "intel_cx0_phy.h" #include "intel_cx0_phy.h"
#include "intel_cx0_phy_regs.h" #include "intel_cx0_phy_regs.h"
@ -16,16 +15,15 @@
#include "intel_ddi_buf_trans.h" #include "intel_ddi_buf_trans.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_hdmi.h" #include "intel_hdmi.h"
#include "intel_lt_phy.h"
#include "intel_panel.h" #include "intel_panel.h"
#include "intel_psr.h" #include "intel_psr.h"
#include "intel_snps_hdmi_pll.h" #include "intel_snps_hdmi_pll.h"
#include "intel_tc.h" #include "intel_tc.h"
#define MB_WRITE_COMMITTED true
#define MB_WRITE_UNCOMMITTED false
#define for_each_cx0_lane_in_mask(__lane_mask, __lane) \ #define for_each_cx0_lane_in_mask(__lane_mask, __lane) \
for ((__lane) = 0; (__lane) < 2; (__lane)++) \ for ((__lane) = 0; (__lane) < 2; (__lane)++) \
for_each_if((__lane_mask) & BIT(__lane)) for_each_if((__lane_mask) & BIT(__lane))
@ -39,14 +37,12 @@ bool intel_encoder_is_c10phy(struct intel_encoder *encoder)
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum phy phy = intel_encoder_to_phy(encoder); enum phy phy = intel_encoder_to_phy(encoder);
/* PTL doesn't have a PHY connected to PORT B; as such, if (display->platform.pantherlake) {
* there will never be a case where PTL uses PHY B. if (display->platform.pantherlake_wildcatlake)
* WCL uses PORT A and B with the C10 PHY. return phy <= PHY_B;
* Reusing the condition for WCL and extending it for PORT B else
* should not cause any issues for PTL. return phy == PHY_A;
*/ }
if (display->platform.pantherlake && phy < PHY_C)
return true;
if ((display->platform.lunarlake || display->platform.meteorlake) && phy < PHY_C) if ((display->platform.lunarlake || display->platform.meteorlake) && phy < PHY_C)
return true; return true;
@ -130,8 +126,8 @@ static void intel_cx0_phy_transaction_end(struct intel_encoder *encoder, intel_w
intel_display_power_put(display, POWER_DOMAIN_DC_OFF, wakeref); intel_display_power_put(display, POWER_DOMAIN_DC_OFF, wakeref);
} }
static void intel_clear_response_ready_flag(struct intel_encoder *encoder, void intel_clear_response_ready_flag(struct intel_encoder *encoder,
int lane) int lane)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
@ -140,7 +136,7 @@ static void intel_clear_response_ready_flag(struct intel_encoder *encoder,
0, XELPDP_PORT_P2M_RESPONSE_READY | XELPDP_PORT_P2M_ERROR_SET); 0, XELPDP_PORT_P2M_RESPONSE_READY | XELPDP_PORT_P2M_ERROR_SET);
} }
static void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane) void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port; enum port port = encoder->port;
@ -161,8 +157,8 @@ static void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane)
intel_clear_response_ready_flag(encoder, lane); intel_clear_response_ready_flag(encoder, lane);
} }
static int intel_cx0_wait_for_ack(struct intel_encoder *encoder, int intel_cx0_wait_for_ack(struct intel_encoder *encoder,
int command, int lane, u32 *val) int command, int lane, u32 *val)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port; enum port port = encoder->port;
@ -273,8 +269,7 @@ static u8 __intel_cx0_read(struct intel_encoder *encoder,
return 0; return 0;
} }
static u8 intel_cx0_read(struct intel_encoder *encoder, u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr)
u8 lane_mask, u16 addr)
{ {
int lane = lane_mask_to_lane(lane_mask); int lane = lane_mask_to_lane(lane_mask);
@ -361,8 +356,8 @@ static void __intel_cx0_write(struct intel_encoder *encoder,
"PHY %c Write %04x failed after %d retries.\n", phy_name(phy), addr, i); "PHY %c Write %04x failed after %d retries.\n", phy_name(phy), addr, i);
} }
static void intel_cx0_write(struct intel_encoder *encoder, void intel_cx0_write(struct intel_encoder *encoder,
u8 lane_mask, u16 addr, u8 data, bool committed) u8 lane_mask, u16 addr, u8 data, bool committed)
{ {
int lane; int lane;
@ -414,8 +409,8 @@ static void __intel_cx0_rmw(struct intel_encoder *encoder,
__intel_cx0_write(encoder, lane, addr, val, committed); __intel_cx0_write(encoder, lane, addr, val, committed);
} }
static void intel_cx0_rmw(struct intel_encoder *encoder, void intel_cx0_rmw(struct intel_encoder *encoder,
u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed) u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed)
{ {
u8 lane; u8 lane;
@ -2105,6 +2100,9 @@ static int intel_c10pll_calc_state(struct intel_crtc_state *crtc_state,
return 0; return 0;
} }
static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder,
const struct intel_c10pll_state *pll_state);
static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder,
struct intel_c10pll_state *pll_state) struct intel_c10pll_state *pll_state)
{ {
@ -2129,6 +2127,8 @@ static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder,
pll_state->tx = intel_cx0_read(encoder, lane, PHY_C10_VDR_TX(0)); pll_state->tx = intel_cx0_read(encoder, lane, PHY_C10_VDR_TX(0));
intel_cx0_phy_transaction_end(encoder, wakeref); intel_cx0_phy_transaction_end(encoder, wakeref);
pll_state->clock = intel_c10pll_calc_port_clock(encoder, pll_state);
} }
static void intel_c10_pll_program(struct intel_display *display, static void intel_c10_pll_program(struct intel_display *display,
@ -2587,20 +2587,6 @@ static bool is_dp2(u32 clock)
return false; return false;
} }
static bool is_hdmi_frl(u32 clock)
{
switch (clock) {
case 300000: /* 3 Gbps */
case 600000: /* 6 Gbps */
case 800000: /* 8 Gbps */
case 1000000: /* 10 Gbps */
case 1200000: /* 12 Gbps */
return true;
default:
return false;
}
}
static bool intel_c20_protocol_switch_valid(struct intel_encoder *encoder) static bool intel_c20_protocol_switch_valid(struct intel_encoder *encoder)
{ {
struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
@ -2614,7 +2600,7 @@ static int intel_get_c20_custom_width(u32 clock, bool dp)
{ {
if (dp && is_dp2(clock)) if (dp && is_dp2(clock))
return 2; return 2;
else if (is_hdmi_frl(clock)) else if (intel_hdmi_is_frl(clock))
return 1; return 1;
else else
return 0; return 0;
@ -2626,11 +2612,13 @@ static void intel_c20_pll_program(struct intel_display *display,
bool is_dp, int port_clock) bool is_dp, int port_clock)
{ {
u8 owned_lane_mask = intel_cx0_get_owned_lane_mask(encoder); u8 owned_lane_mask = intel_cx0_get_owned_lane_mask(encoder);
u8 serdes;
bool cntx; bool cntx;
int i; int i;
/* 1. Read current context selection */ /* 1. Read current context selection */
cntx = intel_cx0_read(encoder, INTEL_CX0_LANE0, PHY_C20_VDR_CUSTOM_SERDES_RATE) & BIT(0); cntx = intel_cx0_read(encoder, INTEL_CX0_LANE0, PHY_C20_VDR_CUSTOM_SERDES_RATE) &
PHY_C20_CONTEXT_TOGGLE;
/* /*
* 2. If there is a protocol switch from HDMI to DP or vice versa, clear * 2. If there is a protocol switch from HDMI to DP or vice versa, clear
@ -2700,28 +2688,31 @@ static void intel_c20_pll_program(struct intel_display *display,
MB_WRITE_COMMITTED); MB_WRITE_COMMITTED);
/* 5. For DP or 6. For HDMI */ /* 5. For DP or 6. For HDMI */
if (is_dp) { serdes = 0;
intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE, if (is_dp)
BIT(6) | PHY_C20_CUSTOM_SERDES_MASK, serdes = PHY_C20_IS_DP |
BIT(6) | PHY_C20_CUSTOM_SERDES(intel_c20_get_dp_rate(port_clock)), PHY_C20_DP_RATE(intel_c20_get_dp_rate(port_clock));
MB_WRITE_COMMITTED); else if (intel_hdmi_is_frl(port_clock))
} else { serdes = PHY_C20_IS_HDMI_FRL;
intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
BIT(7) | PHY_C20_CUSTOM_SERDES_MASK,
is_hdmi_frl(port_clock) ? BIT(7) : 0,
MB_WRITE_COMMITTED);
intel_cx0_write(encoder, INTEL_CX0_BOTH_LANES, PHY_C20_VDR_HDMI_RATE, intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
intel_c20_get_hdmi_rate(port_clock), PHY_C20_IS_DP | PHY_C20_DP_RATE_MASK | PHY_C20_IS_HDMI_FRL,
MB_WRITE_COMMITTED); serdes,
} MB_WRITE_COMMITTED);
if (!is_dp)
intel_cx0_rmw(encoder, INTEL_CX0_BOTH_LANES, PHY_C20_VDR_HDMI_RATE,
PHY_C20_HDMI_RATE_MASK,
intel_c20_get_hdmi_rate(port_clock),
MB_WRITE_COMMITTED);
/* /*
* 7. Write Vendor specific registers to toggle context setting to load * 7. Write Vendor specific registers to toggle context setting to load
* the updated programming toggle context bit * the updated programming toggle context bit
*/ */
intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE, intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
BIT(0), cntx ? 0 : 1, MB_WRITE_COMMITTED); PHY_C20_CONTEXT_TOGGLE, cntx ? 0 : PHY_C20_CONTEXT_TOGGLE,
MB_WRITE_COMMITTED);
} }
static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder,
@ -2768,7 +2759,7 @@ static void intel_program_port_clock_ctl(struct intel_encoder *encoder,
val |= XELPDP_FORWARD_CLOCK_UNGATE; val |= XELPDP_FORWARD_CLOCK_UNGATE;
if (!is_dp && is_hdmi_frl(port_clock)) if (!is_dp && intel_hdmi_is_frl(port_clock))
val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_DIV18CLK); val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_DIV18CLK);
else else
val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK);
@ -2808,8 +2799,8 @@ static u32 intel_cx0_get_powerdown_state(u8 lane_mask, u8 state)
return val; return val;
} }
static void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder, void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
u8 lane_mask, u8 state) u8 lane_mask, u8 state)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port; enum port port = encoder->port;
@ -2839,24 +2830,24 @@ static void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
/* Update Timeout Value */ /* Update Timeout Value */
if (intel_de_wait_custom(display, buf_ctl2_reg, if (intel_de_wait_custom(display, buf_ctl2_reg,
intel_cx0_get_powerdown_update(lane_mask), 0, intel_cx0_get_powerdown_update(lane_mask), 0,
XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_US, 0, NULL)) XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_US, 2, NULL))
drm_warn(display->drm, drm_warn(display->drm,
"PHY %c failed to bring out of Lane reset after %dus.\n", "PHY %c failed to bring out of Lane reset after %dus.\n",
phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US);
} }
static void intel_cx0_setup_powerdown(struct intel_encoder *encoder) void intel_cx0_setup_powerdown(struct intel_encoder *encoder)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port; enum port port = encoder->port;
intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
XELPDP_POWER_STATE_READY_MASK, XELPDP_POWER_STATE_READY_MASK,
XELPDP_POWER_STATE_READY(CX0_P2_STATE_READY)); XELPDP_POWER_STATE_READY(XELPDP_P2_STATE_READY));
intel_de_rmw(display, XELPDP_PORT_BUF_CTL3(display, port), intel_de_rmw(display, XELPDP_PORT_BUF_CTL3(display, port),
XELPDP_POWER_STATE_ACTIVE_MASK | XELPDP_POWER_STATE_ACTIVE_MASK |
XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, XELPDP_PLL_LANE_STAGGERING_DELAY_MASK,
XELPDP_POWER_STATE_ACTIVE(CX0_P0_STATE_ACTIVE) | XELPDP_POWER_STATE_ACTIVE(XELPDP_P0_STATE_ACTIVE) |
XELPDP_PLL_LANE_STAGGERING_DELAY(0)); XELPDP_PLL_LANE_STAGGERING_DELAY(0));
} }
@ -2929,7 +2920,7 @@ static void intel_cx0_phy_lane_reset(struct intel_encoder *encoder,
phy_name(phy), XELPDP_REFCLK_ENABLE_TIMEOUT_US); phy_name(phy), XELPDP_REFCLK_ENABLE_TIMEOUT_US);
intel_cx0_powerdown_change_sequence(encoder, INTEL_CX0_BOTH_LANES, intel_cx0_powerdown_change_sequence(encoder, INTEL_CX0_BOTH_LANES,
CX0_P2_STATE_RESET); XELPDP_P2_STATE_RESET);
intel_cx0_setup_powerdown(encoder); intel_cx0_setup_powerdown(encoder);
intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_pipe_reset, 0); intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_pipe_reset, 0);
@ -3034,7 +3025,7 @@ static void __intel_cx0pll_enable(struct intel_encoder *encoder,
* TODO: For DP alt mode use only one lane. * TODO: For DP alt mode use only one lane.
*/ */
intel_cx0_powerdown_change_sequence(encoder, INTEL_CX0_BOTH_LANES, intel_cx0_powerdown_change_sequence(encoder, INTEL_CX0_BOTH_LANES,
CX0_P2_STATE_READY); XELPDP_P2_STATE_READY);
/* /*
* 4. Program PORT_MSGBUS_TIMER register's Message Bus Timer field to 0xA000. * 4. Program PORT_MSGBUS_TIMER register's Message Bus Timer field to 0xA000.
@ -3160,8 +3151,8 @@ static int intel_mtl_tbt_clock_select(struct intel_display *display,
} }
} }
static void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder, void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state) const struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum phy phy = intel_encoder_to_phy(encoder); enum phy phy = intel_encoder_to_phy(encoder);
@ -3275,13 +3266,13 @@ static u8 cx0_power_control_disable_val(struct intel_encoder *encoder)
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
if (intel_encoder_is_c10phy(encoder)) if (intel_encoder_is_c10phy(encoder))
return CX0_P2PG_STATE_DISABLE; return XELPDP_P2PG_STATE_DISABLE;
if ((display->platform.battlemage && encoder->port == PORT_A) || if ((display->platform.battlemage && encoder->port == PORT_A) ||
(DISPLAY_VER(display) >= 30 && encoder->type == INTEL_OUTPUT_EDP)) (DISPLAY_VER(display) >= 30 && encoder->type == INTEL_OUTPUT_EDP))
return CX0_P2PG_STATE_DISABLE; return XELPDP_P2PG_STATE_DISABLE;
return CX0_P4PG_STATE_DISABLE; return XELPDP_P4PG_STATE_DISABLE;
} }
static void intel_cx0pll_disable(struct intel_encoder *encoder) static void intel_cx0pll_disable(struct intel_encoder *encoder)
@ -3345,7 +3336,7 @@ static bool intel_cx0_pll_is_enabled(struct intel_encoder *encoder)
intel_cx0_get_pclk_pll_request(lane); intel_cx0_get_pclk_pll_request(lane);
} }
static void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder) void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
enum phy phy = intel_encoder_to_phy(encoder); enum phy phy = intel_encoder_to_phy(encoder);
@ -3584,7 +3575,7 @@ void intel_cx0pll_state_verify(struct intel_atomic_state *state,
struct intel_encoder *encoder; struct intel_encoder *encoder;
struct intel_cx0pll_state mpll_hw_state = {}; struct intel_cx0pll_state mpll_hw_state = {};
if (DISPLAY_VER(display) < 14) if (!IS_DISPLAY_VER(display, 14, 30))
return; return;
if (!new_crtc_state->hw.active) if (!new_crtc_state->hw.active)

View File

@ -8,6 +8,9 @@
#include <linux/types.h> #include <linux/types.h>
#define MB_WRITE_COMMITTED true
#define MB_WRITE_UNCOMMITTED false
enum icl_port_dpll_id; enum icl_port_dpll_id;
struct intel_atomic_state; struct intel_atomic_state;
struct intel_c10pll_state; struct intel_c10pll_state;
@ -19,6 +22,8 @@ struct intel_display;
struct intel_encoder; struct intel_encoder;
struct intel_hdmi; struct intel_hdmi;
void intel_clear_response_ready_flag(struct intel_encoder *encoder,
int lane);
bool intel_encoder_is_c10phy(struct intel_encoder *encoder); bool intel_encoder_is_c10phy(struct intel_encoder *encoder);
void intel_mtl_pll_enable(struct intel_encoder *encoder, void intel_mtl_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
@ -41,9 +46,25 @@ bool intel_cx0pll_compare_hw_state(const struct intel_cx0pll_state *a,
const struct intel_cx0pll_state *b); const struct intel_cx0pll_state *b);
void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
u8 lane_mask, u8 state);
int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock);
void intel_cx0_setup_powerdown(struct intel_encoder *encoder);
bool intel_cx0_is_hdmi_frl(u32 clock);
u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr);
void intel_cx0_rmw(struct intel_encoder *encoder,
u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed);
void intel_cx0_write(struct intel_encoder *encoder,
u8 lane_mask, u16 addr, u8 data, bool committed);
int intel_cx0_wait_for_ack(struct intel_encoder *encoder,
int command, int lane, u32 *val);
void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane);
int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder); int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder);
void intel_cx0_pll_power_save_wa(struct intel_display *display); void intel_cx0_pll_power_save_wa(struct intel_display *display);
void intel_lnl_mac_transmit_lfps(struct intel_encoder *encoder, void intel_lnl_mac_transmit_lfps(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder);
#endif /* __INTEL_CX0_PHY_H__ */ #endif /* __INTEL_CX0_PHY_H__ */

View File

@ -50,6 +50,7 @@
#define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1) #define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1)
#define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2) #define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2)
#define XELPDP_PORT_M2P_COMMAND_READ REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3) #define XELPDP_PORT_M2P_COMMAND_READ REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3)
#define XELPDP_PORT_P2P_TRANSACTION_PENDING REG_BIT(24)
#define XELPDP_PORT_M2P_DATA_MASK REG_GENMASK(23, 16) #define XELPDP_PORT_M2P_DATA_MASK REG_GENMASK(23, 16)
#define XELPDP_PORT_M2P_DATA(val) REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val) #define XELPDP_PORT_M2P_DATA(val) REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val)
#define XELPDP_PORT_M2P_TRANSACTION_RESET REG_BIT(15) #define XELPDP_PORT_M2P_TRANSACTION_RESET REG_BIT(15)
@ -104,6 +105,8 @@
#define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1) #define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1)
#define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2) #define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2)
#define XELPDP_PORT_REVERSAL REG_BIT(16) #define XELPDP_PORT_REVERSAL REG_BIT(16)
#define XE3PLPDP_PHY_MODE_MASK REG_GENMASK(15, 12)
#define XE3PLPDP_PHY_MODE_DP REG_FIELD_PREP(XE3PLPDP_PHY_MODE_MASK, 0x3)
#define XELPDP_PORT_BUF_IO_SELECT_TBT REG_BIT(11) #define XELPDP_PORT_BUF_IO_SELECT_TBT REG_BIT(11)
#define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7) #define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7)
#define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6) #define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6)
@ -124,6 +127,7 @@
_XELPDP_PORT_BUF_CTL2(port)) _XELPDP_PORT_BUF_CTL2(port))
#define XELPDP_LANE_PIPE_RESET(lane) _PICK(lane, REG_BIT(31), REG_BIT(30)) #define XELPDP_LANE_PIPE_RESET(lane) _PICK(lane, REG_BIT(31), REG_BIT(30))
#define XELPDP_LANE_PHY_CURRENT_STATUS(lane) _PICK(lane, REG_BIT(29), REG_BIT(28)) #define XELPDP_LANE_PHY_CURRENT_STATUS(lane) _PICK(lane, REG_BIT(29), REG_BIT(28))
#define XE3PLPDP_LANE_PHY_PULSE_STATUS(lane) _PICK(lane, REG_BIT(27), REG_BIT(26))
#define XELPDP_LANE_POWERDOWN_UPDATE(lane) _PICK(lane, REG_BIT(25), REG_BIT(24)) #define XELPDP_LANE_POWERDOWN_UPDATE(lane) _PICK(lane, REG_BIT(25), REG_BIT(24))
#define _XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK REG_GENMASK(23, 20) #define _XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK REG_GENMASK(23, 20)
#define _XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(_XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val) #define _XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(_XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val)
@ -149,11 +153,12 @@
#define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val) #define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val)
#define XELPDP_POWER_STATE_ACTIVE_MASK REG_GENMASK(3, 0) #define XELPDP_POWER_STATE_ACTIVE_MASK REG_GENMASK(3, 0)
#define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val) #define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val)
#define CX0_P0_STATE_ACTIVE 0x0 #define XELPDP_P0_STATE_ACTIVE 0x0
#define CX0_P2_STATE_READY 0x2 #define XELPDP_P2_STATE_READY 0x2
#define CX0_P2PG_STATE_DISABLE 0x9 #define XE3PLPD_P4_STATE_DISABLE 0x4
#define CX0_P4PG_STATE_DISABLE 0xC #define XELPDP_P2PG_STATE_DISABLE 0x9
#define CX0_P2_STATE_RESET 0x2 #define XELPDP_P4PG_STATE_DISABLE 0xC
#define XELPDP_P2_STATE_RESET 0x2
#define _XELPDP_PORT_MSGBUS_TIMER_LN0_A 0x640d8 #define _XELPDP_PORT_MSGBUS_TIMER_LN0_A 0x640d8
#define _XELPDP_PORT_MSGBUS_TIMER_LN0_B 0x641d8 #define _XELPDP_PORT_MSGBUS_TIMER_LN0_B 0x641d8
@ -298,10 +303,14 @@
#define PHY_C20_RD_DATA_L 0xC08 #define PHY_C20_RD_DATA_L 0xC08
#define PHY_C20_RD_DATA_H 0xC09 #define PHY_C20_RD_DATA_H 0xC09
#define PHY_C20_VDR_CUSTOM_SERDES_RATE 0xD00 #define PHY_C20_VDR_CUSTOM_SERDES_RATE 0xD00
#define PHY_C20_VDR_HDMI_RATE 0xD01 #define PHY_C20_IS_HDMI_FRL REG_BIT8(7)
#define PHY_C20_IS_DP REG_BIT8(6)
#define PHY_C20_DP_RATE_MASK REG_GENMASK8(4, 1)
#define PHY_C20_DP_RATE(val) REG_FIELD_PREP8(PHY_C20_DP_RATE_MASK, val)
#define PHY_C20_CONTEXT_TOGGLE REG_BIT8(0) #define PHY_C20_CONTEXT_TOGGLE REG_BIT8(0)
#define PHY_C20_CUSTOM_SERDES_MASK REG_GENMASK8(4, 1) #define PHY_C20_VDR_HDMI_RATE 0xD01
#define PHY_C20_CUSTOM_SERDES(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_SERDES_MASK, val) #define PHY_C20_HDMI_RATE_MASK REG_GENMASK8(1, 0)
#define PHY_C20_HDMI_RATE(val) REG_FIELD_PREP8(PHY_C20_HDMI_RATE_MASK, val)
#define PHY_C20_VDR_CUSTOM_WIDTH 0xD02 #define PHY_C20_VDR_CUSTOM_WIDTH 0xD02
#define PHY_C20_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0) #define PHY_C20_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0)
#define PHY_C20_CUSTOM_WIDTH(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_WIDTH_MASK, val) #define PHY_C20_CUSTOM_WIDTH(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_WIDTH_MASK, val)

View File

@ -0,0 +1,295 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2025 Intel Corporation
*/
#include <drm/drm_print.h>
#include "intel_dbuf_bw.h"
#include "intel_display_core.h"
#include "intel_display_types.h"
#include "skl_watermark.h"
struct intel_dbuf_bw {
unsigned int max_bw[I915_MAX_DBUF_SLICES];
u8 active_planes[I915_MAX_DBUF_SLICES];
};
struct intel_dbuf_bw_state {
struct intel_global_state base;
struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES];
};
struct intel_dbuf_bw_state *to_intel_dbuf_bw_state(struct intel_global_state *obj_state)
{
return container_of(obj_state, struct intel_dbuf_bw_state, base);
}
struct intel_dbuf_bw_state *
intel_atomic_get_old_dbuf_bw_state(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
struct intel_global_state *dbuf_bw_state;
dbuf_bw_state = intel_atomic_get_old_global_obj_state(state, &display->dbuf_bw.obj);
return to_intel_dbuf_bw_state(dbuf_bw_state);
}
struct intel_dbuf_bw_state *
intel_atomic_get_new_dbuf_bw_state(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
struct intel_global_state *dbuf_bw_state;
dbuf_bw_state = intel_atomic_get_new_global_obj_state(state, &display->dbuf_bw.obj);
return to_intel_dbuf_bw_state(dbuf_bw_state);
}
struct intel_dbuf_bw_state *
intel_atomic_get_dbuf_bw_state(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
struct intel_global_state *dbuf_bw_state;
dbuf_bw_state = intel_atomic_get_global_obj_state(state, &display->dbuf_bw.obj);
if (IS_ERR(dbuf_bw_state))
return ERR_CAST(dbuf_bw_state);
return to_intel_dbuf_bw_state(dbuf_bw_state);
}
static bool intel_dbuf_bw_changed(struct intel_display *display,
const struct intel_dbuf_bw *old_dbuf_bw,
const struct intel_dbuf_bw *new_dbuf_bw)
{
enum dbuf_slice slice;
for_each_dbuf_slice(display, slice) {
if (old_dbuf_bw->max_bw[slice] != new_dbuf_bw->max_bw[slice] ||
old_dbuf_bw->active_planes[slice] != new_dbuf_bw->active_planes[slice])
return true;
}
return false;
}
static bool intel_dbuf_bw_state_changed(struct intel_display *display,
const struct intel_dbuf_bw_state *old_dbuf_bw_state,
const struct intel_dbuf_bw_state *new_dbuf_bw_state)
{
enum pipe pipe;
for_each_pipe(display, pipe) {
const struct intel_dbuf_bw *old_dbuf_bw =
&old_dbuf_bw_state->dbuf_bw[pipe];
const struct intel_dbuf_bw *new_dbuf_bw =
&new_dbuf_bw_state->dbuf_bw[pipe];
if (intel_dbuf_bw_changed(display, old_dbuf_bw, new_dbuf_bw))
return true;
}
return false;
}
static void skl_plane_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
struct intel_crtc *crtc,
enum plane_id plane_id,
const struct skl_ddb_entry *ddb,
unsigned int data_rate)
{
struct intel_display *display = to_intel_display(crtc);
unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(display, ddb);
enum dbuf_slice slice;
/*
* The arbiter can only really guarantee an
* equal share of the total bw to each plane.
*/
for_each_dbuf_slice_in_mask(display, slice, dbuf_mask) {
dbuf_bw->max_bw[slice] = max(dbuf_bw->max_bw[slice], data_rate);
dbuf_bw->active_planes[slice] |= BIT(plane_id);
}
}
static void skl_crtc_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
enum plane_id plane_id;
memset(dbuf_bw, 0, sizeof(*dbuf_bw));
if (!crtc_state->hw.active)
return;
for_each_plane_id_on_crtc(crtc, plane_id) {
/*
* We assume cursors are small enough
* to not cause bandwidth problems.
*/
if (plane_id == PLANE_CURSOR)
continue;
skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
&crtc_state->wm.skl.plane_ddb[plane_id],
crtc_state->data_rate[plane_id]);
if (DISPLAY_VER(display) < 11)
skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
&crtc_state->wm.skl.plane_ddb_y[plane_id],
crtc_state->data_rate[plane_id]);
}
}
/* "Maximum Data Buffer Bandwidth" */
int intel_dbuf_bw_min_cdclk(struct intel_display *display,
const struct intel_dbuf_bw_state *dbuf_bw_state)
{
unsigned int total_max_bw = 0;
enum dbuf_slice slice;
for_each_dbuf_slice(display, slice) {
int num_active_planes = 0;
unsigned int max_bw = 0;
enum pipe pipe;
/*
* The arbiter can only really guarantee an
* equal share of the total bw to each plane.
*/
for_each_pipe(display, pipe) {
const struct intel_dbuf_bw *dbuf_bw = &dbuf_bw_state->dbuf_bw[pipe];
max_bw = max(dbuf_bw->max_bw[slice], max_bw);
num_active_planes += hweight8(dbuf_bw->active_planes[slice]);
}
max_bw *= num_active_planes;
total_max_bw = max(total_max_bw, max_bw);
}
return DIV_ROUND_UP(total_max_bw, 64);
}
int intel_dbuf_bw_calc_min_cdclk(struct intel_atomic_state *state,
bool *need_cdclk_calc)
{
struct intel_display *display = to_intel_display(state);
struct intel_dbuf_bw_state *new_dbuf_bw_state = NULL;
const struct intel_dbuf_bw_state *old_dbuf_bw_state = NULL;
const struct intel_crtc_state *old_crtc_state;
const struct intel_crtc_state *new_crtc_state;
struct intel_crtc *crtc;
int ret, i;
if (DISPLAY_VER(display) < 9)
return 0;
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
struct intel_dbuf_bw old_dbuf_bw, new_dbuf_bw;
skl_crtc_calc_dbuf_bw(&old_dbuf_bw, old_crtc_state);
skl_crtc_calc_dbuf_bw(&new_dbuf_bw, new_crtc_state);
if (!intel_dbuf_bw_changed(display, &old_dbuf_bw, &new_dbuf_bw))
continue;
new_dbuf_bw_state = intel_atomic_get_dbuf_bw_state(state);
if (IS_ERR(new_dbuf_bw_state))
return PTR_ERR(new_dbuf_bw_state);
old_dbuf_bw_state = intel_atomic_get_old_dbuf_bw_state(state);
new_dbuf_bw_state->dbuf_bw[crtc->pipe] = new_dbuf_bw;
}
if (!old_dbuf_bw_state)
return 0;
if (intel_dbuf_bw_state_changed(display, old_dbuf_bw_state, new_dbuf_bw_state)) {
ret = intel_atomic_lock_global_state(&new_dbuf_bw_state->base);
if (ret)
return ret;
}
ret = intel_cdclk_update_dbuf_bw_min_cdclk(state,
intel_dbuf_bw_min_cdclk(display, old_dbuf_bw_state),
intel_dbuf_bw_min_cdclk(display, new_dbuf_bw_state),
need_cdclk_calc);
if (ret)
return ret;
return 0;
}
void intel_dbuf_bw_update_hw_state(struct intel_display *display)
{
struct intel_dbuf_bw_state *dbuf_bw_state =
to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
struct intel_crtc *crtc;
if (DISPLAY_VER(display) < 9)
return;
for_each_intel_crtc(display->drm, crtc) {
const struct intel_crtc_state *crtc_state =
to_intel_crtc_state(crtc->base.state);
skl_crtc_calc_dbuf_bw(&dbuf_bw_state->dbuf_bw[crtc->pipe], crtc_state);
}
}
void intel_dbuf_bw_crtc_disable_noatomic(struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(crtc);
struct intel_dbuf_bw_state *dbuf_bw_state =
to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
enum pipe pipe = crtc->pipe;
if (DISPLAY_VER(display) < 9)
return;
memset(&dbuf_bw_state->dbuf_bw[pipe], 0, sizeof(dbuf_bw_state->dbuf_bw[pipe]));
}
static struct intel_global_state *
intel_dbuf_bw_duplicate_state(struct intel_global_obj *obj)
{
struct intel_dbuf_bw_state *state;
state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
if (!state)
return NULL;
return &state->base;
}
static void intel_dbuf_bw_destroy_state(struct intel_global_obj *obj,
struct intel_global_state *state)
{
kfree(state);
}
static const struct intel_global_state_funcs intel_dbuf_bw_funcs = {
.atomic_duplicate_state = intel_dbuf_bw_duplicate_state,
.atomic_destroy_state = intel_dbuf_bw_destroy_state,
};
int intel_dbuf_bw_init(struct intel_display *display)
{
struct intel_dbuf_bw_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
intel_atomic_global_obj_init(display, &display->dbuf_bw.obj,
&state->base, &intel_dbuf_bw_funcs);
return 0;
}

View File

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2025 Intel Corporation
*/
#ifndef __INTEL_DBUF_BW_H__
#define __INTEL_DBUF_BW_H__
#include <drm/drm_atomic.h>
struct intel_atomic_state;
struct intel_dbuf_bw_state;
struct intel_crtc;
struct intel_display;
struct intel_global_state;
struct intel_dbuf_bw_state *
to_intel_dbuf_bw_state(struct intel_global_state *obj_state);
struct intel_dbuf_bw_state *
intel_atomic_get_old_dbuf_bw_state(struct intel_atomic_state *state);
struct intel_dbuf_bw_state *
intel_atomic_get_new_dbuf_bw_state(struct intel_atomic_state *state);
struct intel_dbuf_bw_state *
intel_atomic_get_dbuf_bw_state(struct intel_atomic_state *state);
int intel_dbuf_bw_init(struct intel_display *display);
int intel_dbuf_bw_calc_min_cdclk(struct intel_atomic_state *state,
bool *need_cdclk_calc);
int intel_dbuf_bw_min_cdclk(struct intel_display *display,
const struct intel_dbuf_bw_state *dbuf_bw_state);
void intel_dbuf_bw_update_hw_state(struct intel_display *display);
void intel_dbuf_bw_crtc_disable_noatomic(struct intel_crtc *crtc);
#endif /* __INTEL_DBUF_BW_H__ */

View File

@ -35,7 +35,6 @@
#include <drm/drm_privacy_screen_consumer.h> #include <drm/drm_privacy_screen_consumer.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "icl_dsi.h" #include "icl_dsi.h"
#include "intel_alpm.h" #include "intel_alpm.h"
#include "intel_audio.h" #include "intel_audio.h"
@ -53,6 +52,7 @@
#include "intel_display_power.h" #include "intel_display_power.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dkl_phy.h" #include "intel_dkl_phy.h"
#include "intel_dkl_phy_regs.h" #include "intel_dkl_phy_regs.h"
#include "intel_dp.h" #include "intel_dp.h"
@ -72,6 +72,7 @@
#include "intel_hotplug.h" #include "intel_hotplug.h"
#include "intel_hti.h" #include "intel_hti.h"
#include "intel_lspcon.h" #include "intel_lspcon.h"
#include "intel_lt_phy.h"
#include "intel_mg_phy_regs.h" #include "intel_mg_phy_regs.h"
#include "intel_modeset_lock.h" #include "intel_modeset_lock.h"
#include "intel_panel.h" #include "intel_panel.h"
@ -1466,10 +1467,15 @@ static int translate_signal_level(struct intel_dp *intel_dp,
u8 signal_levels) u8 signal_levels)
{ {
struct intel_display *display = to_intel_display(intel_dp); struct intel_display *display = to_intel_display(intel_dp);
const u8 *signal_array;
size_t array_size;
int i; int i;
for (i = 0; i < ARRAY_SIZE(index_to_dp_signal_levels); i++) { signal_array = index_to_dp_signal_levels;
if (index_to_dp_signal_levels[i] == signal_levels) array_size = ARRAY_SIZE(index_to_dp_signal_levels);
for (i = 0; i < array_size; i++) {
if (signal_array[i] == signal_levels)
return i; return i;
} }
@ -4240,6 +4246,19 @@ void intel_ddi_get_clock(struct intel_encoder *encoder,
&crtc_state->dpll_hw_state); &crtc_state->dpll_hw_state);
} }
static void xe3plpd_ddi_get_config(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state)
{
intel_lt_phy_pll_readout_hw_state(encoder, crtc_state, &crtc_state->dpll_hw_state.ltpll);
if (crtc_state->dpll_hw_state.ltpll.tbt_mode)
crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder);
else
crtc_state->port_clock =
intel_lt_phy_calc_port_clock(encoder, crtc_state);
intel_ddi_get_config(encoder, crtc_state);
}
static void mtl_ddi_get_config(struct intel_encoder *encoder, static void mtl_ddi_get_config(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state) struct intel_crtc_state *crtc_state)
{ {
@ -4559,6 +4578,13 @@ static int intel_ddi_compute_config_late(struct intel_encoder *encoder,
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
struct drm_connector *connector = conn_state->connector; struct drm_connector *connector = conn_state->connector;
u8 port_sync_transcoders = 0; u8 port_sync_transcoders = 0;
int ret = 0;
if (intel_crtc_has_dp_encoder(crtc_state))
ret = intel_dp_compute_config_late(encoder, crtc_state, conn_state);
if (ret)
return ret;
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] [CRTC:%d:%s]\n", drm_dbg_kms(display->drm, "[ENCODER:%d:%s] [CRTC:%d:%s]\n",
encoder->base.base.id, encoder->base.name, encoder->base.base.id, encoder->base.name,
@ -5224,7 +5250,12 @@ void intel_ddi_init(struct intel_display *display,
encoder->cloneable = 0; encoder->cloneable = 0;
encoder->pipe_mask = ~0; encoder->pipe_mask = ~0;
if (DISPLAY_VER(display) >= 14) { if (HAS_LT_PHY(display)) {
encoder->enable_clock = intel_xe3plpd_pll_enable;
encoder->disable_clock = intel_xe3plpd_pll_disable;
encoder->port_pll_type = intel_mtl_port_pll_type;
encoder->get_config = xe3plpd_ddi_get_config;
} else if (DISPLAY_VER(display) >= 14) {
encoder->enable_clock = intel_mtl_pll_enable; encoder->enable_clock = intel_mtl_pll_enable;
encoder->disable_clock = intel_mtl_pll_disable; encoder->disable_clock = intel_mtl_pll_disable;
encoder->port_pll_type = intel_mtl_port_pll_type; encoder->port_pll_type = intel_mtl_port_pll_type;
@ -5289,7 +5320,9 @@ void intel_ddi_init(struct intel_display *display,
encoder->get_config = hsw_ddi_get_config; encoder->get_config = hsw_ddi_get_config;
} }
if (DISPLAY_VER(display) >= 14) { if (HAS_LT_PHY(display)) {
encoder->set_signal_levels = intel_lt_phy_set_signal_levels;
} else if (DISPLAY_VER(display) >= 14) {
encoder->set_signal_levels = intel_cx0_phy_set_signal_levels; encoder->set_signal_levels = intel_cx0_phy_set_signal_levels;
} else if (display->platform.dg2) { } else if (display->platform.dg2) {
encoder->set_signal_levels = intel_snps_phy_set_signal_levels; encoder->set_signal_levels = intel_snps_phy_set_signal_levels;

View File

@ -3,13 +3,14 @@
* Copyright © 2020 Intel Corporation * Copyright © 2020 Intel Corporation
*/ */
#include "i915_utils.h"
#include "intel_cx0_phy.h" #include "intel_cx0_phy.h"
#include "intel_ddi.h" #include "intel_ddi.h"
#include "intel_ddi_buf_trans.h" #include "intel_ddi_buf_trans.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_lt_phy.h"
/* HDMI/DVI modes ignore everything but the last 2 items. So we share /* HDMI/DVI modes ignore everything but the last 2 items. So we share
* them for both DP and FDI transports, allowing those ports to * them for both DP and FDI transports, allowing those ports to
@ -1115,6 +1116,69 @@ static const struct intel_ddi_buf_trans mtl_c20_trans_uhbr = {
.num_entries = ARRAY_SIZE(_mtl_c20_trans_uhbr), .num_entries = ARRAY_SIZE(_mtl_c20_trans_uhbr),
}; };
/* DP1.4 */
static const union intel_ddi_buf_trans_entry _xe3plpd_lt_trans_dp14[] = {
{ .lt = { 1, 0, 0, 21, 0 } },
{ .lt = { 1, 1, 0, 24, 3 } },
{ .lt = { 1, 2, 0, 28, 7 } },
{ .lt = { 0, 3, 0, 35, 13 } },
{ .lt = { 1, 1, 0, 27, 0 } },
{ .lt = { 1, 2, 0, 31, 4 } },
{ .lt = { 0, 3, 0, 39, 9 } },
{ .lt = { 1, 2, 0, 35, 0 } },
{ .lt = { 0, 3, 0, 41, 7 } },
{ .lt = { 0, 3, 0, 48, 0 } },
};
/* DP2.1 */
static const union intel_ddi_buf_trans_entry _xe3plpd_lt_trans_uhbr[] = {
{ .lt = { 0, 0, 0, 48, 0 } },
{ .lt = { 0, 0, 0, 43, 5 } },
{ .lt = { 0, 0, 0, 40, 8 } },
{ .lt = { 0, 0, 0, 37, 11 } },
{ .lt = { 0, 0, 0, 33, 15 } },
{ .lt = { 0, 0, 2, 46, 0 } },
{ .lt = { 0, 0, 2, 42, 4 } },
{ .lt = { 0, 0, 2, 38, 8 } },
{ .lt = { 0, 0, 2, 35, 11 } },
{ .lt = { 0, 0, 2, 33, 13 } },
{ .lt = { 0, 0, 4, 44, 0 } },
{ .lt = { 0, 0, 4, 40, 4 } },
{ .lt = { 0, 0, 4, 37, 7 } },
{ .lt = { 0, 0, 4, 33, 11 } },
{ .lt = { 0, 0, 8, 40, 0 } },
{ .lt = { 1, 0, 2, 26, 2 } },
};
/* eDp */
static const union intel_ddi_buf_trans_entry _xe3plpd_lt_trans_edp[] = {
{ .lt = { 1, 0, 0, 12, 0 } },
{ .lt = { 1, 1, 0, 13, 1 } },
{ .lt = { 1, 2, 0, 15, 3 } },
{ .lt = { 1, 3, 0, 19, 7 } },
{ .lt = { 1, 1, 0, 14, 0 } },
{ .lt = { 1, 2, 0, 16, 2 } },
{ .lt = { 1, 3, 0, 21, 5 } },
{ .lt = { 1, 2, 0, 18, 0 } },
{ .lt = { 1, 3, 0, 22, 4 } },
{ .lt = { 1, 3, 0, 26, 0 } },
};
static const struct intel_ddi_buf_trans xe3plpd_lt_trans_dp14 = {
.entries = _xe3plpd_lt_trans_dp14,
.num_entries = ARRAY_SIZE(_xe3plpd_lt_trans_dp14),
};
static const struct intel_ddi_buf_trans xe3plpd_lt_trans_uhbr = {
.entries = _xe3plpd_lt_trans_uhbr,
.num_entries = ARRAY_SIZE(_xe3plpd_lt_trans_uhbr),
};
static const struct intel_ddi_buf_trans xe3plpd_lt_trans_edp = {
.entries = _xe3plpd_lt_trans_edp,
.num_entries = ARRAY_SIZE(_xe3plpd_lt_trans_edp),
};
bool is_hobl_buf_trans(const struct intel_ddi_buf_trans *table) bool is_hobl_buf_trans(const struct intel_ddi_buf_trans *table)
{ {
return table == &tgl_combo_phy_trans_edp_hbr2_hobl; return table == &tgl_combo_phy_trans_edp_hbr2_hobl;
@ -1707,11 +1771,26 @@ mtl_get_c20_buf_trans(struct intel_encoder *encoder,
return intel_get_buf_trans(&mtl_c20_trans_dp14, n_entries); return intel_get_buf_trans(&mtl_c20_trans_dp14, n_entries);
} }
static const struct intel_ddi_buf_trans *
xe3plpd_get_lt_buf_trans(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
int *n_entries)
{
if (intel_crtc_has_dp_encoder(crtc_state) && intel_dp_is_uhbr(crtc_state))
return intel_get_buf_trans(&xe3plpd_lt_trans_uhbr, n_entries);
else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
return intel_get_buf_trans(&xe3plpd_lt_trans_edp, n_entries);
else
return intel_get_buf_trans(&xe3plpd_lt_trans_dp14, n_entries);
}
void intel_ddi_buf_trans_init(struct intel_encoder *encoder) void intel_ddi_buf_trans_init(struct intel_encoder *encoder)
{ {
struct intel_display *display = to_intel_display(encoder); struct intel_display *display = to_intel_display(encoder);
if (DISPLAY_VER(display) >= 14) { if (HAS_LT_PHY(display)) {
encoder->get_buf_trans = xe3plpd_get_lt_buf_trans;
} else if (DISPLAY_VER(display) >= 14) {
if (intel_encoder_is_c10phy(encoder)) if (intel_encoder_is_c10phy(encoder))
encoder->get_buf_trans = mtl_get_c10_buf_trans; encoder->get_buf_trans = mtl_get_c10_buf_trans;
else else

View File

@ -50,6 +50,14 @@ struct dg2_snps_phy_buf_trans {
u8 post_cursor; u8 post_cursor;
}; };
struct xe3plpd_lt_phy_buf_trans {
u8 txswing;
u8 txswing_level;
u8 pre_cursor;
u8 main_cursor;
u8 post_cursor;
};
union intel_ddi_buf_trans_entry { union intel_ddi_buf_trans_entry {
struct hsw_ddi_buf_trans hsw; struct hsw_ddi_buf_trans hsw;
struct bxt_ddi_buf_trans bxt; struct bxt_ddi_buf_trans bxt;
@ -57,6 +65,7 @@ union intel_ddi_buf_trans_entry {
struct icl_mg_phy_ddi_buf_trans mg; struct icl_mg_phy_ddi_buf_trans mg;
struct tgl_dkl_phy_ddi_buf_trans dkl; struct tgl_dkl_phy_ddi_buf_trans dkl;
struct dg2_snps_phy_buf_trans snps; struct dg2_snps_phy_buf_trans snps;
struct xe3plpd_lt_phy_buf_trans lt;
}; };
struct intel_ddi_buf_trans { struct intel_ddi_buf_trans {

View File

@ -51,7 +51,6 @@
#include "i915_config.h" #include "i915_config.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "i9xx_plane.h" #include "i9xx_plane.h"
#include "i9xx_plane_regs.h" #include "i9xx_plane_regs.h"
#include "i9xx_wm.h" #include "i9xx_wm.h"
@ -60,6 +59,7 @@
#include "intel_audio.h" #include "intel_audio.h"
#include "intel_bo.h" #include "intel_bo.h"
#include "intel_bw.h" #include "intel_bw.h"
#include "intel_casf.h"
#include "intel_cdclk.h" #include "intel_cdclk.h"
#include "intel_clock_gating.h" #include "intel_clock_gating.h"
#include "intel_color.h" #include "intel_color.h"
@ -76,6 +76,7 @@
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_display_wa.h" #include "intel_display_wa.h"
#include "intel_dmc.h" #include "intel_dmc.h"
#include "intel_dp.h" #include "intel_dp.h"
@ -99,6 +100,7 @@
#include "intel_hdmi.h" #include "intel_hdmi.h"
#include "intel_hotplug.h" #include "intel_hotplug.h"
#include "intel_link_bw.h" #include "intel_link_bw.h"
#include "intel_lt_phy.h"
#include "intel_lvds.h" #include "intel_lvds.h"
#include "intel_lvds_regs.h" #include "intel_lvds_regs.h"
#include "intel_modeset_setup.h" #include "intel_modeset_setup.h"
@ -129,11 +131,9 @@
#include "skl_scaler.h" #include "skl_scaler.h"
#include "skl_universal_plane.h" #include "skl_universal_plane.h"
#include "skl_watermark.h" #include "skl_watermark.h"
#include "vlv_dpio_phy_regs.h"
#include "vlv_dsi.h" #include "vlv_dsi.h"
#include "vlv_dsi_pll.h" #include "vlv_dsi_pll.h"
#include "vlv_dsi_regs.h" #include "vlv_dsi_regs.h"
#include "vlv_sideband.h"
static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state); static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state); static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state);
@ -141,65 +141,6 @@ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state);
static void bdw_set_pipe_misc(struct intel_dsb *dsb, static void bdw_set_pipe_misc(struct intel_dsb *dsb,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
/* returns HPLL frequency in kHz */
int vlv_get_hpll_vco(struct drm_device *drm)
{
int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
/* Obtain SKU information */
hpll_freq = vlv_cck_read(drm, CCK_FUSE_REG) &
CCK_FUSE_HPLL_FREQ_MASK;
return vco_freq[hpll_freq] * 1000;
}
int vlv_get_cck_clock(struct drm_device *drm,
const char *name, u32 reg, int ref_freq)
{
u32 val;
int divider;
val = vlv_cck_read(drm, reg);
divider = val & CCK_FREQUENCY_VALUES;
drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
(divider << CCK_FREQUENCY_STATUS_SHIFT),
"%s change in progress\n", name);
return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
}
int vlv_get_cck_clock_hpll(struct drm_device *drm,
const char *name, u32 reg)
{
struct drm_i915_private *dev_priv = to_i915(drm);
int hpll;
vlv_cck_get(drm);
if (dev_priv->hpll_freq == 0)
dev_priv->hpll_freq = vlv_get_hpll_vco(drm);
hpll = vlv_get_cck_clock(drm, name, reg, dev_priv->hpll_freq);
vlv_cck_put(drm);
return hpll;
}
void intel_update_czclk(struct intel_display *display)
{
struct drm_i915_private *dev_priv = to_i915(display->drm);
if (!display->platform.valleyview && !display->platform.cherryview)
return;
dev_priv->czclk_freq = vlv_get_cck_clock_hpll(display->drm, "czclk",
CCK_CZ_CLOCK_CONTROL);
drm_dbg_kms(display->drm, "CZ clock rate: %d kHz\n", dev_priv->czclk_freq);
}
static bool is_hdr_mode(const struct intel_crtc_state *crtc_state) static bool is_hdr_mode(const struct intel_crtc_state *crtc_state)
{ {
return (crtc_state->active_planes & return (crtc_state->active_planes &
@ -891,9 +832,8 @@ static void intel_async_flip_vtd_wa(struct intel_display *display,
static bool needs_async_flip_vtd_wa(const struct intel_crtc_state *crtc_state) static bool needs_async_flip_vtd_wa(const struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
return crtc_state->uapi.async_flip && i915_vtd_active(i915) && return crtc_state->uapi.async_flip && intel_display_vtd_active(display) &&
(DISPLAY_VER(display) == 9 || display->platform.broadwell || (DISPLAY_VER(display) == 9 || display->platform.broadwell ||
display->platform.haswell); display->platform.haswell);
} }
@ -1040,6 +980,24 @@ static bool audio_disabling(const struct intel_crtc_state *old_crtc_state,
memcmp(old_crtc_state->eld, new_crtc_state->eld, MAX_ELD_BYTES) != 0); memcmp(old_crtc_state->eld, new_crtc_state->eld, MAX_ELD_BYTES) != 0);
} }
static bool intel_casf_enabling(const struct intel_crtc_state *new_crtc_state,
const struct intel_crtc_state *old_crtc_state)
{
if (!new_crtc_state->hw.active)
return false;
return is_enabling(hw.casf_params.casf_enable, old_crtc_state, new_crtc_state);
}
static bool intel_casf_disabling(const struct intel_crtc_state *old_crtc_state,
const struct intel_crtc_state *new_crtc_state)
{
if (!new_crtc_state->hw.active)
return false;
return is_disabling(hw.casf_params.casf_enable, old_crtc_state, new_crtc_state);
}
#undef is_disabling #undef is_disabling
#undef is_enabling #undef is_enabling
@ -1195,6 +1153,9 @@ static void intel_pre_plane_update(struct intel_atomic_state *state,
if (audio_disabling(old_crtc_state, new_crtc_state)) if (audio_disabling(old_crtc_state, new_crtc_state))
intel_encoders_audio_disable(state, crtc); intel_encoders_audio_disable(state, crtc);
if (intel_casf_disabling(old_crtc_state, new_crtc_state))
intel_casf_disable(new_crtc_state);
intel_drrs_deactivate(old_crtc_state); intel_drrs_deactivate(old_crtc_state);
if (hsw_ips_pre_update(state, crtc)) if (hsw_ips_pre_update(state, crtc))
@ -1642,8 +1603,7 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
} }
intel_set_transcoder_timings(crtc_state); intel_set_transcoder_timings(crtc_state);
if (HAS_VRR(display)) intel_vrr_set_transcoder_timings(crtc_state);
intel_vrr_set_transcoder_timings(crtc_state);
if (cpu_transcoder != TRANSCODER_EDP) if (cpu_transcoder != TRANSCODER_EDP)
intel_de_write(display, TRANS_MULT(display, cpu_transcoder), intel_de_write(display, TRANS_MULT(display, cpu_transcoder),
@ -2422,39 +2382,44 @@ static int intel_crtc_compute_pipe_mode(struct intel_crtc_state *crtc_state)
return 0; return 0;
} }
static int intel_crtc_vblank_delay(const struct intel_crtc_state *crtc_state) static int intel_crtc_set_context_latency(struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
int vblank_delay = 0; int set_context_latency = 0;
if (!HAS_DSB(display)) if (!HAS_DSB(display))
return 0; return 0;
vblank_delay = max(vblank_delay, intel_psr_min_vblank_delay(crtc_state)); set_context_latency = max(set_context_latency,
intel_psr_min_set_context_latency(crtc_state));
return vblank_delay; return set_context_latency;
} }
static int intel_crtc_compute_vblank_delay(struct intel_atomic_state *state, static int intel_crtc_compute_set_context_latency(struct intel_atomic_state *state,
struct intel_crtc *crtc) struct intel_crtc *crtc)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
struct intel_crtc_state *crtc_state = struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc); intel_atomic_get_new_crtc_state(state, crtc);
struct drm_display_mode *adjusted_mode = struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode; &crtc_state->hw.adjusted_mode;
int vblank_delay, max_vblank_delay; int set_context_latency, max_vblank_delay;
set_context_latency = intel_crtc_set_context_latency(crtc_state);
vblank_delay = intel_crtc_vblank_delay(crtc_state);
max_vblank_delay = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start - 1; max_vblank_delay = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start - 1;
if (vblank_delay > max_vblank_delay) { if (set_context_latency > max_vblank_delay) {
drm_dbg_kms(display->drm, "[CRTC:%d:%s] vblank delay (%d) exceeds max (%d)\n", drm_dbg_kms(display->drm, "[CRTC:%d:%s] set context latency (%d) exceeds max (%d)\n",
crtc->base.base.id, crtc->base.name, vblank_delay, max_vblank_delay); crtc->base.base.id, crtc->base.name,
set_context_latency,
max_vblank_delay);
return -EINVAL; return -EINVAL;
} }
adjusted_mode->crtc_vblank_start += vblank_delay; crtc_state->set_context_latency = set_context_latency;
adjusted_mode->crtc_vblank_start += set_context_latency;
return 0; return 0;
} }
@ -2466,11 +2431,11 @@ static int intel_crtc_compute_config(struct intel_atomic_state *state,
intel_atomic_get_new_crtc_state(state, crtc); intel_atomic_get_new_crtc_state(state, crtc);
int ret; int ret;
ret = intel_crtc_compute_vblank_delay(state, crtc); ret = intel_dpll_crtc_compute_clock(state, crtc);
if (ret) if (ret)
return ret; return ret;
ret = intel_dpll_crtc_compute_clock(state, crtc); ret = intel_crtc_compute_set_context_latency(state, crtc);
if (ret) if (ret)
return ret; return ret;
@ -2487,6 +2452,8 @@ static int intel_crtc_compute_config(struct intel_atomic_state *state,
if (crtc_state->has_pch_encoder) if (crtc_state->has_pch_encoder)
return ilk_fdi_compute_config(crtc, crtc_state); return ilk_fdi_compute_config(crtc, crtc_state);
intel_vrr_compute_guardband(crtc_state);
return 0; return 0;
} }
@ -2678,13 +2645,16 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
if (DISPLAY_VER(display) >= 13) { if (DISPLAY_VER(display) >= 13) {
intel_de_write(display, intel_de_write(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
crtc_vblank_start - crtc_vdisplay); crtc_state->set_context_latency);
/* /*
* VBLANK_START not used by hw, just clear it * VBLANK_START not used by hw, just clear it
* to make it stand out in register dumps. * to make it stand out in register dumps.
*/ */
crtc_vblank_start = 1; crtc_vblank_start = 1;
} else if (DISPLAY_VER(display) == 12) {
/* VBLANK_START - VACTIVE defines SCL on TGL */
crtc_vblank_start = crtc_vdisplay + crtc_state->set_context_latency;
} }
if (DISPLAY_VER(display) >= 4) if (DISPLAY_VER(display) >= 4)
@ -2768,13 +2738,16 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc
if (DISPLAY_VER(display) >= 13) { if (DISPLAY_VER(display) >= 13) {
intel_de_write(display, intel_de_write(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
crtc_vblank_start - crtc_vdisplay); crtc_state->set_context_latency);
/* /*
* VBLANK_START not used by hw, just clear it * VBLANK_START not used by hw, just clear it
* to make it stand out in register dumps. * to make it stand out in register dumps.
*/ */
crtc_vblank_start = 1; crtc_vblank_start = 1;
} else if (DISPLAY_VER(display) == 12) {
/* VBLANK_START - VACTIVE defines SCL on TGL */
crtc_vblank_start = crtc_vdisplay + crtc_state->set_context_latency;
} }
/* /*
@ -2881,11 +2854,24 @@ static void intel_get_transcoder_timings(struct intel_crtc *crtc,
adjusted_mode->crtc_vblank_end += 1; adjusted_mode->crtc_vblank_end += 1;
} }
if (DISPLAY_VER(display) >= 13 && !transcoder_is_dsi(cpu_transcoder)) if (DISPLAY_VER(display) >= 13 && !transcoder_is_dsi(cpu_transcoder)) {
adjusted_mode->crtc_vblank_start = pipe_config->set_context_latency =
adjusted_mode->crtc_vdisplay +
intel_de_read(display, intel_de_read(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder)); TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder));
adjusted_mode->crtc_vblank_start =
adjusted_mode->crtc_vdisplay +
pipe_config->set_context_latency;
} else if (DISPLAY_VER(display) == 12) {
/*
* TGL doesn't have a dedicated register for SCL.
* Instead, the hardware derives SCL from the difference between
* TRANS_VBLANK.vblank_start and TRANS_VTOTAL.vactive.
* To reflect the HW behaviour, readout the value for SCL as
* Vblank start - Vactive.
*/
pipe_config->set_context_latency =
adjusted_mode->crtc_vblank_start - adjusted_mode->crtc_vdisplay;
}
if (DISPLAY_VER(display) >= 30) if (DISPLAY_VER(display) >= 30)
pipe_config->min_hblank = intel_de_read(display, pipe_config->min_hblank = intel_de_read(display,
@ -3952,6 +3938,20 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc,
intel_joiner_get_config(pipe_config); intel_joiner_get_config(pipe_config);
intel_dsc_get_config(pipe_config); intel_dsc_get_config(pipe_config);
/* intel_vrr_get_config() depends on .framestart_delay */
if (!transcoder_is_dsi(pipe_config->cpu_transcoder)) {
tmp = intel_de_read(display, CHICKEN_TRANS(display, pipe_config->cpu_transcoder));
pipe_config->framestart_delay = REG_FIELD_GET(HSW_FRAME_START_DELAY_MASK, tmp) + 1;
} else {
/* no idea if this is correct */
pipe_config->framestart_delay = 1;
}
/*
* intel_vrr_get_config() depends on TRANS_SET_CONTEXT_LATENCY
* readout done by intel_get_transcoder_timings().
*/
if (!transcoder_is_dsi(pipe_config->cpu_transcoder) || if (!transcoder_is_dsi(pipe_config->cpu_transcoder) ||
DISPLAY_VER(display) >= 11) DISPLAY_VER(display) >= 11)
intel_get_transcoder_timings(crtc, pipe_config); intel_get_transcoder_timings(crtc, pipe_config);
@ -4003,15 +4003,6 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc,
pipe_config->pixel_multiplier = 1; pipe_config->pixel_multiplier = 1;
} }
if (!transcoder_is_dsi(pipe_config->cpu_transcoder)) {
tmp = intel_de_read(display, CHICKEN_TRANS(display, pipe_config->cpu_transcoder));
pipe_config->framestart_delay = REG_FIELD_GET(HSW_FRAME_START_DELAY_MASK, tmp) + 1;
} else {
/* no idea if this is correct */
pipe_config->framestart_delay = 1;
}
out: out:
intel_display_power_put_all_in_set(display, &crtc->hw_readout_power_domains); intel_display_power_put_all_in_set(display, &crtc->hw_readout_power_domains);
@ -4258,9 +4249,14 @@ static int intel_crtc_atomic_check(struct intel_atomic_state *state,
return ret; return ret;
} }
ret = intel_casf_compute_config(crtc_state);
if (ret)
return ret;
if (DISPLAY_VER(display) >= 9) { if (DISPLAY_VER(display) >= 9) {
if (intel_crtc_needs_modeset(crtc_state) || if (intel_crtc_needs_modeset(crtc_state) ||
intel_crtc_needs_fastset(crtc_state)) { intel_crtc_needs_fastset(crtc_state) ||
intel_casf_needs_scaler(crtc_state)) {
ret = skl_update_scaler_crtc(crtc_state); ret = skl_update_scaler_crtc(crtc_state);
if (ret) if (ret)
return ret; return ret;
@ -4639,7 +4635,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
if (ret) if (ret)
return ret; return ret;
crtc_state->fec_enable = limits->force_fec_pipes & BIT(crtc->pipe); crtc_state->dsc.compression_enabled_on_link = limits->link_dsc_pipes & BIT(crtc->pipe);
crtc_state->max_link_bpp_x16 = limits->max_bpp_x16[crtc->pipe]; crtc_state->max_link_bpp_x16 = limits->max_bpp_x16[crtc->pipe];
if (crtc_state->pipe_bpp > fxp_q4_to_int(crtc_state->max_link_bpp_x16)) { if (crtc_state->pipe_bpp > fxp_q4_to_int(crtc_state->max_link_bpp_x16)) {
@ -4760,8 +4756,6 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state,
struct drm_connector *connector; struct drm_connector *connector;
int i; int i;
intel_vrr_compute_config_late(crtc_state);
for_each_new_connector_in_state(&state->base, connector, for_each_new_connector_in_state(&state->base, connector,
conn_state, i) { conn_state, i) {
struct intel_encoder *encoder = struct intel_encoder *encoder =
@ -4996,9 +4990,33 @@ static bool allow_vblank_delay_fastset(const struct intel_crtc_state *old_crtc_s
* Allow fastboot to fix up vblank delay (handled via LRR * Allow fastboot to fix up vblank delay (handled via LRR
* codepaths), a bit dodgy as the registers aren't * codepaths), a bit dodgy as the registers aren't
* double buffered but seems to be working more or less... * double buffered but seems to be working more or less...
*
* Also allow this when the VRR timing generator is always on,
* and optimized guardband is used. In such cases,
* vblank delay may vary even without inherited state, but it's
* still safe as VRR guardband is still same.
*/ */
return HAS_LRR(display) && old_crtc_state->inherited && return HAS_LRR(display) &&
!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DSI); (old_crtc_state->inherited || intel_vrr_always_use_vrr_tg(display)) &&
!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DSI);
}
static void
pipe_config_lt_phy_pll_mismatch(struct drm_printer *p, bool fastset,
const struct intel_crtc *crtc,
const char *name,
const struct intel_lt_phy_pll_state *a,
const struct intel_lt_phy_pll_state *b)
{
struct intel_display *display = to_intel_display(crtc);
char *chipname = "LTPHY";
pipe_config_mismatch(p, fastset, crtc, name, chipname);
drm_printf(p, "expected:\n");
intel_lt_phy_dump_hw_state(display, a);
drm_printf(p, "found:\n");
intel_lt_phy_dump_hw_state(display, b);
} }
bool bool
@ -5125,6 +5143,16 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
} \ } \
} while (0) } while (0)
#define PIPE_CONF_CHECK_PLL_LT(name) do { \
if (!intel_lt_phy_pll_compare_hw_state(&current_config->name, \
&pipe_config->name)) { \
pipe_config_lt_phy_pll_mismatch(&p, fastset, crtc, __stringify(name), \
&current_config->name, \
&pipe_config->name); \
ret = false; \
} \
} while (0)
#define PIPE_CONF_CHECK_TIMINGS(name) do { \ #define PIPE_CONF_CHECK_TIMINGS(name) do { \
PIPE_CONF_CHECK_I(name.crtc_hdisplay); \ PIPE_CONF_CHECK_I(name.crtc_hdisplay); \
PIPE_CONF_CHECK_I(name.crtc_htotal); \ PIPE_CONF_CHECK_I(name.crtc_htotal); \
@ -5319,6 +5347,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(scaler_state.scaler_id); PIPE_CONF_CHECK_I(scaler_state.scaler_id);
PIPE_CONF_CHECK_I(pixel_rate); PIPE_CONF_CHECK_I(pixel_rate);
PIPE_CONF_CHECK_BOOL(hw.casf_params.casf_enable);
PIPE_CONF_CHECK_I(hw.casf_params.win_size);
PIPE_CONF_CHECK_I(hw.casf_params.strength);
PIPE_CONF_CHECK_X(gamma_mode); PIPE_CONF_CHECK_X(gamma_mode);
if (display->platform.cherryview) if (display->platform.cherryview)
@ -5349,7 +5380,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_PLL(dpll_hw_state); PIPE_CONF_CHECK_PLL(dpll_hw_state);
/* FIXME convert MTL+ platforms over to dpll_mgr */ /* FIXME convert MTL+ platforms over to dpll_mgr */
if (DISPLAY_VER(display) >= 14) if (HAS_LT_PHY(display))
PIPE_CONF_CHECK_PLL_LT(dpll_hw_state.ltpll);
else if (DISPLAY_VER(display) >= 14)
PIPE_CONF_CHECK_PLL_CX0(dpll_hw_state.cx0pll); PIPE_CONF_CHECK_PLL_CX0(dpll_hw_state.cx0pll);
PIPE_CONF_CHECK_X(dsi_pll.ctrl); PIPE_CONF_CHECK_X(dsi_pll.ctrl);
@ -5443,6 +5476,8 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(vrr.guardband); PIPE_CONF_CHECK_I(vrr.guardband);
} }
PIPE_CONF_CHECK_I(set_context_latency);
#undef PIPE_CONF_CHECK_X #undef PIPE_CONF_CHECK_X
#undef PIPE_CONF_CHECK_I #undef PIPE_CONF_CHECK_I
#undef PIPE_CONF_CHECK_LLI #undef PIPE_CONF_CHECK_LLI
@ -5689,6 +5724,23 @@ static int hsw_mode_set_planes_workaround(struct intel_atomic_state *state)
return 0; return 0;
} }
u8 intel_calc_enabled_pipes(struct intel_atomic_state *state,
u8 enabled_pipes)
{
const struct intel_crtc_state *crtc_state;
struct intel_crtc *crtc;
int i;
for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
if (crtc_state->hw.enable)
enabled_pipes |= BIT(crtc->pipe);
else
enabled_pipes &= ~BIT(crtc->pipe);
}
return enabled_pipes;
}
u8 intel_calc_active_pipes(struct intel_atomic_state *state, u8 intel_calc_active_pipes(struct intel_atomic_state *state,
u8 active_pipes) u8 active_pipes)
{ {
@ -5718,12 +5770,16 @@ static int intel_modeset_checks(struct intel_atomic_state *state)
return 0; return 0;
} }
static bool lrr_params_changed(const struct drm_display_mode *old_adjusted_mode, static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state,
const struct drm_display_mode *new_adjusted_mode) const struct intel_crtc_state *new_crtc_state)
{ {
const struct drm_display_mode *old_adjusted_mode = &old_crtc_state->hw.adjusted_mode;
const struct drm_display_mode *new_adjusted_mode = &new_crtc_state->hw.adjusted_mode;
return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start || return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start ||
old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end || old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end ||
old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal; old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal ||
old_crtc_state->set_context_latency != new_crtc_state->set_context_latency;
} }
static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_state, static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_state,
@ -5749,8 +5805,7 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta
&new_crtc_state->dp_m_n)) &new_crtc_state->dp_m_n))
new_crtc_state->update_m_n = false; new_crtc_state->update_m_n = false;
if (!lrr_params_changed(&old_crtc_state->hw.adjusted_mode, if (!lrr_params_changed(old_crtc_state, new_crtc_state))
&new_crtc_state->hw.adjusted_mode))
new_crtc_state->update_lrr = false; new_crtc_state->update_lrr = false;
if (intel_crtc_needs_modeset(new_crtc_state)) if (intel_crtc_needs_modeset(new_crtc_state))
@ -6342,7 +6397,6 @@ int intel_atomic_check(struct drm_device *dev,
struct intel_crtc_state *old_crtc_state, *new_crtc_state; struct intel_crtc_state *old_crtc_state, *new_crtc_state;
struct intel_crtc *crtc; struct intel_crtc *crtc;
int ret, i; int ret, i;
bool any_ms = false;
if (!intel_display_driver_check_access(display)) if (!intel_display_driver_check_access(display))
return -ENODEV; return -ENODEV;
@ -6450,14 +6504,11 @@ int intel_atomic_check(struct drm_device *dev,
if (!intel_crtc_needs_modeset(new_crtc_state)) if (!intel_crtc_needs_modeset(new_crtc_state))
continue; continue;
any_ms = true;
intel_dpll_release(state, crtc); intel_dpll_release(state, crtc);
} }
if (any_ms && !check_digital_port_conflicts(state)) { if (intel_any_crtc_needs_modeset(state) && !check_digital_port_conflicts(state)) {
drm_dbg_kms(display->drm, drm_dbg_kms(display->drm, "rejecting conflicting digital port configuration\n");
"rejecting conflicting digital port configuration\n");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -6466,29 +6517,25 @@ int intel_atomic_check(struct drm_device *dev,
if (ret) if (ret)
goto fail; goto fail;
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
new_crtc_state->min_cdclk = intel_crtc_min_cdclk(new_crtc_state);
ret = intel_compute_global_watermarks(state); ret = intel_compute_global_watermarks(state);
if (ret) if (ret)
goto fail; goto fail;
ret = intel_bw_atomic_check(state, any_ms); ret = intel_bw_atomic_check(state);
if (ret) if (ret)
goto fail; goto fail;
ret = intel_cdclk_atomic_check(state, &any_ms); ret = intel_cdclk_atomic_check(state);
if (ret) if (ret)
goto fail; goto fail;
if (intel_any_crtc_needs_modeset(state)) if (intel_any_crtc_needs_modeset(state)) {
any_ms = true;
if (any_ms) {
ret = intel_modeset_checks(state); ret = intel_modeset_checks(state);
if (ret) if (ret)
goto fail; goto fail;
ret = intel_modeset_calc_cdclk(state);
if (ret)
return ret;
} }
ret = intel_pmdemand_atomic_check(state); ret = intel_pmdemand_atomic_check(state);
@ -6739,6 +6786,11 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
intel_vrr_set_transcoder_timings(new_crtc_state); intel_vrr_set_transcoder_timings(new_crtc_state);
} }
if (intel_casf_enabling(new_crtc_state, old_crtc_state))
intel_casf_enable(new_crtc_state);
else if (new_crtc_state->hw.casf_params.strength != old_crtc_state->hw.casf_params.strength)
intel_casf_update_strength(new_crtc_state);
intel_fbc_update(state, crtc); intel_fbc_update(state, crtc);
drm_WARN_ON(display->drm, !intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF)); drm_WARN_ON(display->drm, !intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF));
@ -7307,7 +7359,7 @@ static void intel_atomic_dsb_finish(struct intel_atomic_state *state,
intel_dsb_wait_vblanks(new_crtc_state->dsb_commit, 1); intel_dsb_wait_vblanks(new_crtc_state->dsb_commit, 1);
intel_vrr_send_push(new_crtc_state->dsb_commit, new_crtc_state); intel_vrr_send_push(new_crtc_state->dsb_commit, new_crtc_state);
intel_dsb_wait_vblank_delay(state, new_crtc_state->dsb_commit); intel_dsb_wait_for_delayed_vblank(state, new_crtc_state->dsb_commit);
intel_vrr_check_push_sent(new_crtc_state->dsb_commit, intel_vrr_check_push_sent(new_crtc_state->dsb_commit,
new_crtc_state); new_crtc_state);
intel_dsb_interrupt(new_crtc_state->dsb_commit); intel_dsb_interrupt(new_crtc_state->dsb_commit);
@ -7397,13 +7449,13 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
*/ */
intel_pmdemand_pre_plane_update(state); intel_pmdemand_pre_plane_update(state);
if (state->modeset) { if (state->modeset)
drm_atomic_helper_update_legacy_modeset_state(display->drm, &state->base); drm_atomic_helper_update_legacy_modeset_state(display->drm, &state->base);
intel_set_cdclk_pre_plane_update(state); intel_set_cdclk_pre_plane_update(state);
if (state->modeset)
intel_modeset_verify_disabled(state); intel_modeset_verify_disabled(state);
}
intel_sagv_pre_plane_update(state); intel_sagv_pre_plane_update(state);
@ -7516,8 +7568,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
intel_verify_planes(state); intel_verify_planes(state);
intel_sagv_post_plane_update(state); intel_sagv_post_plane_update(state);
if (state->modeset) intel_set_cdclk_post_plane_update(state);
intel_set_cdclk_post_plane_update(state);
intel_pmdemand_post_plane_update(state); intel_pmdemand_post_plane_update(state);
drm_atomic_helper_commit_hw_done(&state->base); drm_atomic_helper_commit_hw_done(&state->base);
@ -8003,6 +8054,14 @@ enum drm_mode_status intel_mode_valid(struct drm_device *dev,
mode->vtotal > vtotal_max) mode->vtotal > vtotal_max)
return MODE_V_ILLEGAL; return MODE_V_ILLEGAL;
/*
* WM_LINETIME only goes up to (almost) 64 usec, and also
* knowing that the linetime is always bounded will ease the
* mind during various calculations.
*/
if (DIV_ROUND_UP(mode->htotal * 1000, mode->clock) > 64)
return MODE_H_ILLEGAL;
return MODE_OK; return MODE_OK;
} }
@ -8327,7 +8386,5 @@ void i830_disable_pipe(struct intel_display *display, enum pipe pipe)
bool intel_scanout_needs_vtd_wa(struct intel_display *display) bool intel_scanout_needs_vtd_wa(struct intel_display *display)
{ {
struct drm_i915_private *i915 = to_i915(display->drm); return IS_DISPLAY_VER(display, 6, 11) && intel_display_vtd_active(display);
return IS_DISPLAY_VER(display, 6, 11) && i915_vtd_active(i915);
} }

View File

@ -394,6 +394,8 @@ enum phy_fia {
i) i)
int intel_atomic_check(struct drm_device *dev, struct drm_atomic_state *state); int intel_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
u8 intel_calc_enabled_pipes(struct intel_atomic_state *state,
u8 enabled_pipes);
u8 intel_calc_active_pipes(struct intel_atomic_state *state, u8 intel_calc_active_pipes(struct intel_atomic_state *state,
u8 active_pipes); u8 active_pipes);
void intel_link_compute_m_n(u16 bpp, int nlanes, void intel_link_compute_m_n(u16 bpp, int nlanes,
@ -435,11 +437,6 @@ void intel_enable_transcoder(const struct intel_crtc_state *new_crtc_state);
void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state); void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state);
void i830_enable_pipe(struct intel_display *display, enum pipe pipe); void i830_enable_pipe(struct intel_display *display, enum pipe pipe);
void i830_disable_pipe(struct intel_display *display, enum pipe pipe); void i830_disable_pipe(struct intel_display *display, enum pipe pipe);
int vlv_get_hpll_vco(struct drm_device *drm);
int vlv_get_cck_clock(struct drm_device *drm,
const char *name, u32 reg, int ref_freq);
int vlv_get_cck_clock_hpll(struct drm_device *drm,
const char *name, u32 reg);
bool intel_has_pending_fb_unpin(struct intel_display *display); bool intel_has_pending_fb_unpin(struct intel_display *display);
void intel_encoder_destroy(struct drm_encoder *encoder); void intel_encoder_destroy(struct drm_encoder *encoder);
struct drm_display_mode * struct drm_display_mode *
@ -528,7 +525,6 @@ void intel_init_display_hooks(struct intel_display *display);
void intel_setup_outputs(struct intel_display *display); void intel_setup_outputs(struct intel_display *display);
int intel_initial_commit(struct intel_display *display); int intel_initial_commit(struct intel_display *display);
void intel_panel_sanitize_ssc(struct intel_display *display); void intel_panel_sanitize_ssc(struct intel_display *display);
void intel_update_czclk(struct intel_display *display);
enum drm_mode_status intel_mode_valid(struct drm_device *dev, enum drm_mode_status intel_mode_valid(struct drm_device *dev,
const struct drm_display_mode *mode); const struct drm_display_mode *mode);
int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state, int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state,

View File

@ -1,15 +1,21 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/* Copyright © 2024 Intel Corporation */ /* Copyright © 2024 Intel Corporation */
#include "i915_drv.h" #include <drm/intel/display_member.h>
#include "intel_display_conversion.h"
static struct intel_display *__i915_to_display(struct drm_i915_private *i915) #include "intel_display_conversion.h"
{
return i915->display;
}
struct intel_display *__drm_to_display(struct drm_device *drm) struct intel_display *__drm_to_display(struct drm_device *drm)
{ {
return __i915_to_display(to_i915(drm)); /*
* Note: This relies on both struct drm_i915_private and struct
* xe_device having the struct drm_device and struct intel_display *
* members at the same relative offsets, as defined by struct
* __intel_generic_device.
*
* See also INTEL_DISPLAY_MEMBER_STATIC_ASSERT().
*/
struct __intel_generic_device *d = container_of(drm, struct __intel_generic_device, drm);
return d->display;
} }

View File

@ -41,6 +41,7 @@ struct intel_cdclk_vals;
struct intel_color_funcs; struct intel_color_funcs;
struct intel_crtc; struct intel_crtc;
struct intel_crtc_state; struct intel_crtc_state;
struct intel_display_parent_interface;
struct intel_dmc; struct intel_dmc;
struct intel_dpll_global_funcs; struct intel_dpll_global_funcs;
struct intel_dpll_mgr; struct intel_dpll_mgr;
@ -291,6 +292,9 @@ struct intel_display {
/* Intel PCH: where the south display engine lives */ /* Intel PCH: where the south display engine lives */
enum intel_pch pch_type; enum intel_pch pch_type;
/* Parent, or core, driver functions exposed to display */
const struct intel_display_parent_interface *parent;
/* Display functions */ /* Display functions */
struct { struct {
/* Top level crtc-ish functions */ /* Top level crtc-ish functions */
@ -369,6 +373,10 @@ struct intel_display {
struct intel_global_obj obj; struct intel_global_obj obj;
} dbuf; } dbuf;
struct {
struct intel_global_obj obj;
} dbuf_bw;
struct { struct {
/* /*
* dkl.phy_lock protects against concurrent access of the * dkl.phy_lock protects against concurrent access of the
@ -475,7 +483,21 @@ struct intel_display {
struct work_struct vblank_notify_work; struct work_struct vblank_notify_work;
u32 de_irq_mask[I915_MAX_PIPES]; /*
* Cached value of VLV/CHV IMR to avoid reads in updating the
* bitfield.
*/
u32 vlv_imr_mask;
/*
* Cached value of gen 5-7 DE IMR to avoid reads in updating the
* bitfield.
*/
u32 ilk_de_imr_mask;
/*
* Cached value of BDW+ DE pipe IMR to avoid reads in updating
* the bitfield.
*/
u32 de_pipe_imr_mask[I915_MAX_PIPES];
u32 pipestat_irq_mask[I915_MAX_PIPES]; u32 pipestat_irq_mask[I915_MAX_PIPES];
} irq; } irq;
@ -567,6 +589,11 @@ struct intel_display {
u32 bxt_phy_grc; u32 bxt_phy_grc;
} state; } state;
struct {
unsigned int hpll_freq;
unsigned int czclk_freq;
} vlv_clock;
struct { struct {
/* ordered wq for modesets */ /* ordered wq for modesets */
struct workqueue_struct *modeset; struct workqueue_struct *modeset;

View File

@ -47,6 +47,7 @@
#include "intel_psr_regs.h" #include "intel_psr_regs.h"
#include "intel_vdsc.h" #include "intel_vdsc.h"
#include "intel_wm.h" #include "intel_wm.h"
#include "intel_tc.h"
static struct intel_display *node_to_intel_display(struct drm_info_node *node) static struct intel_display *node_to_intel_display(struct drm_info_node *node)
{ {
@ -246,6 +247,8 @@ static void intel_connector_info(struct seq_file *m,
{ {
struct intel_connector *intel_connector = to_intel_connector(connector); struct intel_connector *intel_connector = to_intel_connector(connector);
const struct drm_display_mode *mode; const struct drm_display_mode *mode;
struct drm_printer p = drm_seq_file_printer(m);
struct intel_digital_port *dig_port = NULL;
seq_printf(m, "[CONNECTOR:%d:%s]: status: %s\n", seq_printf(m, "[CONNECTOR:%d:%s]: status: %s\n",
connector->base.id, connector->name, connector->base.id, connector->name,
@ -268,14 +271,19 @@ static void intel_connector_info(struct seq_file *m,
intel_dp_mst_info(m, intel_connector); intel_dp_mst_info(m, intel_connector);
else else
intel_dp_info(m, intel_connector); intel_dp_info(m, intel_connector);
dig_port = dp_to_dig_port(intel_attached_dp(intel_connector));
break; break;
case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIA:
intel_hdmi_info(m, intel_connector); intel_hdmi_info(m, intel_connector);
dig_port = hdmi_to_dig_port(intel_attached_hdmi(intel_connector));
break; break;
default: default:
break; break;
} }
if (dig_port != NULL && intel_encoder_is_tc(&dig_port->base))
intel_tc_info(&p, dig_port);
intel_hdcp_info(m, intel_connector); intel_hdcp_info(m, intel_connector);
seq_printf(m, "\tmax bpc: %u\n", connector->display_info.bpc); seq_printf(m, "\tmax bpc: %u\n", connector->display_info.bpc);

View File

@ -1404,8 +1404,20 @@ static const struct platform_desc bmg_desc = {
PLATFORM_GROUP(dgfx), PLATFORM_GROUP(dgfx),
}; };
static const u16 wcl_ids[] = {
INTEL_WCL_IDS(ID),
0
};
static const struct platform_desc ptl_desc = { static const struct platform_desc ptl_desc = {
PLATFORM(pantherlake), PLATFORM(pantherlake),
.subplatforms = (const struct subplatform_desc[]) {
{
SUBPLATFORM(pantherlake, wildcatlake),
.pciidlist = wcl_ids,
},
{},
}
}; };
__diag_pop(); __diag_pop();
@ -1482,6 +1494,7 @@ static const struct {
INTEL_LNL_IDS(INTEL_DISPLAY_DEVICE, &lnl_desc), INTEL_LNL_IDS(INTEL_DISPLAY_DEVICE, &lnl_desc),
INTEL_BMG_IDS(INTEL_DISPLAY_DEVICE, &bmg_desc), INTEL_BMG_IDS(INTEL_DISPLAY_DEVICE, &bmg_desc),
INTEL_PTL_IDS(INTEL_DISPLAY_DEVICE, &ptl_desc), INTEL_PTL_IDS(INTEL_DISPLAY_DEVICE, &ptl_desc),
INTEL_WCL_IDS(INTEL_DISPLAY_DEVICE, &ptl_desc),
}; };
static const struct { static const struct {
@ -1634,7 +1647,8 @@ static void display_platforms_or(struct intel_display_platforms *dst,
bitmap_or(dst->bitmap, dst->bitmap, src->bitmap, display_platforms_num_bits()); bitmap_or(dst->bitmap, dst->bitmap, src->bitmap, display_platforms_num_bits());
} }
struct intel_display *intel_display_device_probe(struct pci_dev *pdev) struct intel_display *intel_display_device_probe(struct pci_dev *pdev,
const struct intel_display_parent_interface *parent)
{ {
struct intel_display *display; struct intel_display *display;
const struct intel_display_device_info *info; const struct intel_display_device_info *info;
@ -1650,6 +1664,8 @@ struct intel_display *intel_display_device_probe(struct pci_dev *pdev)
/* Add drm device backpointer as early as possible. */ /* Add drm device backpointer as early as possible. */
display->drm = pci_get_drvdata(pdev); display->drm = pci_get_drvdata(pdev);
display->parent = parent;
intel_display_params_copy(&display->params); intel_display_params_copy(&display->params);
if (has_no_display(pdev)) { if (has_no_display(pdev)) {

View File

@ -13,6 +13,7 @@
struct drm_printer; struct drm_printer;
struct intel_display; struct intel_display;
struct intel_display_parent_interface;
struct pci_dev; struct pci_dev;
/* /*
@ -101,7 +102,9 @@ struct pci_dev;
/* Display ver 14.1 (based on GMD ID) */ \ /* Display ver 14.1 (based on GMD ID) */ \
func(battlemage) \ func(battlemage) \
/* Display ver 30 (based on GMD ID) */ \ /* Display ver 30 (based on GMD ID) */ \
func(pantherlake) func(pantherlake) \
func(pantherlake_wildcatlake)
#define __MEMBER(name) unsigned long name:1; #define __MEMBER(name) unsigned long name:1;
#define __COUNT(x) 1 + #define __COUNT(x) 1 +
@ -140,10 +143,13 @@ struct intel_display_platforms {
func(overlay_needs_physical); \ func(overlay_needs_physical); \
func(supports_tv); func(supports_tv);
#define HAS_128B_Y_TILING(__display) (!(__display)->platform.i915g && !(__display)->platform.i915gm)
#define HAS_4TILE(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14) #define HAS_4TILE(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14)
#define HAS_ASYNC_FLIPS(__display) (DISPLAY_VER(__display) >= 5) #define HAS_ASYNC_FLIPS(__display) (DISPLAY_VER(__display) >= 5)
#define HAS_AS_SDP(__display) (DISPLAY_VER(__display) >= 13) #define HAS_AS_SDP(__display) (DISPLAY_VER(__display) >= 13)
#define HAS_AUX_CCS(__display) (IS_DISPLAY_VER(__display, 9, 12) || (__display)->platform.alderlake_p || (__display)->platform.meteorlake)
#define HAS_BIGJOINER(__display) (DISPLAY_VER(__display) >= 11 && HAS_DSC(__display)) #define HAS_BIGJOINER(__display) (DISPLAY_VER(__display) >= 11 && HAS_DSC(__display))
#define HAS_CASF(__display) (DISPLAY_VER(__display) >= 20)
#define HAS_CDCLK_CRAWL(__display) (DISPLAY_INFO(__display)->has_cdclk_crawl) #define HAS_CDCLK_CRAWL(__display) (DISPLAY_INFO(__display)->has_cdclk_crawl)
#define HAS_CDCLK_SQUASH(__display) (DISPLAY_INFO(__display)->has_cdclk_squash) #define HAS_CDCLK_SQUASH(__display) (DISPLAY_INFO(__display)->has_cdclk_squash)
#define HAS_CMRR(__display) (DISPLAY_VER(__display) >= 20) #define HAS_CMRR(__display) (DISPLAY_VER(__display) >= 20)
@ -155,7 +161,7 @@ struct intel_display_platforms {
#define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0) #define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0)
#define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc) #define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc)
#define HAS_DMC_WAKELOCK(__display) (DISPLAY_VER(__display) >= 20) #define HAS_DMC_WAKELOCK(__display) (DISPLAY_VER(__display) >= 20)
#define HAS_DOUBLE_BUFFERED_M_N(__display) (DISPLAY_VER(__display) >= 9 || (__display)->platform.broadwell) #define HAS_DOUBLE_BUFFERED_M_N(__display) (IS_DISPLAY_VER((__display), 9, 14) || (__display)->platform.broadwell)
#define HAS_DOUBLE_BUFFERED_LUT(__display) (DISPLAY_VER(__display) >= 30) #define HAS_DOUBLE_BUFFERED_LUT(__display) (DISPLAY_VER(__display) >= 30)
#define HAS_DOUBLE_WIDE(__display) (DISPLAY_VER(__display) < 4) #define HAS_DOUBLE_WIDE(__display) (DISPLAY_VER(__display) < 4)
#define HAS_DP20(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14) #define HAS_DP20(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14)
@ -308,7 +314,8 @@ struct intel_display_device_info {
bool intel_display_device_present(struct intel_display *display); bool intel_display_device_present(struct intel_display *display);
bool intel_display_device_enabled(struct intel_display *display); bool intel_display_device_enabled(struct intel_display *display);
struct intel_display *intel_display_device_probe(struct pci_dev *pdev); struct intel_display *intel_display_device_probe(struct pci_dev *pdev,
const struct intel_display_parent_interface *parent);
void intel_display_device_remove(struct intel_display *display); void intel_display_device_remove(struct intel_display *display);
void intel_display_device_info_runtime_init(struct intel_display *display); void intel_display_device_info_runtime_init(struct intel_display *display);

View File

@ -18,7 +18,7 @@
#include <drm/drm_vblank.h> #include <drm/drm_vblank.h>
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_utils.h" #include "i915_utils.h" /* for i915_inject_probe_failure() */
#include "i9xx_wm.h" #include "i9xx_wm.h"
#include "intel_acpi.h" #include "intel_acpi.h"
#include "intel_atomic.h" #include "intel_atomic.h"
@ -28,12 +28,14 @@
#include "intel_cdclk.h" #include "intel_cdclk.h"
#include "intel_color.h" #include "intel_color.h"
#include "intel_crtc.h" #include "intel_crtc.h"
#include "intel_dbuf_bw.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_debugfs.h" #include "intel_display_debugfs.h"
#include "intel_display_driver.h" #include "intel_display_driver.h"
#include "intel_display_irq.h" #include "intel_display_irq.h"
#include "intel_display_power.h" #include "intel_display_power.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_display_wa.h" #include "intel_display_wa.h"
#include "intel_dkl_phy.h" #include "intel_dkl_phy.h"
#include "intel_dmc.h" #include "intel_dmc.h"
@ -285,6 +287,10 @@ int intel_display_driver_probe_noirq(struct intel_display *display)
if (ret) if (ret)
goto cleanup_wq_unordered; goto cleanup_wq_unordered;
ret = intel_dbuf_bw_init(display);
if (ret)
goto cleanup_wq_unordered;
ret = intel_bw_init(display); ret = intel_bw_init(display);
if (ret) if (ret)
goto cleanup_wq_unordered; goto cleanup_wq_unordered;
@ -482,7 +488,6 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
intel_dpll_init(display); intel_dpll_init(display);
intel_fdi_pll_freq_update(display); intel_fdi_pll_freq_update(display);
intel_update_czclk(display);
intel_display_driver_init_hw(display); intel_display_driver_init_hw(display);
intel_dpll_update_ref_clks(display); intel_dpll_update_ref_clks(display);

View File

@ -140,14 +140,14 @@ void ilk_update_display_irq(struct intel_display *display,
lockdep_assert_held(&display->irq.lock); lockdep_assert_held(&display->irq.lock);
drm_WARN_ON(display->drm, enabled_irq_mask & ~interrupt_mask); drm_WARN_ON(display->drm, enabled_irq_mask & ~interrupt_mask);
new_val = dev_priv->irq_mask; new_val = display->irq.ilk_de_imr_mask;
new_val &= ~interrupt_mask; new_val &= ~interrupt_mask;
new_val |= (~enabled_irq_mask & interrupt_mask); new_val |= (~enabled_irq_mask & interrupt_mask);
if (new_val != dev_priv->irq_mask && if (new_val != display->irq.ilk_de_imr_mask &&
!drm_WARN_ON(display->drm, !intel_irqs_enabled(dev_priv))) { !drm_WARN_ON(display->drm, !intel_irqs_enabled(dev_priv))) {
dev_priv->irq_mask = new_val; display->irq.ilk_de_imr_mask = new_val;
intel_de_write(display, DEIMR, dev_priv->irq_mask); intel_de_write(display, DEIMR, display->irq.ilk_de_imr_mask);
intel_de_posting_read(display, DEIMR); intel_de_posting_read(display, DEIMR);
} }
} }
@ -215,13 +215,13 @@ static void bdw_update_pipe_irq(struct intel_display *display,
if (drm_WARN_ON(display->drm, !intel_irqs_enabled(dev_priv))) if (drm_WARN_ON(display->drm, !intel_irqs_enabled(dev_priv)))
return; return;
new_val = display->irq.de_irq_mask[pipe]; new_val = display->irq.de_pipe_imr_mask[pipe];
new_val &= ~interrupt_mask; new_val &= ~interrupt_mask;
new_val |= (~enabled_irq_mask & interrupt_mask); new_val |= (~enabled_irq_mask & interrupt_mask);
if (new_val != display->irq.de_irq_mask[pipe]) { if (new_val != display->irq.de_pipe_imr_mask[pipe]) {
display->irq.de_irq_mask[pipe] = new_val; display->irq.de_pipe_imr_mask[pipe] = new_val;
intel_de_write(display, GEN8_DE_PIPE_IMR(pipe), display->irq.de_irq_mask[pipe]); intel_de_write(display, GEN8_DE_PIPE_IMR(pipe), display->irq.de_pipe_imr_mask[pipe]);
intel_de_posting_read(display, GEN8_DE_PIPE_IMR(pipe)); intel_de_posting_read(display, GEN8_DE_PIPE_IMR(pipe));
} }
} }
@ -872,7 +872,7 @@ static void ilk_gtt_fault_irq_handler(struct intel_display *display)
} }
} }
void ilk_display_irq_handler(struct intel_display *display, u32 de_iir) static void _ilk_display_irq_handler(struct intel_display *display, u32 de_iir)
{ {
enum pipe pipe; enum pipe pipe;
u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG; u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG;
@ -923,7 +923,7 @@ void ilk_display_irq_handler(struct intel_display *display, u32 de_iir)
ilk_display_rps_irq_handler(display); ilk_display_rps_irq_handler(display);
} }
void ivb_display_irq_handler(struct intel_display *display, u32 de_iir) static void _ivb_display_irq_handler(struct intel_display *display, u32 de_iir)
{ {
enum pipe pipe; enum pipe pipe;
u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB; u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB;
@ -972,6 +972,53 @@ void ivb_display_irq_handler(struct intel_display *display, u32 de_iir)
} }
} }
void ilk_display_irq_master_disable(struct intel_display *display, u32 *de_ier, u32 *sde_ier)
{
/* disable master interrupt before clearing iir */
*de_ier = intel_de_read_fw(display, DEIER);
intel_de_write_fw(display, DEIER, *de_ier & ~DE_MASTER_IRQ_CONTROL);
/*
* Disable south interrupts. We'll only write to SDEIIR once, so further
* interrupts will be stored on its back queue, and then we'll be able
* to process them after we restore SDEIER (as soon as we restore it,
* we'll get an interrupt if SDEIIR still has something to process due
* to its back queue).
*/
if (!HAS_PCH_NOP(display)) {
*sde_ier = intel_de_read_fw(display, SDEIER);
intel_de_write_fw(display, SDEIER, 0);
} else {
*sde_ier = 0;
}
}
void ilk_display_irq_master_enable(struct intel_display *display, u32 de_ier, u32 sde_ier)
{
intel_de_write_fw(display, DEIER, de_ier);
if (sde_ier)
intel_de_write_fw(display, SDEIER, sde_ier);
}
bool ilk_display_irq_handler(struct intel_display *display)
{
u32 de_iir;
bool handled = false;
de_iir = intel_de_read_fw(display, DEIIR);
if (de_iir) {
intel_de_write_fw(display, DEIIR, de_iir);
if (DISPLAY_VER(display) >= 7)
_ivb_display_irq_handler(display, de_iir);
else
_ilk_display_irq_handler(display, de_iir);
handled = true;
}
return handled;
}
static u32 gen8_de_port_aux_mask(struct intel_display *display) static u32 gen8_de_port_aux_mask(struct intel_display *display)
{ {
u32 mask; u32 mask;
@ -1865,8 +1912,6 @@ void vlv_display_error_irq_handler(struct intel_display *display,
static void _vlv_display_irq_reset(struct intel_display *display) static void _vlv_display_irq_reset(struct intel_display *display)
{ {
struct drm_i915_private *dev_priv = to_i915(display->drm);
if (display->platform.cherryview) if (display->platform.cherryview)
intel_de_write(display, DPINVGTT, DPINVGTT_STATUS_MASK_CHV); intel_de_write(display, DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
else else
@ -1881,7 +1926,7 @@ static void _vlv_display_irq_reset(struct intel_display *display)
i9xx_pipestat_irq_reset(display); i9xx_pipestat_irq_reset(display);
intel_display_irq_regs_reset(display, VLV_IRQ_REGS); intel_display_irq_regs_reset(display, VLV_IRQ_REGS);
dev_priv->irq_mask = ~0u; display->irq.vlv_imr_mask = ~0u;
} }
void vlv_display_irq_reset(struct intel_display *display) void vlv_display_irq_reset(struct intel_display *display)
@ -1902,6 +1947,22 @@ void i9xx_display_irq_reset(struct intel_display *display)
i9xx_pipestat_irq_reset(display); i9xx_pipestat_irq_reset(display);
} }
u32 i9xx_display_irq_enable_mask(struct intel_display *display)
{
u32 enable_mask;
enable_mask = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
if (DISPLAY_VER(display) >= 3)
enable_mask |= I915_ASLE_INTERRUPT;
if (HAS_HOTPLUG(display))
enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
return enable_mask;
}
void i915_display_irq_postinstall(struct intel_display *display) void i915_display_irq_postinstall(struct intel_display *display)
{ {
/* /*
@ -1939,7 +2000,6 @@ static u32 vlv_error_mask(void)
static void _vlv_display_irq_postinstall(struct intel_display *display) static void _vlv_display_irq_postinstall(struct intel_display *display)
{ {
struct drm_i915_private *dev_priv = to_i915(display->drm);
u32 pipestat_mask; u32 pipestat_mask;
u32 enable_mask; u32 enable_mask;
enum pipe pipe; enum pipe pipe;
@ -1973,11 +2033,11 @@ static void _vlv_display_irq_postinstall(struct intel_display *display)
enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT | enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
I915_LPE_PIPE_C_INTERRUPT; I915_LPE_PIPE_C_INTERRUPT;
drm_WARN_ON(display->drm, dev_priv->irq_mask != ~0u); drm_WARN_ON(display->drm, display->irq.vlv_imr_mask != ~0u);
dev_priv->irq_mask = ~enable_mask; display->irq.vlv_imr_mask = ~enable_mask;
intel_display_irq_regs_init(display, VLV_IRQ_REGS, dev_priv->irq_mask, enable_mask); intel_display_irq_regs_init(display, VLV_IRQ_REGS, display->irq.vlv_imr_mask, enable_mask);
} }
void vlv_display_irq_postinstall(struct intel_display *display) void vlv_display_irq_postinstall(struct intel_display *display)
@ -1988,7 +2048,7 @@ void vlv_display_irq_postinstall(struct intel_display *display)
spin_unlock_irq(&display->irq.lock); spin_unlock_irq(&display->irq.lock);
} }
void ibx_display_irq_reset(struct intel_display *display) static void ibx_display_irq_reset(struct intel_display *display)
{ {
if (HAS_PCH_NOP(display)) if (HAS_PCH_NOP(display))
return; return;
@ -1999,6 +2059,24 @@ void ibx_display_irq_reset(struct intel_display *display)
intel_de_write(display, SERR_INT, 0xffffffff); intel_de_write(display, SERR_INT, 0xffffffff);
} }
void ilk_display_irq_reset(struct intel_display *display)
{
struct intel_uncore *uncore = to_intel_uncore(display->drm);
gen2_irq_reset(uncore, DE_IRQ_REGS);
display->irq.ilk_de_imr_mask = ~0u;
if (DISPLAY_VER(display) == 7)
intel_de_write(display, GEN7_ERR_INT, 0xffffffff);
if (display->platform.haswell) {
intel_de_write(display, EDP_PSR_IMR, 0xffffffff);
intel_de_write(display, EDP_PSR_IIR, 0xffffffff);
}
ibx_display_irq_reset(display);
}
void gen8_display_irq_reset(struct intel_display *display) void gen8_display_irq_reset(struct intel_display *display)
{ {
enum pipe pipe; enum pipe pipe;
@ -2088,8 +2166,8 @@ void gen8_irq_power_well_post_enable(struct intel_display *display,
for_each_pipe_masked(display, pipe, pipe_mask) for_each_pipe_masked(display, pipe, pipe_mask)
intel_display_irq_regs_init(display, GEN8_DE_PIPE_IRQ_REGS(pipe), intel_display_irq_regs_init(display, GEN8_DE_PIPE_IRQ_REGS(pipe),
display->irq.de_irq_mask[pipe], display->irq.de_pipe_imr_mask[pipe],
~display->irq.de_irq_mask[pipe] | extra_ier); ~display->irq.de_pipe_imr_mask[pipe] | extra_ier);
spin_unlock_irq(&display->irq.lock); spin_unlock_irq(&display->irq.lock);
} }
@ -2183,8 +2261,6 @@ out:
void ilk_de_irq_postinstall(struct intel_display *display) void ilk_de_irq_postinstall(struct intel_display *display)
{ {
struct drm_i915_private *i915 = to_i915(display->drm);
u32 display_mask, extra_mask; u32 display_mask, extra_mask;
if (DISPLAY_VER(display) >= 7) { if (DISPLAY_VER(display) >= 7) {
@ -2216,11 +2292,11 @@ void ilk_de_irq_postinstall(struct intel_display *display)
if (display->platform.ironlake && display->platform.mobile) if (display->platform.ironlake && display->platform.mobile)
extra_mask |= DE_PCU_EVENT; extra_mask |= DE_PCU_EVENT;
i915->irq_mask = ~display_mask; display->irq.ilk_de_imr_mask = ~display_mask;
ibx_irq_postinstall(display); ibx_irq_postinstall(display);
intel_display_irq_regs_init(display, DE_IRQ_REGS, i915->irq_mask, intel_display_irq_regs_init(display, DE_IRQ_REGS, display->irq.ilk_de_imr_mask,
display_mask | extra_mask); display_mask | extra_mask);
} }
@ -2305,12 +2381,12 @@ void gen8_de_irq_postinstall(struct intel_display *display)
} }
for_each_pipe(display, pipe) { for_each_pipe(display, pipe) {
display->irq.de_irq_mask[pipe] = ~de_pipe_masked; display->irq.de_pipe_imr_mask[pipe] = ~de_pipe_masked;
if (intel_display_power_is_enabled(display, if (intel_display_power_is_enabled(display,
POWER_DOMAIN_PIPE(pipe))) POWER_DOMAIN_PIPE(pipe)))
intel_display_irq_regs_init(display, GEN8_DE_PIPE_IRQ_REGS(pipe), intel_display_irq_regs_init(display, GEN8_DE_PIPE_IRQ_REGS(pipe),
display->irq.de_irq_mask[pipe], display->irq.de_pipe_imr_mask[pipe],
de_pipe_enables); de_pipe_enables);
} }

View File

@ -47,8 +47,9 @@ void i965_disable_vblank(struct drm_crtc *crtc);
void ilk_disable_vblank(struct drm_crtc *crtc); void ilk_disable_vblank(struct drm_crtc *crtc);
void bdw_disable_vblank(struct drm_crtc *crtc); void bdw_disable_vblank(struct drm_crtc *crtc);
void ivb_display_irq_handler(struct intel_display *display, u32 de_iir); void ilk_display_irq_master_disable(struct intel_display *display, u32 *de_ier, u32 *sde_ier);
void ilk_display_irq_handler(struct intel_display *display, u32 de_iir); void ilk_display_irq_master_enable(struct intel_display *display, u32 de_ier, u32 sde_ier);
bool ilk_display_irq_handler(struct intel_display *display);
void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl); void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl);
void gen11_display_irq_handler(struct intel_display *display); void gen11_display_irq_handler(struct intel_display *display);
@ -56,11 +57,12 @@ u32 gen11_gu_misc_irq_ack(struct intel_display *display, const u32 master_ctl);
void gen11_gu_misc_irq_handler(struct intel_display *display, const u32 iir); void gen11_gu_misc_irq_handler(struct intel_display *display, const u32 iir);
void i9xx_display_irq_reset(struct intel_display *display); void i9xx_display_irq_reset(struct intel_display *display);
void ibx_display_irq_reset(struct intel_display *display); void ilk_display_irq_reset(struct intel_display *display);
void vlv_display_irq_reset(struct intel_display *display); void vlv_display_irq_reset(struct intel_display *display);
void gen8_display_irq_reset(struct intel_display *display); void gen8_display_irq_reset(struct intel_display *display);
void gen11_display_irq_reset(struct intel_display *display); void gen11_display_irq_reset(struct intel_display *display);
u32 i9xx_display_irq_enable_mask(struct intel_display *display);
void i915_display_irq_postinstall(struct intel_display *display); void i915_display_irq_postinstall(struct intel_display *display);
void i965_display_irq_postinstall(struct intel_display *display); void i965_display_irq_postinstall(struct intel_display *display);
void vlv_display_irq_postinstall(struct intel_display *display); void vlv_display_irq_postinstall(struct intel_display *display);

View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __INTEL_DISPLAY_JIFFIES_H__
#define __INTEL_DISPLAY_JIFFIES_H__
#include <linux/jiffies.h>
static inline unsigned long msecs_to_jiffies_timeout(const unsigned int m)
{
unsigned long j = msecs_to_jiffies(m);
return min_t(unsigned long, MAX_JIFFY_OFFSET, j + 1);
}
/*
* If you need to wait X milliseconds between events A and B, but event B
* doesn't happen exactly after event A, you record the timestamp (jiffies) of
* when event A happened, then just before event B you call this function and
* pass the timestamp as the first argument, and X as the second argument.
*/
static inline void
wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms)
{
unsigned long target_jiffies, tmp_jiffies, remaining_jiffies;
/*
* Don't re-read the value of "jiffies" every time since it may change
* behind our back and break the math.
*/
tmp_jiffies = jiffies;
target_jiffies = timestamp_jiffies +
msecs_to_jiffies_timeout(to_wait_ms);
if (time_after(target_jiffies, tmp_jiffies)) {
remaining_jiffies = target_jiffies - tmp_jiffies;
while (remaining_jiffies)
remaining_jiffies =
schedule_timeout_uninterruptible(remaining_jiffies);
}
}
#endif /* __INTEL_DISPLAY_JIFFIES_H__ */

View File

@ -11,7 +11,6 @@
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_irq.h" #include "i915_irq.h"
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_backlight_regs.h" #include "intel_backlight_regs.h"
#include "intel_cdclk.h" #include "intel_cdclk.h"
#include "intel_clock_gating.h" #include "intel_clock_gating.h"
@ -23,6 +22,7 @@
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dmc.h" #include "intel_dmc.h"
#include "intel_mchbar_regs.h" #include "intel_mchbar_regs.h"
#include "intel_pch_refclk.h" #include "intel_pch_refclk.h"

View File

@ -1516,7 +1516,11 @@ static const struct i915_power_well_desc xelpdp_power_wells_main[] = {
.ops = &hsw_power_well_ops, .ops = &hsw_power_well_ops,
.irq_pipe_mask = BIT(PIPE_D), .irq_pipe_mask = BIT(PIPE_D),
.has_fuses = true, .has_fuses = true,
}, { },
};
static const struct i915_power_well_desc xelpdp_power_wells_aux[] = {
{
.instances = &I915_PW_INSTANCES( .instances = &I915_PW_INSTANCES(
I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A), I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A),
I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B), I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B),
@ -1534,6 +1538,7 @@ static const struct i915_power_well_desc_list xelpdp_power_wells[] = {
I915_PW_DESCRIPTORS(icl_power_wells_pw_1), I915_PW_DESCRIPTORS(icl_power_wells_pw_1),
I915_PW_DESCRIPTORS(xelpd_power_wells_dc_off), I915_PW_DESCRIPTORS(xelpd_power_wells_dc_off),
I915_PW_DESCRIPTORS(xelpdp_power_wells_main), I915_PW_DESCRIPTORS(xelpdp_power_wells_main),
I915_PW_DESCRIPTORS(xelpdp_power_wells_aux),
}; };
I915_DECL_PW_DOMAINS(xe2lpd_pwdoms_pica_tc, I915_DECL_PW_DOMAINS(xe2lpd_pwdoms_pica_tc,
@ -1584,6 +1589,7 @@ static const struct i915_power_well_desc_list xe2lpd_power_wells[] = {
I915_PW_DESCRIPTORS(xe2lpd_power_wells_dcoff), I915_PW_DESCRIPTORS(xe2lpd_power_wells_dcoff),
I915_PW_DESCRIPTORS(xelpdp_power_wells_main), I915_PW_DESCRIPTORS(xelpdp_power_wells_main),
I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica), I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica),
I915_PW_DESCRIPTORS(xelpdp_power_wells_aux),
}; };
/* /*
@ -1677,16 +1683,6 @@ static const struct i915_power_well_desc xe3lpd_power_wells_main[] = {
.ops = &hsw_power_well_ops, .ops = &hsw_power_well_ops,
.irq_pipe_mask = BIT(PIPE_D), .irq_pipe_mask = BIT(PIPE_D),
.has_fuses = true, .has_fuses = true,
}, {
.instances = &I915_PW_INSTANCES(
I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A),
I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B),
I915_PW("AUX_TC1", &xelpdp_pwdoms_aux_tc1, .xelpdp.aux_ch = AUX_CH_USBC1),
I915_PW("AUX_TC2", &xelpdp_pwdoms_aux_tc2, .xelpdp.aux_ch = AUX_CH_USBC2),
I915_PW("AUX_TC3", &xelpdp_pwdoms_aux_tc3, .xelpdp.aux_ch = AUX_CH_USBC3),
I915_PW("AUX_TC4", &xelpdp_pwdoms_aux_tc4, .xelpdp.aux_ch = AUX_CH_USBC4),
),
.ops = &xelpdp_aux_power_well_ops,
}, },
}; };
@ -1715,6 +1711,7 @@ static const struct i915_power_well_desc_list xe3lpd_power_wells[] = {
I915_PW_DESCRIPTORS(xe3lpd_power_wells_dcoff), I915_PW_DESCRIPTORS(xe3lpd_power_wells_dcoff),
I915_PW_DESCRIPTORS(xe3lpd_power_wells_main), I915_PW_DESCRIPTORS(xe3lpd_power_wells_main),
I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica), I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica),
I915_PW_DESCRIPTORS(xelpdp_power_wells_aux),
}; };
static const struct i915_power_well_desc wcl_power_wells_main[] = { static const struct i915_power_well_desc wcl_power_wells_main[] = {
@ -1751,7 +1748,11 @@ static const struct i915_power_well_desc wcl_power_wells_main[] = {
.ops = &hsw_power_well_ops, .ops = &hsw_power_well_ops,
.irq_pipe_mask = BIT(PIPE_C), .irq_pipe_mask = BIT(PIPE_C),
.has_fuses = true, .has_fuses = true,
}, { },
};
static const struct i915_power_well_desc wcl_power_wells_aux[] = {
{
.instances = &I915_PW_INSTANCES( .instances = &I915_PW_INSTANCES(
I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A), I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A),
I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B), I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B),
@ -1768,6 +1769,7 @@ static const struct i915_power_well_desc_list wcl_power_wells[] = {
I915_PW_DESCRIPTORS(xe3lpd_power_wells_dcoff), I915_PW_DESCRIPTORS(xe3lpd_power_wells_dcoff),
I915_PW_DESCRIPTORS(wcl_power_wells_main), I915_PW_DESCRIPTORS(wcl_power_wells_main),
I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica), I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica),
I915_PW_DESCRIPTORS(wcl_power_wells_aux),
}; };
static void init_power_well_domains(const struct i915_power_well_instance *inst, static void init_power_well_domains(const struct i915_power_well_instance *inst,

View File

@ -1864,18 +1864,36 @@ static void xelpdp_aux_power_well_enable(struct intel_display *display,
* expected to just wait a fixed 600us after raising the request * expected to just wait a fixed 600us after raising the request
* bit. * bit.
*/ */
usleep_range(600, 1200); if (DISPLAY_VER(display) >= 35) {
if (intel_de_wait_for_set(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch),
XELPDP_DP_AUX_CH_CTL_POWER_STATUS, 2))
drm_warn(display->drm,
"Timeout waiting for PHY %c AUX channel power to be up\n",
phy_name(phy));
} else {
usleep_range(600, 1200);
}
} }
static void xelpdp_aux_power_well_disable(struct intel_display *display, static void xelpdp_aux_power_well_disable(struct intel_display *display,
struct i915_power_well *power_well) struct i915_power_well *power_well)
{ {
enum aux_ch aux_ch = i915_power_well_instance(power_well)->xelpdp.aux_ch; enum aux_ch aux_ch = i915_power_well_instance(power_well)->xelpdp.aux_ch;
enum phy phy = icl_aux_pw_to_phy(display, power_well);
intel_de_rmw(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch), intel_de_rmw(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch),
XELPDP_DP_AUX_CH_CTL_POWER_REQUEST, XELPDP_DP_AUX_CH_CTL_POWER_REQUEST,
0); 0);
usleep_range(10, 30);
if (DISPLAY_VER(display) >= 35) {
if (intel_de_wait_for_clear(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch),
XELPDP_DP_AUX_CH_CTL_POWER_STATUS, 1))
drm_warn(display->drm,
"Timeout waiting for PHY %c AUX channel to powerdown\n",
phy_name(phy));
} else {
usleep_range(10, 30);
}
} }
static bool xelpdp_aux_power_well_enabled(struct intel_display *display, static bool xelpdp_aux_power_well_enabled(struct intel_display *display,

View File

@ -1,69 +1,62 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */ /* Copyright © 2025 Intel Corporation */
#include "i915_drv.h" #include <drm/intel/display_parent_interface.h>
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_runtime_pm.h"
static struct intel_runtime_pm *display_to_rpm(struct intel_display *display)
{
struct drm_i915_private *i915 = to_i915(display->drm);
return &i915->runtime_pm;
}
struct ref_tracker *intel_display_rpm_get_raw(struct intel_display *display) struct ref_tracker *intel_display_rpm_get_raw(struct intel_display *display)
{ {
return intel_runtime_pm_get_raw(display_to_rpm(display)); return display->parent->rpm->get_raw(display->drm);
} }
void intel_display_rpm_put_raw(struct intel_display *display, struct ref_tracker *wakeref) void intel_display_rpm_put_raw(struct intel_display *display, struct ref_tracker *wakeref)
{ {
intel_runtime_pm_put_raw(display_to_rpm(display), wakeref); display->parent->rpm->put_raw(display->drm, wakeref);
} }
struct ref_tracker *intel_display_rpm_get(struct intel_display *display) struct ref_tracker *intel_display_rpm_get(struct intel_display *display)
{ {
return intel_runtime_pm_get(display_to_rpm(display)); return display->parent->rpm->get(display->drm);
} }
struct ref_tracker *intel_display_rpm_get_if_in_use(struct intel_display *display) struct ref_tracker *intel_display_rpm_get_if_in_use(struct intel_display *display)
{ {
return intel_runtime_pm_get_if_in_use(display_to_rpm(display)); return display->parent->rpm->get_if_in_use(display->drm);
} }
struct ref_tracker *intel_display_rpm_get_noresume(struct intel_display *display) struct ref_tracker *intel_display_rpm_get_noresume(struct intel_display *display)
{ {
return intel_runtime_pm_get_noresume(display_to_rpm(display)); return display->parent->rpm->get_noresume(display->drm);
} }
void intel_display_rpm_put(struct intel_display *display, struct ref_tracker *wakeref) void intel_display_rpm_put(struct intel_display *display, struct ref_tracker *wakeref)
{ {
intel_runtime_pm_put(display_to_rpm(display), wakeref); display->parent->rpm->put(display->drm, wakeref);
} }
void intel_display_rpm_put_unchecked(struct intel_display *display) void intel_display_rpm_put_unchecked(struct intel_display *display)
{ {
intel_runtime_pm_put_unchecked(display_to_rpm(display)); display->parent->rpm->put_unchecked(display->drm);
} }
bool intel_display_rpm_suspended(struct intel_display *display) bool intel_display_rpm_suspended(struct intel_display *display)
{ {
return intel_runtime_pm_suspended(display_to_rpm(display)); return display->parent->rpm->suspended(display->drm);
} }
void assert_display_rpm_held(struct intel_display *display) void assert_display_rpm_held(struct intel_display *display)
{ {
assert_rpm_wakelock_held(display_to_rpm(display)); display->parent->rpm->assert_held(display->drm);
} }
void intel_display_rpm_assert_block(struct intel_display *display) void intel_display_rpm_assert_block(struct intel_display *display)
{ {
disable_rpm_wakeref_asserts(display_to_rpm(display)); display->parent->rpm->assert_block(display->drm);
} }
void intel_display_rpm_assert_unblock(struct intel_display *display) void intel_display_rpm_assert_unblock(struct intel_display *display)
{ {
enable_rpm_wakeref_asserts(display_to_rpm(display)); display->parent->rpm->assert_unblock(display->drm);
} }

View File

@ -551,7 +551,16 @@ struct intel_connector {
u8 fec_capability; u8 fec_capability;
u8 dsc_hblank_expansion_quirk:1; u8 dsc_hblank_expansion_quirk:1;
u8 dsc_throughput_quirk:1;
u8 dsc_decompression_enabled:1; u8 dsc_decompression_enabled:1;
struct {
struct {
int rgb_yuv444;
int yuv422_420;
} overall_throughput;
int max_line_width;
} dsc_branch_caps;
} dp; } dp;
struct { struct {
@ -946,6 +955,26 @@ struct intel_csc_matrix {
u16 postoff[3]; u16 postoff[3];
}; };
enum intel_panel_replay_dsc_support {
INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED,
INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY,
INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE,
};
struct scaler_filter_coeff {
u16 sign;
u16 exp;
u16 mantissa;
};
struct intel_casf {
#define SCALER_FILTER_NUM_TAPS 7
struct scaler_filter_coeff coeff[SCALER_FILTER_NUM_TAPS];
u8 strength;
u8 win_size;
bool casf_enable;
};
struct intel_crtc_state { struct intel_crtc_state {
/* /*
* uapi (drm) state. This is the software state shown to userspace. * uapi (drm) state. This is the software state shown to userspace.
@ -982,6 +1011,7 @@ struct intel_crtc_state {
struct drm_property_blob *degamma_lut, *gamma_lut, *ctm; struct drm_property_blob *degamma_lut, *gamma_lut, *ctm;
struct drm_display_mode mode, pipe_mode, adjusted_mode; struct drm_display_mode mode, pipe_mode, adjusted_mode;
enum drm_scaling_filter scaling_filter; enum drm_scaling_filter scaling_filter;
struct intel_casf casf_params;
} hw; } hw;
/* actual state of LUTs */ /* actual state of LUTs */
@ -1124,9 +1154,12 @@ struct intel_crtc_state {
bool has_panel_replay; bool has_panel_replay;
bool wm_level_disabled; bool wm_level_disabled;
bool pkg_c_latency_used; bool pkg_c_latency_used;
/* Only used for state verification. */
enum intel_panel_replay_dsc_support panel_replay_dsc_support;
u32 dc3co_exitline; u32 dc3co_exitline;
u16 su_y_granularity; u16 su_y_granularity;
u8 active_non_psr_pipes; u8 active_non_psr_pipes;
const char *no_psr_reason;
/* /*
* Frequency the dpll for the port should run at. Differs from the * Frequency the dpll for the port should run at. Differs from the
@ -1183,7 +1216,9 @@ struct intel_crtc_state {
struct intel_crtc_wm_state wm; struct intel_crtc_wm_state wm;
int min_cdclk[I915_MAX_PLANES]; int min_cdclk;
int plane_min_cdclk[I915_MAX_PLANES];
/* for packed/planar CbCr */ /* for packed/planar CbCr */
u32 data_rate[I915_MAX_PLANES]; u32 data_rate[I915_MAX_PLANES];
@ -1268,6 +1303,8 @@ struct intel_crtc_state {
/* Display Stream compression state */ /* Display Stream compression state */
struct { struct {
/* Only used for state computation, not read out from the HW. */
bool compression_enabled_on_link;
bool compression_enable; bool compression_enable;
int num_streams; int num_streams;
/* Compressed Bpp in U6.4 format (first 4 bits for fractional part) */ /* Compressed Bpp in U6.4 format (first 4 bits for fractional part) */
@ -1341,6 +1378,20 @@ struct intel_crtc_state {
/* LOBF flag */ /* LOBF flag */
bool has_lobf; bool has_lobf;
/* W2 window or 'set context latency' lines */
u16 set_context_latency;
struct {
u8 io_wake_lines;
u8 fast_wake_lines;
/* LNL and beyond */
u8 check_entry_lines;
u8 aux_less_wake_lines;
u8 silence_period_sym_clocks;
u8 lfps_half_cycle_num_of_syms;
} alpm_state;
}; };
enum intel_pipe_crc_source { enum intel_pipe_crc_source {
@ -1679,16 +1730,22 @@ struct intel_psr {
bool source_panel_replay_support; bool source_panel_replay_support;
bool sink_panel_replay_support; bool sink_panel_replay_support;
bool sink_panel_replay_su_support; bool sink_panel_replay_su_support;
enum intel_panel_replay_dsc_support sink_panel_replay_dsc_support;
bool panel_replay_enabled; bool panel_replay_enabled;
u32 dc3co_exitline; u32 dc3co_exitline;
u32 dc3co_exit_delay; u32 dc3co_exit_delay;
struct delayed_work dc3co_work; struct delayed_work dc3co_work;
u8 entry_setup_frames; u8 entry_setup_frames;
u8 io_wake_lines;
u8 fast_wake_lines;
bool link_ok; bool link_ok;
bool pkg_c_latency_used; bool pkg_c_latency_used;
u8 active_non_psr_pipes; u8 active_non_psr_pipes;
const char *no_psr_reason;
}; };
struct intel_dp { struct intel_dp {
@ -1844,19 +1901,12 @@ struct intel_dp {
bool colorimetry_support; bool colorimetry_support;
struct { struct {
u8 io_wake_lines;
u8 fast_wake_lines;
enum transcoder transcoder; enum transcoder transcoder;
struct mutex lock; struct mutex lock;
/* LNL and beyond */
u8 check_entry_lines;
u8 aux_less_wake_lines;
u8 silence_period_sym_clocks;
u8 lfps_half_cycle_num_of_syms;
bool lobf_disable_debug; bool lobf_disable_debug;
bool sink_alpm_error; bool sink_alpm_error;
} alpm_parameters; } alpm;
u8 alpm_dpcd; u8 alpm_dpcd;

View File

@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
#include <linux/device.h>
#include <drm/drm_device.h>
#ifdef CONFIG_X86
#include <asm/hypervisor.h>
#endif
#include "intel_display_core.h"
#include "intel_display_utils.h"
bool intel_display_run_as_guest(struct intel_display *display)
{
#if IS_ENABLED(CONFIG_X86)
return !hypervisor_is_type(X86_HYPER_NATIVE);
#else
/* Not supported yet */
return false;
#endif
}
bool intel_display_vtd_active(struct intel_display *display)
{
if (device_iommu_mapped(display->drm->dev))
return true;
/* Running as a guest, we assume the host is enforcing VT'd */
return intel_display_run_as_guest(display);
}

View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __INTEL_DISPLAY_UTILS__
#define __INTEL_DISPLAY_UTILS__
#include <linux/bug.h>
#include <linux/types.h>
struct intel_display;
#ifndef MISSING_CASE
#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
__stringify(x), (long)(x))
#endif
#ifndef fetch_and_zero
#define fetch_and_zero(ptr) ({ \
typeof(*ptr) __T = *(ptr); \
*(ptr) = (typeof(*ptr))0; \
__T; \
})
#endif
#define KHz(x) (1000 * (x))
#define MHz(x) KHz(1000 * (x))
bool intel_display_run_as_guest(struct intel_display *display);
bool intel_display_vtd_active(struct intel_display *display);
#endif /* __INTEL_DISPLAY_UTILS__ */

View File

@ -67,6 +67,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa,
return intel_display_needs_wa_16025573575(display); return intel_display_needs_wa_16025573575(display);
case INTEL_DISPLAY_WA_14011503117: case INTEL_DISPLAY_WA_14011503117:
return DISPLAY_VER(display) == 13; return DISPLAY_VER(display) == 13;
case INTEL_DISPLAY_WA_22014263786:
return IS_DISPLAY_VERx100(display, 1100, 1400);
default: default:
drm_WARN(display->drm, 1, "Missing Wa number: %s\n", name); drm_WARN(display->drm, 1, "Missing Wa number: %s\n", name);
break; break;

View File

@ -25,6 +25,7 @@ enum intel_display_wa {
INTEL_DISPLAY_WA_16023588340, INTEL_DISPLAY_WA_16023588340,
INTEL_DISPLAY_WA_16025573575, INTEL_DISPLAY_WA_16025573575,
INTEL_DISPLAY_WA_14011503117, INTEL_DISPLAY_WA_14011503117,
INTEL_DISPLAY_WA_22014263786,
}; };
bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, const char *name); bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, const char *name);

View File

@ -30,13 +30,13 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_crtc.h" #include "intel_crtc.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_power_well.h" #include "intel_display_power_well.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dmc.h" #include "intel_dmc.h"
#include "intel_dmc_regs.h" #include "intel_dmc_regs.h"
#include "intel_flipq.h" #include "intel_flipq.h"
@ -127,6 +127,9 @@ static bool dmc_firmware_param_disabled(struct intel_display *display)
#define DISPLAY_VER13_DMC_MAX_FW_SIZE 0x20000 #define DISPLAY_VER13_DMC_MAX_FW_SIZE 0x20000
#define DISPLAY_VER12_DMC_MAX_FW_SIZE ICL_DMC_MAX_FW_SIZE #define DISPLAY_VER12_DMC_MAX_FW_SIZE ICL_DMC_MAX_FW_SIZE
#define XE3LPD_3002_DMC_PATH DMC_PATH(xe3lpd_3002)
MODULE_FIRMWARE(XE3LPD_3002_DMC_PATH);
#define XE3LPD_DMC_PATH DMC_PATH(xe3lpd) #define XE3LPD_DMC_PATH DMC_PATH(xe3lpd)
MODULE_FIRMWARE(XE3LPD_DMC_PATH); MODULE_FIRMWARE(XE3LPD_DMC_PATH);
@ -183,9 +186,10 @@ static const char *dmc_firmware_default(struct intel_display *display, u32 *size
{ {
const char *fw_path = NULL; const char *fw_path = NULL;
u32 max_fw_size = 0; u32 max_fw_size = 0;
if (DISPLAY_VERx100(display) == 3002) {
if (DISPLAY_VERx100(display) == 3002 || fw_path = XE3LPD_3002_DMC_PATH;
DISPLAY_VERx100(display) == 3000) { max_fw_size = XE2LPD_DMC_MAX_FW_SIZE;
} else if (DISPLAY_VERx100(display) == 3000) {
fw_path = XE3LPD_DMC_PATH; fw_path = XE3LPD_DMC_PATH;
max_fw_size = XE2LPD_DMC_MAX_FW_SIZE; max_fw_size = XE2LPD_DMC_MAX_FW_SIZE;
} else if (DISPLAY_VERx100(display) == 2000) { } else if (DISPLAY_VERx100(display) == 2000) {
@ -509,10 +513,16 @@ static u32 pipedmc_interrupt_mask(struct intel_display *display)
PIPEDMC_ATS_FAULT; PIPEDMC_ATS_FAULT;
} }
static u32 dmc_evt_ctl_disable(void) static u32 dmc_evt_ctl_disable(u32 dmc_evt_ctl)
{ {
return REG_FIELD_PREP(DMC_EVT_CTL_TYPE_MASK, /*
DMC_EVT_CTL_TYPE_EDGE_0_1) | * DMC_EVT_CTL_ENABLE cannot be cleared once set. Always
* configure it based on the original event definition to
* avoid mismatches in assert_dmc_loaded().
*/
return (dmc_evt_ctl & DMC_EVT_CTL_ENABLE) |
REG_FIELD_PREP(DMC_EVT_CTL_TYPE_MASK,
DMC_EVT_CTL_TYPE_EDGE_0_1) |
REG_FIELD_PREP(DMC_EVT_CTL_EVENT_ID_MASK, REG_FIELD_PREP(DMC_EVT_CTL_EVENT_ID_MASK,
DMC_EVENT_FALSE); DMC_EVENT_FALSE);
} }
@ -546,6 +556,51 @@ static bool is_event_handler(struct intel_display *display,
REG_FIELD_GET(DMC_EVT_CTL_EVENT_ID_MASK, data) == event_id; REG_FIELD_GET(DMC_EVT_CTL_EVENT_ID_MASK, data) == event_id;
} }
static bool fixup_dmc_evt(struct intel_display *display,
enum intel_dmc_id dmc_id,
i915_reg_t reg_ctl, u32 *data_ctl,
i915_reg_t reg_htp, u32 *data_htp)
{
if (!is_dmc_evt_ctl_reg(display, dmc_id, reg_ctl))
return false;
if (!is_dmc_evt_htp_reg(display, dmc_id, reg_htp))
return false;
/* make sure reg_ctl and reg_htp are for the same event */
if (i915_mmio_reg_offset(reg_ctl) - i915_mmio_reg_offset(DMC_EVT_CTL(display, dmc_id, 0)) !=
i915_mmio_reg_offset(reg_htp) - i915_mmio_reg_offset(DMC_EVT_HTP(display, dmc_id, 0)))
return false;
/*
* On ADL-S the HRR event handler is not restored after DC6.
* Clear it to zero from the beginning to avoid mismatches later.
*/
if (display->platform.alderlake_s && dmc_id == DMC_FW_MAIN &&
is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_A, reg_ctl, *data_ctl)) {
*data_ctl = 0;
*data_htp = 0;
return true;
}
/*
* TGL/ADL-S DMC firmware incorrectly uses the undelayed vblank
* event for the HRR handler, when it should be using the delayed
* vblank event instead. Fixed firmware was never released
* so the Windows driver just hacks around it by overriding
* the event ID. Do the same.
*/
if ((display->platform.tigerlake || display->platform.alderlake_s) &&
is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_A, reg_ctl, *data_ctl)) {
*data_ctl &= ~DMC_EVT_CTL_EVENT_ID_MASK;
*data_ctl |= REG_FIELD_PREP(DMC_EVT_CTL_EVENT_ID_MASK,
MAINDMC_EVENT_VBLANK_DELAYED_A);
return true;
}
return false;
}
static bool disable_dmc_evt(struct intel_display *display, static bool disable_dmc_evt(struct intel_display *display,
enum intel_dmc_id dmc_id, enum intel_dmc_id dmc_id,
i915_reg_t reg, u32 data) i915_reg_t reg, u32 data)
@ -564,7 +619,7 @@ static bool disable_dmc_evt(struct intel_display *display,
/* also disable the HRR event on the main DMC on TGL/ADLS */ /* also disable the HRR event on the main DMC on TGL/ADLS */
if ((display->platform.tigerlake || display->platform.alderlake_s) && if ((display->platform.tigerlake || display->platform.alderlake_s) &&
is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_A, reg, data)) is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_DELAYED_A, reg, data))
return true; return true;
return false; return false;
@ -577,7 +632,7 @@ static u32 dmc_mmiodata(struct intel_display *display,
if (disable_dmc_evt(display, dmc_id, if (disable_dmc_evt(display, dmc_id,
dmc->dmc_info[dmc_id].mmioaddr[i], dmc->dmc_info[dmc_id].mmioaddr[i],
dmc->dmc_info[dmc_id].mmiodata[i])) dmc->dmc_info[dmc_id].mmiodata[i]))
return dmc_evt_ctl_disable(); return dmc_evt_ctl_disable(dmc->dmc_info[dmc_id].mmiodata[i]);
else else
return dmc->dmc_info[dmc_id].mmiodata[i]; return dmc->dmc_info[dmc_id].mmiodata[i];
} }
@ -636,12 +691,6 @@ static void assert_dmc_loaded(struct intel_display *display,
found = intel_de_read(display, reg); found = intel_de_read(display, reg);
expected = dmc_mmiodata(display, dmc, dmc_id, i); expected = dmc_mmiodata(display, dmc, dmc_id, i);
/* once set DMC_EVT_CTL_ENABLE can't be cleared :/ */
if (is_dmc_evt_ctl_reg(display, dmc_id, reg)) {
found &= ~DMC_EVT_CTL_ENABLE;
expected &= ~DMC_EVT_CTL_ENABLE;
}
drm_WARN(display->drm, found != expected, drm_WARN(display->drm, found != expected,
"DMC %d mmio[%d]/0x%x incorrect (expected 0x%x, current 0x%x)\n", "DMC %d mmio[%d]/0x%x incorrect (expected 0x%x, current 0x%x)\n",
dmc_id, i, i915_mmio_reg_offset(reg), expected, found); dmc_id, i, i915_mmio_reg_offset(reg), expected, found);
@ -794,7 +843,7 @@ static void dmc_configure_event(struct intel_display *display,
if (!is_event_handler(display, dmc_id, event_id, reg, data)) if (!is_event_handler(display, dmc_id, event_id, reg, data))
continue; continue;
intel_de_write(display, reg, enable ? data : dmc_evt_ctl_disable()); intel_de_write(display, reg, enable ? data : dmc_evt_ctl_disable(data));
num_handlers++; num_handlers++;
} }
@ -1064,9 +1113,32 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
for (i = 0; i < mmio_count; i++) { for (i = 0; i < mmio_count; i++) {
dmc_info->mmioaddr[i] = _MMIO(mmioaddr[i]); dmc_info->mmioaddr[i] = _MMIO(mmioaddr[i]);
dmc_info->mmiodata[i] = mmiodata[i]; dmc_info->mmiodata[i] = mmiodata[i];
}
for (i = 0; i < mmio_count - 1; i++) {
u32 orig_mmiodata[2] = {
dmc_info->mmiodata[i],
dmc_info->mmiodata[i+1],
};
if (!fixup_dmc_evt(display, dmc_id,
dmc_info->mmioaddr[i], &dmc_info->mmiodata[i],
dmc_info->mmioaddr[i+1], &dmc_info->mmiodata[i+1]))
continue;
drm_dbg_kms(display->drm,
" mmio[%d]: 0x%x = 0x%x->0x%x (EVT_CTL)\n",
i, i915_mmio_reg_offset(dmc_info->mmioaddr[i]),
orig_mmiodata[0], dmc_info->mmiodata[i]);
drm_dbg_kms(display->drm,
" mmio[%d]: 0x%x = 0x%x->0x%x (EVT_HTP)\n",
i+1, i915_mmio_reg_offset(dmc_info->mmioaddr[i+1]),
orig_mmiodata[1], dmc_info->mmiodata[i+1]);
}
for (i = 0; i < mmio_count; i++) {
drm_dbg_kms(display->drm, " mmio[%d]: 0x%x = 0x%x%s%s\n", drm_dbg_kms(display->drm, " mmio[%d]: 0x%x = 0x%x%s%s\n",
i, mmioaddr[i], mmiodata[i], i, i915_mmio_reg_offset(dmc_info->mmioaddr[i]), dmc_info->mmiodata[i],
is_dmc_evt_ctl_reg(display, dmc_id, dmc_info->mmioaddr[i]) ? " (EVT_CTL)" : is_dmc_evt_ctl_reg(display, dmc_id, dmc_info->mmioaddr[i]) ? " (EVT_CTL)" :
is_dmc_evt_htp_reg(display, dmc_id, dmc_info->mmioaddr[i]) ? " (EVT_HTP)" : "", is_dmc_evt_htp_reg(display, dmc_id, dmc_info->mmioaddr[i]) ? " (EVT_HTP)" : "",
disable_dmc_evt(display, dmc_id, dmc_info->mmioaddr[i], disable_dmc_evt(display, dmc_id, dmc_info->mmioaddr[i],
@ -1141,7 +1213,7 @@ parse_dmc_fw_package(struct intel_dmc *dmc,
} }
num_entries = package_header->num_entries; num_entries = package_header->num_entries;
if (WARN_ON(package_header->num_entries > max_entries)) if (WARN_ON(num_entries > max_entries))
num_entries = max_entries; num_entries = max_entries;
fw_info = (const struct intel_fw_info *) fw_info = (const struct intel_fw_info *)

View File

@ -51,7 +51,6 @@
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include "g4x_dp.h" #include "g4x_dp.h"
#include "i915_utils.h"
#include "intel_alpm.h" #include "intel_alpm.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_audio.h" #include "intel_audio.h"
@ -64,6 +63,8 @@
#include "intel_ddi.h" #include "intel_ddi.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_driver.h" #include "intel_display_driver.h"
#include "intel_display_jiffies.h"
#include "intel_display_utils.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
@ -93,14 +94,10 @@
#include "intel_psr.h" #include "intel_psr.h"
#include "intel_quirks.h" #include "intel_quirks.h"
#include "intel_tc.h" #include "intel_tc.h"
#include "intel_vblank.h"
#include "intel_vdsc.h" #include "intel_vdsc.h"
#include "intel_vrr.h" #include "intel_vrr.h"
/* DP DSC throughput values used for slice count calculations KPixels/s */
#define DP_DSC_PEAK_PIXEL_RATE 2720000
#define DP_DSC_MAX_ENC_THROUGHPUT_0 340000
#define DP_DSC_MAX_ENC_THROUGHPUT_1 400000
/* Max DSC line buffer depth supported by HW. */ /* Max DSC line buffer depth supported by HW. */
#define INTEL_DP_DSC_MAX_LINE_BUF_DEPTH 13 #define INTEL_DP_DSC_MAX_LINE_BUF_DEPTH 13
@ -1018,13 +1015,43 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector,
struct intel_display *display = to_intel_display(connector); struct intel_display *display = to_intel_display(connector);
u8 min_slice_count, i; u8 min_slice_count, i;
int max_slice_width; int max_slice_width;
int tp_rgb_yuv444;
int tp_yuv422_420;
if (mode_clock <= DP_DSC_PEAK_PIXEL_RATE) /*
min_slice_count = DIV_ROUND_UP(mode_clock, * TODO: Use the throughput value specific to the actual RGB/YUV
DP_DSC_MAX_ENC_THROUGHPUT_0); * format of the output.
else * The RGB/YUV444 throughput value should be always either equal
min_slice_count = DIV_ROUND_UP(mode_clock, * or smaller than the YUV422/420 value, but let's not depend on
DP_DSC_MAX_ENC_THROUGHPUT_1); * this assumption.
*/
if (mode_clock > max(connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444,
connector->dp.dsc_branch_caps.overall_throughput.yuv422_420))
return 0;
if (mode_hdisplay > connector->dp.dsc_branch_caps.max_line_width)
return 0;
/*
* TODO: Pass the total pixel rate of all the streams transferred to
* an MST tiled display, calculate the total slice count for all tiles
* from this and the per-tile slice count from the total slice count.
*/
tp_rgb_yuv444 = drm_dp_dsc_sink_max_slice_throughput(connector->dp.dsc_dpcd,
mode_clock, true);
tp_yuv422_420 = drm_dp_dsc_sink_max_slice_throughput(connector->dp.dsc_dpcd,
mode_clock, false);
/*
* TODO: Use the throughput value specific to the actual RGB/YUV
* format of the output.
* For now use the smaller of these, which is ok, potentially
* resulting in a higher than required minimum slice count.
* The RGB/YUV444 throughput value should be always either equal
* or smaller than the YUV422/420 value, but let's not depend on
* this assumption.
*/
min_slice_count = DIV_ROUND_UP(mode_clock, min(tp_rgb_yuv444, tp_yuv422_420));
/* /*
* Due to some DSC engine BW limitations, we need to enable second * Due to some DSC engine BW limitations, we need to enable second
@ -2340,24 +2367,26 @@ static int intel_edp_dsc_compute_pipe_bpp(struct intel_dp *intel_dp,
return 0; return 0;
} }
static void intel_dp_fec_compute_config(struct intel_dp *intel_dp, /*
struct intel_crtc_state *crtc_state) * Return whether FEC must be enabled for 8b10b SST or MST links. On 128b132b
* links FEC is always enabled implicitly by the HW, so this function returns
* false for that case.
*/
bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
bool dsc_enabled_on_crtc)
{ {
if (crtc_state->fec_enable) if (intel_dp_is_uhbr(crtc_state))
return; return false;
/* /*
* Though eDP v1.5 supports FEC with DSC, unlike DP, it is optional. * Though eDP v1.5 supports FEC with DSC, unlike DP, it is optional.
* Since, FEC is a bandwidth overhead, continue to not enable it for * Since, FEC is a bandwidth overhead, continue to not enable it for
* eDP. Until, there is a good reason to do so. * eDP. Until, there is a good reason to do so.
*/ */
if (intel_dp_is_edp(intel_dp)) if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
return; return false;
if (intel_dp_is_uhbr(crtc_state)) return dsc_enabled_on_crtc || intel_dsc_enabled_on_link(crtc_state);
return;
crtc_state->fec_enable = true;
} }
int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
@ -2375,7 +2404,11 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
bool is_mst = intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST); bool is_mst = intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST);
int ret; int ret;
intel_dp_fec_compute_config(intel_dp, pipe_config); /*
* FIXME: set the FEC enabled state once pipe_config->port_clock is
* already known, so the UHBR/non-UHBR mode can be determined.
*/
pipe_config->fec_enable = intel_dp_needs_8b10b_fec(pipe_config, true);
if (!intel_dp_dsc_supports_format(connector, pipe_config->output_format)) if (!intel_dp_dsc_supports_format(connector, pipe_config->output_format))
return -EINVAL; return -EINVAL;
@ -2450,7 +2483,8 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
return ret; return ret;
} }
pipe_config->dsc.compression_enable = true; intel_dsc_enable_on_crtc(pipe_config);
drm_dbg_kms(display->drm, "DP DSC computed with Input Bpp = %d " drm_dbg_kms(display->drm, "DP DSC computed with Input Bpp = %d "
"Compressed Bpp = " FXP_Q4_FMT " Slice Count = %d\n", "Compressed Bpp = " FXP_Q4_FMT " Slice Count = %d\n",
pipe_config->pipe_bpp, pipe_config->pipe_bpp,
@ -2460,6 +2494,40 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
return 0; return 0;
} }
static int
dsc_throughput_quirk_max_bpp_x16(const struct intel_connector *connector,
const struct intel_crtc_state *crtc_state)
{
const struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
if (!connector->dp.dsc_throughput_quirk)
return INT_MAX;
/*
* Synaptics Panamera branch devices have a problem decompressing a
* stream with a compressed link-bpp higher than 12, if the pixel
* clock is higher than ~50 % of the maximum overall throughput
* reported by the branch device. Work around this by limiting the
* maximum link bpp for such pixel clocks.
*
* TODO: Use the throughput value specific to the actual RGB/YUV
* format of the output, after determining the pixel clock limit for
* YUV modes. For now use the smaller of the throughput values, which
* may result in limiting the link-bpp value already at a lower than
* required mode clock in case of native YUV422/420 output formats.
* The RGB/YUV444 throughput value should be always either equal or
* smaller than the YUV422/420 value, but let's not depend on this
* assumption.
*/
if (adjusted_mode->crtc_clock <
min(connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444,
connector->dp.dsc_branch_caps.overall_throughput.yuv422_420) / 2)
return INT_MAX;
return fxp_q4_from_int(12);
}
/* /*
* Calculate the output link min, max bpp values in limits based on the pipe bpp * 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. * range, crtc_state and dsc mode. Return true on success.
@ -2491,6 +2559,7 @@ intel_dp_compute_config_link_bpp_limits(struct intel_dp *intel_dp,
} else { } else {
int dsc_src_min_bpp, dsc_sink_min_bpp, dsc_min_bpp; int dsc_src_min_bpp, dsc_sink_min_bpp, dsc_min_bpp;
int dsc_src_max_bpp, dsc_sink_max_bpp, dsc_max_bpp; int dsc_src_max_bpp, dsc_sink_max_bpp, dsc_max_bpp;
int throughput_max_bpp_x16;
dsc_src_min_bpp = intel_dp_dsc_min_src_compressed_bpp(); dsc_src_min_bpp = intel_dp_dsc_min_src_compressed_bpp();
dsc_sink_min_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state); dsc_sink_min_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state);
@ -2505,6 +2574,19 @@ intel_dp_compute_config_link_bpp_limits(struct intel_dp *intel_dp,
min(dsc_sink_max_bpp, dsc_src_max_bpp) : dsc_src_max_bpp; min(dsc_sink_max_bpp, dsc_src_max_bpp) : dsc_src_max_bpp;
max_link_bpp_x16 = min(max_link_bpp_x16, fxp_q4_from_int(dsc_max_bpp)); max_link_bpp_x16 = min(max_link_bpp_x16, fxp_q4_from_int(dsc_max_bpp));
throughput_max_bpp_x16 = dsc_throughput_quirk_max_bpp_x16(connector, crtc_state);
throughput_max_bpp_x16 = clamp(throughput_max_bpp_x16,
limits->link.min_bpp_x16, max_link_bpp_x16);
if (throughput_max_bpp_x16 < max_link_bpp_x16) {
max_link_bpp_x16 = throughput_max_bpp_x16;
drm_dbg_kms(display->drm,
"[CRTC:%d:%s][CONNECTOR:%d:%s] Decreasing link max bpp to " FXP_Q4_FMT " due to DSC throughput quirk\n",
crtc->base.base.id, crtc->base.name,
connector->base.base.id, connector->base.name,
FXP_Q4_ARGS(max_link_bpp_x16));
}
} }
limits->link.max_bpp_x16 = max_link_bpp_x16; limits->link.max_bpp_x16 = max_link_bpp_x16;
@ -4169,7 +4251,36 @@ static void intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux,
dsc_dpcd); dsc_dpcd);
} }
void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector) static void init_dsc_overall_throughput_limits(struct intel_connector *connector, bool is_branch)
{
u8 branch_caps[DP_DSC_BRANCH_CAP_SIZE];
int line_width;
connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444 = INT_MAX;
connector->dp.dsc_branch_caps.overall_throughput.yuv422_420 = INT_MAX;
connector->dp.dsc_branch_caps.max_line_width = INT_MAX;
if (!is_branch)
return;
if (drm_dp_dpcd_read_data(connector->dp.dsc_decompression_aux,
DP_DSC_BRANCH_OVERALL_THROUGHPUT_0, branch_caps,
sizeof(branch_caps)) != 0)
return;
connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444 =
drm_dp_dsc_branch_max_overall_throughput(branch_caps, true) ? : INT_MAX;
connector->dp.dsc_branch_caps.overall_throughput.yuv422_420 =
drm_dp_dsc_branch_max_overall_throughput(branch_caps, false) ? : INT_MAX;
line_width = drm_dp_dsc_branch_max_line_width(branch_caps);
connector->dp.dsc_branch_caps.max_line_width = line_width > 0 ? line_width : INT_MAX;
}
void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
const struct drm_dp_desc *desc, bool is_branch,
struct intel_connector *connector)
{ {
struct intel_display *display = to_intel_display(connector); struct intel_display *display = to_intel_display(connector);
@ -4182,6 +4293,9 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
/* Clear fec_capable to avoid using stale values */ /* Clear fec_capable to avoid using stale values */
connector->dp.fec_capability = 0; connector->dp.fec_capability = 0;
memset(&connector->dp.dsc_branch_caps, 0, sizeof(connector->dp.dsc_branch_caps));
connector->dp.dsc_throughput_quirk = false;
if (dpcd_rev < DP_DPCD_REV_14) if (dpcd_rev < DP_DPCD_REV_14)
return; return;
@ -4196,6 +4310,19 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
drm_dbg_kms(display->drm, "FEC CAPABILITY: %x\n", drm_dbg_kms(display->drm, "FEC CAPABILITY: %x\n",
connector->dp.fec_capability); connector->dp.fec_capability);
if (!(connector->dp.dsc_dpcd[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED))
return;
init_dsc_overall_throughput_limits(connector, is_branch);
/*
* TODO: Move the HW rev check as well to the DRM core quirk table if
* that's required after clarifying the list of affected devices.
*/
if (drm_dp_has_quirk(desc, DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) &&
desc->ident.hw_rev == 0x10)
connector->dp.dsc_throughput_quirk = true;
} }
static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *connector) static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *connector)
@ -4204,6 +4331,9 @@ static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *
return; return;
intel_dp_read_dsc_dpcd(connector->dp.dsc_decompression_aux, connector->dp.dsc_dpcd); intel_dp_read_dsc_dpcd(connector->dp.dsc_decompression_aux, connector->dp.dsc_dpcd);
if (connector->dp.dsc_dpcd[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED)
init_dsc_overall_throughput_limits(connector, false);
} }
static void static void
@ -4220,6 +4350,7 @@ intel_dp_detect_dsc_caps(struct intel_dp *intel_dp, struct intel_connector *conn
connector); connector);
else else
intel_dp_get_dsc_sink_cap(intel_dp->dpcd[DP_DPCD_REV], intel_dp_get_dsc_sink_cap(intel_dp->dpcd[DP_DPCD_REV],
&intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd),
connector); connector);
} }
@ -5553,7 +5684,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
if (intel_alpm_get_error(intel_dp)) { if (intel_alpm_get_error(intel_dp)) {
intel_alpm_disable(intel_dp); intel_alpm_disable(intel_dp);
intel_dp->alpm_parameters.sink_alpm_error = true; intel_dp->alpm.sink_alpm_error = true;
} }
if (intel_dp_test_short_pulse(intel_dp)) if (intel_dp_test_short_pulse(intel_dp))
@ -5921,6 +6052,8 @@ intel_dp_detect(struct drm_connector *_connector,
memset(connector->dp.dsc_dpcd, 0, sizeof(connector->dp.dsc_dpcd)); memset(connector->dp.dsc_dpcd, 0, sizeof(connector->dp.dsc_dpcd));
intel_dp->psr.sink_panel_replay_support = false; intel_dp->psr.sink_panel_replay_support = false;
intel_dp->psr.sink_panel_replay_su_support = false; intel_dp->psr.sink_panel_replay_su_support = false;
intel_dp->psr.sink_panel_replay_dsc_support =
INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED;
intel_dp_mst_disconnect(intel_dp); intel_dp_mst_disconnect(intel_dp);
@ -6857,3 +6990,81 @@ void intel_dp_mst_resume(struct intel_display *display)
} }
} }
} }
static
int intel_dp_sdp_compute_config_late(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
int guardband = intel_crtc_vblank_length(crtc_state);
int min_sdp_guardband = intel_dp_sdp_min_guardband(crtc_state, false);
if (guardband < min_sdp_guardband) {
drm_dbg_kms(display->drm, "guardband %d < min sdp guardband %d\n",
guardband, min_sdp_guardband);
return -EINVAL;
}
return 0;
}
int intel_dp_compute_config_late(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
int ret;
intel_psr_compute_config_late(intel_dp, crtc_state);
ret = intel_dp_sdp_compute_config_late(crtc_state);
if (ret)
return ret;
return 0;
}
static
int intel_dp_get_lines_for_sdp(const struct intel_crtc_state *crtc_state, u32 type)
{
switch (type) {
case DP_SDP_VSC_EXT_VESA:
case DP_SDP_VSC_EXT_CEA:
return 10;
case HDMI_PACKET_TYPE_GAMUT_METADATA:
return 8;
case DP_SDP_PPS:
return 7;
case DP_SDP_ADAPTIVE_SYNC:
return crtc_state->vrr.vsync_start + 1;
default:
break;
}
return 0;
}
int intel_dp_sdp_min_guardband(const struct intel_crtc_state *crtc_state,
bool assume_all_enabled)
{
struct intel_display *display = to_intel_display(crtc_state);
int sdp_guardband = 0;
if (assume_all_enabled ||
crtc_state->infoframes.enable &
intel_hdmi_infoframe_enable(HDMI_PACKET_TYPE_GAMUT_METADATA))
sdp_guardband = max(sdp_guardband,
intel_dp_get_lines_for_sdp(crtc_state,
HDMI_PACKET_TYPE_GAMUT_METADATA));
if (assume_all_enabled ||
crtc_state->dsc.compression_enable)
sdp_guardband = max(sdp_guardband,
intel_dp_get_lines_for_sdp(crtc_state, DP_SDP_PPS));
if ((assume_all_enabled && HAS_AS_SDP(display)) ||
crtc_state->infoframes.enable & intel_hdmi_infoframe_enable(DP_SDP_ADAPTIVE_SYNC))
sdp_guardband = max(sdp_guardband,
intel_dp_get_lines_for_sdp(crtc_state, DP_SDP_ADAPTIVE_SYNC));
return sdp_guardband;
}

View File

@ -12,6 +12,7 @@ enum intel_output_format;
enum pipe; enum pipe;
enum port; enum port;
struct drm_connector_state; struct drm_connector_state;
struct drm_dp_desc;
struct drm_dp_vsc_sdp; struct drm_dp_vsc_sdp;
struct drm_encoder; struct drm_encoder;
struct drm_modeset_acquire_ctx; struct drm_modeset_acquire_ctx;
@ -72,6 +73,8 @@ void intel_dp_encoder_flush_work(struct drm_encoder *encoder);
int intel_dp_compute_config(struct intel_encoder *encoder, int intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config, struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state); struct drm_connector_state *conn_state);
bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
bool dsc_enabled_on_crtc);
int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *pipe_config, struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state, struct drm_connector_state *conn_state,
@ -199,7 +202,9 @@ bool intel_dp_compute_config_limits(struct intel_dp *intel_dp,
bool dsc, bool dsc,
struct link_config_limits *limits); struct link_config_limits *limits);
void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector); void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
const struct drm_dp_desc *desc, bool is_branch,
struct intel_connector *connector);
bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder); bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder);
bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
@ -215,5 +220,10 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state,
int intel_dp_dsc_bpp_step_x16(const struct intel_connector *connector); int intel_dp_dsc_bpp_step_x16(const struct intel_connector *connector);
void intel_dp_dpcd_set_probe(struct intel_dp *intel_dp, bool force_on_external); void intel_dp_dpcd_set_probe(struct intel_dp *intel_dp, bool force_on_external);
bool intel_dp_in_hdr_mode(const struct drm_connector_state *conn_state); bool intel_dp_in_hdr_mode(const struct drm_connector_state *conn_state);
int intel_dp_compute_config_late(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
int intel_dp_sdp_min_guardband(const struct intel_crtc_state *crtc_state,
bool assume_all_enabled);
#endif /* __INTEL_DP_H__ */ #endif /* __INTEL_DP_H__ */

View File

@ -5,9 +5,9 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_dp_aux.h" #include "intel_dp_aux.h"
#include "intel_dp_aux_regs.h" #include "intel_dp_aux_regs.h"

View File

@ -27,9 +27,10 @@
#include <drm/display/drm_dp_helper.h> #include <drm/display/drm_dp_helper.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_jiffies.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_dp_link_training.h" #include "intel_dp_link_training.h"
#include "intel_encoder.h" #include "intel_encoder.h"

View File

@ -33,7 +33,6 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include "i915_utils.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_audio.h" #include "intel_audio.h"
#include "intel_connector.h" #include "intel_connector.h"
@ -43,6 +42,7 @@
#include "intel_display_driver.h" #include "intel_display_driver.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_dp_hdcp.h" #include "intel_dp_hdcp.h"
#include "intel_dp_link_training.h" #include "intel_dp_link_training.h"
@ -293,12 +293,22 @@ int intel_dp_mtp_tu_compute_config(struct intel_dp *intel_dp,
mst_stream_update_slots(crtc_state, mst_state); mst_stream_update_slots(crtc_state, mst_state);
} }
if (dsc) { /*
if (!intel_dp_supports_fec(intel_dp, connector, crtc_state)) * NOTE: The following must reset crtc_state->fec_enable for UHBR/DSC
return -EINVAL; * after it was set by intel_dp_dsc_compute_config() ->
* intel_dp_needs_8b10b_fec().
crtc_state->fec_enable = !intel_dp_is_uhbr(crtc_state); */
} crtc_state->fec_enable = intel_dp_needs_8b10b_fec(crtc_state, dsc);
/*
* If FEC gets enabled only because of another compressed stream, FEC
* may not be supported for this uncompressed stream on the whole link
* path until the sink DPRX. In this case a downstream branch device
* will disable FEC for the uncompressed stream as expected and so the
* FEC support doesn't need to be checked for this uncompressed stream.
*/
if (crtc_state->fec_enable && dsc &&
!intel_dp_supports_fec(intel_dp, connector, crtc_state))
return -EINVAL;
max_dpt_bpp_x16 = fxp_q4_from_int(intel_dp_mst_max_dpt_bpp(crtc_state, dsc)); max_dpt_bpp_x16 = fxp_q4_from_int(intel_dp_mst_max_dpt_bpp(crtc_state, dsc));
if (max_dpt_bpp_x16 && max_bpp_x16 > max_dpt_bpp_x16) { if (max_dpt_bpp_x16 && max_bpp_x16 > max_dpt_bpp_x16) {
@ -811,14 +821,14 @@ static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state,
return mask; return mask;
} }
static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state, static int intel_dp_mst_check_dsc_change(struct intel_atomic_state *state,
struct drm_dp_mst_topology_mgr *mst_mgr, struct drm_dp_mst_topology_mgr *mst_mgr,
struct intel_link_bw_limits *limits) struct intel_link_bw_limits *limits)
{ {
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
struct intel_crtc *crtc; struct intel_crtc *crtc;
u8 mst_pipe_mask; u8 mst_pipe_mask;
u8 fec_pipe_mask = 0; u8 dsc_pipe_mask = 0;
int ret; int ret;
mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL); mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL);
@ -831,16 +841,16 @@ static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state,
if (drm_WARN_ON(display->drm, !crtc_state)) if (drm_WARN_ON(display->drm, !crtc_state))
return -EINVAL; return -EINVAL;
if (crtc_state->fec_enable) if (intel_dsc_enabled_on_link(crtc_state))
fec_pipe_mask |= BIT(crtc->pipe); dsc_pipe_mask |= BIT(crtc->pipe);
} }
if (!fec_pipe_mask || mst_pipe_mask == fec_pipe_mask) if (!dsc_pipe_mask || mst_pipe_mask == dsc_pipe_mask)
return 0; return 0;
limits->force_fec_pipes |= mst_pipe_mask; limits->link_dsc_pipes |= mst_pipe_mask;
ret = intel_modeset_pipes_in_mask_early(state, "MST FEC", ret = intel_modeset_pipes_in_mask_early(state, "MST DSC",
mst_pipe_mask); mst_pipe_mask);
return ret ? : -EAGAIN; return ret ? : -EAGAIN;
@ -894,7 +904,7 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
int i; int i;
for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) { for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) {
ret = intel_dp_mst_check_fec_change(state, mgr, limits); ret = intel_dp_mst_check_dsc_change(state, mgr, limits);
if (ret) if (ret)
return ret; return ret;
@ -1658,6 +1668,7 @@ intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
struct intel_connector *connector) struct intel_connector *connector)
{ {
u8 dpcd_caps[DP_RECEIVER_CAP_SIZE]; u8 dpcd_caps[DP_RECEIVER_CAP_SIZE];
struct drm_dp_desc desc;
if (!connector->dp.dsc_decompression_aux) if (!connector->dp.dsc_decompression_aux)
return; return;
@ -1665,7 +1676,13 @@ intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd_caps) < 0) if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd_caps) < 0)
return; return;
intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV], connector); if (drm_dp_read_desc(connector->dp.dsc_decompression_aux, &desc,
drm_dp_is_branch(dpcd_caps)) < 0)
return;
intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV],
&desc, drm_dp_is_branch(dpcd_caps),
connector);
} }
static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector) static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector)

View File

@ -24,13 +24,13 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "bxt_dpio_phy_regs.h" #include "bxt_dpio_phy_regs.h"
#include "i915_utils.h"
#include "intel_ddi.h" #include "intel_ddi.h"
#include "intel_ddi_buf_trans.h" #include "intel_ddi_buf_trans.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_power_well.h" #include "intel_display_power_well.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_dpio_phy.h" #include "intel_dpio_phy.h"
#include "vlv_dpio_phy_regs.h" #include "vlv_dpio_phy_regs.h"

View File

@ -17,6 +17,7 @@
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_dpio_phy.h" #include "intel_dpio_phy.h"
#include "intel_dpll.h" #include "intel_dpll.h"
#include "intel_lt_phy.h"
#include "intel_lvds.h" #include "intel_lvds.h"
#include "intel_lvds_regs.h" #include "intel_lvds_regs.h"
#include "intel_panel.h" #include "intel_panel.h"
@ -1232,6 +1233,28 @@ static int mtl_crtc_compute_clock(struct intel_atomic_state *state,
return 0; return 0;
} }
static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
struct intel_encoder *encoder =
intel_get_crtc_new_encoder(state, crtc_state);
int ret;
ret = intel_lt_phy_pll_calc_state(crtc_state, encoder);
if (ret)
return ret;
/* TODO: Do the readback via intel_compute_shared_dplls() */
crtc_state->port_clock =
intel_lt_phy_calc_port_clock(encoder, crtc_state);
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
return 0;
}
static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state) static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
@ -1691,6 +1714,10 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state,
return 0; return 0;
} }
static const struct intel_dpll_global_funcs xe3plpd_dpll_funcs = {
.crtc_compute_clock = xe3plpd_crtc_compute_clock,
};
static const struct intel_dpll_global_funcs mtl_dpll_funcs = { static const struct intel_dpll_global_funcs mtl_dpll_funcs = {
.crtc_compute_clock = mtl_crtc_compute_clock, .crtc_compute_clock = mtl_crtc_compute_clock,
}; };
@ -1789,7 +1816,9 @@ int intel_dpll_crtc_get_dpll(struct intel_atomic_state *state,
void void
intel_dpll_init_clock_hook(struct intel_display *display) intel_dpll_init_clock_hook(struct intel_display *display)
{ {
if (DISPLAY_VER(display) >= 14) if (HAS_LT_PHY(display))
display->funcs.dpll = &xe3plpd_dpll_funcs;
else if (DISPLAY_VER(display) >= 14)
display->funcs.dpll = &mtl_dpll_funcs; display->funcs.dpll = &mtl_dpll_funcs;
else if (display->platform.dg2) else if (display->platform.dg2)
display->funcs.dpll = &dg2_dpll_funcs; display->funcs.dpll = &dg2_dpll_funcs;

View File

@ -27,11 +27,11 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "bxt_dpio_phy_regs.h" #include "bxt_dpio_phy_regs.h"
#include "i915_utils.h"
#include "intel_cx0_phy.h" #include "intel_cx0_phy.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dkl_phy.h" #include "intel_dkl_phy.h"
#include "intel_dkl_phy_regs.h" #include "intel_dkl_phy_regs.h"
#include "intel_dpio_phy.h" #include "intel_dpio_phy.h"

View File

@ -267,6 +267,16 @@ struct intel_cx0pll_state {
bool tbt_mode; bool tbt_mode;
}; };
struct intel_lt_phy_pll_state {
u32 clock; /* in kHz */
u8 addr_msb[13];
u8 addr_lsb[13];
u8 data[13][4];
u8 config[3];
bool ssc_enabled;
bool tbt_mode;
};
struct intel_dpll_hw_state { struct intel_dpll_hw_state {
union { union {
struct i9xx_dpll_hw_state i9xx; struct i9xx_dpll_hw_state i9xx;
@ -276,6 +286,7 @@ struct intel_dpll_hw_state {
struct icl_dpll_hw_state icl; struct icl_dpll_hw_state icl;
struct intel_mpllb_state mpllb; struct intel_mpllb_state mpllb;
struct intel_cx0pll_state cx0pll; struct intel_cx0pll_state cx0pll;
struct intel_lt_phy_pll_state ltpll;
}; };
}; };

View File

@ -115,24 +115,6 @@ static bool pre_commit_is_vrr_active(struct intel_atomic_state *state,
return old_crtc_state->vrr.enable && !intel_crtc_vrr_disabling(state, crtc); return old_crtc_state->vrr.enable && !intel_crtc_vrr_disabling(state, crtc);
} }
static int dsb_vblank_delay(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
const struct intel_crtc_state *crtc_state =
intel_pre_commit_crtc_state(state, crtc);
if (pre_commit_is_vrr_active(state, crtc))
/*
* When the push is sent during vblank it will trigger
* on the next scanline, hence we have up to one extra
* scanline until the delayed vblank occurs after
* TRANS_PUSH has been written.
*/
return intel_vrr_vblank_delay(crtc_state) + 1;
else
return intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode);
}
static int dsb_vtotal(struct intel_atomic_state *state, static int dsb_vtotal(struct intel_atomic_state *state,
struct intel_crtc *crtc) struct intel_crtc *crtc)
{ {
@ -723,7 +705,7 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state,
intel_dsb_emit_wait_dsl(dsb, DSB_OPCODE_WAIT_DSL_OUT, 0, 0); intel_dsb_emit_wait_dsl(dsb, DSB_OPCODE_WAIT_DSL_OUT, 0, 0);
if (pre_commit_is_vrr_active(state, crtc)) { if (pre_commit_is_vrr_active(state, crtc)) {
int vblank_delay = intel_vrr_vblank_delay(crtc_state); int vblank_delay = crtc_state->set_context_latency;
end = intel_vrr_vmin_vblank_start(crtc_state); end = intel_vrr_vmin_vblank_start(crtc_state);
start = end - vblank_delay - latency; start = end - vblank_delay - latency;
@ -815,16 +797,43 @@ void intel_dsb_chain(struct intel_atomic_state *state,
wait_for_vblank ? DSB_WAIT_FOR_VBLANK : 0); wait_for_vblank ? DSB_WAIT_FOR_VBLANK : 0);
} }
void intel_dsb_wait_vblank_delay(struct intel_atomic_state *state, void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
struct intel_dsb *dsb) struct intel_dsb *dsb)
{ {
struct intel_crtc *crtc = dsb->crtc; struct intel_crtc *crtc = dsb->crtc;
const struct intel_crtc_state *crtc_state = const struct intel_crtc_state *crtc_state =
intel_pre_commit_crtc_state(state, crtc); intel_pre_commit_crtc_state(state, crtc);
int usecs = intel_scanlines_to_usecs(&crtc_state->hw.adjusted_mode, const struct drm_display_mode *adjusted_mode =
dsb_vblank_delay(state, crtc)); &crtc_state->hw.adjusted_mode;
int wait_scanlines;
intel_dsb_wait_usec(dsb, usecs); if (pre_commit_is_vrr_active(state, crtc)) {
/*
* If the push happened before the vmin decision boundary
* we don't know how far we are from the undelayed vblank.
* Wait until we're past the vmin safe window, at which
* point we're SCL lines away from the delayed vblank.
*
* If the push happened after the vmin decision boundary
* the hardware itself guarantees that we're SCL lines
* away from the delayed vblank, and we won't be inside
* the vmin safe window so this extra wait does nothing.
*/
intel_dsb_wait_scanline_out(state, dsb,
intel_vrr_safe_window_start(crtc_state),
intel_vrr_vmin_safe_window_end(crtc_state));
/*
* When the push is sent during vblank it will trigger
* on the next scanline, hence we have up to one extra
* scanline until the delayed vblank occurs after
* TRANS_PUSH has been written.
*/
wait_scanlines = crtc_state->set_context_latency + 1;
} else {
wait_scanlines = intel_mode_vblank_delay(adjusted_mode);
}
intel_dsb_wait_usec(dsb, intel_scanlines_to_usecs(adjusted_mode, wait_scanlines));
} }
/** /**

View File

@ -48,8 +48,8 @@ void intel_dsb_nonpost_end(struct intel_dsb *dsb);
void intel_dsb_interrupt(struct intel_dsb *dsb); void intel_dsb_interrupt(struct intel_dsb *dsb);
void intel_dsb_wait_usec(struct intel_dsb *dsb, int count); void intel_dsb_wait_usec(struct intel_dsb *dsb, int count);
void intel_dsb_wait_vblanks(struct intel_dsb *dsb, int count); void intel_dsb_wait_vblanks(struct intel_dsb *dsb, int count);
void intel_dsb_wait_vblank_delay(struct intel_atomic_state *state, void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
struct intel_dsb *dsb); struct intel_dsb *dsb);
void intel_dsb_wait_scanline_in(struct intel_atomic_state *state, void intel_dsb_wait_scanline_in(struct intel_atomic_state *state,
struct intel_dsb *dsb, struct intel_dsb *dsb,
int lower, int upper); int lower, int upper);

View File

@ -38,10 +38,10 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <video/mipi_display.h> #include <video/mipi_display.h>
#include "i915_utils.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dsi.h" #include "intel_dsi.h"
#include "intel_dsi_vbt.h" #include "intel_dsi_vbt.h"
#include "intel_gmbus_regs.h" #include "intel_gmbus_regs.h"
@ -106,8 +106,8 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi,
u8 type, flags, seq_port; u8 type, flags, seq_port;
u16 len; u16 len;
enum port port; enum port port;
ssize_t ret;
drm_dbg_kms(display->drm, "\n"); bool hs_mode;
flags = *data++; flags = *data++;
type = *data++; type = *data++;
@ -129,45 +129,53 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi,
goto out; goto out;
} }
if ((flags >> MIPI_TRANSFER_MODE_SHIFT) & 1) hs_mode = (flags >> MIPI_TRANSFER_MODE_SHIFT) & 1;
if (hs_mode)
dsi_device->mode_flags &= ~MIPI_DSI_MODE_LPM; dsi_device->mode_flags &= ~MIPI_DSI_MODE_LPM;
else else
dsi_device->mode_flags |= MIPI_DSI_MODE_LPM; dsi_device->mode_flags |= MIPI_DSI_MODE_LPM;
dsi_device->channel = (flags >> MIPI_VIRTUAL_CHANNEL_SHIFT) & 3; dsi_device->channel = (flags >> MIPI_VIRTUAL_CHANNEL_SHIFT) & 3;
drm_dbg_kms(display->drm, "DSI packet: Port %c (seq %u), Flags 0x%02x, VC %u, %s, Type 0x%02x, Length %u, Data %*ph\n",
port_name(port), seq_port, flags, dsi_device->channel,
hs_mode ? "HS" : "LP", type, len, (int)len, data);
switch (type) { switch (type) {
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
mipi_dsi_generic_write(dsi_device, NULL, 0); ret = mipi_dsi_generic_write(dsi_device, NULL, 0);
break; break;
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
mipi_dsi_generic_write(dsi_device, data, 1); ret = mipi_dsi_generic_write(dsi_device, data, 1);
break; break;
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
mipi_dsi_generic_write(dsi_device, data, 2); ret = mipi_dsi_generic_write(dsi_device, data, 2);
break; break;
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
drm_dbg_kms(display->drm, "Generic Read not yet implemented or used\n"); ret = -EOPNOTSUPP;
break; break;
case MIPI_DSI_GENERIC_LONG_WRITE: case MIPI_DSI_GENERIC_LONG_WRITE:
mipi_dsi_generic_write(dsi_device, data, len); ret = mipi_dsi_generic_write(dsi_device, data, len);
break; break;
case MIPI_DSI_DCS_SHORT_WRITE: case MIPI_DSI_DCS_SHORT_WRITE:
mipi_dsi_dcs_write_buffer(dsi_device, data, 1); ret = mipi_dsi_dcs_write_buffer(dsi_device, data, 1);
break; break;
case MIPI_DSI_DCS_SHORT_WRITE_PARAM: case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
mipi_dsi_dcs_write_buffer(dsi_device, data, 2); ret = mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
break; break;
case MIPI_DSI_DCS_READ: case MIPI_DSI_DCS_READ:
drm_dbg_kms(display->drm, "DCS Read not yet implemented or used\n"); ret = -EOPNOTSUPP;
break; break;
case MIPI_DSI_DCS_LONG_WRITE: case MIPI_DSI_DCS_LONG_WRITE:
mipi_dsi_dcs_write_buffer(dsi_device, data, len); ret = mipi_dsi_dcs_write_buffer(dsi_device, data, len);
break; break;
} }
if (ret < 0)
drm_err(display->drm, "DSI send packet failed with %pe\n", ERR_PTR(ret));
if (DISPLAY_VER(display) < 11) if (DISPLAY_VER(display) < 11)
vlv_dsi_wait_for_fifo_empty(intel_dsi, port); vlv_dsi_wait_for_fifo_empty(intel_dsi, port);

View File

@ -34,12 +34,12 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include "i915_utils.h"
#include "intel_connector.h" #include "intel_connector.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_driver.h" #include "intel_display_driver.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dvo.h" #include "intel_dvo.h"
#include "intel_dvo_dev.h" #include "intel_dvo_dev.h"
#include "intel_dvo_regs.h" #include "intel_dvo_regs.h"

View File

@ -10,12 +10,11 @@
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_modeset_helper.h> #include <drm/drm_modeset_helper.h>
#include "i915_drv.h"
#include "i915_utils.h"
#include "intel_bo.h" #include "intel_bo.h"
#include "intel_display.h" #include "intel_display.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dpt.h" #include "intel_dpt.h"
#include "intel_fb.h" #include "intel_fb.h"
#include "intel_fb_bo.h" #include "intel_fb_bo.h"
@ -547,8 +546,6 @@ static bool plane_has_modifier(struct intel_display *display,
u8 plane_caps, u8 plane_caps,
const struct intel_modifier_desc *md) const struct intel_modifier_desc *md)
{ {
struct drm_i915_private *i915 = to_i915(display->drm);
if (!IS_DISPLAY_VER(display, md->display_ver.from, md->display_ver.until)) if (!IS_DISPLAY_VER(display, md->display_ver.from, md->display_ver.until))
return false; return false;
@ -560,15 +557,15 @@ static bool plane_has_modifier(struct intel_display *display,
* where supported. * where supported.
*/ */
if (intel_fb_is_ccs_modifier(md->modifier) && if (intel_fb_is_ccs_modifier(md->modifier) &&
HAS_FLAT_CCS(i915) != !md->ccs.packed_aux_planes) HAS_AUX_CCS(display) != !!md->ccs.packed_aux_planes)
return false; return false;
if (md->modifier == I915_FORMAT_MOD_4_TILED_BMG_CCS && if (md->modifier == I915_FORMAT_MOD_4_TILED_BMG_CCS &&
(GRAPHICS_VER(i915) < 20 || !display->platform.dgfx)) (DISPLAY_VER(display) < 14 || !display->platform.dgfx))
return false; return false;
if (md->modifier == I915_FORMAT_MOD_4_TILED_LNL_CCS && if (md->modifier == I915_FORMAT_MOD_4_TILED_LNL_CCS &&
(GRAPHICS_VER(i915) < 20 || display->platform.dgfx)) (DISPLAY_VER(display) < 20 || display->platform.dgfx))
return false; return false;
return true; return true;
@ -777,7 +774,6 @@ unsigned int
intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane) intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)
{ {
struct intel_display *display = to_intel_display(fb->dev); struct intel_display *display = to_intel_display(fb->dev);
struct drm_i915_private *i915 = to_i915(display->drm);
unsigned int cpp = fb->format->cpp[color_plane]; unsigned int cpp = fb->format->cpp[color_plane];
switch (fb->modifier) { switch (fb->modifier) {
@ -814,7 +810,7 @@ intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)
return 64; return 64;
fallthrough; fallthrough;
case I915_FORMAT_MOD_Y_TILED: case I915_FORMAT_MOD_Y_TILED:
if (DISPLAY_VER(display) == 2 || HAS_128_BYTE_Y_TILING(i915)) if (HAS_128B_Y_TILING(display))
return 128; return 128;
else else
return 512; return 512;
@ -2117,6 +2113,7 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
intel_frontbuffer_put(intel_fb->frontbuffer); intel_frontbuffer_put(intel_fb->frontbuffer);
kfree(intel_fb->panic);
kfree(intel_fb); kfree(intel_fb);
} }
@ -2215,18 +2212,24 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
struct intel_display *display = to_intel_display(obj->dev); struct intel_display *display = to_intel_display(obj->dev);
struct drm_framebuffer *fb = &intel_fb->base; struct drm_framebuffer *fb = &intel_fb->base;
u32 max_stride; u32 max_stride;
int ret = -EINVAL; int ret;
int i; int i;
intel_fb->panic = intel_panic_alloc();
if (!intel_fb->panic)
return -ENOMEM;
/* /*
* intel_frontbuffer_get() must be done before * intel_frontbuffer_get() must be done before
* intel_fb_bo_framebuffer_init() to avoid set_tiling vs. addfb race. * intel_fb_bo_framebuffer_init() to avoid set_tiling vs. addfb race.
*/ */
intel_fb->frontbuffer = intel_frontbuffer_get(obj); intel_fb->frontbuffer = intel_frontbuffer_get(obj);
if (!intel_fb->frontbuffer) if (!intel_fb->frontbuffer) {
return -ENOMEM; ret = -ENOMEM;
goto err_free_panic;
}
ret = intel_fb_bo_framebuffer_init(fb, obj, mode_cmd); ret = intel_fb_bo_framebuffer_init(obj, mode_cmd);
if (ret) if (ret)
goto err_frontbuffer_put; goto err_frontbuffer_put;
@ -2323,6 +2326,9 @@ err_bo_framebuffer_fini:
intel_fb_bo_framebuffer_fini(obj); intel_fb_bo_framebuffer_fini(obj);
err_frontbuffer_put: err_frontbuffer_put:
intel_frontbuffer_put(intel_fb->frontbuffer); intel_frontbuffer_put(intel_fb->frontbuffer);
err_free_panic:
kfree(intel_fb->panic);
return ret; return ret;
} }
@ -2349,20 +2355,11 @@ intel_user_framebuffer_create(struct drm_device *dev,
struct intel_framebuffer *intel_framebuffer_alloc(void) struct intel_framebuffer *intel_framebuffer_alloc(void)
{ {
struct intel_framebuffer *intel_fb; struct intel_framebuffer *intel_fb;
struct intel_panic *panic;
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
if (!intel_fb) if (!intel_fb)
return NULL; return NULL;
panic = intel_panic_alloc();
if (!panic) {
kfree(intel_fb);
return NULL;
}
intel_fb->panic = panic;
return intel_fb; return intel_fb;
} }

View File

@ -18,8 +18,7 @@ void intel_fb_bo_framebuffer_fini(struct drm_gem_object *obj)
/* Nothing to do for i915 */ /* Nothing to do for i915 */
} }
int intel_fb_bo_framebuffer_init(struct drm_framebuffer *fb, int intel_fb_bo_framebuffer_init(struct drm_gem_object *_obj,
struct drm_gem_object *_obj,
struct drm_mode_fb_cmd2 *mode_cmd) struct drm_mode_fb_cmd2 *mode_cmd)
{ {
struct drm_i915_gem_object *obj = to_intel_bo(_obj); struct drm_i915_gem_object *obj = to_intel_bo(_obj);

View File

@ -14,8 +14,7 @@ struct drm_mode_fb_cmd2;
void intel_fb_bo_framebuffer_fini(struct drm_gem_object *obj); void intel_fb_bo_framebuffer_fini(struct drm_gem_object *obj);
int intel_fb_bo_framebuffer_init(struct drm_framebuffer *fb, int intel_fb_bo_framebuffer_init(struct drm_gem_object *obj,
struct drm_gem_object *obj,
struct drm_mode_fb_cmd2 *mode_cmd); struct drm_mode_fb_cmd2 *mode_cmd);
struct drm_gem_object * struct drm_gem_object *

View File

@ -49,17 +49,16 @@
#include "gt/intel_gt_types.h" #include "gt/intel_gt_types.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_utils.h"
#include "i915_vgpu.h" #include "i915_vgpu.h"
#include "i915_vma.h" #include "i915_vma.h"
#include "i9xx_plane_regs.h" #include "i9xx_plane_regs.h"
#include "intel_cdclk.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_device.h" #include "intel_display_device.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_trace.h" #include "intel_display_trace.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_display_wa.h" #include "intel_display_wa.h"
#include "intel_fbc.h" #include "intel_fbc.h"
#include "intel_fbc_regs.h" #include "intel_fbc_regs.h"
@ -102,7 +101,8 @@ struct intel_fbc {
struct mutex lock; struct mutex lock;
unsigned int busy_bits; unsigned int busy_bits;
struct i915_stolen_fb compressed_fb, compressed_llb; struct intel_stolen_node *compressed_fb;
struct intel_stolen_node *compressed_llb;
enum intel_fbc_id id; enum intel_fbc_id id;
@ -141,15 +141,18 @@ static unsigned int intel_fbc_plane_stride(const struct intel_plane_state *plane
return stride; return stride;
} }
static unsigned int intel_fbc_cfb_cpp(void) static unsigned int intel_fbc_cfb_cpp(const struct intel_plane_state *plane_state)
{ {
return 4; /* FBC always 4 bytes per pixel */ const struct drm_framebuffer *fb = plane_state->hw.fb;
unsigned int cpp = fb->format->cpp[0];
return max(cpp, 4);
} }
/* plane stride based cfb stride in bytes, assuming 1:1 compression limit */ /* plane stride based cfb stride in bytes, assuming 1:1 compression limit */
static unsigned int intel_fbc_plane_cfb_stride(const struct intel_plane_state *plane_state) static unsigned int intel_fbc_plane_cfb_stride(const struct intel_plane_state *plane_state)
{ {
unsigned int cpp = intel_fbc_cfb_cpp(); unsigned int cpp = intel_fbc_cfb_cpp(plane_state);
return intel_fbc_plane_stride(plane_state) * cpp; return intel_fbc_plane_stride(plane_state) * cpp;
} }
@ -203,7 +206,7 @@ static unsigned int intel_fbc_cfb_stride(const struct intel_plane_state *plane_s
struct intel_display *display = to_intel_display(plane_state->uapi.plane->dev); struct intel_display *display = to_intel_display(plane_state->uapi.plane->dev);
unsigned int stride = intel_fbc_plane_cfb_stride(plane_state); unsigned int stride = intel_fbc_plane_cfb_stride(plane_state);
unsigned int width = drm_rect_width(&plane_state->uapi.src) >> 16; unsigned int width = drm_rect_width(&plane_state->uapi.src) >> 16;
unsigned int cpp = intel_fbc_cfb_cpp(); unsigned int cpp = intel_fbc_cfb_cpp(plane_state);
return _intel_fbc_cfb_stride(display, cpp, width, stride); return _intel_fbc_cfb_stride(display, cpp, width, stride);
} }
@ -376,20 +379,19 @@ static void i8xx_fbc_nuke(struct intel_fbc *fbc)
static void i8xx_fbc_program_cfb(struct intel_fbc *fbc) static void i8xx_fbc_program_cfb(struct intel_fbc *fbc)
{ {
struct intel_display *display = fbc->display; struct intel_display *display = fbc->display;
struct drm_i915_private *i915 = to_i915(display->drm);
drm_WARN_ON(display->drm, drm_WARN_ON(display->drm,
range_end_overflows_t(u64, i915_gem_stolen_area_address(i915), range_end_overflows_t(u64, i915_gem_stolen_area_address(display->drm),
i915_gem_stolen_node_offset(&fbc->compressed_fb), i915_gem_stolen_node_offset(fbc->compressed_fb),
U32_MAX)); U32_MAX));
drm_WARN_ON(display->drm, drm_WARN_ON(display->drm,
range_end_overflows_t(u64, i915_gem_stolen_area_address(i915), range_end_overflows_t(u64, i915_gem_stolen_area_address(display->drm),
i915_gem_stolen_node_offset(&fbc->compressed_llb), i915_gem_stolen_node_offset(fbc->compressed_llb),
U32_MAX)); U32_MAX));
intel_de_write(display, FBC_CFB_BASE, intel_de_write(display, FBC_CFB_BASE,
i915_gem_stolen_node_address(i915, &fbc->compressed_fb)); i915_gem_stolen_node_address(fbc->compressed_fb));
intel_de_write(display, FBC_LL_BASE, intel_de_write(display, FBC_LL_BASE,
i915_gem_stolen_node_address(i915, &fbc->compressed_llb)); i915_gem_stolen_node_address(fbc->compressed_llb));
} }
static const struct intel_fbc_funcs i8xx_fbc_funcs = { static const struct intel_fbc_funcs i8xx_fbc_funcs = {
@ -497,7 +499,7 @@ static void g4x_fbc_program_cfb(struct intel_fbc *fbc)
struct intel_display *display = fbc->display; struct intel_display *display = fbc->display;
intel_de_write(display, DPFC_CB_BASE, intel_de_write(display, DPFC_CB_BASE,
i915_gem_stolen_node_offset(&fbc->compressed_fb)); i915_gem_stolen_node_offset(fbc->compressed_fb));
} }
static const struct intel_fbc_funcs g4x_fbc_funcs = { static const struct intel_fbc_funcs g4x_fbc_funcs = {
@ -566,7 +568,7 @@ static void ilk_fbc_program_cfb(struct intel_fbc *fbc)
struct intel_display *display = fbc->display; struct intel_display *display = fbc->display;
intel_de_write(display, ILK_DPFC_CB_BASE(fbc->id), intel_de_write(display, ILK_DPFC_CB_BASE(fbc->id),
i915_gem_stolen_node_offset(&fbc->compressed_fb)); i915_gem_stolen_node_offset(fbc->compressed_fb));
} }
static const struct intel_fbc_funcs ilk_fbc_funcs = { static const struct intel_fbc_funcs ilk_fbc_funcs = {
@ -797,7 +799,6 @@ static u64 intel_fbc_cfb_base_max(struct intel_display *display)
static u64 intel_fbc_stolen_end(struct intel_display *display) static u64 intel_fbc_stolen_end(struct intel_display *display)
{ {
struct drm_i915_private __maybe_unused *i915 = to_i915(display->drm);
u64 end; u64 end;
/* The FBC hardware for BDW/SKL doesn't have access to the stolen /* The FBC hardware for BDW/SKL doesn't have access to the stolen
@ -806,7 +807,7 @@ static u64 intel_fbc_stolen_end(struct intel_display *display)
* underruns, even if that range is not reserved by the BIOS. */ * underruns, even if that range is not reserved by the BIOS. */
if (display->platform.broadwell || if (display->platform.broadwell ||
(DISPLAY_VER(display) == 9 && !display->platform.broxton)) (DISPLAY_VER(display) == 9 && !display->platform.broxton))
end = i915_gem_stolen_area_size(i915) - 8 * 1024 * 1024; end = i915_gem_stolen_area_size(display->drm) - 8 * 1024 * 1024;
else else
end = U64_MAX; end = U64_MAX;
@ -835,20 +836,19 @@ static int find_compression_limit(struct intel_fbc *fbc,
unsigned int size, int min_limit) unsigned int size, int min_limit)
{ {
struct intel_display *display = fbc->display; struct intel_display *display = fbc->display;
struct drm_i915_private *i915 = to_i915(display->drm);
u64 end = intel_fbc_stolen_end(display); u64 end = intel_fbc_stolen_end(display);
int ret, limit = min_limit; int ret, limit = min_limit;
size /= limit; size /= limit;
/* Try to over-allocate to reduce reallocations and fragmentation. */ /* Try to over-allocate to reduce reallocations and fragmentation. */
ret = i915_gem_stolen_insert_node_in_range(i915, &fbc->compressed_fb, ret = i915_gem_stolen_insert_node_in_range(fbc->compressed_fb,
size <<= 1, 4096, 0, end); size <<= 1, 4096, 0, end);
if (ret == 0) if (ret == 0)
return limit; return limit;
for (; limit <= intel_fbc_max_limit(display); limit <<= 1) { for (; limit <= intel_fbc_max_limit(display); limit <<= 1) {
ret = i915_gem_stolen_insert_node_in_range(i915, &fbc->compressed_fb, ret = i915_gem_stolen_insert_node_in_range(fbc->compressed_fb,
size >>= 1, 4096, 0, end); size >>= 1, 4096, 0, end);
if (ret == 0) if (ret == 0)
return limit; return limit;
@ -861,17 +861,15 @@ static int intel_fbc_alloc_cfb(struct intel_fbc *fbc,
unsigned int size, int min_limit) unsigned int size, int min_limit)
{ {
struct intel_display *display = fbc->display; struct intel_display *display = fbc->display;
struct drm_i915_private *i915 = to_i915(display->drm);
int ret; int ret;
drm_WARN_ON(display->drm, drm_WARN_ON(display->drm,
i915_gem_stolen_node_allocated(&fbc->compressed_fb)); i915_gem_stolen_node_allocated(fbc->compressed_fb));
drm_WARN_ON(display->drm, drm_WARN_ON(display->drm,
i915_gem_stolen_node_allocated(&fbc->compressed_llb)); i915_gem_stolen_node_allocated(fbc->compressed_llb));
if (DISPLAY_VER(display) < 5 && !display->platform.g4x) { if (DISPLAY_VER(display) < 5 && !display->platform.g4x) {
ret = i915_gem_stolen_insert_node(i915, &fbc->compressed_llb, ret = i915_gem_stolen_insert_node(fbc->compressed_llb, 4096, 4096);
4096, 4096);
if (ret) if (ret)
goto err; goto err;
} }
@ -887,14 +885,14 @@ static int intel_fbc_alloc_cfb(struct intel_fbc *fbc,
drm_dbg_kms(display->drm, drm_dbg_kms(display->drm,
"reserved %llu bytes of contiguous stolen space for FBC, limit: %d\n", "reserved %llu bytes of contiguous stolen space for FBC, limit: %d\n",
i915_gem_stolen_node_size(&fbc->compressed_fb), fbc->limit); i915_gem_stolen_node_size(fbc->compressed_fb), fbc->limit);
return 0; return 0;
err_llb: err_llb:
if (i915_gem_stolen_node_allocated(&fbc->compressed_llb)) if (i915_gem_stolen_node_allocated(fbc->compressed_llb))
i915_gem_stolen_remove_node(i915, &fbc->compressed_llb); i915_gem_stolen_remove_node(fbc->compressed_llb);
err: err:
if (i915_gem_stolen_initialized(i915)) if (i915_gem_stolen_initialized(display->drm))
drm_info_once(display->drm, drm_info_once(display->drm,
"not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size); "not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size);
return -ENOSPC; return -ENOSPC;
@ -932,9 +930,12 @@ static void intel_fbc_program_workarounds(struct intel_fbc *fbc)
if (IS_DISPLAY_VER(display, 11, 12)) if (IS_DISPLAY_VER(display, 11, 12))
intel_de_rmw(display, ILK_DPFC_CHICKEN(fbc->id), intel_de_rmw(display, ILK_DPFC_CHICKEN(fbc->id),
0, DPFC_CHICKEN_COMP_DUMMY_PIXEL); 0, DPFC_CHICKEN_COMP_DUMMY_PIXEL);
/*
/* Wa_22014263786:icl,jsl,tgl,dg1,rkl,adls,adlp,mtl */ * Wa_22014263786
if (DISPLAY_VER(display) >= 11 && !display->platform.dg2) * Fixes: Screen flicker with FBC and Package C state enabled
* Workaround: Forced SLB invalidation before start of new frame.
*/
if (intel_display_wa(display, 22014263786))
intel_de_rmw(display, ILK_DPFC_CHICKEN(fbc->id), intel_de_rmw(display, ILK_DPFC_CHICKEN(fbc->id),
0, DPFC_CHICKEN_FORCE_SLB_INVALIDATION); 0, DPFC_CHICKEN_FORCE_SLB_INVALIDATION);
@ -945,16 +946,13 @@ static void intel_fbc_program_workarounds(struct intel_fbc *fbc)
static void __intel_fbc_cleanup_cfb(struct intel_fbc *fbc) static void __intel_fbc_cleanup_cfb(struct intel_fbc *fbc)
{ {
struct intel_display *display = fbc->display;
struct drm_i915_private *i915 = to_i915(display->drm);
if (WARN_ON(intel_fbc_hw_is_active(fbc))) if (WARN_ON(intel_fbc_hw_is_active(fbc)))
return; return;
if (i915_gem_stolen_node_allocated(&fbc->compressed_llb)) if (i915_gem_stolen_node_allocated(fbc->compressed_llb))
i915_gem_stolen_remove_node(i915, &fbc->compressed_llb); i915_gem_stolen_remove_node(fbc->compressed_llb);
if (i915_gem_stolen_node_allocated(&fbc->compressed_fb)) if (i915_gem_stolen_node_allocated(fbc->compressed_fb))
i915_gem_stolen_remove_node(i915, &fbc->compressed_fb); i915_gem_stolen_remove_node(fbc->compressed_fb);
} }
void intel_fbc_cleanup(struct intel_display *display) void intel_fbc_cleanup(struct intel_display *display)
@ -967,6 +965,9 @@ void intel_fbc_cleanup(struct intel_display *display)
__intel_fbc_cleanup_cfb(fbc); __intel_fbc_cleanup_cfb(fbc);
mutex_unlock(&fbc->lock); mutex_unlock(&fbc->lock);
i915_gem_stolen_node_free(fbc->compressed_fb);
i915_gem_stolen_node_free(fbc->compressed_llb);
kfree(fbc); kfree(fbc);
} }
} }
@ -1083,11 +1084,57 @@ static bool lnl_fbc_pixel_format_is_valid(const struct intel_plane_state *plane_
} }
} }
static bool
xe3p_lpd_fbc_fp16_format_is_valid(const struct intel_plane_state *plane_state)
{
const struct drm_framebuffer *fb = plane_state->hw.fb;
switch (fb->format->format) {
case DRM_FORMAT_ARGB16161616F:
case DRM_FORMAT_ABGR16161616F:
return true;
default:
return false;
}
}
static bool xe3p_lpd_fbc_pixel_format_is_valid(const struct intel_plane_state *plane_state)
{
const struct drm_framebuffer *fb = plane_state->hw.fb;
if (lnl_fbc_pixel_format_is_valid(plane_state))
return true;
if (xe3p_lpd_fbc_fp16_format_is_valid(plane_state))
return true;
switch (fb->format->format) {
case DRM_FORMAT_XRGB16161616:
case DRM_FORMAT_XBGR16161616:
case DRM_FORMAT_ARGB16161616:
case DRM_FORMAT_ABGR16161616:
return true;
default:
return false;
}
}
bool
intel_fbc_is_enable_pixel_normalizer(const struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(plane_state);
return DISPLAY_VER(display) >= 35 &&
xe3p_lpd_fbc_fp16_format_is_valid(plane_state);
}
static bool pixel_format_is_valid(const struct intel_plane_state *plane_state) static bool pixel_format_is_valid(const struct intel_plane_state *plane_state)
{ {
struct intel_display *display = to_intel_display(plane_state->uapi.plane->dev); struct intel_display *display = to_intel_display(plane_state->uapi.plane->dev);
if (DISPLAY_VER(display) >= 20) if (DISPLAY_VER(display) >= 35)
return xe3p_lpd_fbc_pixel_format_is_valid(plane_state);
else if (DISPLAY_VER(display) >= 20)
return lnl_fbc_pixel_format_is_valid(plane_state); return lnl_fbc_pixel_format_is_valid(plane_state);
else if (DISPLAY_VER(display) >= 5 || display->platform.g4x) else if (DISPLAY_VER(display) >= 5 || display->platform.g4x)
return g4x_fbc_pixel_format_is_valid(plane_state); return g4x_fbc_pixel_format_is_valid(plane_state);
@ -1355,7 +1402,7 @@ static bool intel_fbc_is_cfb_ok(const struct intel_plane_state *plane_state)
return intel_fbc_min_limit(plane_state) <= fbc->limit && return intel_fbc_min_limit(plane_state) <= fbc->limit &&
intel_fbc_cfb_size(plane_state) <= fbc->limit * intel_fbc_cfb_size(plane_state) <= fbc->limit *
i915_gem_stolen_node_size(&fbc->compressed_fb); i915_gem_stolen_node_size(fbc->compressed_fb);
} }
static bool intel_fbc_is_ok(const struct intel_plane_state *plane_state) static bool intel_fbc_is_ok(const struct intel_plane_state *plane_state)
@ -1421,6 +1468,18 @@ intel_fbc_prepare_dirty_rect(struct intel_atomic_state *state,
} }
} }
static int _intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
/* WaFbcExceedCdClockThreshold:hsw,bdw */
if (display->platform.haswell || display->platform.broadwell)
return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
/* no FBC specific limits to worry about */
return 0;
}
static int intel_fbc_check_plane(struct intel_atomic_state *state, static int intel_fbc_check_plane(struct intel_atomic_state *state,
struct intel_plane *plane) struct intel_plane *plane)
{ {
@ -1436,7 +1495,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
if (!fbc) if (!fbc)
return 0; return 0;
if (!i915_gem_stolen_initialized(i915)) { if (!i915_gem_stolen_initialized(display->drm)) {
plane_state->no_fbc_reason = "stolen memory not initialised"; plane_state->no_fbc_reason = "stolen memory not initialised";
return 0; return 0;
} }
@ -1462,7 +1521,8 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
} }
/* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */ /* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */
if (i915_vtd_active(i915) && (display->platform.skylake || display->platform.broxton)) { if (intel_display_vtd_active(display) &&
(display->platform.skylake || display->platform.broxton)) {
plane_state->no_fbc_reason = "VT-d enabled"; plane_state->no_fbc_reason = "VT-d enabled";
return 0; return 0;
} }
@ -1560,18 +1620,9 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
return 0; return 0;
} }
/* WaFbcExceedCdClockThreshold:hsw,bdw */ if (_intel_fbc_min_cdclk(crtc_state) > display->cdclk.max_cdclk_freq) {
if (display->platform.haswell || display->platform.broadwell) { plane_state->no_fbc_reason = "pixel rate too high";
const struct intel_cdclk_state *cdclk_state; return 0;
cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
if (crtc_state->pixel_rate >= intel_cdclk_logical(cdclk_state) * 95 / 100) {
plane_state->no_fbc_reason = "pixel rate too high";
return 0;
}
} }
plane_state->no_fbc_reason = NULL; plane_state->no_fbc_reason = NULL;
@ -1579,6 +1630,27 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
return 0; return 0;
} }
int intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
struct intel_plane *plane = to_intel_plane(crtc->base.primary);
int min_cdclk;
if (!plane->fbc)
return 0;
min_cdclk = _intel_fbc_min_cdclk(crtc_state);
/*
* Do not ask for more than the max CDCLK frequency,
* if that is not enough FBC will simply not be used.
*/
if (min_cdclk > display->cdclk.max_cdclk_freq)
return 0;
return min_cdclk;
}
static bool intel_fbc_can_flip_nuke(struct intel_atomic_state *state, static bool intel_fbc_can_flip_nuke(struct intel_atomic_state *state,
struct intel_crtc *crtc, struct intel_crtc *crtc,
@ -2083,6 +2155,13 @@ static struct intel_fbc *intel_fbc_create(struct intel_display *display,
if (!fbc) if (!fbc)
return NULL; return NULL;
fbc->compressed_fb = i915_gem_stolen_node_alloc(display->drm);
if (!fbc->compressed_fb)
goto err;
fbc->compressed_llb = i915_gem_stolen_node_alloc(display->drm);
if (!fbc->compressed_llb)
goto err;
fbc->id = fbc_id; fbc->id = fbc_id;
fbc->display = display; fbc->display = display;
INIT_WORK(&fbc->underrun_work, intel_fbc_underrun_work_fn); INIT_WORK(&fbc->underrun_work, intel_fbc_underrun_work_fn);
@ -2102,6 +2181,13 @@ static struct intel_fbc *intel_fbc_create(struct intel_display *display,
fbc->funcs = &i8xx_fbc_funcs; fbc->funcs = &i8xx_fbc_funcs;
return fbc; return fbc;
err:
i915_gem_stolen_node_free(fbc->compressed_llb);
i915_gem_stolen_node_free(fbc->compressed_fb);
kfree(fbc);
return NULL;
} }
/** /**

View File

@ -28,6 +28,7 @@ enum intel_fbc_id {
}; };
int intel_fbc_atomic_check(struct intel_atomic_state *state); int intel_fbc_atomic_check(struct intel_atomic_state *state);
int intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state);
bool intel_fbc_pre_update(struct intel_atomic_state *state, bool intel_fbc_pre_update(struct intel_atomic_state *state,
struct intel_crtc *crtc); struct intel_crtc *crtc);
void intel_fbc_post_update(struct intel_atomic_state *state, void intel_fbc_post_update(struct intel_atomic_state *state,
@ -52,5 +53,7 @@ void intel_fbc_prepare_dirty_rect(struct intel_atomic_state *state,
struct intel_crtc *crtc); struct intel_crtc *crtc);
void intel_fbc_dirty_rect_update_noarm(struct intel_dsb *dsb, void intel_fbc_dirty_rect_update_noarm(struct intel_dsb *dsb,
struct intel_plane *plane); struct intel_plane *plane);
bool
intel_fbc_is_enable_pixel_normalizer(const struct intel_plane_state *plane_state);
#endif /* __INTEL_FBC_H__ */ #endif /* __INTEL_FBC_H__ */

View File

@ -205,6 +205,62 @@ static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.fb_set_suspend = intelfb_set_suspend, .fb_set_suspend = intelfb_set_suspend,
}; };
static void intel_fbdev_fill_mode_cmd(struct drm_fb_helper_surface_size *sizes,
struct drm_mode_fb_cmd2 *mode_cmd)
{
/* we don't do packed 24bpp */
if (sizes->surface_bpp == 24)
sizes->surface_bpp = 32;
mode_cmd->flags = DRM_MODE_FB_MODIFIERS;
mode_cmd->width = sizes->surface_width;
mode_cmd->height = sizes->surface_height;
mode_cmd->pitches[0] = intel_fbdev_fb_pitch_align(mode_cmd->width * DIV_ROUND_UP(sizes->surface_bpp, 8));
mode_cmd->pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
mode_cmd->modifier[0] = DRM_FORMAT_MOD_LINEAR;
}
static struct intel_framebuffer *
__intel_fbdev_fb_alloc(struct intel_display *display,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_mode_fb_cmd2 mode_cmd = {};
struct drm_framebuffer *fb;
struct drm_gem_object *obj;
int size;
intel_fbdev_fill_mode_cmd(sizes, &mode_cmd);
size = mode_cmd.pitches[0] * mode_cmd.height;
size = PAGE_ALIGN(size);
obj = intel_fbdev_fb_bo_create(display->drm, size);
if (IS_ERR(obj)) {
fb = ERR_CAST(obj);
goto err;
}
fb = intel_framebuffer_create(obj,
drm_get_format_info(display->drm,
mode_cmd.pixel_format,
mode_cmd.modifier[0]),
&mode_cmd);
if (IS_ERR(fb)) {
intel_fbdev_fb_bo_destroy(obj);
goto err;
}
drm_gem_object_put(obj);
return to_intel_framebuffer(fb);
err:
return ERR_CAST(fb);
}
int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes) struct drm_fb_helper_surface_size *sizes)
{ {
@ -235,7 +291,8 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
if (!fb || drm_WARN_ON(display->drm, !intel_fb_bo(&fb->base))) { if (!fb || drm_WARN_ON(display->drm, !intel_fb_bo(&fb->base))) {
drm_dbg_kms(display->drm, drm_dbg_kms(display->drm,
"no BIOS fb, allocating a new one\n"); "no BIOS fb, allocating a new one\n");
fb = intel_fbdev_fb_alloc(helper, sizes);
fb = __intel_fbdev_fb_alloc(display, sizes);
if (IS_ERR(fb)) if (IS_ERR(fb))
return PTR_ERR(fb); return PTR_ERR(fb);
} else { } else {
@ -275,7 +332,7 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
obj = intel_fb_bo(&fb->base); obj = intel_fb_bo(&fb->base);
ret = intel_fbdev_fb_fill_info(display, info, obj, vma); ret = intel_fbdev_fb_fill_info(display->drm, info, obj, vma);
if (ret) if (ret)
goto out_unpin; goto out_unpin;

View File

@ -3,40 +3,22 @@
* Copyright © 2023 Intel Corporation * Copyright © 2023 Intel Corporation
*/ */
#include <drm/drm_fb_helper.h> #include <linux/fb.h>
#include "gem/i915_gem_lmem.h" #include "gem/i915_gem_lmem.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "intel_display_core.h"
#include "intel_display_types.h"
#include "intel_fb.h"
#include "intel_fbdev_fb.h" #include "intel_fbdev_fb.h"
struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper, u32 intel_fbdev_fb_pitch_align(u32 stride)
struct drm_fb_helper_surface_size *sizes)
{ {
struct intel_display *display = to_intel_display(helper->dev); return ALIGN(stride, 64);
struct drm_i915_private *dev_priv = to_i915(display->drm); }
struct drm_framebuffer *fb;
struct drm_mode_fb_cmd2 mode_cmd = {}; struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size)
{
struct drm_i915_private *dev_priv = to_i915(drm);
struct drm_i915_gem_object *obj; struct drm_i915_gem_object *obj;
int size;
/* we don't do packed 24bpp */
if (sizes->surface_bpp == 24)
sizes->surface_bpp = 32;
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
DIV_ROUND_UP(sizes->surface_bpp, 8), 64);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
size = mode_cmd.pitches[0] * mode_cmd.height;
size = PAGE_ALIGN(size);
obj = ERR_PTR(-ENODEV); obj = ERR_PTR(-ENODEV);
if (HAS_LMEM(dev_priv)) { if (HAS_LMEM(dev_priv)) {
@ -51,31 +33,29 @@ struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
* *
* Also skip stolen on MTL as Wa_22018444074 mitigation. * Also skip stolen on MTL as Wa_22018444074 mitigation.
*/ */
if (!display->platform.meteorlake && size * 2 < dev_priv->dsm.usable_size) if (!IS_METEORLAKE(dev_priv) && size * 2 < dev_priv->dsm.usable_size)
obj = i915_gem_object_create_stolen(dev_priv, size); obj = i915_gem_object_create_stolen(dev_priv, size);
if (IS_ERR(obj)) if (IS_ERR(obj))
obj = i915_gem_object_create_shmem(dev_priv, size); obj = i915_gem_object_create_shmem(dev_priv, size);
} }
if (IS_ERR(obj)) { if (IS_ERR(obj)) {
drm_err(display->drm, "failed to allocate framebuffer (%pe)\n", obj); drm_err(drm, "failed to allocate framebuffer (%pe)\n", obj);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
fb = intel_framebuffer_create(intel_bo_to_drm_bo(obj), return &obj->base;
drm_get_format_info(display->drm,
mode_cmd.pixel_format,
mode_cmd.modifier[0]),
&mode_cmd);
i915_gem_object_put(obj);
return to_intel_framebuffer(fb);
} }
int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info, void intel_fbdev_fb_bo_destroy(struct drm_gem_object *obj)
{
drm_gem_object_put(obj);
}
int intel_fbdev_fb_fill_info(struct drm_device *drm, struct fb_info *info,
struct drm_gem_object *_obj, struct i915_vma *vma) struct drm_gem_object *_obj, struct i915_vma *vma)
{ {
struct drm_i915_private *i915 = to_i915(display->drm); struct drm_i915_private *i915 = to_i915(drm);
struct drm_i915_gem_object *obj = to_intel_bo(_obj); struct drm_i915_gem_object *obj = to_intel_bo(_obj);
struct i915_gem_ww_ctx ww; struct i915_gem_ww_ctx ww;
void __iomem *vaddr; void __iomem *vaddr;
@ -107,7 +87,7 @@ int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info
vaddr = i915_vma_pin_iomap(vma); vaddr = i915_vma_pin_iomap(vma);
if (IS_ERR(vaddr)) { if (IS_ERR(vaddr)) {
drm_err(display->drm, drm_err(drm,
"Failed to remap framebuffer into virtual memory (%pe)\n", vaddr); "Failed to remap framebuffer into virtual memory (%pe)\n", vaddr);
ret = PTR_ERR(vaddr); ret = PTR_ERR(vaddr);
continue; continue;

View File

@ -6,16 +6,18 @@
#ifndef __INTEL_FBDEV_FB_H__ #ifndef __INTEL_FBDEV_FB_H__
#define __INTEL_FBDEV_FB_H__ #define __INTEL_FBDEV_FB_H__
struct drm_fb_helper; #include <linux/types.h>
struct drm_fb_helper_surface_size;
struct drm_device;
struct drm_gem_object; struct drm_gem_object;
struct drm_mode_fb_cmd2;
struct fb_info; struct fb_info;
struct i915_vma; struct i915_vma;
struct intel_display;
struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper, u32 intel_fbdev_fb_pitch_align(u32 stride);
struct drm_fb_helper_surface_size *sizes); struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size);
int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info, void intel_fbdev_fb_bo_destroy(struct drm_gem_object *obj);
int intel_fbdev_fb_fill_info(struct drm_device *drm, struct fb_info *info,
struct drm_gem_object *obj, struct i915_vma *vma); struct drm_gem_object *obj, struct i915_vma *vma);
#endif #endif

View File

@ -9,13 +9,13 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_crtc.h" #include "intel_crtc.h"
#include "intel_ddi.h" #include "intel_ddi.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_fdi.h" #include "intel_fdi.h"
#include "intel_fdi_regs.h" #include "intel_fdi_regs.h"

View File

@ -7,16 +7,16 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_step.h"
#include "intel_crtc.h" #include "intel_crtc.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_flipq.h" #include "intel_display_utils.h"
#include "intel_dmc.h" #include "intel_dmc.h"
#include "intel_dmc_regs.h" #include "intel_dmc_regs.h"
#include "intel_dsb.h" #include "intel_dsb.h"
#include "intel_flipq.h"
#include "intel_step.h"
#include "intel_vblank.h" #include "intel_vblank.h"
#include "intel_vrr.h" #include "intel_vrr.h"

View File

@ -19,9 +19,9 @@
#include <drm/intel/i915_component.h> #include <drm/intel/i915_component.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_connector.h" #include "intel_connector.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_jiffies.h"
#include "intel_display_power.h" #include "intel_display_power.h"
#include "intel_display_power_well.h" #include "intel_display_power_well.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"

View File

@ -9,7 +9,6 @@
#include "gt/intel_gt.h" #include "gt/intel_gt.h"
#include "gt/uc/intel_gsc_uc_heci_cmd_submit.h" #include "gt/uc/intel_gsc_uc_heci_cmd_submit.h"
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_utils.h"
#include "intel_hdcp_gsc.h" #include "intel_hdcp_gsc.h"
struct intel_hdcp_gsc_context { struct intel_hdcp_gsc_context {

View File

@ -45,7 +45,6 @@
#include <media/cec-notifier.h> #include <media/cec-notifier.h>
#include "g4x_hdmi.h" #include "g4x_hdmi.h"
#include "i915_utils.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_audio.h" #include "intel_audio.h"
#include "intel_connector.h" #include "intel_connector.h"
@ -55,6 +54,7 @@
#include "intel_display_driver.h" #include "intel_display_driver.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_gmbus.h" #include "intel_gmbus.h"
#include "intel_hdcp.h" #include "intel_hdcp.h"
@ -68,6 +68,20 @@
#include "intel_snps_phy.h" #include "intel_snps_phy.h"
#include "intel_vrr.h" #include "intel_vrr.h"
bool intel_hdmi_is_frl(u32 clock)
{
switch (clock) {
case 300000: /* 3 Gbps */
case 600000: /* 6 Gbps */
case 800000: /* 8 Gbps */
case 1000000: /* 10 Gbps */
case 1200000: /* 12 Gbps */
return true;
default:
return false;
}
}
static void static void
assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi) assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi)
{ {

View File

@ -60,6 +60,7 @@ int intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state,
int src_max_slices, int src_max_slice_width, int src_max_slices, int src_max_slice_width,
int hdmi_max_slices, int hdmi_throughput); int hdmi_max_slices, int hdmi_throughput);
int intel_hdmi_dsc_get_slice_height(int vactive); int intel_hdmi_dsc_get_slice_height(int vactive);
bool intel_hdmi_is_frl(u32 clock);
void hsw_write_infoframe(struct intel_encoder *encoder, void hsw_write_infoframe(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state, const struct intel_crtc_state *crtc_state,

View File

@ -28,12 +28,12 @@
#include "i915_drv.h" #include "i915_drv.h"
#include "i915_irq.h" #include "i915_irq.h"
#include "i915_utils.h"
#include "intel_connector.h" #include "intel_connector.h"
#include "intel_display_power.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_power.h"
#include "intel_display_rpm.h" #include "intel_display_rpm.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_hdcp.h" #include "intel_hdcp.h"
#include "intel_hotplug.h" #include "intel_hotplug.h"

View File

@ -6,11 +6,11 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_irq.h" #include "intel_display_irq.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp_aux.h" #include "intel_dp_aux.h"
#include "intel_gmbus.h" #include "intel_gmbus.h"
#include "intel_hotplug.h" #include "intel_hotplug.h"
@ -420,6 +420,9 @@ u32 i9xx_hpd_irq_ack(struct intel_display *display)
u32 hotplug_status = 0, hotplug_status_mask; u32 hotplug_status = 0, hotplug_status_mask;
int i; int i;
if (!HAS_HOTPLUG(display))
return 0;
if (display->platform.g4x || if (display->platform.g4x ||
display->platform.valleyview || display->platform.cherryview) display->platform.valleyview || display->platform.cherryview)
hotplug_status_mask = HOTPLUG_INT_STATUS_G4X | hotplug_status_mask = HOTPLUG_INT_STATUS_G4X |

View File

@ -20,6 +20,7 @@
#include "intel_dp_tunnel.h" #include "intel_dp_tunnel.h"
#include "intel_fdi.h" #include "intel_fdi.h"
#include "intel_link_bw.h" #include "intel_link_bw.h"
#include "intel_vdsc.h"
static int get_forced_link_bpp_x16(struct intel_atomic_state *state, static int get_forced_link_bpp_x16(struct intel_atomic_state *state,
const struct intel_crtc *crtc) const struct intel_crtc *crtc)
@ -55,7 +56,7 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
struct intel_display *display = to_intel_display(state); struct intel_display *display = to_intel_display(state);
enum pipe pipe; enum pipe pipe;
limits->force_fec_pipes = 0; limits->link_dsc_pipes = 0;
limits->bpp_limit_reached_pipes = 0; limits->bpp_limit_reached_pipes = 0;
for_each_pipe(display, pipe) { for_each_pipe(display, pipe) {
struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe); struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe);
@ -65,8 +66,8 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
if (state->base.duplicated && crtc_state) { if (state->base.duplicated && crtc_state) {
limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16; limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16;
if (crtc_state->fec_enable) if (intel_dsc_enabled_on_link(crtc_state))
limits->force_fec_pipes |= BIT(pipe); limits->link_dsc_pipes |= BIT(pipe);
} else { } else {
limits->max_bpp_x16[pipe] = INT_MAX; limits->max_bpp_x16[pipe] = INT_MAX;
} }
@ -265,10 +266,10 @@ assert_link_limit_change_valid(struct intel_display *display,
bool bpps_changed = false; bool bpps_changed = false;
enum pipe pipe; enum pipe pipe;
/* FEC can't be forced off after it was forced on. */ /* DSC can't be disabled after it was enabled. */
if (drm_WARN_ON(display->drm, if (drm_WARN_ON(display->drm,
(old_limits->force_fec_pipes & new_limits->force_fec_pipes) != (old_limits->link_dsc_pipes & new_limits->link_dsc_pipes) !=
old_limits->force_fec_pipes)) old_limits->link_dsc_pipes))
return false; return false;
for_each_pipe(display, pipe) { for_each_pipe(display, pipe) {
@ -286,8 +287,8 @@ assert_link_limit_change_valid(struct intel_display *display,
/* At least one limit must change. */ /* At least one limit must change. */
if (drm_WARN_ON(display->drm, if (drm_WARN_ON(display->drm,
!bpps_changed && !bpps_changed &&
new_limits->force_fec_pipes == new_limits->link_dsc_pipes ==
old_limits->force_fec_pipes)) old_limits->link_dsc_pipes))
return false; return false;
return true; return true;

View File

@ -15,7 +15,7 @@ struct intel_connector;
struct intel_crtc_state; struct intel_crtc_state;
struct intel_link_bw_limits { struct intel_link_bw_limits {
u8 force_fec_pipes; u8 link_dsc_pipes;
u8 bpp_limit_reached_pipes; u8 bpp_limit_reached_pipes;
/* in 1/16 bpp units */ /* in 1/16 bpp units */
int max_bpp_x16[I915_MAX_PIPES]; int max_bpp_x16[I915_MAX_PIPES];

View File

@ -31,10 +31,10 @@
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_hdmi.h" #include "intel_hdmi.h"
#include "intel_lspcon.h" #include "intel_lspcon.h"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: MIT
*
* Copyright © 2025 Intel Corporation
*/
#ifndef __INTEL_LT_PHY_H__
#define __INTEL_LT_PHY_H__
#include <linux/types.h>
struct intel_atomic_state;
struct intel_display;
struct intel_encoder;
struct intel_crtc_state;
struct intel_crtc;
struct intel_lt_phy_pll_state;
void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_lt_phy_pll_disable(struct intel_encoder *encoder);
int
intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state,
struct intel_encoder *encoder);
int intel_lt_phy_calc_port_clock(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_lt_phy_dump_hw_state(struct intel_display *display,
const struct intel_lt_phy_pll_state *hw_state);
bool
intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a,
const struct intel_lt_phy_pll_state *b);
void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
struct intel_lt_phy_pll_state *pll_state);
void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state,
struct intel_crtc *crtc);
void intel_xe3plpd_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_xe3plpd_pll_disable(struct intel_encoder *encoder);
#define HAS_LT_PHY(display) (DISPLAY_VER(display) >= 35)
#endif /* __INTEL_LT_PHY_H__ */

View File

@ -0,0 +1,75 @@
/* SPDX-License-Identifier: MIT
*
* Copyright © 2025 Intel Corporation
*/
#ifndef __INTEL_LT_PHY_REGS_H__
#define __INTEL_LT_PHY_REGS_H__
#define XE3PLPD_MSGBUS_TIMEOUT_FAST_US 500
#define XE3PLPD_MACCLK_TURNON_LATENCY_MS 1
#define XE3PLPD_MACCLK_TURNON_LATENCY_US 21
#define XE3PLPD_MACCLK_TURNOFF_LATENCY_US 1
#define XE3PLPD_RATE_CALIB_DONE_LATENCY_US 50
#define XE3PLPD_RESET_START_LATENCY_US 10
#define XE3PLPD_PWRDN_TO_RDY_LATENCY_US 4
#define XE3PLPD_RESET_END_LATENCY_US 200
/* LT Phy MAC Register */
#define LT_PHY_MAC_VDR _MMIO(0xC00)
#define LT_PHY_PCLKIN_GATE REG_BIT8(0)
/* LT Phy Pipe Spec Registers */
#define LT_PHY_TXY_CTL8(idx) (0x408 + (0x200 * (idx)))
#define LT_PHY_TX_SWING_LEVEL_MASK REG_GENMASK8(7, 4)
#define LT_PHY_TX_SWING_LEVEL(val) REG_FIELD_PREP8(LT_PHY_TX_SWING_LEVEL_MASK, val)
#define LT_PHY_TX_SWING_MASK REG_BIT8(3)
#define LT_PHY_TX_SWING(val) REG_FIELD_PREP8(LT_PHY_TX_SWING_MASK, val)
#define LT_PHY_TXY_CTL2(idx) (0x402 + (0x200 * (idx)))
#define LT_PHY_TXY_CTL3(idx) (0x403 + (0x200 * (idx)))
#define LT_PHY_TXY_CTL4(idx) (0x404 + (0x200 * (idx)))
#define LT_PHY_TX_CURSOR_MASK REG_GENMASK8(5, 0)
#define LT_PHY_TX_CURSOR(val) REG_FIELD_PREP8(LT_PHY_TX_CURSOR_MASK, val)
#define LT_PHY_TXY_CTL10(idx) (0x40A + (0x200 * (idx)))
#define LT_PHY_TXY_CTL10_MAC(idx) _MMIO(LT_PHY_TXY_CTL10(idx))
#define LT_PHY_TX_LANE_ENABLE REG_BIT8(0)
/* LT Phy Vendor Register */
#define LT_PHY_VDR_0_CONFIG 0xC02
#define LT_PHY_VDR_DP_PLL_ENABLE REG_BIT(7)
#define LT_PHY_VDR_1_CONFIG 0xC03
#define LT_PHY_VDR_RATE_ENCODING_MASK REG_GENMASK8(6, 3)
#define LT_PHY_VDR_MODE_ENCODING_MASK REG_GENMASK8(2, 0)
#define LT_PHY_VDR_2_CONFIG 0xCC3
#define LT_PHY_VDR_X_ADDR_MSB(idx) (0xC04 + 0x6 * (idx))
#define LT_PHY_VDR_X_ADDR_LSB(idx) (0xC05 + 0x6 * (idx))
#define LT_PHY_VDR_X_DATAY(idx, y) ((0xC06 + (3 - (y))) + 0x6 * (idx))
#define LT_PHY_RATE_UPDATE 0xCC4
#define LT_PHY_RATE_CONTROL_VDR_UPDATE REG_BIT8(0)
#define _XE3PLPD_PORT_BUF_CTL5(idx) _MMIO(_PICK_EVEN_2RANGES(idx, PORT_TC1, \
_XELPDP_PORT_BUF_CTL1_LN0_A, \
_XELPDP_PORT_BUF_CTL1_LN0_B, \
_XELPDP_PORT_BUF_CTL1_LN0_USBC1, \
_XELPDP_PORT_BUF_CTL1_LN0_USBC2) \
+ 0x34)
#define XE3PLPD_PORT_BUF_CTL5(port) _XE3PLPD_PORT_BUF_CTL5(__xe2lpd_port_idx(port))
#define XE3PLPD_MACCLK_RESET_0 REG_BIT(11)
#define XE3PLPD_MACCLK_RATE_MASK REG_GENMASK(4, 0)
#define XE3PLPD_MACCLK_RATE_DEF REG_FIELD_PREP(XE3PLPD_MACCLK_RATE_MASK, 0x1F)
#define _XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(idx, lane) _MMIO(_PICK_EVEN_2RANGES(idx, PORT_TC1, \
_XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \
_XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \
_XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \
_XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2) \
+ 0x60 + (lane) * 0x4)
#define XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(port, lane) _XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(__xe2lpd_port_idx(port), \
lane)
#define XE3LPD_PORT_P2M_ADDR_MASK REG_GENMASK(11, 0)
#endif /* __INTEL_LT_PHY_REGS_H__ */

View File

@ -19,6 +19,7 @@
#include "intel_color.h" #include "intel_color.h"
#include "intel_crtc.h" #include "intel_crtc.h"
#include "intel_crtc_state_dump.h" #include "intel_crtc_state_dump.h"
#include "intel_dbuf_bw.h"
#include "intel_ddi.h" #include "intel_ddi.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display.h" #include "intel_display.h"
@ -176,6 +177,7 @@ static void intel_crtc_disable_noatomic_complete(struct intel_crtc *crtc)
intel_cdclk_crtc_disable_noatomic(crtc); intel_cdclk_crtc_disable_noatomic(crtc);
skl_wm_crtc_disable_noatomic(crtc); skl_wm_crtc_disable_noatomic(crtc);
intel_bw_crtc_disable_noatomic(crtc); intel_bw_crtc_disable_noatomic(crtc);
intel_dbuf_bw_crtc_disable_noatomic(crtc);
intel_pmdemand_update_port_clock(display, pmdemand_state, pipe, 0); intel_pmdemand_update_port_clock(display, pmdemand_state, pipe, 0);
} }
@ -851,18 +853,23 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
*/ */
if (plane_state->uapi.visible && plane->min_cdclk) { if (plane_state->uapi.visible && plane->min_cdclk) {
if (crtc_state->double_wide || DISPLAY_VER(display) >= 10) if (crtc_state->double_wide || DISPLAY_VER(display) >= 10)
crtc_state->min_cdclk[plane->id] = crtc_state->plane_min_cdclk[plane->id] =
DIV_ROUND_UP(crtc_state->pixel_rate, 2); DIV_ROUND_UP(crtc_state->pixel_rate, 2);
else else
crtc_state->min_cdclk[plane->id] = crtc_state->plane_min_cdclk[plane->id] =
crtc_state->pixel_rate; crtc_state->pixel_rate;
} }
drm_dbg_kms(display->drm, drm_dbg_kms(display->drm,
"[PLANE:%d:%s] min_cdclk %d kHz\n", "[PLANE:%d:%s] min_cdclk %d kHz\n",
plane->base.base.id, plane->base.name, plane->base.base.id, plane->base.name,
crtc_state->min_cdclk[plane->id]); crtc_state->plane_min_cdclk[plane->id]);
} }
crtc_state->min_cdclk = intel_crtc_min_cdclk(crtc_state);
drm_dbg_kms(display->drm, "[CRTC:%d:%s] min_cdclk %d kHz\n",
crtc->base.base.id, crtc->base.name, crtc_state->min_cdclk);
intel_pmdemand_update_port_clock(display, pmdemand_state, pipe, intel_pmdemand_update_port_clock(display, pmdemand_state, pipe,
crtc_state->port_clock); crtc_state->port_clock);
} }
@ -872,6 +879,7 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
intel_wm_get_hw_state(display); intel_wm_get_hw_state(display);
intel_bw_update_hw_state(display); intel_bw_update_hw_state(display);
intel_dbuf_bw_update_hw_state(display);
intel_cdclk_update_hw_state(display); intel_cdclk_update_hw_state(display);
intel_pmdemand_init_pmdemand_params(display, pmdemand_state); intel_pmdemand_init_pmdemand_params(display, pmdemand_state);

View File

@ -16,6 +16,7 @@
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_fdi.h" #include "intel_fdi.h"
#include "intel_lt_phy.h"
#include "intel_modeset_verify.h" #include "intel_modeset_verify.h"
#include "intel_snps_phy.h" #include "intel_snps_phy.h"
#include "skl_watermark.h" #include "skl_watermark.h"
@ -246,6 +247,7 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state,
intel_dpll_state_verify(state, crtc); intel_dpll_state_verify(state, crtc);
intel_mpllb_state_verify(state, crtc); intel_mpllb_state_verify(state, crtc);
intel_cx0pll_state_verify(state, crtc); intel_cx0pll_state_verify(state, crtc);
intel_lt_phy_pll_state_verify(state, crtc);
} }
void intel_modeset_verify_disabled(struct intel_atomic_state *state) void intel_modeset_verify_disabled(struct intel_atomic_state *state)

View File

@ -5,8 +5,8 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_utils.h"
#include "intel_pch.h" #include "intel_pch.h"
#define INTEL_PCH_DEVICE_ID_MASK 0xff80 #define INTEL_PCH_DEVICE_ID_MASK 0xff80
@ -328,7 +328,7 @@ void intel_pch_detect(struct intel_display *display)
"Display disabled, reverting to NOP PCH\n"); "Display disabled, reverting to NOP PCH\n");
display->pch_type = PCH_NOP; display->pch_type = PCH_NOP;
} else if (!pch) { } else if (!pch) {
if (i915_run_as_guest() && HAS_DISPLAY(display)) { if (intel_display_run_as_guest(display) && HAS_DISPLAY(display)) {
intel_virt_detect_pch(display, &id, &pch_type); intel_virt_detect_pch(display, &id, &pch_type);
display->pch_type = pch_type; display->pch_type = pch_type;
} else { } else {

View File

@ -6,10 +6,10 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_reg.h" #include "i915_reg.h"
#include "i915_utils.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_panel.h" #include "intel_panel.h"
#include "intel_pch_refclk.h" #include "intel_pch_refclk.h"
#include "intel_sbi.h" #include "intel_sbi.h"

View File

@ -5,12 +5,12 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_core.h" #include "intel_display_core.h"
#include "intel_display_driver.h" #include "intel_display_driver.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_lvds_regs.h" #include "intel_lvds_regs.h"
#include "intel_pfit.h" #include "intel_pfit.h"
#include "intel_pfit_regs.h" #include "intel_pfit_regs.h"

View File

@ -45,7 +45,6 @@
#include <drm/drm_panic.h> #include <drm/drm_panic.h>
#include "gem/i915_gem_object.h" #include "gem/i915_gem_object.h"
#include "i915_scheduler_types.h"
#include "i9xx_plane_regs.h" #include "i9xx_plane_regs.h"
#include "intel_cdclk.h" #include "intel_cdclk.h"
#include "intel_cursor.h" #include "intel_cursor.h"
@ -292,64 +291,21 @@ intel_plane_relative_data_rate(const struct intel_crtc_state *crtc_state,
rel_data_rate); rel_data_rate);
} }
int intel_plane_calc_min_cdclk(struct intel_atomic_state *state, static void intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
struct intel_plane *plane, struct intel_plane *plane)
bool *need_cdclk_calc)
{ {
struct intel_display *display = to_intel_display(plane);
const struct intel_plane_state *plane_state = const struct intel_plane_state *plane_state =
intel_atomic_get_new_plane_state(state, plane); intel_atomic_get_new_plane_state(state, plane);
struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc); struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc);
const struct intel_cdclk_state *cdclk_state;
const struct intel_crtc_state *old_crtc_state;
struct intel_crtc_state *new_crtc_state; struct intel_crtc_state *new_crtc_state;
if (!plane_state->uapi.visible || !plane->min_cdclk) if (!plane_state->uapi.visible || !plane->min_cdclk)
return 0; return;
old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc); new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
new_crtc_state->min_cdclk[plane->id] = new_crtc_state->plane_min_cdclk[plane->id] =
plane->min_cdclk(new_crtc_state, plane_state); plane->min_cdclk(new_crtc_state, plane_state);
/*
* No need to check against the cdclk state if
* the min cdclk for the plane doesn't increase.
*
* Ie. we only ever increase the cdclk due to plane
* requirements. This can reduce back and forth
* display blinking due to constant cdclk changes.
*/
if (new_crtc_state->min_cdclk[plane->id] <=
old_crtc_state->min_cdclk[plane->id])
return 0;
cdclk_state = intel_atomic_get_cdclk_state(state);
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
/*
* No need to recalculate the cdclk state if
* the min cdclk for the pipe doesn't increase.
*
* Ie. we only ever increase the cdclk due to plane
* requirements. This can reduce back and forth
* display blinking due to constant cdclk changes.
*/
if (new_crtc_state->min_cdclk[plane->id] <=
intel_cdclk_min_cdclk(cdclk_state, crtc->pipe))
return 0;
drm_dbg_kms(display->drm,
"[PLANE:%d:%s] min cdclk (%d kHz) > [CRTC:%d:%s] min cdclk (%d kHz)\n",
plane->base.base.id, plane->base.name,
new_crtc_state->min_cdclk[plane->id],
crtc->base.base.id, crtc->base.name,
intel_cdclk_min_cdclk(cdclk_state, crtc->pipe));
*need_cdclk_calc = true;
return 0;
} }
static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state) static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state)
@ -435,7 +391,7 @@ void intel_plane_set_invisible(struct intel_crtc_state *crtc_state,
crtc_state->data_rate_y[plane->id] = 0; crtc_state->data_rate_y[plane->id] = 0;
crtc_state->rel_data_rate[plane->id] = 0; crtc_state->rel_data_rate[plane->id] = 0;
crtc_state->rel_data_rate_y[plane->id] = 0; crtc_state->rel_data_rate_y[plane->id] = 0;
crtc_state->min_cdclk[plane->id] = 0; crtc_state->plane_min_cdclk[plane->id] = 0;
plane_state->uapi.visible = false; plane_state->uapi.visible = false;
} }
@ -1172,7 +1128,6 @@ static int
intel_prepare_plane_fb(struct drm_plane *_plane, intel_prepare_plane_fb(struct drm_plane *_plane,
struct drm_plane_state *_new_plane_state) struct drm_plane_state *_new_plane_state)
{ {
struct i915_sched_attr attr = { .priority = I915_PRIORITY_DISPLAY };
struct intel_plane *plane = to_intel_plane(_plane); struct intel_plane *plane = to_intel_plane(_plane);
struct intel_display *display = to_intel_display(plane); struct intel_display *display = to_intel_display(plane);
struct intel_plane_state *new_plane_state = struct intel_plane_state *new_plane_state =
@ -1221,8 +1176,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
goto unpin_fb; goto unpin_fb;
if (new_plane_state->uapi.fence) { if (new_plane_state->uapi.fence) {
i915_gem_fence_wait_priority(new_plane_state->uapi.fence, i915_gem_fence_wait_priority_display(new_plane_state->uapi.fence);
&attr);
intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc, intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc,
new_plane_state->uapi.fence); new_plane_state->uapi.fence);
@ -1746,5 +1700,8 @@ int intel_plane_atomic_check(struct intel_atomic_state *state)
return ret; return ret;
} }
for_each_new_intel_plane_in_state(state, plane, plane_state, i)
intel_plane_calc_min_cdclk(state, plane);
return 0; return 0;
} }

View File

@ -69,9 +69,6 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
struct intel_crtc_state *crtc_state, struct intel_crtc_state *crtc_state,
const struct intel_plane_state *old_plane_state, const struct intel_plane_state *old_plane_state,
struct intel_plane_state *intel_state); struct intel_plane_state *intel_state);
int intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
struct intel_plane *plane,
bool *need_cdclk_calc);
int intel_plane_check_clipping(struct intel_plane_state *plane_state, int intel_plane_check_clipping(struct intel_plane_state *plane_state,
struct intel_crtc_state *crtc_state, struct intel_crtc_state *crtc_state,
int min_scale, int max_scale, int min_scale, int max_scale,

View File

@ -7,13 +7,14 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "i915_utils.h"
#include "intel_atomic.h" #include "intel_atomic.h"
#include "intel_bw.h" #include "intel_bw.h"
#include "intel_cdclk.h" #include "intel_cdclk.h"
#include "intel_de.h" #include "intel_de.h"
#include "intel_display_jiffies.h"
#include "intel_display_regs.h" #include "intel_display_regs.h"
#include "intel_display_trace.h" #include "intel_display_trace.h"
#include "intel_display_utils.h"
#include "intel_pmdemand.h" #include "intel_pmdemand.h"
#include "intel_step.h" #include "intel_step.h"
#include "skl_watermark.h" #include "skl_watermark.h"

Some files were not shown because too many files have changed in this diff Show More