mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 11:33:28 +02:00
Cross-subsystem Changes:
- Overflow: add range_overflows and range_end_overflows (Jani) Core Changes: - Get rid of dev->struct_mutex (Luiz) Non-display related: - GVT: Remove redundant ternary operators (Liao) - Various i915_utils clean-ups (Jani) Display related: - Wait PSR idle before on dsb commit (Jouni) - Fix size for for_each_set_bit() in abox iteration (Jani) - Abstract figuring out encoder name (Jani) - Remove FBC modulo 4 restriction for ADL-P+ (Uma) - Panic: refactor framebuffer allocation (Jani) - Backlight luminance control improvements (Suraj, Aaron) - Add intel_display_device_present (Jani) -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEbSBwaO7dZQkcLOKj+mJfZA7rE8oFAmjEIKwACgkQ+mJfZA7r E8o0+wf+Ora59wlwzifwcTCGxz6AoCFqdWLrsG//WACiB80fAj2/adABZsDjAoT9 1BFFYUzqmmWXKfeRjKTiO3uMF42aKpp87oDW5BvVr4lxB50Qf8hZWxrFT1NfLb7e 7pGlJvJKPN8IaHtJ3ObgOL8uDZtynucQE8z+FuOxXWY+IQFFNtkUSWwEu+TQfgUM Osr004NtfjfJH1ZqvhzlQVyys1EnoldaqPtc0g8gqYKY9CyihUn29YnJ8cmBa4cn M1VD7+3H0Ekgd5khiwyYg9oIoMFlBFppDVsr8vdl0YAZNaE7gZPSk86iBw0KhEb/ Z49FlYwMTktY5d++qjBXjNACObePUg== =MUrL -----END PGP SIGNATURE----- Merge tag 'drm-intel-next-2025-09-12' of https://gitlab.freedesktop.org/drm/i915/kernel into drm-next Cross-subsystem Changes: - Overflow: add range_overflows and range_end_overflows (Jani) Core Changes: - Get rid of dev->struct_mutex (Luiz) Non-display related: - GVT: Remove redundant ternary operators (Liao) - Various i915_utils clean-ups (Jani) Display related: - Wait PSR idle before on dsb commit (Jouni) - Fix size for for_each_set_bit() in abox iteration (Jani) - Abstract figuring out encoder name (Jani) - Remove FBC modulo 4 restriction for ADL-P+ (Uma) - Panic: refactor framebuffer allocation (Jani) - Backlight luminance control improvements (Suraj, Aaron) - Add intel_display_device_present (Jani) Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rodrigo Vivi <rodrigo.vivi@intel.com> Link: https://lore.kernel.org/r/aMxX_lBxm7wd5wmi@intel.com
This commit is contained in:
commit
748f41f353
|
|
@ -358,8 +358,6 @@ Locking Guidelines
|
|||
#. All locking rules and interface contracts with cross-driver interfaces
|
||||
(dma-buf, dma_fence) need to be followed.
|
||||
|
||||
#. No struct_mutex anywhere in the code
|
||||
|
||||
#. dma_resv will be the outermost lock (when needed) and ww_acquire_ctx
|
||||
is to be hoisted at highest level and passed down within i915_gem_ctx
|
||||
in the call chain
|
||||
|
|
@ -367,11 +365,6 @@ Locking Guidelines
|
|||
#. While holding lru/memory manager (buddy, drm_mm, whatever) locks
|
||||
system memory allocations are not allowed
|
||||
|
||||
* Enforce this by priming lockdep (with fs_reclaim). If we
|
||||
allocate memory while holding these looks we get a rehash
|
||||
of the shrinker vs. struct_mutex saga, and that would be
|
||||
real bad.
|
||||
|
||||
#. Do not nest different lru/memory manager locks within each other.
|
||||
Take them in turn to update memory allocations, relying on the object’s
|
||||
dma_resv ww_mutex to serialize against other operations.
|
||||
|
|
|
|||
|
|
@ -173,31 +173,6 @@ Contact: Simona Vetter
|
|||
|
||||
Level: Intermediate
|
||||
|
||||
Get rid of dev->struct_mutex from GEM drivers
|
||||
---------------------------------------------
|
||||
|
||||
``dev->struct_mutex`` is the Big DRM Lock from legacy days and infested
|
||||
everything. Nowadays in modern drivers the only bit where it's mandatory is
|
||||
serializing GEM buffer object destruction. Which unfortunately means drivers
|
||||
have to keep track of that lock and either call ``unreference`` or
|
||||
``unreference_locked`` depending upon context.
|
||||
|
||||
Core GEM doesn't have a need for ``struct_mutex`` any more since kernel 4.8,
|
||||
and there's a GEM object ``free`` callback for any drivers which are
|
||||
entirely ``struct_mutex`` free.
|
||||
|
||||
For drivers that need ``struct_mutex`` it should be replaced with a driver-
|
||||
private lock. The tricky part is the BO free functions, since those can't
|
||||
reliably take that lock any more. Instead state needs to be protected with
|
||||
suitable subordinate locks or some cleanup work pushed to a worker thread. For
|
||||
performance-critical drivers it might also be better to go with a more
|
||||
fine-grained per-buffer object and per-context lockings scheme. Currently only
|
||||
the ``msm`` and `i915` drivers use ``struct_mutex``.
|
||||
|
||||
Contact: Simona Vetter, respective driver maintainers
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Move Buffer Object Locking to dma_resv_lock()
|
||||
---------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -696,7 +696,6 @@ static void drm_dev_init_release(struct drm_device *dev, void *res)
|
|||
mutex_destroy(&dev->master_mutex);
|
||||
mutex_destroy(&dev->clientlist_mutex);
|
||||
mutex_destroy(&dev->filelist_mutex);
|
||||
mutex_destroy(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
static int drm_dev_init(struct drm_device *dev,
|
||||
|
|
@ -737,7 +736,6 @@ static int drm_dev_init(struct drm_device *dev,
|
|||
INIT_LIST_HEAD(&dev->vblank_event_list);
|
||||
|
||||
spin_lock_init(&dev->event_lock);
|
||||
mutex_init(&dev->struct_mutex);
|
||||
mutex_init(&dev->filelist_mutex);
|
||||
mutex_init(&dev->clientlist_mutex);
|
||||
mutex_init(&dev->master_mutex);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ i915-y += \
|
|||
i915_scatterlist.o \
|
||||
i915_switcheroo.o \
|
||||
i915_sysfs.o \
|
||||
i915_timer_util.o \
|
||||
i915_utils.o \
|
||||
intel_clock_gating.o \
|
||||
intel_cpu_info.o \
|
||||
|
|
@ -280,6 +281,7 @@ i915-y += \
|
|||
display/intel_modeset_setup.o \
|
||||
display/intel_modeset_verify.o \
|
||||
display/intel_overlay.o \
|
||||
display/intel_panic.o \
|
||||
display/intel_pch.o \
|
||||
display/intel_pch_display.o \
|
||||
display/intel_pch_refclk.o \
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#include "i9xx_plane.h"
|
||||
#include "i9xx_plane_regs.h"
|
||||
#include "intel_atomic.h"
|
||||
#include "intel_bo.h"
|
||||
#include "intel_de.h"
|
||||
#include "intel_display_irq.h"
|
||||
#include "intel_display_regs.h"
|
||||
|
|
@ -23,6 +22,7 @@
|
|||
#include "intel_fb.h"
|
||||
#include "intel_fbc.h"
|
||||
#include "intel_frontbuffer.h"
|
||||
#include "intel_panic.h"
|
||||
#include "intel_plane.h"
|
||||
#include "intel_sprite.h"
|
||||
|
||||
|
|
@ -1178,7 +1178,7 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc,
|
|||
|
||||
drm_WARN_ON(display->drm, pipe != crtc->pipe);
|
||||
|
||||
intel_fb = intel_bo_alloc_framebuffer();
|
||||
intel_fb = intel_framebuffer_alloc();
|
||||
if (!intel_fb) {
|
||||
drm_dbg_kms(display->drm, "failed to alloc fb\n");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -59,18 +59,3 @@ void intel_bo_describe(struct seq_file *m, struct drm_gem_object *obj)
|
|||
{
|
||||
i915_debugfs_describe_obj(m, to_intel_bo(obj));
|
||||
}
|
||||
|
||||
struct intel_framebuffer *intel_bo_alloc_framebuffer(void)
|
||||
{
|
||||
return i915_gem_object_alloc_framebuffer();
|
||||
}
|
||||
|
||||
int intel_bo_panic_setup(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
return i915_gem_object_panic_setup(sb);
|
||||
}
|
||||
|
||||
void intel_bo_panic_finish(struct intel_framebuffer *fb)
|
||||
{
|
||||
return i915_gem_object_panic_finish(fb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,5 @@ struct intel_frontbuffer *intel_bo_set_frontbuffer(struct drm_gem_object *obj,
|
|||
struct intel_frontbuffer *front);
|
||||
|
||||
void intel_bo_describe(struct seq_file *m, struct drm_gem_object *obj);
|
||||
struct intel_framebuffer *intel_bo_alloc_framebuffer(void);
|
||||
int intel_bo_panic_setup(struct drm_scanout_buffer *sb);
|
||||
void intel_bo_panic_finish(struct intel_framebuffer *fb);
|
||||
|
||||
#endif /* __INTEL_BO__ */
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include <drm/display/drm_dp_helper.h>
|
||||
|
|
@ -5067,11 +5068,45 @@ static bool port_in_use(struct intel_display *display, enum port port)
|
|||
return false;
|
||||
}
|
||||
|
||||
static const char *intel_ddi_encoder_name(struct intel_display *display,
|
||||
enum port port, enum phy phy,
|
||||
struct seq_buf *s)
|
||||
{
|
||||
if (DISPLAY_VER(display) >= 13 && port >= PORT_D_XELPD) {
|
||||
seq_buf_printf(s, "DDI %c/PHY %c",
|
||||
port_name(port - PORT_D_XELPD + PORT_D),
|
||||
phy_name(phy));
|
||||
} else if (DISPLAY_VER(display) >= 12) {
|
||||
enum tc_port tc_port = intel_port_to_tc(display, port);
|
||||
|
||||
seq_buf_printf(s, "DDI %s%c/PHY %s%c",
|
||||
port >= PORT_TC1 ? "TC" : "",
|
||||
port >= PORT_TC1 ? port_tc_name(port) : port_name(port),
|
||||
tc_port != TC_PORT_NONE ? "TC" : "",
|
||||
tc_port != TC_PORT_NONE ? tc_port_name(tc_port) : phy_name(phy));
|
||||
} else if (DISPLAY_VER(display) >= 11) {
|
||||
enum tc_port tc_port = intel_port_to_tc(display, port);
|
||||
|
||||
seq_buf_printf(s, "DDI %c%s/PHY %s%c",
|
||||
port_name(port),
|
||||
port >= PORT_C ? " (TC)" : "",
|
||||
tc_port != TC_PORT_NONE ? "TC" : "",
|
||||
tc_port != TC_PORT_NONE ? tc_port_name(tc_port) : phy_name(phy));
|
||||
} else {
|
||||
seq_buf_printf(s, "DDI %c/PHY %c", port_name(port), phy_name(phy));
|
||||
}
|
||||
|
||||
drm_WARN_ON(display->drm, seq_buf_has_overflowed(s));
|
||||
|
||||
return seq_buf_str(s);
|
||||
}
|
||||
|
||||
void intel_ddi_init(struct intel_display *display,
|
||||
const struct intel_bios_encoder_data *devdata)
|
||||
{
|
||||
struct intel_digital_port *dig_port;
|
||||
struct intel_encoder *encoder;
|
||||
DECLARE_SEQ_BUF(encoder_name, 20);
|
||||
bool init_hdmi, init_dp;
|
||||
enum port port;
|
||||
enum phy phy;
|
||||
|
|
@ -5156,37 +5191,9 @@ void intel_ddi_init(struct intel_display *display,
|
|||
encoder = &dig_port->base;
|
||||
encoder->devdata = devdata;
|
||||
|
||||
if (DISPLAY_VER(display) >= 13 && port >= PORT_D_XELPD) {
|
||||
drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
|
||||
DRM_MODE_ENCODER_TMDS,
|
||||
"DDI %c/PHY %c",
|
||||
port_name(port - PORT_D_XELPD + PORT_D),
|
||||
phy_name(phy));
|
||||
} else if (DISPLAY_VER(display) >= 12) {
|
||||
enum tc_port tc_port = intel_port_to_tc(display, port);
|
||||
|
||||
drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
|
||||
DRM_MODE_ENCODER_TMDS,
|
||||
"DDI %s%c/PHY %s%c",
|
||||
port >= PORT_TC1 ? "TC" : "",
|
||||
port >= PORT_TC1 ? port_tc_name(port) : port_name(port),
|
||||
tc_port != TC_PORT_NONE ? "TC" : "",
|
||||
tc_port != TC_PORT_NONE ? tc_port_name(tc_port) : phy_name(phy));
|
||||
} else if (DISPLAY_VER(display) >= 11) {
|
||||
enum tc_port tc_port = intel_port_to_tc(display, port);
|
||||
|
||||
drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
|
||||
DRM_MODE_ENCODER_TMDS,
|
||||
"DDI %c%s/PHY %s%c",
|
||||
port_name(port),
|
||||
port >= PORT_C ? " (TC)" : "",
|
||||
tc_port != TC_PORT_NONE ? "TC" : "",
|
||||
tc_port != TC_PORT_NONE ? tc_port_name(tc_port) : phy_name(phy));
|
||||
} else {
|
||||
drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
|
||||
DRM_MODE_ENCODER_TMDS,
|
||||
"DDI %c/PHY %c", port_name(port), phy_name(phy));
|
||||
}
|
||||
drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, "%s",
|
||||
intel_ddi_encoder_name(display, port, phy, &encoder_name));
|
||||
|
||||
intel_encoder_link_check_init(encoder, intel_ddi_link_check);
|
||||
|
||||
|
|
|
|||
|
|
@ -7271,6 +7271,9 @@ static void intel_atomic_dsb_finish(struct intel_atomic_state *state,
|
|||
intel_psr_trigger_frame_change_event(new_crtc_state->dsb_commit,
|
||||
state, crtc);
|
||||
|
||||
intel_psr_wait_for_idle_dsb(new_crtc_state->dsb_commit,
|
||||
new_crtc_state);
|
||||
|
||||
if (new_crtc_state->use_dsb)
|
||||
intel_dsb_vblank_evade(state, new_crtc_state->dsb_commit);
|
||||
|
||||
|
|
|
|||
|
|
@ -1944,6 +1944,11 @@ void intel_display_device_info_print(const struct intel_display_device_info *inf
|
|||
drm_printf(p, "rawclk rate: %u kHz\n", runtime->rawclk_freq);
|
||||
}
|
||||
|
||||
bool intel_display_device_present(struct intel_display *display)
|
||||
{
|
||||
return display && HAS_DISPLAY(display);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assuming the device has display hardware, should it be enabled?
|
||||
*
|
||||
|
|
|
|||
|
|
@ -306,6 +306,7 @@ struct intel_display_device_info {
|
|||
} color;
|
||||
};
|
||||
|
||||
bool intel_display_device_present(struct intel_display *display);
|
||||
bool intel_display_device_enabled(struct intel_display *display);
|
||||
struct intel_display *intel_display_device_probe(struct pci_dev *pdev);
|
||||
void intel_display_device_remove(struct intel_display *display);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ struct intel_ddi_buf_trans;
|
|||
struct intel_fbc;
|
||||
struct intel_global_objs_state;
|
||||
struct intel_hdcp_shim;
|
||||
struct intel_panic;
|
||||
struct intel_tc_port;
|
||||
|
||||
/*
|
||||
|
|
@ -149,6 +150,7 @@ struct intel_framebuffer {
|
|||
unsigned int vtd_guard;
|
||||
|
||||
unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width);
|
||||
struct intel_panic *panic;
|
||||
};
|
||||
|
||||
enum intel_hotplug_state {
|
||||
|
|
|
|||
|
|
@ -508,9 +508,6 @@ static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state
|
|||
struct intel_panel *panel = &connector->panel;
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
|
||||
|
||||
if (panel->backlight.edp.vesa.luminance_control_support)
|
||||
return;
|
||||
|
||||
drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info);
|
||||
|
||||
if (!panel->backlight.edp.vesa.info.aux_enable)
|
||||
|
|
@ -533,7 +530,7 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
|
|||
luminance_range->max_luminance,
|
||||
panel->vbt.backlight.pwm_freq_hz,
|
||||
intel_dp->edp_dpcd, ¤t_level, ¤t_mode,
|
||||
false);
|
||||
panel->backlight.edp.vesa.luminance_control_support);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "intel_fb.h"
|
||||
#include "intel_fb_bo.h"
|
||||
#include "intel_frontbuffer.h"
|
||||
#include "intel_panic.h"
|
||||
#include "intel_plane.h"
|
||||
|
||||
#define check_array_bounds(display, a, i) drm_WARN_ON((display)->drm, (i) >= ARRAY_SIZE(a))
|
||||
|
|
@ -2343,6 +2344,26 @@ intel_user_framebuffer_create(struct drm_device *dev,
|
|||
return fb;
|
||||
}
|
||||
|
||||
struct intel_framebuffer *intel_framebuffer_alloc(void)
|
||||
{
|
||||
struct intel_framebuffer *intel_fb;
|
||||
struct intel_panic *panic;
|
||||
|
||||
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
|
||||
if (!intel_fb)
|
||||
return NULL;
|
||||
|
||||
panic = intel_panic_alloc();
|
||||
if (!panic) {
|
||||
kfree(intel_fb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
intel_fb->panic = panic;
|
||||
|
||||
return intel_fb;
|
||||
}
|
||||
|
||||
struct drm_framebuffer *
|
||||
intel_framebuffer_create(struct drm_gem_object *obj,
|
||||
const struct drm_format_info *info,
|
||||
|
|
@ -2351,7 +2372,7 @@ intel_framebuffer_create(struct drm_gem_object *obj,
|
|||
struct intel_framebuffer *intel_fb;
|
||||
int ret;
|
||||
|
||||
intel_fb = intel_bo_alloc_framebuffer();
|
||||
intel_fb = intel_framebuffer_alloc();
|
||||
if (!intel_fb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ int intel_framebuffer_init(struct intel_framebuffer *ifb,
|
|||
struct drm_gem_object *obj,
|
||||
const struct drm_format_info *info,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd);
|
||||
|
||||
struct intel_framebuffer *intel_framebuffer_alloc(void);
|
||||
|
||||
struct drm_framebuffer *
|
||||
intel_framebuffer_create(struct drm_gem_object *obj,
|
||||
const struct drm_format_info *info,
|
||||
|
|
|
|||
|
|
@ -98,11 +98,7 @@ struct intel_fbc {
|
|||
struct intel_display *display;
|
||||
const struct intel_fbc_funcs *funcs;
|
||||
|
||||
/*
|
||||
* This is always the inner lock when overlapping with
|
||||
* struct_mutex and it's the outer lock when overlapping
|
||||
* with stolen_lock.
|
||||
*/
|
||||
/* This is always the outer lock when overlapping with stolen_lock */
|
||||
struct mutex lock;
|
||||
unsigned int busy_bits;
|
||||
|
||||
|
|
@ -383,11 +379,11 @@ static void i8xx_fbc_program_cfb(struct intel_fbc *fbc)
|
|||
struct drm_i915_private *i915 = to_i915(display->drm);
|
||||
|
||||
drm_WARN_ON(display->drm,
|
||||
range_overflows_end_t(u64, i915_gem_stolen_area_address(i915),
|
||||
range_end_overflows_t(u64, i915_gem_stolen_area_address(i915),
|
||||
i915_gem_stolen_node_offset(&fbc->compressed_fb),
|
||||
U32_MAX));
|
||||
drm_WARN_ON(display->drm,
|
||||
range_overflows_end_t(u64, i915_gem_stolen_area_address(i915),
|
||||
range_end_overflows_t(u64, i915_gem_stolen_area_address(i915),
|
||||
i915_gem_stolen_node_offset(&fbc->compressed_llb),
|
||||
U32_MAX));
|
||||
intel_de_write(display, FBC_CFB_BASE,
|
||||
|
|
@ -1550,14 +1546,14 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
|
|||
* having a Y offset that isn't divisible by 4 causes FIFO underrun
|
||||
* and screen flicker.
|
||||
*/
|
||||
if (DISPLAY_VER(display) >= 9 &&
|
||||
if (IS_DISPLAY_VER(display, 9, 12) &&
|
||||
plane_state->view.color_plane[0].y & 3) {
|
||||
plane_state->no_fbc_reason = "plane start Y offset misaligned";
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wa_22010751166: icl, ehl, tgl, dg1, rkl */
|
||||
if (DISPLAY_VER(display) >= 11 &&
|
||||
if (IS_DISPLAY_VER(display, 9, 12) &&
|
||||
(plane_state->view.color_plane[0].y +
|
||||
(drm_rect_height(&plane_state->uapi.src) >> 16)) & 3) {
|
||||
plane_state->no_fbc_reason = "plane end Y offset misaligned";
|
||||
|
|
|
|||
27
drivers/gpu/drm/i915/display/intel_panic.c
Normal file
27
drivers/gpu/drm/i915/display/intel_panic.c
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#include <drm/drm_panic.h>
|
||||
|
||||
#include "gem/i915_gem_object.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_fb.h"
|
||||
#include "intel_panic.h"
|
||||
|
||||
struct intel_panic *intel_panic_alloc(void)
|
||||
{
|
||||
return i915_gem_object_alloc_panic();
|
||||
}
|
||||
|
||||
int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
|
||||
{
|
||||
struct intel_framebuffer *fb = sb->private;
|
||||
struct drm_gem_object *obj = intel_fb_bo(&fb->base);
|
||||
|
||||
return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling);
|
||||
}
|
||||
|
||||
void intel_panic_finish(struct intel_panic *panic)
|
||||
{
|
||||
return i915_gem_object_panic_finish(panic);
|
||||
}
|
||||
14
drivers/gpu/drm/i915/display/intel_panic.h
Normal file
14
drivers/gpu/drm/i915/display/intel_panic.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#ifndef __INTEL_PANIC_H__
|
||||
#define __INTEL_PANIC_H__
|
||||
|
||||
struct drm_scanout_buffer;
|
||||
struct intel_panic;
|
||||
|
||||
struct intel_panic *intel_panic_alloc(void);
|
||||
int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb);
|
||||
void intel_panic_finish(struct intel_panic *panic);
|
||||
|
||||
#endif /* __INTEL_PANIC_H__ */
|
||||
|
|
@ -47,7 +47,6 @@
|
|||
#include "gem/i915_gem_object.h"
|
||||
#include "i915_scheduler_types.h"
|
||||
#include "i9xx_plane_regs.h"
|
||||
#include "intel_bo.h"
|
||||
#include "intel_cdclk.h"
|
||||
#include "intel_cursor.h"
|
||||
#include "intel_display_rps.h"
|
||||
|
|
@ -56,6 +55,7 @@
|
|||
#include "intel_fb.h"
|
||||
#include "intel_fb_pin.h"
|
||||
#include "intel_fbdev.h"
|
||||
#include "intel_panic.h"
|
||||
#include "intel_plane.h"
|
||||
#include "intel_psr.h"
|
||||
#include "skl_scaler.h"
|
||||
|
|
@ -1326,7 +1326,7 @@ static void intel_panic_flush(struct drm_plane *plane)
|
|||
struct drm_framebuffer *fb = plane_state->hw.fb;
|
||||
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
|
||||
|
||||
intel_bo_panic_finish(intel_fb);
|
||||
intel_panic_finish(intel_fb->panic);
|
||||
|
||||
if (crtc_state->enable_psr2_sel_fetch) {
|
||||
/* Force a full update for psr2 */
|
||||
|
|
@ -1409,7 +1409,7 @@ static int intel_get_scanout_buffer(struct drm_plane *plane,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
sb->private = intel_fb;
|
||||
ret = intel_bo_panic_setup(sb);
|
||||
ret = intel_panic_setup(intel_fb->panic, sb);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "intel_dmc.h"
|
||||
#include "intel_dp.h"
|
||||
#include "intel_dp_aux.h"
|
||||
#include "intel_dsb.h"
|
||||
#include "intel_frontbuffer.h"
|
||||
#include "intel_hdmi.h"
|
||||
#include "intel_psr.h"
|
||||
|
|
@ -494,12 +495,14 @@ static u8 intel_dp_get_su_capability(struct intel_dp *intel_dp)
|
|||
{
|
||||
u8 su_capability = 0;
|
||||
|
||||
if (intel_dp->psr.sink_panel_replay_su_support)
|
||||
drm_dp_dpcd_readb(&intel_dp->aux,
|
||||
DP_PANEL_REPLAY_CAP_CAPABILITY,
|
||||
&su_capability);
|
||||
else
|
||||
if (intel_dp->psr.sink_panel_replay_su_support) {
|
||||
if (drm_dp_dpcd_read_byte(&intel_dp->aux,
|
||||
DP_PANEL_REPLAY_CAP_CAPABILITY,
|
||||
&su_capability) < 0)
|
||||
return 0;
|
||||
} else {
|
||||
su_capability = intel_dp->psr_dpcd[1];
|
||||
}
|
||||
|
||||
return su_capability;
|
||||
}
|
||||
|
|
@ -2997,35 +3000,57 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
|
|||
}
|
||||
}
|
||||
|
||||
static int _psr2_ready_for_pipe_update_locked(struct intel_dp *intel_dp)
|
||||
/*
|
||||
* From bspec: Panel Self Refresh (BDW+)
|
||||
* Max. time for PSR to idle = Inverse of the refresh rate + 6 ms of
|
||||
* exit training time + 1.5 ms of aux channel handshake. 50 ms is
|
||||
* defensive enough to cover everything.
|
||||
*/
|
||||
#define PSR_IDLE_TIMEOUT_MS 50
|
||||
|
||||
static int
|
||||
_psr2_ready_for_pipe_update_locked(const struct intel_crtc_state *new_crtc_state,
|
||||
struct intel_dsb *dsb)
|
||||
{
|
||||
struct intel_display *display = to_intel_display(intel_dp);
|
||||
enum transcoder cpu_transcoder = intel_dp->psr.transcoder;
|
||||
struct intel_display *display = to_intel_display(new_crtc_state);
|
||||
enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder;
|
||||
|
||||
/*
|
||||
* Any state lower than EDP_PSR2_STATUS_STATE_DEEP_SLEEP is enough.
|
||||
* As all higher states has bit 4 of PSR2 state set we can just wait for
|
||||
* EDP_PSR2_STATUS_STATE_DEEP_SLEEP to be cleared.
|
||||
*/
|
||||
if (dsb) {
|
||||
intel_dsb_poll(dsb, EDP_PSR2_STATUS(display, cpu_transcoder),
|
||||
EDP_PSR2_STATUS_STATE_DEEP_SLEEP, 0, 200,
|
||||
PSR_IDLE_TIMEOUT_MS * 1000 / 200);
|
||||
return true;
|
||||
}
|
||||
|
||||
return intel_de_wait_for_clear(display,
|
||||
EDP_PSR2_STATUS(display, cpu_transcoder),
|
||||
EDP_PSR2_STATUS_STATE_DEEP_SLEEP, 50);
|
||||
EDP_PSR2_STATUS_STATE_DEEP_SLEEP,
|
||||
PSR_IDLE_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static int _psr1_ready_for_pipe_update_locked(struct intel_dp *intel_dp)
|
||||
static int
|
||||
_psr1_ready_for_pipe_update_locked(const struct intel_crtc_state *new_crtc_state,
|
||||
struct intel_dsb *dsb)
|
||||
{
|
||||
struct intel_display *display = to_intel_display(intel_dp);
|
||||
enum transcoder cpu_transcoder = intel_dp->psr.transcoder;
|
||||
struct intel_display *display = to_intel_display(new_crtc_state);
|
||||
enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder;
|
||||
|
||||
if (dsb) {
|
||||
intel_dsb_poll(dsb, psr_status_reg(display, cpu_transcoder),
|
||||
EDP_PSR_STATUS_STATE_MASK, 0, 200,
|
||||
PSR_IDLE_TIMEOUT_MS * 1000 / 200);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* From bspec: Panel Self Refresh (BDW+)
|
||||
* Max. time for PSR to idle = Inverse of the refresh rate + 6 ms of
|
||||
* exit training time + 1.5 ms of aux channel handshake. 50 ms is
|
||||
* defensive enough to cover everything.
|
||||
*/
|
||||
return intel_de_wait_for_clear(display,
|
||||
psr_status_reg(display, cpu_transcoder),
|
||||
EDP_PSR_STATUS_STATE_MASK, 50);
|
||||
EDP_PSR_STATUS_STATE_MASK,
|
||||
PSR_IDLE_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3054,9 +3079,11 @@ void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_stat
|
|||
continue;
|
||||
|
||||
if (intel_dp->psr.sel_update_enabled)
|
||||
ret = _psr2_ready_for_pipe_update_locked(intel_dp);
|
||||
ret = _psr2_ready_for_pipe_update_locked(new_crtc_state,
|
||||
NULL);
|
||||
else
|
||||
ret = _psr1_ready_for_pipe_update_locked(intel_dp);
|
||||
ret = _psr1_ready_for_pipe_update_locked(new_crtc_state,
|
||||
NULL);
|
||||
|
||||
if (ret)
|
||||
drm_err(display->drm,
|
||||
|
|
@ -3064,6 +3091,18 @@ void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_stat
|
|||
}
|
||||
}
|
||||
|
||||
void intel_psr_wait_for_idle_dsb(struct intel_dsb *dsb,
|
||||
const struct intel_crtc_state *new_crtc_state)
|
||||
{
|
||||
if (!new_crtc_state->has_psr || new_crtc_state->has_panel_replay)
|
||||
return;
|
||||
|
||||
if (new_crtc_state->has_sel_update)
|
||||
_psr2_ready_for_pipe_update_locked(new_crtc_state, dsb);
|
||||
else
|
||||
_psr1_ready_for_pipe_update_locked(new_crtc_state, dsb);
|
||||
}
|
||||
|
||||
static bool __psr_wait_for_idle_locked(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct intel_display *display = to_intel_display(intel_dp);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ void intel_psr_get_config(struct intel_encoder *encoder,
|
|||
void intel_psr_irq_handler(struct intel_dp *intel_dp, u32 psr_iir);
|
||||
void intel_psr_short_pulse(struct intel_dp *intel_dp);
|
||||
void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_state);
|
||||
void intel_psr_wait_for_idle_dsb(struct intel_dsb *dsb,
|
||||
const struct intel_crtc_state *new_crtc_state);
|
||||
bool intel_psr_enabled(struct intel_dp *intel_dp);
|
||||
int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
|
||||
struct intel_crtc *crtc);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "intel_fb.h"
|
||||
#include "intel_fbc.h"
|
||||
#include "intel_frontbuffer.h"
|
||||
#include "intel_panic.h"
|
||||
#include "intel_plane.h"
|
||||
#include "intel_psr.h"
|
||||
#include "intel_psr_regs.h"
|
||||
|
|
@ -3028,7 +3029,7 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
|
|||
return;
|
||||
}
|
||||
|
||||
intel_fb = intel_bo_alloc_framebuffer();
|
||||
intel_fb = intel_framebuffer_alloc();
|
||||
if (!intel_fb) {
|
||||
drm_dbg_kms(display->drm, "failed to alloc fb\n");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ enum {
|
|||
* the object. Simple! ... The relocation entries are stored in user memory
|
||||
* and so to access them we have to copy them into a local buffer. That copy
|
||||
* has to avoid taking any pagefaults as they may lead back to a GEM object
|
||||
* requiring the struct_mutex (i.e. recursive deadlock). So once again we split
|
||||
* requiring the vm->mutex (i.e. recursive deadlock). So once again we split
|
||||
* the relocation into multiple passes. First we try to do everything within an
|
||||
* atomic context (avoid the pagefaults) which requires that we never wait. If
|
||||
* we detect that we may wait, or if we need to fault, then we have to fallback
|
||||
|
|
|
|||
|
|
@ -459,8 +459,8 @@ static void i915_gem_free_object(struct drm_gem_object *gem_obj)
|
|||
atomic_inc(&i915->mm.free_count);
|
||||
|
||||
/*
|
||||
* Since we require blocking on struct_mutex to unbind the freed
|
||||
* object from the GPU before releasing resources back to the
|
||||
* Since we require blocking on drm_i915_gem_object->vma.lock to unbind
|
||||
* the freed object from the GPU before releasing resources back to the
|
||||
* system, we can not do that directly from the RCU callback (which may
|
||||
* be a softirq context), but must instead then defer that work onto a
|
||||
* kthread. We use the RCU callback rather than move the freed object
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
#include "i915_gem_ww.h"
|
||||
#include "i915_vma_types.h"
|
||||
|
||||
struct drm_scanout_buffer;
|
||||
enum intel_region_id;
|
||||
struct intel_framebuffer;
|
||||
struct drm_scanout_buffer;
|
||||
struct intel_panic;
|
||||
|
||||
#define obj_to_i915(obj__) to_i915((obj__)->base.dev)
|
||||
|
||||
|
|
@ -693,9 +693,10 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
|
|||
int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
|
||||
int i915_gem_object_truncate(struct drm_i915_gem_object *obj);
|
||||
|
||||
struct intel_framebuffer *i915_gem_object_alloc_framebuffer(void);
|
||||
int i915_gem_object_panic_setup(struct drm_scanout_buffer *sb);
|
||||
void i915_gem_object_panic_finish(struct intel_framebuffer *fb);
|
||||
struct intel_panic *i915_gem_object_alloc_panic(void);
|
||||
int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
|
||||
struct drm_gem_object *_obj, bool panic_tiling);
|
||||
void i915_gem_object_panic_finish(struct intel_panic *panic);
|
||||
|
||||
/**
|
||||
* i915_gem_object_pin_map - return a contiguous mapping of the entire object
|
||||
|
|
|
|||
|
|
@ -357,23 +357,13 @@ static void *i915_gem_object_map_pfn(struct drm_i915_gem_object *obj,
|
|||
return vaddr ?: ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
struct i915_panic_data {
|
||||
struct intel_panic {
|
||||
struct page **pages;
|
||||
int page;
|
||||
void *vaddr;
|
||||
};
|
||||
|
||||
struct i915_framebuffer {
|
||||
struct intel_framebuffer base;
|
||||
struct i915_panic_data panic;
|
||||
};
|
||||
|
||||
static inline struct i915_panic_data *to_i915_panic_data(struct intel_framebuffer *fb)
|
||||
{
|
||||
return &container_of_const(fb, struct i915_framebuffer, base)->panic;
|
||||
}
|
||||
|
||||
static void i915_panic_kunmap(struct i915_panic_data *panic)
|
||||
static void i915_panic_kunmap(struct intel_panic *panic)
|
||||
{
|
||||
if (panic->vaddr) {
|
||||
drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
|
||||
|
|
@ -420,7 +410,7 @@ static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb,
|
|||
unsigned int new_page;
|
||||
unsigned int offset;
|
||||
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
|
||||
struct i915_panic_data *panic = to_i915_panic_data(fb);
|
||||
struct intel_panic *panic = fb->panic;
|
||||
|
||||
if (fb->panic_tiling)
|
||||
offset = fb->panic_tiling(sb->width, x, y);
|
||||
|
|
@ -441,14 +431,13 @@ static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb,
|
|||
}
|
||||
}
|
||||
|
||||
struct intel_framebuffer *i915_gem_object_alloc_framebuffer(void)
|
||||
struct intel_panic *i915_gem_object_alloc_panic(void)
|
||||
{
|
||||
struct i915_framebuffer *i915_fb;
|
||||
struct intel_panic *panic;
|
||||
|
||||
i915_fb = kzalloc(sizeof(*i915_fb), GFP_KERNEL);
|
||||
if (i915_fb)
|
||||
return &i915_fb->base;
|
||||
return NULL;
|
||||
panic = kzalloc(sizeof(*panic), GFP_KERNEL);
|
||||
|
||||
return panic;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -456,12 +445,11 @@ struct intel_framebuffer *i915_gem_object_alloc_framebuffer(void)
|
|||
* Use current vaddr if it exists, or setup a list of pages.
|
||||
* pfn is not supported yet.
|
||||
*/
|
||||
int i915_gem_object_panic_setup(struct drm_scanout_buffer *sb)
|
||||
int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
|
||||
struct drm_gem_object *_obj, bool panic_tiling)
|
||||
{
|
||||
enum i915_map_type has_type;
|
||||
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
|
||||
struct i915_panic_data *panic = to_i915_panic_data(fb);
|
||||
struct drm_i915_gem_object *obj = to_intel_bo(intel_fb_bo(&fb->base));
|
||||
struct drm_i915_gem_object *obj = to_intel_bo(_obj);
|
||||
void *ptr;
|
||||
|
||||
ptr = page_unpack_bits(obj->mm.mapping, &has_type);
|
||||
|
|
@ -471,7 +459,7 @@ int i915_gem_object_panic_setup(struct drm_scanout_buffer *sb)
|
|||
else
|
||||
iosys_map_set_vaddr(&sb->map[0], ptr);
|
||||
|
||||
if (fb->panic_tiling)
|
||||
if (panic_tiling)
|
||||
sb->set_pixel = i915_gem_object_panic_map_set_pixel;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -486,10 +474,8 @@ int i915_gem_object_panic_setup(struct drm_scanout_buffer *sb)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
void i915_gem_object_panic_finish(struct intel_framebuffer *fb)
|
||||
void i915_gem_object_panic_finish(struct intel_panic *panic)
|
||||
{
|
||||
struct i915_panic_data *panic = to_i915_panic_data(fb);
|
||||
|
||||
i915_panic_kunmap(panic);
|
||||
panic->page = -1;
|
||||
kfree(panic->pages);
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ i915_gem_shrink(struct i915_gem_ww_ctx *ww,
|
|||
* Also note that although these lists do not hold a reference to
|
||||
* the object we can safely grab one here: The final object
|
||||
* unreferencing and the bound_list are both protected by the
|
||||
* dev->struct_mutex and so we won't ever be able to observe an
|
||||
* i915->mm.obj_lock and so we won't ever be able to observe an
|
||||
* object on the bound_list with a reference count equals 0.
|
||||
*/
|
||||
for (phase = phases; phase->list; phase++) {
|
||||
|
|
@ -185,7 +185,7 @@ i915_gem_shrink(struct i915_gem_ww_ctx *ww,
|
|||
|
||||
/*
|
||||
* We serialize our access to unreferenced objects through
|
||||
* the use of the struct_mutex. While the objects are not
|
||||
* the use of the obj_lock. While the objects are not
|
||||
* yet freed (due to RCU then a workqueue) we still want
|
||||
* to be able to shrink their pages, so they remain on
|
||||
* the unbound/bound list until actually freed.
|
||||
|
|
|
|||
|
|
@ -217,10 +217,10 @@ static unsigned long to_wait_timeout(s64 timeout_ns)
|
|||
*
|
||||
* The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any
|
||||
* non-zero timeout parameter the wait ioctl will wait for the given number of
|
||||
* nanoseconds on an object becoming unbusy. Since the wait itself does so
|
||||
* without holding struct_mutex the object may become re-busied before this
|
||||
* function completes. A similar but shorter * race condition exists in the busy
|
||||
* ioctl
|
||||
* nanoseconds on an object becoming unbusy. Since the wait occurs without
|
||||
* holding a global or exclusive lock the object may become re-busied before
|
||||
* this function completes. A similar but shorter * race condition exists
|
||||
* in the busy ioctl
|
||||
*/
|
||||
int
|
||||
i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "i915_selftest.h"
|
||||
|
||||
#include "display/intel_display_core.h"
|
||||
#include "display/intel_display_device.h"
|
||||
#include "gt/intel_context.h"
|
||||
#include "gt/intel_engine_regs.h"
|
||||
#include "gt/intel_engine_user.h"
|
||||
|
|
@ -122,7 +122,7 @@ static bool fastblit_supports_x_tiling(const struct drm_i915_private *i915)
|
|||
if (GRAPHICS_VER_FULL(i915) < IP_VER(12, 55))
|
||||
return false;
|
||||
|
||||
return HAS_DISPLAY(display);
|
||||
return intel_display_device_present(display);
|
||||
}
|
||||
|
||||
static bool fast_blit_ok(const struct blit_buffer *buf)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include "i915_active_types.h"
|
||||
#include "i915_sw_fence.h"
|
||||
#include "i915_utils.h"
|
||||
#include "intel_engine_types.h"
|
||||
#include "intel_sseu.h"
|
||||
#include "intel_wakeref.h"
|
||||
|
|
|
|||
|
|
@ -106,14 +106,18 @@
|
|||
* preemption, but just sampling the new tail pointer).
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include "gen8_engine_cs.h"
|
||||
#include "i915_drv.h"
|
||||
#include "i915_list_util.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_timer_util.h"
|
||||
#include "i915_trace.h"
|
||||
#include "i915_vgpu.h"
|
||||
#include "gen8_engine_cs.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_breadcrumbs.h"
|
||||
#include "intel_context.h"
|
||||
#include "intel_engine_heartbeat.h"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "gem/i915_gem_object.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_list_util.h"
|
||||
#include "intel_engine_pm.h"
|
||||
#include "intel_gt_buffer_pool.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_gt.h"
|
||||
#include "intel_gt_mcr.h"
|
||||
#include "intel_gt_print.h"
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ static int vlv_rc6_init(struct intel_rc6 *rc6)
|
|||
return PTR_ERR(pctx);
|
||||
}
|
||||
|
||||
GEM_BUG_ON(range_overflows_end_t(u64,
|
||||
GEM_BUG_ON(range_end_overflows_t(u64,
|
||||
i915->dsm.stolen.start,
|
||||
pctx->stolen->start,
|
||||
U32_MAX));
|
||||
|
|
|
|||
|
|
@ -9,18 +9,17 @@
|
|||
|
||||
#include "display/intel_display_reset.h"
|
||||
#include "display/intel_overlay.h"
|
||||
|
||||
#include "gem/i915_gem_context.h"
|
||||
|
||||
#include "gt/intel_gt_regs.h"
|
||||
|
||||
#include "gt/uc/intel_gsc_fw.h"
|
||||
#include "uc/intel_guc.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_file_private.h"
|
||||
#include "i915_gpu_error.h"
|
||||
#include "i915_irq.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_breadcrumbs.h"
|
||||
#include "intel_engine_pm.h"
|
||||
#include "intel_engine_regs.h"
|
||||
|
|
@ -32,8 +31,6 @@
|
|||
#include "intel_pci_config.h"
|
||||
#include "intel_reset.h"
|
||||
|
||||
#include "uc/intel_guc.h"
|
||||
|
||||
#define RESET_MAX_RETRIES 3
|
||||
|
||||
static void client_mark_guilty(struct i915_gem_context *ctx, bool banned)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ struct intel_reset {
|
|||
* FENCE registers).
|
||||
*
|
||||
* #I915_RESET_ENGINE[num_engines] - Since the driver doesn't need to
|
||||
* acquire the struct_mutex to reset an engine, we need an explicit
|
||||
* acquire a global lock to reset an engine, we need an explicit
|
||||
* flag to prevent two concurrent reset attempts in the same engine.
|
||||
* As the number of engines continues to grow, allocate the flags from
|
||||
* the most significant bits.
|
||||
|
|
|
|||
|
|
@ -15,18 +15,19 @@
|
|||
#include "i915_irq.h"
|
||||
#include "i915_mitigations.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_breadcrumbs.h"
|
||||
#include "intel_context.h"
|
||||
#include "intel_engine_heartbeat.h"
|
||||
#include "intel_engine_pm.h"
|
||||
#include "intel_engine_regs.h"
|
||||
#include "intel_gt.h"
|
||||
#include "intel_gt_irq.h"
|
||||
#include "intel_gt_print.h"
|
||||
#include "intel_gt_regs.h"
|
||||
#include "intel_reset.h"
|
||||
#include "intel_ring.h"
|
||||
#include "shmem_utils.h"
|
||||
#include "intel_engine_heartbeat.h"
|
||||
#include "intel_engine_pm.h"
|
||||
#include "intel_gt_print.h"
|
||||
|
||||
/* Rough estimate of the typical request size, performing a flush,
|
||||
* set-context and then emitting the batch.
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@
|
|||
#include "display/intel_display.h"
|
||||
#include "display/intel_display_rps.h"
|
||||
#include "soc/intel_dram.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_irq.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_breadcrumbs.h"
|
||||
#include "intel_gt.h"
|
||||
#include "intel_gt_clock_utils.h"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/lockdep.h>
|
||||
|
||||
#include "i915_active.h"
|
||||
#include "i915_list_util.h"
|
||||
#include "i915_syncmap.h"
|
||||
#include "intel_timeline_types.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@
|
|||
* Copyright © 2022 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "i915_selftest.h"
|
||||
|
||||
#include "gem/i915_gem_internal.h"
|
||||
#include "gem/i915_gem_lmem.h"
|
||||
#include "gem/i915_gem_region.h"
|
||||
|
||||
#include "gen8_engine_cs.h"
|
||||
#include "i915_gem_ww.h"
|
||||
#include "i915_selftest.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_context.h"
|
||||
#include "intel_engine_regs.h"
|
||||
#include "intel_gpu_commands.h"
|
||||
#include "intel_context.h"
|
||||
#include "intel_gt.h"
|
||||
#include "intel_ring.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/sysfs.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_timer_util.h"
|
||||
#include "intel_engine.h"
|
||||
#include "intel_engine_heartbeat.h"
|
||||
#include "sysfs_engines.h"
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@
|
|||
|
||||
#include "gt/intel_gt.h"
|
||||
#include "gt/intel_gt_print.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_gsc_proxy.h"
|
||||
#include "intel_gsc_uc.h"
|
||||
#include "intel_gsc_uc_heci_cmd_submit.h"
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
|
||||
/*
|
||||
* GSC proxy:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include "gt/intel_gpu_commands.h"
|
||||
#include "gt/intel_gt.h"
|
||||
#include "gt/intel_ring.h"
|
||||
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_gsc_uc_heci_cmd_submit.h"
|
||||
|
||||
struct gsc_heci_pkt {
|
||||
|
|
|
|||
|
|
@ -8,15 +8,17 @@
|
|||
#include "gt/intel_gt_irq.h"
|
||||
#include "gt/intel_gt_pm_irq.h"
|
||||
#include "gt/intel_gt_regs.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_irq.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_guc.h"
|
||||
#include "intel_guc_ads.h"
|
||||
#include "intel_guc_capture.h"
|
||||
#include "intel_guc_print.h"
|
||||
#include "intel_guc_slpc.h"
|
||||
#include "intel_guc_submission.h"
|
||||
#include "i915_drv.h"
|
||||
#include "i915_irq.h"
|
||||
#include "i915_reg.h"
|
||||
|
||||
/**
|
||||
* DOC: GuC
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@
|
|||
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_guc_ct.h"
|
||||
#include "intel_guc_print.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@
|
|||
#include "gt/intel_gt_mcr.h"
|
||||
#include "gt/intel_gt_regs.h"
|
||||
#include "gt/intel_rps.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_guc_fw.h"
|
||||
#include "intel_guc_print.h"
|
||||
#include "i915_drv.h"
|
||||
|
||||
static void guc_prepare_xfer(struct intel_gt *gt)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include "gt/intel_gt.h"
|
||||
#include "i915_drv.h"
|
||||
#include "i915_irq.h"
|
||||
|
|
@ -511,7 +513,11 @@ static void guc_log_relay_unmap(struct intel_guc_log *log)
|
|||
|
||||
void intel_guc_log_init_early(struct intel_guc_log *log)
|
||||
{
|
||||
mutex_init(&log->relay.lock);
|
||||
struct intel_guc *guc = log_to_guc(log);
|
||||
struct drm_i915_private *i915 = guc_to_i915(guc);
|
||||
|
||||
drmm_mutex_init(&i915->drm, &log->relay.lock);
|
||||
drmm_mutex_init(&i915->drm, &log->guc_lock);
|
||||
INIT_WORK(&log->relay.flush_work, copy_debug_logs_work);
|
||||
log->relay.started = false;
|
||||
}
|
||||
|
|
@ -677,7 +683,7 @@ int intel_guc_log_set_level(struct intel_guc_log *log, u32 level)
|
|||
if (level < GUC_LOG_LEVEL_DISABLED || level > GUC_LOG_LEVEL_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&i915->drm.struct_mutex);
|
||||
mutex_lock(&log->guc_lock);
|
||||
|
||||
if (log->level == level)
|
||||
goto out_unlock;
|
||||
|
|
@ -695,7 +701,7 @@ int intel_guc_log_set_level(struct intel_guc_log *log, u32 level)
|
|||
log->level = level;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&i915->drm.struct_mutex);
|
||||
mutex_unlock(&log->guc_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,14 @@ enum {
|
|||
struct intel_guc_log {
|
||||
u32 level;
|
||||
|
||||
/*
|
||||
* Protects concurrent access and modification of intel_guc_log->level.
|
||||
*
|
||||
* This lock replaces the legacy struct_mutex usage in
|
||||
* intel_guc_log system.
|
||||
*/
|
||||
struct mutex guc_lock;
|
||||
|
||||
/* Allocation settings */
|
||||
struct {
|
||||
s32 bytes; /* Size in bytes */
|
||||
|
|
|
|||
|
|
@ -3,18 +3,21 @@
|
|||
* Copyright © 2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <drm/drm_cache.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "intel_guc_slpc.h"
|
||||
#include "intel_guc_print.h"
|
||||
#include "intel_mchbar_regs.h"
|
||||
#include <drm/drm_cache.h>
|
||||
|
||||
#include "gt/intel_gt.h"
|
||||
#include "gt/intel_gt_regs.h"
|
||||
#include "gt/intel_rps.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_guc_print.h"
|
||||
#include "intel_guc_slpc.h"
|
||||
#include "intel_mchbar_regs.h"
|
||||
|
||||
/**
|
||||
* DOC: SLPC - Dynamic Frequency management
|
||||
*
|
||||
|
|
|
|||
|
|
@ -25,16 +25,16 @@
|
|||
#include "gt/intel_mocs.h"
|
||||
#include "gt/intel_ring.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_irq.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_trace.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_guc_ads.h"
|
||||
#include "intel_guc_capture.h"
|
||||
#include "intel_guc_print.h"
|
||||
#include "intel_guc_submission.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_irq.h"
|
||||
#include "i915_trace.h"
|
||||
|
||||
/**
|
||||
* DOC: GuC-based command submission
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1921,7 +1921,7 @@ static int perform_bb_shadow(struct parser_exec_state *s)
|
|||
if (!bb)
|
||||
return -ENOMEM;
|
||||
|
||||
bb->ppgtt = (s->buf_addr_type == GTT_BUFFER) ? false : true;
|
||||
bb->ppgtt = s->buf_addr_type != GTT_BUFFER;
|
||||
|
||||
/*
|
||||
* The start_offset stores the batch buffer's start gma's
|
||||
|
|
|
|||
|
|
@ -33,14 +33,16 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "gt/intel_context.h"
|
||||
#include "gt/intel_engine_regs.h"
|
||||
#include "gt/intel_gpu_commands.h"
|
||||
#include "gt/intel_gt_regs.h"
|
||||
#include "gt/intel_ring.h"
|
||||
|
||||
#include "gvt.h"
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define GEN9_MOCS_SIZE 64
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
|
||||
#include "gem/i915_gem_context.h"
|
||||
|
|
@ -54,6 +54,7 @@
|
|||
#include "i915_irq.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_scheduler.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_mchbar_regs.h"
|
||||
|
||||
static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node)
|
||||
|
|
|
|||
|
|
@ -51,13 +51,15 @@
|
|||
#include "display/intel_bw.h"
|
||||
#include "display/intel_cdclk.h"
|
||||
#include "display/intel_crtc.h"
|
||||
#include "display/intel_display_core.h"
|
||||
#include "display/intel_display_device.h"
|
||||
#include "display/intel_display_driver.h"
|
||||
#include "display/intel_display_power.h"
|
||||
#include "display/intel_dmc.h"
|
||||
#include "display/intel_dp.h"
|
||||
#include "display/intel_dpt.h"
|
||||
#include "display/intel_encoder.h"
|
||||
#include "display/intel_fbdev.h"
|
||||
#include "display/intel_gmbus.h"
|
||||
#include "display/intel_hotplug.h"
|
||||
#include "display/intel_opregion.h"
|
||||
#include "display/intel_overlay.h"
|
||||
|
|
@ -977,7 +979,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
|
|||
intel_power_domains_disable(display);
|
||||
|
||||
drm_client_dev_suspend(&i915->drm, false);
|
||||
if (HAS_DISPLAY(display)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
drm_kms_helper_poll_disable(&i915->drm);
|
||||
intel_display_driver_disable_user_access(display);
|
||||
|
||||
|
|
@ -989,7 +991,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
|
|||
intel_irq_suspend(i915);
|
||||
intel_hpd_cancel_work(display);
|
||||
|
||||
if (HAS_DISPLAY(display))
|
||||
if (intel_display_device_present(display))
|
||||
intel_display_driver_suspend_access(display);
|
||||
|
||||
intel_encoder_suspend_all(display);
|
||||
|
|
@ -1060,7 +1062,7 @@ static int i915_drm_suspend(struct drm_device *dev)
|
|||
* properly. */
|
||||
intel_power_domains_disable(display);
|
||||
drm_client_dev_suspend(dev, false);
|
||||
if (HAS_DISPLAY(display)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
intel_display_driver_disable_user_access(display);
|
||||
}
|
||||
|
|
@ -1072,7 +1074,7 @@ static int i915_drm_suspend(struct drm_device *dev)
|
|||
intel_irq_suspend(dev_priv);
|
||||
intel_hpd_cancel_work(display);
|
||||
|
||||
if (HAS_DISPLAY(display))
|
||||
if (intel_display_device_present(display))
|
||||
intel_display_driver_suspend_access(display);
|
||||
|
||||
intel_encoder_suspend_all(display);
|
||||
|
|
@ -1219,7 +1221,7 @@ static int i915_drm_resume(struct drm_device *dev)
|
|||
*/
|
||||
intel_irq_resume(dev_priv);
|
||||
|
||||
if (HAS_DISPLAY(display))
|
||||
if (intel_display_device_present(display))
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
i915_gem_resume(dev_priv);
|
||||
|
|
@ -1228,14 +1230,14 @@ static int i915_drm_resume(struct drm_device *dev)
|
|||
|
||||
intel_clock_gating_init(dev_priv);
|
||||
|
||||
if (HAS_DISPLAY(display))
|
||||
if (intel_display_device_present(display))
|
||||
intel_display_driver_resume_access(display);
|
||||
|
||||
intel_hpd_init(display);
|
||||
|
||||
intel_display_driver_resume(display);
|
||||
|
||||
if (HAS_DISPLAY(display)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
intel_display_driver_enable_user_access(display);
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,8 +114,7 @@ struct i915_gem_mm {
|
|||
struct intel_memory_region *stolen_region;
|
||||
/** Memory allocator for GTT stolen memory */
|
||||
struct drm_mm stolen;
|
||||
/** Protects the usage of the GTT stolen memory allocator. This is
|
||||
* always the inner lock when overlapping with struct_mutex. */
|
||||
/** Protects the usage of the GTT stolen memory allocator */
|
||||
struct mutex stolen_lock;
|
||||
|
||||
/* Protects bound_list/unbound_list and #drm_i915_gem_object.mm.link */
|
||||
|
|
@ -222,6 +221,9 @@ struct drm_i915_private {
|
|||
|
||||
bool irqs_enabled;
|
||||
|
||||
/* LPT/WPT IOSF sideband protection */
|
||||
struct mutex sbi_lock;
|
||||
|
||||
/* VLV/CHV IOSF sideband */
|
||||
struct {
|
||||
struct mutex lock; /* protect sideband access */
|
||||
|
|
|
|||
|
|
@ -847,8 +847,7 @@ void i915_gem_runtime_suspend(struct drm_i915_private *i915)
|
|||
/*
|
||||
* Only called during RPM suspend. All users of the userfault_list
|
||||
* must be holding an RPM wakeref to ensure that this can not
|
||||
* run concurrently with themselves (and use the struct_mutex for
|
||||
* protection between themselves).
|
||||
* run concurrently with themselves.
|
||||
*/
|
||||
|
||||
list_for_each_entry_safe(obj, on,
|
||||
|
|
|
|||
|
|
@ -163,11 +163,6 @@ static void ivb_parity_work(struct work_struct *work)
|
|||
u32 misccpctl;
|
||||
u8 slice = 0;
|
||||
|
||||
/* We must turn off DOP level clock gating to access the L3 registers.
|
||||
* In order to prevent a get/put style interface, acquire struct mutex
|
||||
* any time we access those registers.
|
||||
*/
|
||||
mutex_lock(&dev_priv->drm.struct_mutex);
|
||||
|
||||
/* If we've screwed up tracking, just let the interrupt fire again */
|
||||
if (drm_WARN_ON(&dev_priv->drm, !dev_priv->l3_parity.which_slice))
|
||||
|
|
@ -225,7 +220,6 @@ static void ivb_parity_work(struct work_struct *work)
|
|||
gen5_gt_enable_irq(gt, GT_PARITY_ERROR(dev_priv));
|
||||
spin_unlock_irq(gt->irq_lock);
|
||||
|
||||
mutex_unlock(&dev_priv->drm.struct_mutex);
|
||||
}
|
||||
|
||||
static irqreturn_t valleyview_irq_handler(int irq, void *arg)
|
||||
|
|
|
|||
23
drivers/gpu/drm/i915/i915_list_util.h
Normal file
23
drivers/gpu/drm/i915/i915_list_util.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#ifndef __I915_LIST_UTIL_H__
|
||||
#define __I915_LIST_UTIL_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <asm/rwonce.h>
|
||||
|
||||
static inline void __list_del_many(struct list_head *head,
|
||||
struct list_head *first)
|
||||
{
|
||||
first->prev = head;
|
||||
WRITE_ONCE(head->next, first);
|
||||
}
|
||||
|
||||
static inline int list_is_last_rcu(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return READ_ONCE(list->next) == head;
|
||||
}
|
||||
|
||||
#endif /* __I915_LIST_UTIL_H__ */
|
||||
66
drivers/gpu/drm/i915/i915_ptr_util.h
Normal file
66
drivers/gpu/drm/i915/i915_ptr_util.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#ifndef __I915_PTR_UTIL_H__
|
||||
#define __I915_PTR_UTIL_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ptr_mask_bits(ptr, n) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
(typeof(ptr))(__v & -BIT(n)); \
|
||||
})
|
||||
|
||||
#define ptr_unmask_bits(ptr, n) ((unsigned long)(ptr) & (BIT(n) - 1))
|
||||
|
||||
#define ptr_unpack_bits(ptr, bits, n) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
*(bits) = __v & (BIT(n) - 1); \
|
||||
(typeof(ptr))(__v & -BIT(n)); \
|
||||
})
|
||||
|
||||
#define ptr_pack_bits(ptr, bits, n) ({ \
|
||||
unsigned long __bits = (bits); \
|
||||
GEM_BUG_ON(__bits & -BIT(n)); \
|
||||
((typeof(ptr))((unsigned long)(ptr) | __bits)); \
|
||||
})
|
||||
|
||||
#define ptr_dec(ptr) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
(typeof(ptr))(__v - 1); \
|
||||
})
|
||||
|
||||
#define ptr_inc(ptr) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
(typeof(ptr))(__v + 1); \
|
||||
})
|
||||
|
||||
#define page_mask_bits(ptr) ptr_mask_bits(ptr, PAGE_SHIFT)
|
||||
#define page_unmask_bits(ptr) ptr_unmask_bits(ptr, PAGE_SHIFT)
|
||||
#define page_pack_bits(ptr, bits) ptr_pack_bits(ptr, bits, PAGE_SHIFT)
|
||||
#define page_unpack_bits(ptr, bits) ptr_unpack_bits(ptr, bits, PAGE_SHIFT)
|
||||
|
||||
static __always_inline ptrdiff_t ptrdiff(const void *a, const void *b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
|
||||
#define u64_to_ptr(T, x) ({ \
|
||||
typecheck(u64, x); \
|
||||
(T *)(uintptr_t)(x); \
|
||||
})
|
||||
|
||||
/*
|
||||
* container_of_user: Extract the superclass from a pointer to a member.
|
||||
*
|
||||
* Exactly like container_of() with the exception that it plays nicely
|
||||
* with sparse for __user @ptr.
|
||||
*/
|
||||
#define container_of_user(ptr, type, member) ({ \
|
||||
void __user *__mptr = (void __user *)(ptr); \
|
||||
BUILD_BUG_ON_MSG(!__same_type(*(ptr), typeof_member(type, member)) && \
|
||||
!__same_type(*(ptr), void), \
|
||||
"pointer type mismatch in container_of()"); \
|
||||
((type __user *)(__mptr - offsetof(type, member))); })
|
||||
|
||||
#endif /* __I915_PTR_UTIL_H__ */
|
||||
|
|
@ -31,19 +31,20 @@
|
|||
#include <linux/llist.h>
|
||||
#include <linux/lockdep.h>
|
||||
|
||||
#include <uapi/drm/i915_drm.h>
|
||||
|
||||
#include "gem/i915_gem_context_types.h"
|
||||
#include "gt/intel_context_types.h"
|
||||
#include "gt/intel_engine_types.h"
|
||||
#include "gt/intel_timeline_types.h"
|
||||
|
||||
#include "i915_gem.h"
|
||||
#include "i915_ptr_util.h"
|
||||
#include "i915_scheduler.h"
|
||||
#include "i915_selftest.h"
|
||||
#include "i915_sw_fence.h"
|
||||
#include "i915_vma_resource.h"
|
||||
|
||||
#include <uapi/drm/i915_drm.h>
|
||||
|
||||
struct drm_file;
|
||||
struct drm_i915_gem_object;
|
||||
struct drm_printer;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include <linux/vga_switcheroo.h>
|
||||
|
||||
#include "display/intel_display_core.h"
|
||||
#include "display/intel_display_device.h"
|
||||
|
||||
#include "i915_driver.h"
|
||||
#include "i915_drv.h"
|
||||
|
|
@ -22,7 +22,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev,
|
|||
dev_err(&pdev->dev, "DRM not initialized, aborting switch.\n");
|
||||
return;
|
||||
}
|
||||
if (!HAS_DISPLAY(display)) {
|
||||
if (!intel_display_device_present(display)) {
|
||||
dev_err(&pdev->dev, "Device state not initialized, aborting switch.\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -52,7 +52,8 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
|
|||
* locking inversion with the driver load path. And the access here is
|
||||
* completely racy anyway. So don't bother with locking for now.
|
||||
*/
|
||||
return i915 && HAS_DISPLAY(display) && atomic_read(&i915->drm.open_count) == 0;
|
||||
return i915 && intel_display_device_present(display) &&
|
||||
atomic_read(&i915->drm.open_count) == 0;
|
||||
}
|
||||
|
||||
static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
|
||||
|
|
|
|||
36
drivers/gpu/drm/i915/i915_timer_util.c
Normal file
36
drivers/gpu/drm/i915/i915_timer_util.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include "i915_timer_util.h"
|
||||
|
||||
void cancel_timer(struct timer_list *t)
|
||||
{
|
||||
if (!timer_active(t))
|
||||
return;
|
||||
|
||||
timer_delete(t);
|
||||
WRITE_ONCE(t->expires, 0);
|
||||
}
|
||||
|
||||
void set_timer_ms(struct timer_list *t, unsigned long timeout)
|
||||
{
|
||||
if (!timeout) {
|
||||
cancel_timer(t);
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = msecs_to_jiffies(timeout);
|
||||
|
||||
/*
|
||||
* Paranoia to make sure the compiler computes the timeout before
|
||||
* loading 'jiffies' as jiffies is volatile and may be updated in
|
||||
* the background by a timer tick. All to reduce the complexity
|
||||
* of the addition and reduce the risk of losing a jiffy.
|
||||
*/
|
||||
barrier();
|
||||
|
||||
/* Keep t->expires = 0 reserved to indicate a canceled timer. */
|
||||
mod_timer(t, jiffies + timeout ?: 1);
|
||||
}
|
||||
23
drivers/gpu/drm/i915/i915_timer_util.h
Normal file
23
drivers/gpu/drm/i915/i915_timer_util.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#ifndef __I915_TIMER_UTIL_H__
|
||||
#define __I915_TIMER_UTIL_H__
|
||||
|
||||
#include <linux/timer.h>
|
||||
#include <asm/rwonce.h>
|
||||
|
||||
void cancel_timer(struct timer_list *t);
|
||||
void set_timer_ms(struct timer_list *t, unsigned long timeout);
|
||||
|
||||
static inline bool timer_active(const struct timer_list *t)
|
||||
{
|
||||
return READ_ONCE(t->expires);
|
||||
}
|
||||
|
||||
static inline bool timer_expired(const struct timer_list *t)
|
||||
{
|
||||
return timer_active(t) && !timer_pending(t);
|
||||
}
|
||||
|
||||
#endif /* __I915_TIMER_UTIL_H__ */
|
||||
|
|
@ -47,36 +47,6 @@ bool i915_error_injected(void)
|
|||
|
||||
#endif
|
||||
|
||||
void cancel_timer(struct timer_list *t)
|
||||
{
|
||||
if (!timer_active(t))
|
||||
return;
|
||||
|
||||
timer_delete(t);
|
||||
WRITE_ONCE(t->expires, 0);
|
||||
}
|
||||
|
||||
void set_timer_ms(struct timer_list *t, unsigned long timeout)
|
||||
{
|
||||
if (!timeout) {
|
||||
cancel_timer(t);
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = msecs_to_jiffies(timeout);
|
||||
|
||||
/*
|
||||
* Paranoia to make sure the compiler computes the timeout before
|
||||
* loading 'jiffies' as jiffies is volatile and may be updated in
|
||||
* the background by a timer tick. All to reduce the complexity
|
||||
* of the addition and reduce the risk of losing a jiffy.
|
||||
*/
|
||||
barrier();
|
||||
|
||||
/* Keep t->expires = 0 reserved to indicate a canceled timer. */
|
||||
mod_timer(t, jiffies + timeout ?: 1);
|
||||
}
|
||||
|
||||
bool i915_vtd_active(struct drm_i915_private *i915)
|
||||
{
|
||||
if (device_iommu_mapped(i915->drm.dev))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
#ifndef __I915_UTILS_H
|
||||
#define __I915_UTILS_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
|
@ -38,7 +37,6 @@
|
|||
#endif
|
||||
|
||||
struct drm_i915_private;
|
||||
struct timer_list;
|
||||
|
||||
#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
|
||||
__stringify(x), (long)(x))
|
||||
|
|
@ -67,88 +65,12 @@ bool i915_error_injected(void);
|
|||
drm_err(&(i915)->drm, fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define range_overflows(start, size, max) ({ \
|
||||
typeof(start) start__ = (start); \
|
||||
typeof(size) size__ = (size); \
|
||||
typeof(max) max__ = (max); \
|
||||
(void)(&start__ == &size__); \
|
||||
(void)(&start__ == &max__); \
|
||||
start__ >= max__ || size__ > max__ - start__; \
|
||||
})
|
||||
|
||||
#define range_overflows_t(type, start, size, max) \
|
||||
range_overflows((type)(start), (type)(size), (type)(max))
|
||||
|
||||
#define range_overflows_end(start, size, max) ({ \
|
||||
typeof(start) start__ = (start); \
|
||||
typeof(size) size__ = (size); \
|
||||
typeof(max) max__ = (max); \
|
||||
(void)(&start__ == &size__); \
|
||||
(void)(&start__ == &max__); \
|
||||
start__ > max__ || size__ > max__ - start__; \
|
||||
})
|
||||
|
||||
#define range_overflows_end_t(type, start, size, max) \
|
||||
range_overflows_end((type)(start), (type)(size), (type)(max))
|
||||
|
||||
#define ptr_mask_bits(ptr, n) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
(typeof(ptr))(__v & -BIT(n)); \
|
||||
})
|
||||
|
||||
#define ptr_unmask_bits(ptr, n) ((unsigned long)(ptr) & (BIT(n) - 1))
|
||||
|
||||
#define ptr_unpack_bits(ptr, bits, n) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
*(bits) = __v & (BIT(n) - 1); \
|
||||
(typeof(ptr))(__v & -BIT(n)); \
|
||||
})
|
||||
|
||||
#define ptr_pack_bits(ptr, bits, n) ({ \
|
||||
unsigned long __bits = (bits); \
|
||||
GEM_BUG_ON(__bits & -BIT(n)); \
|
||||
((typeof(ptr))((unsigned long)(ptr) | __bits)); \
|
||||
})
|
||||
|
||||
#define ptr_dec(ptr) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
(typeof(ptr))(__v - 1); \
|
||||
})
|
||||
|
||||
#define ptr_inc(ptr) ({ \
|
||||
unsigned long __v = (unsigned long)(ptr); \
|
||||
(typeof(ptr))(__v + 1); \
|
||||
})
|
||||
|
||||
#define page_mask_bits(ptr) ptr_mask_bits(ptr, PAGE_SHIFT)
|
||||
#define page_unmask_bits(ptr) ptr_unmask_bits(ptr, PAGE_SHIFT)
|
||||
#define page_pack_bits(ptr, bits) ptr_pack_bits(ptr, bits, PAGE_SHIFT)
|
||||
#define page_unpack_bits(ptr, bits) ptr_unpack_bits(ptr, bits, PAGE_SHIFT)
|
||||
|
||||
#define fetch_and_zero(ptr) ({ \
|
||||
typeof(*ptr) __T = *(ptr); \
|
||||
*(ptr) = (typeof(*ptr))0; \
|
||||
__T; \
|
||||
})
|
||||
|
||||
static __always_inline ptrdiff_t ptrdiff(const void *a, const void *b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/*
|
||||
* container_of_user: Extract the superclass from a pointer to a member.
|
||||
*
|
||||
* Exactly like container_of() with the exception that it plays nicely
|
||||
* with sparse for __user @ptr.
|
||||
*/
|
||||
#define container_of_user(ptr, type, member) ({ \
|
||||
void __user *__mptr = (void __user *)(ptr); \
|
||||
BUILD_BUG_ON_MSG(!__same_type(*(ptr), typeof_member(type, member)) && \
|
||||
!__same_type(*(ptr), void), \
|
||||
"pointer type mismatch in container_of()"); \
|
||||
((type __user *)(__mptr - offsetof(type, member))); })
|
||||
|
||||
/*
|
||||
* check_user_mbz: Check that a user value exists and is zero
|
||||
*
|
||||
|
|
@ -167,11 +89,6 @@ static __always_inline ptrdiff_t ptrdiff(const void *a, const void *b)
|
|||
get_user(mbz__, (U)) ? -EFAULT : mbz__ ? -EINVAL : 0; \
|
||||
})
|
||||
|
||||
#define u64_to_ptr(T, x) ({ \
|
||||
typecheck(u64, x); \
|
||||
(T *)(uintptr_t)(x); \
|
||||
})
|
||||
|
||||
#define __mask_next_bit(mask) ({ \
|
||||
int __idx = ffs(mask) - 1; \
|
||||
mask &= ~BIT(__idx); \
|
||||
|
|
@ -183,19 +100,6 @@ static inline bool is_power_of_2_u64(u64 n)
|
|||
return (n != 0 && ((n & (n - 1)) == 0));
|
||||
}
|
||||
|
||||
static inline void __list_del_many(struct list_head *head,
|
||||
struct list_head *first)
|
||||
{
|
||||
first->prev = head;
|
||||
WRITE_ONCE(head->next, first);
|
||||
}
|
||||
|
||||
static inline int list_is_last_rcu(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return READ_ONCE(list->next) == head;
|
||||
}
|
||||
|
||||
static inline unsigned long msecs_to_jiffies_timeout(const unsigned int m)
|
||||
{
|
||||
unsigned long j = msecs_to_jiffies(m);
|
||||
|
|
@ -230,112 +134,6 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* __wait_for - magic wait macro
|
||||
*
|
||||
* Macro to help avoid open coding check/wait/timeout patterns. Note that it's
|
||||
* important that we check the condition again after having timed out, since the
|
||||
* timeout could be due to preemption or similar and we've never had a chance to
|
||||
* check the condition before the timeout.
|
||||
*/
|
||||
#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
|
||||
const ktime_t end__ = ktime_add_ns(ktime_get_raw(), 1000ll * (US)); \
|
||||
long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
|
||||
int ret__; \
|
||||
might_sleep(); \
|
||||
for (;;) { \
|
||||
const bool expired__ = ktime_after(ktime_get_raw(), end__); \
|
||||
OP; \
|
||||
/* Guarantee COND check prior to timeout */ \
|
||||
barrier(); \
|
||||
if (COND) { \
|
||||
ret__ = 0; \
|
||||
break; \
|
||||
} \
|
||||
if (expired__) { \
|
||||
ret__ = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
usleep_range(wait__, wait__ * 2); \
|
||||
if (wait__ < (Wmax)) \
|
||||
wait__ <<= 1; \
|
||||
} \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define _wait_for(COND, US, Wmin, Wmax) __wait_for(, (COND), (US), (Wmin), \
|
||||
(Wmax))
|
||||
#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000)
|
||||
|
||||
/*
|
||||
* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false.
|
||||
* On PREEMPT_RT the context isn't becoming atomic because it is used in an
|
||||
* interrupt handler or because a spinlock_t is acquired. This leads to
|
||||
* warnings which don't occur otherwise and therefore the check is disabled.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG) && IS_ENABLED(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT)
|
||||
# define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) WARN_ON_ONCE((ATOMIC) && !in_atomic())
|
||||
#else
|
||||
# define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define _wait_for_atomic(COND, US, ATOMIC) \
|
||||
({ \
|
||||
int cpu, ret, timeout = (US) * 1000; \
|
||||
u64 base; \
|
||||
_WAIT_FOR_ATOMIC_CHECK(ATOMIC); \
|
||||
if (!(ATOMIC)) { \
|
||||
preempt_disable(); \
|
||||
cpu = smp_processor_id(); \
|
||||
} \
|
||||
base = local_clock(); \
|
||||
for (;;) { \
|
||||
u64 now = local_clock(); \
|
||||
if (!(ATOMIC)) \
|
||||
preempt_enable(); \
|
||||
/* Guarantee COND check prior to timeout */ \
|
||||
barrier(); \
|
||||
if (COND) { \
|
||||
ret = 0; \
|
||||
break; \
|
||||
} \
|
||||
if (now - base >= timeout) { \
|
||||
ret = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
cpu_relax(); \
|
||||
if (!(ATOMIC)) { \
|
||||
preempt_disable(); \
|
||||
if (unlikely(cpu != smp_processor_id())) { \
|
||||
timeout -= now - base; \
|
||||
cpu = smp_processor_id(); \
|
||||
base = local_clock(); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define wait_for_us(COND, US) \
|
||||
({ \
|
||||
int ret__; \
|
||||
BUILD_BUG_ON(!__builtin_constant_p(US)); \
|
||||
if ((US) > 10) \
|
||||
ret__ = _wait_for((COND), (US), 10, 10); \
|
||||
else \
|
||||
ret__ = _wait_for_atomic((COND), (US), 0); \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define wait_for_atomic_us(COND, US) \
|
||||
({ \
|
||||
BUILD_BUG_ON(!__builtin_constant_p(US)); \
|
||||
BUILD_BUG_ON((US) > 50000); \
|
||||
_wait_for_atomic((COND), (US), 1); \
|
||||
})
|
||||
|
||||
#define wait_for_atomic(COND, MS) wait_for_atomic_us((COND), (MS) * 1000)
|
||||
|
||||
#define KHz(x) (1000 * (x))
|
||||
#define MHz(x) KHz(1000 * (x))
|
||||
|
||||
|
|
@ -351,19 +149,6 @@ static inline void __add_taint_for_CI(unsigned int taint)
|
|||
add_taint(taint, LOCKDEP_STILL_OK);
|
||||
}
|
||||
|
||||
void cancel_timer(struct timer_list *t);
|
||||
void set_timer_ms(struct timer_list *t, unsigned long timeout);
|
||||
|
||||
static inline bool timer_active(const struct timer_list *t)
|
||||
{
|
||||
return READ_ONCE(t->expires);
|
||||
}
|
||||
|
||||
static inline bool timer_expired(const struct timer_list *t)
|
||||
{
|
||||
return timer_active(t) && !timer_pending(t);
|
||||
}
|
||||
|
||||
static inline bool i915_run_as_guest(void)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_X86)
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@
|
|||
|
||||
#include <drm/drm_mm.h>
|
||||
|
||||
#include "gt/intel_ggtt_fencing.h"
|
||||
#include "gem/i915_gem_object.h"
|
||||
|
||||
#include "i915_gem_gtt.h"
|
||||
#include "gt/intel_ggtt_fencing.h"
|
||||
|
||||
#include "i915_active.h"
|
||||
#include "i915_gem_gtt.h"
|
||||
#include "i915_ptr_util.h"
|
||||
#include "i915_request.h"
|
||||
#include "i915_vma_resource.h"
|
||||
#include "i915_vma_types.h"
|
||||
|
|
|
|||
119
drivers/gpu/drm/i915/i915_wait_util.h
Normal file
119
drivers/gpu/drm/i915/i915_wait_util.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#ifndef __I915_WAIT_UTIL_H__
|
||||
#define __I915_WAIT_UTIL_H__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
/*
|
||||
* __wait_for - magic wait macro
|
||||
*
|
||||
* Macro to help avoid open coding check/wait/timeout patterns. Note that it's
|
||||
* important that we check the condition again after having timed out, since the
|
||||
* timeout could be due to preemption or similar and we've never had a chance to
|
||||
* check the condition before the timeout.
|
||||
*/
|
||||
#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
|
||||
const ktime_t end__ = ktime_add_ns(ktime_get_raw(), 1000ll * (US)); \
|
||||
long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
|
||||
int ret__; \
|
||||
might_sleep(); \
|
||||
for (;;) { \
|
||||
const bool expired__ = ktime_after(ktime_get_raw(), end__); \
|
||||
OP; \
|
||||
/* Guarantee COND check prior to timeout */ \
|
||||
barrier(); \
|
||||
if (COND) { \
|
||||
ret__ = 0; \
|
||||
break; \
|
||||
} \
|
||||
if (expired__) { \
|
||||
ret__ = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
usleep_range(wait__, wait__ * 2); \
|
||||
if (wait__ < (Wmax)) \
|
||||
wait__ <<= 1; \
|
||||
} \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define _wait_for(COND, US, Wmin, Wmax) __wait_for(, (COND), (US), (Wmin), \
|
||||
(Wmax))
|
||||
#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000)
|
||||
|
||||
/*
|
||||
* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false.
|
||||
* On PREEMPT_RT the context isn't becoming atomic because it is used in an
|
||||
* interrupt handler or because a spinlock_t is acquired. This leads to
|
||||
* warnings which don't occur otherwise and therefore the check is disabled.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG) && IS_ENABLED(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT)
|
||||
# define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) WARN_ON_ONCE((ATOMIC) && !in_atomic())
|
||||
#else
|
||||
# define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define _wait_for_atomic(COND, US, ATOMIC) \
|
||||
({ \
|
||||
int cpu, ret, timeout = (US) * 1000; \
|
||||
u64 base; \
|
||||
_WAIT_FOR_ATOMIC_CHECK(ATOMIC); \
|
||||
if (!(ATOMIC)) { \
|
||||
preempt_disable(); \
|
||||
cpu = smp_processor_id(); \
|
||||
} \
|
||||
base = local_clock(); \
|
||||
for (;;) { \
|
||||
u64 now = local_clock(); \
|
||||
if (!(ATOMIC)) \
|
||||
preempt_enable(); \
|
||||
/* Guarantee COND check prior to timeout */ \
|
||||
barrier(); \
|
||||
if (COND) { \
|
||||
ret = 0; \
|
||||
break; \
|
||||
} \
|
||||
if (now - base >= timeout) { \
|
||||
ret = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
cpu_relax(); \
|
||||
if (!(ATOMIC)) { \
|
||||
preempt_disable(); \
|
||||
if (unlikely(cpu != smp_processor_id())) { \
|
||||
timeout -= now - base; \
|
||||
cpu = smp_processor_id(); \
|
||||
base = local_clock(); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define wait_for_us(COND, US) \
|
||||
({ \
|
||||
int ret__; \
|
||||
BUILD_BUG_ON(!__builtin_constant_p(US)); \
|
||||
if ((US) > 10) \
|
||||
ret__ = _wait_for((COND), (US), 10, 10); \
|
||||
else \
|
||||
ret__ = _wait_for_atomic((COND), (US), 0); \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define wait_for_atomic_us(COND, US) \
|
||||
({ \
|
||||
BUILD_BUG_ON(!__builtin_constant_p(US)); \
|
||||
BUILD_BUG_ON((US) > 50000); \
|
||||
_wait_for_atomic((COND), (US), 1); \
|
||||
})
|
||||
|
||||
#define wait_for_atomic(COND, MS) wait_for_atomic_us((COND), (MS) * 1000)
|
||||
|
||||
#endif /* __I915_WAIT_UTIL_H__ */
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_pcode.h"
|
||||
|
||||
static int gen6_check_mailbox_status(u32 mbox)
|
||||
|
|
|
|||
|
|
@ -21,19 +21,20 @@
|
|||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "display/intel_display_core.h"
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include "gt/intel_gt.h"
|
||||
#include "display/intel_display_core.h"
|
||||
#include "gt/intel_engine_regs.h"
|
||||
#include "gt/intel_gt.h"
|
||||
#include "gt/intel_gt_regs.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_iosf_mbi.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_vgpu.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_uncore_trace.h"
|
||||
|
||||
#define FORCEWAKE_ACK_TIMEOUT_MS 50
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
/*
|
||||
* Copyright(c) 2020 Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "gem/i915_gem_context.h"
|
||||
|
||||
#include "gt/intel_context.h"
|
||||
#include "gt/intel_gt.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_pxp.h"
|
||||
#include "intel_pxp_gsccs.h"
|
||||
#include "intel_pxp_irq.h"
|
||||
|
|
|
|||
|
|
@ -22,14 +22,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/prime_numbers.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/prime_numbers.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include "gem/i915_gem_internal.h"
|
||||
#include "gem/i915_gem_pm.h"
|
||||
#include "gem/selftests/mock_context.h"
|
||||
|
||||
#include "gt/intel_engine_heartbeat.h"
|
||||
#include "gt/intel_engine_pm.h"
|
||||
#include "gt/intel_engine_user.h"
|
||||
|
|
@ -40,11 +39,11 @@
|
|||
|
||||
#include "i915_random.h"
|
||||
#include "i915_selftest.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "igt_flush_test.h"
|
||||
#include "igt_live_test.h"
|
||||
#include "igt_spinner.h"
|
||||
#include "lib_sw_fence.h"
|
||||
|
||||
#include "mock_drm.h"
|
||||
#include "mock_gem_device.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include "i915_driver.h"
|
||||
#include "i915_drv.h"
|
||||
#include "i915_selftest.h"
|
||||
|
||||
#include "i915_wait_util.h"
|
||||
#include "igt_flush_test.h"
|
||||
|
||||
struct i915_selftest i915_selftest __read_mostly = {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
*
|
||||
* Copyright © 2018 Intel Corporation
|
||||
*/
|
||||
#include "gt/intel_gpu_commands.h"
|
||||
#include "gt/intel_gt.h"
|
||||
|
||||
#include "gem/i915_gem_internal.h"
|
||||
#include "gem/selftests/igt_gem_utils.h"
|
||||
#include "gt/intel_gpu_commands.h"
|
||||
#include "gt/intel_gt.h"
|
||||
|
||||
#include "i915_wait_util.h"
|
||||
#include "igt_spinner.h"
|
||||
|
||||
int igt_spinner_init(struct igt_spinner *spin, struct intel_gt *gt)
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ int intel_dram_detect(struct drm_i915_private *i915)
|
|||
struct dram_info *dram_info;
|
||||
int ret;
|
||||
|
||||
if (IS_DG2(i915) || !HAS_DISPLAY(display))
|
||||
if (IS_DG2(i915) || !intel_display_device_present(display))
|
||||
return 0;
|
||||
|
||||
dram_info = drmm_kzalloc(&i915->drm, sizeof(*dram_info), GFP_KERNEL);
|
||||
|
|
|
|||
|
|
@ -8,16 +8,17 @@
|
|||
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "gt/intel_gt_regs.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_trace.h"
|
||||
#include "i915_utils.h"
|
||||
#include "i915_wait_util.h"
|
||||
#include "intel_clock_gating.h"
|
||||
#include "intel_uncore_trace.h"
|
||||
#include "vlv_suspend.h"
|
||||
|
||||
#include "gt/intel_gt_regs.h"
|
||||
|
||||
struct vlv_s0ix_state {
|
||||
/* GAM */
|
||||
u32 wr_watermark;
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
|
|||
display/xe_dsb_buffer.o \
|
||||
display/xe_fb_pin.o \
|
||||
display/xe_hdcp_gsc.o \
|
||||
display/xe_panic.o \
|
||||
display/xe_plane_initial.o \
|
||||
display/xe_tdf.o
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/* Copyright © 2024 Intel Corporation */
|
||||
|
||||
#include <drm/drm_cache.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_panic.h>
|
||||
|
||||
#include "intel_fb.h"
|
||||
#include "intel_display_types.h"
|
||||
|
||||
#include "xe_bo.h"
|
||||
#include "intel_bo.h"
|
||||
|
|
@ -64,89 +59,3 @@ void intel_bo_describe(struct seq_file *m, struct drm_gem_object *obj)
|
|||
{
|
||||
/* FIXME */
|
||||
}
|
||||
|
||||
struct xe_panic_data {
|
||||
struct page **pages;
|
||||
int page;
|
||||
void *vaddr;
|
||||
};
|
||||
|
||||
struct xe_framebuffer {
|
||||
struct intel_framebuffer base;
|
||||
struct xe_panic_data panic;
|
||||
};
|
||||
|
||||
static inline struct xe_panic_data *to_xe_panic_data(struct intel_framebuffer *fb)
|
||||
{
|
||||
return &container_of_const(fb, struct xe_framebuffer, base)->panic;
|
||||
}
|
||||
|
||||
static void xe_panic_kunmap(struct xe_panic_data *panic)
|
||||
{
|
||||
if (panic->vaddr) {
|
||||
drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
|
||||
kunmap_local(panic->vaddr);
|
||||
panic->vaddr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The scanout buffer pages are not mapped, so for each pixel,
|
||||
* use kmap_local_page_try_from_panic() to map the page, and write the pixel.
|
||||
* Try to keep the map from the previous pixel, to avoid too much map/unmap.
|
||||
*/
|
||||
static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
|
||||
unsigned int y, u32 color)
|
||||
{
|
||||
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
|
||||
struct xe_panic_data *panic = to_xe_panic_data(fb);
|
||||
struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
|
||||
unsigned int new_page;
|
||||
unsigned int offset;
|
||||
|
||||
if (fb->panic_tiling)
|
||||
offset = fb->panic_tiling(sb->width, x, y);
|
||||
else
|
||||
offset = y * sb->pitch[0] + x * sb->format->cpp[0];
|
||||
|
||||
new_page = offset >> PAGE_SHIFT;
|
||||
offset = offset % PAGE_SIZE;
|
||||
if (new_page != panic->page) {
|
||||
xe_panic_kunmap(panic);
|
||||
panic->page = new_page;
|
||||
panic->vaddr = ttm_bo_kmap_try_from_panic(&bo->ttm,
|
||||
panic->page);
|
||||
}
|
||||
if (panic->vaddr) {
|
||||
u32 *pix = panic->vaddr + offset;
|
||||
*pix = color;
|
||||
}
|
||||
}
|
||||
|
||||
struct intel_framebuffer *intel_bo_alloc_framebuffer(void)
|
||||
{
|
||||
struct xe_framebuffer *xe_fb;
|
||||
|
||||
xe_fb = kzalloc(sizeof(*xe_fb), GFP_KERNEL);
|
||||
if (xe_fb)
|
||||
return &xe_fb->base;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int intel_bo_panic_setup(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
|
||||
struct xe_panic_data *panic = to_xe_panic_data(fb);
|
||||
|
||||
panic->page = -1;
|
||||
sb->set_pixel = xe_panic_page_set_pixel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void intel_bo_panic_finish(struct intel_framebuffer *fb)
|
||||
{
|
||||
struct xe_panic_data *panic = to_xe_panic_data(fb);
|
||||
|
||||
xe_panic_kunmap(panic);
|
||||
panic->page = -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "intel_audio.h"
|
||||
#include "intel_bw.h"
|
||||
#include "intel_display.h"
|
||||
#include "intel_display_core.h"
|
||||
#include "intel_display_device.h"
|
||||
#include "intel_display_driver.h"
|
||||
#include "intel_display_irq.h"
|
||||
#include "intel_display_types.h"
|
||||
|
|
@ -37,13 +37,6 @@
|
|||
|
||||
/* Xe device functions */
|
||||
|
||||
static bool has_display(struct xe_device *xe)
|
||||
{
|
||||
struct intel_display *display = xe->display;
|
||||
|
||||
return HAS_DISPLAY(display);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_display_driver_probe_defer - Detect if we need to wait for other drivers
|
||||
* early on
|
||||
|
|
@ -290,7 +283,7 @@ static void xe_display_enable_d3cold(struct xe_device *xe)
|
|||
|
||||
intel_dmc_suspend(display);
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
intel_hpd_poll_enable(display);
|
||||
}
|
||||
|
||||
|
|
@ -303,14 +296,14 @@ static void xe_display_disable_d3cold(struct xe_device *xe)
|
|||
|
||||
intel_dmc_resume(display);
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
drm_mode_config_reset(&xe->drm);
|
||||
|
||||
intel_display_driver_init_hw(display);
|
||||
|
||||
intel_hpd_init(display);
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
intel_hpd_poll_disable(display);
|
||||
|
||||
intel_opregion_resume(display);
|
||||
|
|
@ -333,7 +326,7 @@ void xe_display_pm_suspend(struct xe_device *xe)
|
|||
intel_power_domains_disable(display);
|
||||
drm_client_dev_suspend(&xe->drm, false);
|
||||
|
||||
if (has_display(xe)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
drm_kms_helper_poll_disable(&xe->drm);
|
||||
intel_display_driver_disable_user_access(display);
|
||||
intel_display_driver_suspend(display);
|
||||
|
|
@ -345,7 +338,7 @@ void xe_display_pm_suspend(struct xe_device *xe)
|
|||
|
||||
intel_hpd_cancel_work(display);
|
||||
|
||||
if (has_display(xe)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
intel_display_driver_suspend_access(display);
|
||||
intel_encoder_suspend_all(display);
|
||||
}
|
||||
|
|
@ -365,7 +358,7 @@ void xe_display_pm_shutdown(struct xe_device *xe)
|
|||
intel_power_domains_disable(display);
|
||||
drm_client_dev_suspend(&xe->drm, false);
|
||||
|
||||
if (has_display(xe)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
drm_kms_helper_poll_disable(&xe->drm);
|
||||
intel_display_driver_disable_user_access(display);
|
||||
intel_display_driver_suspend(display);
|
||||
|
|
@ -376,7 +369,7 @@ void xe_display_pm_shutdown(struct xe_device *xe)
|
|||
intel_encoder_block_all_hpds(display);
|
||||
intel_hpd_cancel_work(display);
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
intel_display_driver_suspend_access(display);
|
||||
|
||||
intel_encoder_suspend_all(display);
|
||||
|
|
@ -465,25 +458,25 @@ void xe_display_pm_resume(struct xe_device *xe)
|
|||
|
||||
intel_dmc_resume(display);
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
drm_mode_config_reset(&xe->drm);
|
||||
|
||||
intel_display_driver_init_hw(display);
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
intel_display_driver_resume_access(display);
|
||||
|
||||
intel_hpd_init(display);
|
||||
|
||||
intel_encoder_unblock_all_hpds(display);
|
||||
|
||||
if (has_display(xe)) {
|
||||
if (intel_display_device_present(display)) {
|
||||
intel_display_driver_resume(display);
|
||||
drm_kms_helper_poll_enable(&xe->drm);
|
||||
intel_display_driver_enable_user_access(display);
|
||||
}
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
intel_hpd_poll_disable(display);
|
||||
|
||||
intel_opregion_resume(display);
|
||||
|
|
@ -548,7 +541,7 @@ int xe_display_probe(struct xe_device *xe)
|
|||
|
||||
xe->display = display;
|
||||
|
||||
if (has_display(xe))
|
||||
if (intel_display_device_present(display))
|
||||
return 0;
|
||||
|
||||
no_display:
|
||||
|
|
|
|||
80
drivers/gpu/drm/xe/display/xe_panic.c
Normal file
80
drivers/gpu/drm/xe/display/xe_panic.c
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/* Copyright © 2025 Intel Corporation */
|
||||
|
||||
#include <drm/drm_cache.h>
|
||||
#include <drm/drm_panic.h>
|
||||
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_fb.h"
|
||||
#include "intel_panic.h"
|
||||
#include "xe_bo.h"
|
||||
|
||||
struct intel_panic {
|
||||
struct page **pages;
|
||||
int page;
|
||||
void *vaddr;
|
||||
};
|
||||
|
||||
static void xe_panic_kunmap(struct intel_panic *panic)
|
||||
{
|
||||
if (panic->vaddr) {
|
||||
drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
|
||||
kunmap_local(panic->vaddr);
|
||||
panic->vaddr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The scanout buffer pages are not mapped, so for each pixel,
|
||||
* use kmap_local_page_try_from_panic() to map the page, and write the pixel.
|
||||
* Try to keep the map from the previous pixel, to avoid too much map/unmap.
|
||||
*/
|
||||
static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
|
||||
unsigned int y, u32 color)
|
||||
{
|
||||
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
|
||||
struct intel_panic *panic = fb->panic;
|
||||
struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
|
||||
unsigned int new_page;
|
||||
unsigned int offset;
|
||||
|
||||
if (fb->panic_tiling)
|
||||
offset = fb->panic_tiling(sb->width, x, y);
|
||||
else
|
||||
offset = y * sb->pitch[0] + x * sb->format->cpp[0];
|
||||
|
||||
new_page = offset >> PAGE_SHIFT;
|
||||
offset = offset % PAGE_SIZE;
|
||||
if (new_page != panic->page) {
|
||||
xe_panic_kunmap(panic);
|
||||
panic->page = new_page;
|
||||
panic->vaddr = ttm_bo_kmap_try_from_panic(&bo->ttm,
|
||||
panic->page);
|
||||
}
|
||||
if (panic->vaddr) {
|
||||
u32 *pix = panic->vaddr + offset;
|
||||
*pix = color;
|
||||
}
|
||||
}
|
||||
|
||||
struct intel_panic *intel_panic_alloc(void)
|
||||
{
|
||||
struct intel_panic *panic;
|
||||
|
||||
panic = kzalloc(sizeof(*panic), GFP_KERNEL);
|
||||
|
||||
return panic;
|
||||
}
|
||||
|
||||
int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
|
||||
{
|
||||
panic->page = -1;
|
||||
sb->set_pixel = xe_panic_page_set_pixel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void intel_panic_finish(struct intel_panic *panic)
|
||||
{
|
||||
xe_panic_kunmap(panic);
|
||||
panic->page = -1;
|
||||
}
|
||||
|
|
@ -13,15 +13,6 @@
|
|||
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#define range_overflows(start, size, max) ({ \
|
||||
typeof(start) start__ = (start); \
|
||||
typeof(size) size__ = (size); \
|
||||
typeof(max) max__ = (max); \
|
||||
(void)(&start__ == &size__); \
|
||||
(void)(&start__ == &max__); \
|
||||
start__ >= max__ || size__ > max__ - start__; \
|
||||
})
|
||||
|
||||
#define DRM_BUDDY_RANGE_ALLOCATION BIT(0)
|
||||
#define DRM_BUDDY_TOPDOWN_ALLOCATION BIT(1)
|
||||
#define DRM_BUDDY_CONTIGUOUS_ALLOCATION BIT(2)
|
||||
|
|
|
|||
|
|
@ -192,16 +192,6 @@ struct drm_device {
|
|||
/** @unique: Unique name of the device */
|
||||
char *unique;
|
||||
|
||||
/**
|
||||
* @struct_mutex:
|
||||
*
|
||||
* Lock for others (not &drm_minor.master and &drm_file.is_master)
|
||||
*
|
||||
* TODO: This lock used to be the BKL of the DRM subsystem. Move the
|
||||
* lock into i915, which is the only remaining user.
|
||||
*/
|
||||
struct mutex struct_mutex;
|
||||
|
||||
/**
|
||||
* @master_mutex:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -238,6 +238,76 @@ static inline bool __must_check __must_check_overflow(bool overflow)
|
|||
__overflows_type_constexpr(n, T), \
|
||||
__overflows_type(n, T))
|
||||
|
||||
/**
|
||||
* range_overflows() - Check if a range is out of bounds
|
||||
* @start: Start of the range.
|
||||
* @size: Size of the range.
|
||||
* @max: Exclusive upper boundary.
|
||||
*
|
||||
* A strict check to determine if the range [@start, @start + @size) is
|
||||
* invalid with respect to the allowable range [0, @max). Any range
|
||||
* starting at or beyond @max is considered an overflow, even if @size is 0.
|
||||
*
|
||||
* Returns: true if the range is out of bounds.
|
||||
*/
|
||||
#define range_overflows(start, size, max) ({ \
|
||||
typeof(start) start__ = (start); \
|
||||
typeof(size) size__ = (size); \
|
||||
typeof(max) max__ = (max); \
|
||||
(void)(&start__ == &size__); \
|
||||
(void)(&start__ == &max__); \
|
||||
start__ >= max__ || size__ > max__ - start__; \
|
||||
})
|
||||
|
||||
/**
|
||||
* range_overflows_t() - Check if a range is out of bounds
|
||||
* @type: Data type to use.
|
||||
* @start: Start of the range.
|
||||
* @size: Size of the range.
|
||||
* @max: Exclusive upper boundary.
|
||||
*
|
||||
* Same as range_overflows() but forcing the parameters to @type.
|
||||
*
|
||||
* Returns: true if the range is out of bounds.
|
||||
*/
|
||||
#define range_overflows_t(type, start, size, max) \
|
||||
range_overflows((type)(start), (type)(size), (type)(max))
|
||||
|
||||
/**
|
||||
* range_end_overflows() - Check if a range's endpoint is out of bounds
|
||||
* @start: Start of the range.
|
||||
* @size: Size of the range.
|
||||
* @max: Exclusive upper boundary.
|
||||
*
|
||||
* Checks only if the endpoint of a range (@start + @size) exceeds @max.
|
||||
* Unlike range_overflows(), a zero-sized range at the boundary (@start == @max)
|
||||
* is not considered an overflow. Useful for iterator-style checks.
|
||||
*
|
||||
* Returns: true if the endpoint exceeds the boundary.
|
||||
*/
|
||||
#define range_end_overflows(start, size, max) ({ \
|
||||
typeof(start) start__ = (start); \
|
||||
typeof(size) size__ = (size); \
|
||||
typeof(max) max__ = (max); \
|
||||
(void)(&start__ == &size__); \
|
||||
(void)(&start__ == &max__); \
|
||||
start__ > max__ || size__ > max__ - start__; \
|
||||
})
|
||||
|
||||
/**
|
||||
* range_end_overflows_t() - Check if a range's endpoint is out of bounds
|
||||
* @type: Data type to use.
|
||||
* @start: Start of the range.
|
||||
* @size: Size of the range.
|
||||
* @max: Exclusive upper boundary.
|
||||
*
|
||||
* Same as range_end_overflows() but forcing the parameters to @type.
|
||||
*
|
||||
* Returns: true if the endpoint exceeds the boundary.
|
||||
*/
|
||||
#define range_end_overflows_t(type, start, size, max) \
|
||||
range_end_overflows((type)(start), (type)(size), (type)(max))
|
||||
|
||||
/**
|
||||
* castable_to_type - like __same_type(), but also allows for casted literals
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user