Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,579 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2023 Intel Corporation
*/
#include "xe_display.h"
#include "regs/xe_irq_regs.h"
#include <linux/fb.h>
#include <drm/drm_client.h>
#include <drm/drm_client_event.h>
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
#include <drm/intel/display_member.h>
#include <drm/intel/display_parent_interface.h>
#include <uapi/drm/xe_drm.h>
#include "intel_acpi.h"
#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"
#include "intel_dmc.h"
#include "intel_dmc_wl.h"
#include "intel_dp.h"
#include "intel_dram.h"
#include "intel_encoder.h"
#include "intel_fbdev.h"
#include "intel_hdcp.h"
#include "intel_hotplug.h"
#include "intel_opregion.h"
#include "skl_watermark.h"
#include "xe_device.h"
#include "xe_display_bo.h"
#include "xe_display_pcode.h"
#include "xe_display_rpm.h"
#include "xe_dsb_buffer.h"
#include "xe_fb_pin.h"
#include "xe_frontbuffer.h"
#include "xe_hdcp_gsc.h"
#include "xe_initial_plane.h"
#include "xe_module.h"
#include "xe_panic.h"
#include "xe_stolen.h"
/* Ensure drm and display members are placed properly. */
INTEL_DISPLAY_MEMBER_STATIC_ASSERT(struct xe_device, drm, display);
/* Xe device functions */
/**
* xe_display_driver_probe_defer - Detect if we need to wait for other drivers
* early on
* @pdev: PCI device
*
* Note: This is called before xe or display device creation.
*
* Returns: true if probe needs to be deferred, false otherwise
*/
bool xe_display_driver_probe_defer(struct pci_dev *pdev)
{
if (!xe_modparam.probe_display)
return 0;
return intel_display_driver_probe_defer(pdev);
}
static void unset_display_features(struct xe_device *xe)
{
xe->drm.driver_features &= ~XE_DISPLAY_DRIVER_FEATURES;
}
static void xe_display_fini_early(void *arg)
{
struct xe_device *xe = arg;
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_hpd_cancel_work(display);
intel_display_driver_remove_nogem(display);
intel_display_driver_remove_noirq(display);
intel_opregion_cleanup(display);
intel_display_power_cleanup(display);
}
int xe_display_init_early(struct xe_device *xe)
{
struct intel_display *display = xe->display;
int err;
if (!xe->info.probe_display)
return 0;
/* Fake uncore lock */
spin_lock_init(&xe->uncore.lock);
intel_display_driver_early_probe(display);
intel_display_device_info_runtime_init(display);
/* Display may have been disabled at runtime init */
if (!intel_display_device_present(display)) {
xe->info.probe_display = false;
unset_display_features(xe);
return 0;
}
/* Early display init.. */
intel_opregion_setup(display);
/*
* Fill the dram structure to get the system dram info. This will be
* used for memory latency calculation.
*/
err = intel_dram_detect(display);
if (err)
goto err_opregion;
intel_bw_init_hw(display);
err = intel_display_driver_probe_noirq(display);
if (err)
goto err_opregion;
err = intel_display_driver_probe_nogem(display);
if (err)
goto err_noirq;
return devm_add_action_or_reset(xe->drm.dev, xe_display_fini_early, xe);
err_noirq:
intel_display_driver_remove_noirq(display);
intel_display_power_cleanup(display);
err_opregion:
intel_opregion_cleanup(display);
return err;
}
static void xe_display_fini(void *arg)
{
struct xe_device *xe = arg;
struct intel_display *display = xe->display;
intel_hpd_poll_fini(display);
intel_hdcp_component_fini(display);
intel_audio_deinit(display);
intel_display_driver_remove(display);
}
int xe_display_init(struct xe_device *xe)
{
struct intel_display *display = xe->display;
int err;
if (!xe->info.probe_display)
return 0;
err = intel_display_driver_probe(display);
if (err)
return err;
return devm_add_action_or_reset(xe->drm.dev, xe_display_fini, xe);
}
void xe_display_register(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_driver_register(display);
intel_display_power_enable(display);
}
void xe_display_unregister(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_power_disable(display);
intel_display_driver_unregister(display);
}
/* IRQ-related functions */
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
if (master_ctl & DISPLAY_IRQ)
intel_display_irq_handler(display, NULL);
}
void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
if (gu_misc_iir & GU_MISC_GSE)
intel_opregion_asle_intr(display);
}
void xe_display_irq_reset(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_irq_reset(display);
}
void xe_display_irq_postinstall(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_irq_postinstall(display);
}
static bool suspend_to_idle(void)
{
#if IS_ENABLED(CONFIG_ACPI_SLEEP)
if (acpi_target_system_state() < ACPI_STATE_S3)
return true;
#endif
return false;
}
static void xe_display_enable_d3cold(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
/*
* We do a lot of poking in a lot of registers, make sure they work
* properly.
*/
intel_display_power_disable(display);
intel_display_flush_cleanup_work(display);
intel_opregion_suspend(display, PCI_D3cold);
intel_dmc_suspend(display);
if (intel_display_device_present(display))
intel_hpd_poll_enable(display);
}
static void xe_display_disable_d3cold(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_dmc_resume(display);
if (intel_display_device_present(display))
drm_mode_config_reset(&xe->drm);
intel_display_driver_init_hw(display);
intel_hpd_init(display);
if (intel_display_device_present(display))
intel_hpd_poll_disable(display);
intel_opregion_resume(display);
intel_display_power_enable(display);
}
void xe_display_pm_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
/*
* We do a lot of poking in a lot of registers, make sure they work
* properly.
*/
intel_display_power_disable(display);
drm_client_dev_suspend(&xe->drm);
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);
}
intel_display_flush_cleanup_work(display);
intel_encoder_block_all_hpds(display);
intel_hpd_cancel_work(display);
if (intel_display_device_present(display)) {
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
}
intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold);
intel_dmc_suspend(display);
}
void xe_display_pm_shutdown(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_power_disable(display);
drm_client_dev_suspend(&xe->drm);
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);
}
intel_display_flush_cleanup_work(display);
intel_dp_mst_suspend(display);
intel_encoder_block_all_hpds(display);
intel_hpd_cancel_work(display);
if (intel_display_device_present(display))
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
intel_encoder_shutdown_all(display);
intel_opregion_suspend(display, PCI_D3cold);
intel_dmc_suspend(display);
}
void xe_display_pm_runtime_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
if (xe->d3cold.allowed) {
xe_display_enable_d3cold(xe);
return;
}
intel_hpd_poll_enable(display);
}
void xe_display_pm_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
intel_display_power_suspend_late(display, s2idle);
}
void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
if (xe->d3cold.allowed)
xe_display_pm_suspend_late(xe);
/*
* If xe_display_pm_suspend_late() is not called, it is likely
* that we will be on dynamic DC states with DMC wakelock enabled. We
* need to flush the release work in that case.
*/
intel_dmc_wl_flush_release_work(display);
}
void xe_display_pm_shutdown_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
/*
* The only requirement is to reboot with display DC states disabled,
* for now leaving all display power wells in the INIT power domain
* enabled.
*/
intel_display_power_driver_remove(display);
}
void xe_display_pm_resume_early(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_power_resume_early(display);
}
void xe_display_pm_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_dmc_resume(display);
if (intel_display_device_present(display))
drm_mode_config_reset(&xe->drm);
intel_display_driver_init_hw(display);
if (intel_display_device_present(display))
intel_display_driver_resume_access(display);
intel_hpd_init(display);
intel_encoder_unblock_all_hpds(display);
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 (intel_display_device_present(display))
intel_hpd_poll_disable(display);
intel_opregion_resume(display);
drm_client_dev_resume(&xe->drm);
intel_display_power_enable(display);
}
void xe_display_pm_runtime_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
if (xe->d3cold.allowed) {
xe_display_disable_d3cold(xe);
return;
}
intel_hpd_init(display);
intel_hpd_poll_disable(display);
skl_watermark_ipc_update(display);
}
static void display_device_remove(struct drm_device *dev, void *arg)
{
struct xe_device *xe = arg;
intel_display_device_remove(xe->display);
xe->display = NULL;
}
static bool irq_enabled(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
return atomic_read(&xe->irq.enabled);
}
static void irq_synchronize(struct drm_device *drm)
{
synchronize_irq(to_pci_dev(drm->dev)->irq);
}
static const struct intel_display_irq_interface xe_display_irq_interface = {
.enabled = irq_enabled,
.synchronize = irq_synchronize,
};
static bool has_auxccs(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
return xe->info.platform == XE_ALDERLAKE_P;
}
static const struct intel_display_parent_interface parent = {
.bo = &xe_display_bo_interface,
.dsb = &xe_display_dsb_interface,
.fb_pin = &xe_display_fb_pin_interface,
.frontbuffer = &xe_display_frontbuffer_interface,
.hdcp = &xe_display_hdcp_interface,
.initial_plane = &xe_display_initial_plane_interface,
.irq = &xe_display_irq_interface,
.panic = &xe_display_panic_interface,
.pcode = &xe_display_pcode_interface,
.rpm = &xe_display_rpm_interface,
.stolen = &xe_display_stolen_interface,
.has_auxccs = has_auxccs,
};
/**
* xe_display_probe - probe display and create display struct
* @xe: XE device instance
*
* Initialize all fields used by the display part.
*
* TODO: once everything can be inside a single struct, make the struct opaque
* to the rest of xe and return it to be xe->display.
*
* Returns: 0 on success
*/
int xe_display_probe(struct xe_device *xe)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
struct intel_display *display;
int err;
if (!xe->info.probe_display)
goto no_display;
display = intel_display_device_probe(pdev, &parent);
if (IS_ERR(display))
return PTR_ERR(display);
xe->display = display;
err = drmm_add_action_or_reset(&xe->drm, display_device_remove, xe);
if (err)
return err;
if (intel_display_device_present(display))
return 0;
no_display:
xe->info.probe_display = false;
unset_display_features(xe);
return 0;
}
#ifdef CONFIG_DRM_FBDEV_EMULATION
int xe_display_driver_fbdev_probe(struct drm_fb_helper *fbh,
struct drm_fb_helper_surface_size *sizes)
{
return intel_fbdev_driver_fbdev_probe(fbh, sizes);
}
#endif
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Intel Corporation
*/
#ifndef _XE_DISPLAY_H_
#define _XE_DISPLAY_H_
#include <linux/types.h>
struct drm_driver;
struct drm_fb_helper;
struct drm_fb_helper_surface_size;
struct pci_dev;
struct xe_device;
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
bool xe_display_driver_probe_defer(struct pci_dev *pdev);
int xe_display_driver_fbdev_probe(struct drm_fb_helper *fbh,
struct drm_fb_helper_surface_size *sizes);
int xe_display_probe(struct xe_device *xe);
int xe_display_init_early(struct xe_device *xe);
int xe_display_init(struct xe_device *xe);
void xe_display_register(struct xe_device *xe);
void xe_display_unregister(struct xe_device *xe);
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl);
void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir);
void xe_display_irq_reset(struct xe_device *xe);
void xe_display_irq_postinstall(struct xe_device *xe);
void xe_display_pm_suspend(struct xe_device *xe);
void xe_display_pm_shutdown(struct xe_device *xe);
void xe_display_pm_suspend_late(struct xe_device *xe);
void xe_display_pm_shutdown_late(struct xe_device *xe);
void xe_display_pm_resume_early(struct xe_device *xe);
void xe_display_pm_resume(struct xe_device *xe);
void xe_display_pm_runtime_suspend(struct xe_device *xe);
void xe_display_pm_runtime_suspend_late(struct xe_device *xe);
void xe_display_pm_runtime_resume(struct xe_device *xe);
#define XE_DISPLAY_DRIVER_FEATURES (DRIVER_MODESET | DRIVER_ATOMIC)
#define XE_DISPLAY_DRIVER_OPS \
.fbdev_probe = PTR_IF(IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION), \
xe_display_driver_fbdev_probe)
#else
#define XE_DISPLAY_DRIVER_FEATURES 0
#define XE_DISPLAY_DRIVER_OPS \
.fbdev_probe = NULL
static inline int xe_display_driver_probe_defer(struct pci_dev *pdev) { return 0; }
static inline int xe_display_probe(struct xe_device *xe) { return 0; }
static inline int xe_display_init_early(struct xe_device *xe) { return 0; }
static inline int xe_display_init(struct xe_device *xe) { return 0; }
static inline void xe_display_register(struct xe_device *xe) {}
static inline void xe_display_unregister(struct xe_device *xe) {}
static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {}
static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {}
static inline void xe_display_irq_reset(struct xe_device *xe) {}
static inline void xe_display_irq_postinstall(struct xe_device *xe) {}
static inline void xe_display_pm_suspend(struct xe_device *xe) {}
static inline void xe_display_pm_shutdown(struct xe_device *xe) {}
static inline void xe_display_pm_suspend_late(struct xe_device *xe) {}
static inline void xe_display_pm_shutdown_late(struct xe_device *xe) {}
static inline void xe_display_pm_resume_early(struct xe_device *xe) {}
static inline void xe_display_pm_resume(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_suspend_late(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_resume(struct xe_device *xe) {}
#endif /* CONFIG_DRM_XE_DISPLAY */
#endif /* _XE_DISPLAY_H_ */
@@ -0,0 +1,233 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2024 Intel Corporation */
#include <linux/fb.h>
#include <drm/drm_gem.h>
#include <drm/intel/display_parent_interface.h>
#include "intel_fb.h"
#include "xe_bo.h"
#include "xe_display_bo.h"
#include "xe_pxp.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_wa.h"
#include <generated/xe_device_wa_oob.h>
static bool xe_display_bo_is_protected(struct drm_gem_object *obj)
{
return xe_bo_is_protected(gem_to_xe_bo(obj));
}
static int xe_display_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, int size)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
return xe_bo_read(bo, offset, dst, size);
}
static int xe_display_bo_framebuffer_init(struct drm_gem_object *obj,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
struct xe_device *xe = to_xe_device(bo->ttm.base.dev);
int ret;
/*
* Some modifiers require physical alignment of 64KiB VRAM pages;
* require that the BO in those cases is created correctly.
*/
if (XE_IOCTL_DBG(xe, intel_fb_needs_64k_phys(mode_cmd->modifier[0]) &&
!(bo->flags & XE_BO_FLAG_NEEDS_64K)))
return -EINVAL;
xe_bo_get(bo);
ret = ttm_bo_reserve(&bo->ttm, true, false, NULL);
if (ret)
goto err;
if (!(bo->flags & XE_BO_FLAG_FORCE_WC) &&
bo->ttm.type != ttm_bo_type_sg) {
/*
* XE_BO_FLAG_FORCE_WC should ideally be set at creation, or is
* automatically set when creating FB. We cannot change caching
* mode when the bo is VM_BINDed, so we can only set
* coherency with display when unbound.
*/
if (XE_IOCTL_DBG(xe, xe_bo_is_vm_bound(bo))) {
ttm_bo_unreserve(&bo->ttm);
ret = -EINVAL;
goto err;
}
bo->flags |= XE_BO_FLAG_FORCE_WC;
}
ttm_bo_unreserve(&bo->ttm);
return 0;
err:
xe_bo_put(bo);
return ret;
}
static void xe_display_bo_framebuffer_fini(struct drm_gem_object *obj)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
if (bo->flags & XE_BO_FLAG_PINNED) {
/* Unpin our kernel fb first */
xe_bo_lock(bo, false);
xe_bo_unpin(bo);
xe_bo_unlock(bo);
}
xe_bo_put(bo);
}
static struct drm_gem_object *
xe_display_bo_framebuffer_lookup(struct drm_device *drm,
struct drm_file *filp,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_bo *bo;
struct drm_gem_object *gem = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
if (!gem)
return ERR_PTR(-ENOENT);
bo = gem_to_xe_bo(gem);
/* Require vram placement or dma-buf import */
if (IS_DGFX(xe) &&
!xe_bo_can_migrate(bo, XE_PL_VRAM0) &&
bo->ttm.type != ttm_bo_type_sg) {
drm_gem_object_put(gem);
return ERR_PTR(-EREMOTE);
}
return gem;
}
#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
/*
* FIXME: There shouldn't be any reason to have XE_PAGE_SIZE stride
* alignment. The same 64 as i915 uses should be fine, and we shouldn't need to
* have driver specific values. However, dropping the stride alignment to 64
* leads to underflowing the bo pin count in the atomic cleanup work.
*/
static u32 xe_display_bo_fbdev_pitch_align(u32 stride)
{
return ALIGN(stride, XE_PAGE_SIZE);
}
bool xe_display_bo_fbdev_prefer_stolen(struct xe_device *xe, unsigned int size)
{
struct ttm_resource_manager *stolen;
stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
if (!stolen)
return false;
if (IS_DGFX(xe))
return false;
if (XE_DEVICE_WA(xe, 22019338487_display))
return false;
/*
* If the FB is too big, just don't use it since fbdev is not very
* important and we should probably use that space with FBC or other
* features.
*/
return stolen->size >= (size * 2) >> PAGE_SHIFT;
}
static struct drm_gem_object *xe_display_bo_fbdev_create(struct drm_device *drm, int size)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_bo *obj;
obj = ERR_PTR(-ENODEV);
if (xe_display_bo_fbdev_prefer_stolen(xe, size)) {
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe),
size,
ttm_bo_type_kernel,
XE_BO_FLAG_FORCE_WC |
XE_BO_FLAG_STOLEN |
XE_BO_FLAG_GGTT,
false);
if (!IS_ERR(obj))
drm_info(&xe->drm, "Allocated fbdev into stolen\n");
else
drm_info(&xe->drm, "Allocated fbdev into stolen failed: %li\n", PTR_ERR(obj));
} else {
drm_info(&xe->drm, "Allocating fbdev: Stolen memory not preferred.\n");
}
if (IS_ERR(obj)) {
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size,
ttm_bo_type_kernel,
XE_BO_FLAG_FORCE_WC |
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_GGTT,
false);
}
if (IS_ERR(obj)) {
drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj);
return ERR_PTR(-ENOMEM);
}
return &obj->ttm.base;
}
static void xe_display_bo_fbdev_destroy(struct drm_gem_object *obj)
{
xe_bo_unpin_map_no_vm(gem_to_xe_bo(obj));
}
static int xe_display_bo_fbdev_fill_info(struct drm_gem_object *_obj, struct fb_info *info,
struct i915_vma *vma)
{
struct xe_bo *obj = gem_to_xe_bo(_obj);
struct pci_dev *pdev = to_pci_dev(_obj->dev->dev);
if (!(obj->flags & XE_BO_FLAG_SYSTEM)) {
if (obj->flags & XE_BO_FLAG_STOLEN)
info->fix.smem_start = xe_ttm_stolen_io_offset(obj, 0);
else
info->fix.smem_start =
pci_resource_start(pdev, 2) +
xe_bo_addr(obj, 0, XE_PAGE_SIZE);
info->fix.smem_len = obj->ttm.base.size;
} else {
/* XXX: Pure fiction, as the BO may not be physically accessible.. */
info->fix.smem_start = 0;
info->fix.smem_len = obj->ttm.base.size;
}
XE_WARN_ON(iosys_map_is_null(&obj->vmap));
info->screen_base = obj->vmap.vaddr_iomem;
info->screen_size = obj->ttm.base.size;
return 0;
}
#endif
const struct intel_display_bo_interface xe_display_bo_interface = {
.is_protected = xe_display_bo_is_protected,
.key_check = xe_pxp_obj_key_check,
.fb_mmap = drm_gem_prime_mmap,
.read_from_page = xe_display_bo_read_from_page,
.framebuffer_init = xe_display_bo_framebuffer_init,
.framebuffer_fini = xe_display_bo_framebuffer_fini,
.framebuffer_lookup = xe_display_bo_framebuffer_lookup,
#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
.fbdev_create = xe_display_bo_fbdev_create,
.fbdev_destroy = xe_display_bo_fbdev_destroy,
.fbdev_fill_info = xe_display_bo_fbdev_fill_info,
.fbdev_pitch_align = xe_display_bo_fbdev_pitch_align,
#endif
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef __XE_DISPLAY_BO_H__
#define __XE_DISPLAY_BO_H__
#include <linux/types.h>
struct xe_device;
bool xe_display_bo_fbdev_prefer_stolen(struct xe_device *xe, unsigned int size);
extern const struct intel_display_bo_interface xe_display_bo_interface;
#endif
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2026 Intel Corporation */
#include <drm/intel/display_parent_interface.h>
#include "xe_device.h"
#include "xe_pcode.h"
static int xe_display_pcode_read(struct drm_device *drm, u32 mbox, u32 *val, u32 *val1)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_tile *tile = xe_device_get_root_tile(xe);
return xe_pcode_read(tile, mbox, val, val1);
}
static int xe_display_pcode_write_timeout(struct drm_device *drm, u32 mbox, u32 val, int timeout_ms)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_tile *tile = xe_device_get_root_tile(xe);
return xe_pcode_write_timeout(tile, mbox, val, timeout_ms);
}
static int xe_display_pcode_request(struct drm_device *drm, u32 mbox, u32 request,
u32 reply_mask, u32 reply, int timeout_base_ms)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_tile *tile = xe_device_get_root_tile(xe);
return xe_pcode_request(tile, mbox, request, reply_mask, reply, timeout_base_ms);
}
const struct intel_display_pcode_interface xe_display_pcode_interface = {
.read = xe_display_pcode_read,
.write = xe_display_pcode_write_timeout,
.request = xe_display_pcode_request,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef __XE_DISPLAY_PCODE_H__
#define __XE_DISPLAY_PCODE_H__
extern const struct intel_display_pcode_interface xe_display_pcode_interface;
#endif
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
#include <drm/intel/display_parent_interface.h>
#include "intel_display_core.h"
#include "intel_display_rpm.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_pm.h"
/* -ENOENT means we got the ref, but there's no tracking */
#define INTEL_WAKEREF_DEF ERR_PTR(-ENOENT)
static struct ref_tracker *xe_display_rpm_get(const struct drm_device *drm)
{
return xe_pm_runtime_resume_and_get(to_xe_device(drm)) ? INTEL_WAKEREF_DEF : NULL;
}
static struct ref_tracker *xe_display_rpm_get_if_in_use(const struct drm_device *drm)
{
return xe_pm_runtime_get_if_in_use(to_xe_device(drm)) ? INTEL_WAKEREF_DEF : NULL;
}
static struct ref_tracker *xe_display_rpm_get_noresume(const struct drm_device *drm)
{
xe_pm_runtime_get_noresume(to_xe_device(drm));
return INTEL_WAKEREF_DEF;
}
static void xe_display_rpm_put(const struct drm_device *drm, struct ref_tracker *wakeref)
{
if (wakeref)
xe_pm_runtime_put(to_xe_device(drm));
}
static void xe_display_rpm_put_unchecked(const struct drm_device *drm)
{
xe_pm_runtime_put(to_xe_device(drm));
}
static bool xe_display_rpm_suspended(const struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
return pm_runtime_suspended(xe->drm.dev);
}
static void xe_display_rpm_assert_held(const struct drm_device *drm)
{
/* FIXME */
}
static void xe_display_rpm_assert_block(const struct drm_device *drm)
{
/* FIXME */
}
static void xe_display_rpm_assert_unblock(const struct drm_device *drm)
{
/* FIXME */
}
const struct intel_display_rpm_interface xe_display_rpm_interface = {
.get = xe_display_rpm_get,
.get_raw = xe_display_rpm_get,
.get_if_in_use = xe_display_rpm_get_if_in_use,
.get_noresume = xe_display_rpm_get_noresume,
.put = xe_display_rpm_put,
.put_raw = xe_display_rpm_put,
.put_unchecked = xe_display_rpm_put_unchecked,
.suspended = xe_display_rpm_suspended,
.assert_held = xe_display_rpm_assert_held,
.assert_block = xe_display_rpm_assert_block,
.assert_unblock = xe_display_rpm_assert_unblock
};
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2025 Intel Corporation
*/
#ifndef _XE_DISPLAY_RPM_H_
#define _XE_DISPLAY_RPM_H_
extern const struct intel_display_rpm_interface xe_display_rpm_interface;
#endif /* _XE_DISPLAY_RPM_H_ */
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef _XE_DISPLAY_VMA_H_
#define _XE_DISPLAY_VMA_H_
#include <linux/refcount.h>
struct xe_bo;
struct xe_ggtt_node;
struct i915_vma {
refcount_t ref;
struct xe_bo *bo, *dpt;
struct xe_ggtt_node *node;
};
#endif
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2024 Intel Corporation
*/
#include "intel_display_core.h"
#include "intel_display_wa.h"
#include "xe_device.h"
#include "xe_wa.h"
#include <generated/xe_wa_oob.h>
bool intel_display_needs_wa_16023588340(struct intel_display *display)
{
struct xe_device *xe = to_xe_device(display->drm);
struct xe_gt *wa_gt = xe_root_mmio_gt(xe);
return wa_gt && XE_GT_WA(wa_gt, 16023588340);
}
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2023, Intel Corporation.
*/
#include <drm/intel/display_parent_interface.h>
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_dsb_buffer.h"
struct intel_dsb_buffer {
u32 *cmd_buf;
struct xe_bo *bo;
size_t buf_size;
};
static u32 xe_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf)
{
return xe_bo_ggtt_addr(dsb_buf->bo);
}
static void xe_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val)
{
iosys_map_wr(&dsb_buf->bo->vmap, idx * 4, u32, val);
}
static u32 xe_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx)
{
return iosys_map_rd(&dsb_buf->bo->vmap, idx * 4, u32);
}
static void xe_dsb_buffer_fill(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size)
{
WARN_ON(idx > (dsb_buf->buf_size - size) / sizeof(*dsb_buf->cmd_buf));
iosys_map_memset(&dsb_buf->bo->vmap, idx * 4, val, size);
}
static struct intel_dsb_buffer *xe_dsb_buffer_create(struct drm_device *drm, size_t size)
{
struct xe_device *xe = to_xe_device(drm);
struct intel_dsb_buffer *dsb_buf;
struct xe_bo *obj;
int ret;
dsb_buf = kzalloc_obj(*dsb_buf);
if (!dsb_buf)
return ERR_PTR(-ENOMEM);
/* Set scanout flag for WC mapping */
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe),
PAGE_ALIGN(size),
ttm_bo_type_kernel,
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_FORCE_WC |
XE_BO_FLAG_GGTT,
false);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
goto err_pin_map;
}
dsb_buf->bo = obj;
dsb_buf->buf_size = size;
return dsb_buf;
err_pin_map:
kfree(dsb_buf);
return ERR_PTR(ret);
}
static void xe_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf)
{
xe_bo_unpin_map_no_vm(dsb_buf->bo);
kfree(dsb_buf);
}
static void xe_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf)
{
struct xe_device *xe = dsb_buf->bo->tile->xe;
/*
* The memory barrier here is to ensure coherency of DSB vs MMIO,
* both for weak ordering archs and discrete cards.
*/
xe_device_wmb(xe);
xe_device_l2_flush(xe);
}
const struct intel_display_dsb_interface xe_display_dsb_interface = {
.ggtt_offset = xe_dsb_buffer_ggtt_offset,
.write = xe_dsb_buffer_write,
.read = xe_dsb_buffer_read,
.fill = xe_dsb_buffer_fill,
.create = xe_dsb_buffer_create,
.cleanup = xe_dsb_buffer_cleanup,
.flush_map = xe_dsb_buffer_flush_map,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef __XE_DSB_BUFFER_H__
#define __XE_DSB_BUFFER_H__
extern const struct intel_display_dsb_interface xe_display_dsb_interface;
#endif
@@ -0,0 +1,491 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021 Intel Corporation
*/
#include <drm/intel/display_parent_interface.h>
#include <drm/ttm/ttm_bo.h>
/* FIXME move the types to parent interface? */
#include "i915_gtt_view_types.h"
/* FIXME move intel_remapped_info_size() & co. to parent interface? */
#include "intel_fb.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_display_vma.h"
#include "xe_fb_pin.h"
#include "xe_ggtt.h"
#include "xe_pat.h"
#include "xe_pm.h"
#include "xe_vram_types.h"
static void
write_dpt_rotated(struct xe_bo *bo, struct iosys_map *map, u32 *dpt_ofs, u32 bo_ofs,
u32 width, u32 height, u32 src_stride, u32 dst_stride)
{
struct xe_device *xe = xe_bo_device(bo);
struct xe_ggtt *ggtt = xe_device_get_root_tile(xe)->mem.ggtt;
u32 column, row;
u64 pte = xe_ggtt_encode_pte_flags(ggtt, bo, xe_cache_pat_idx(xe, XE_CACHE_NONE));
/* TODO: Maybe rewrite so we can traverse the bo addresses sequentially,
* by writing dpt/ggtt in a different order?
*/
for (column = 0; column < width; column++) {
u32 src_idx = src_stride * (height - 1) + column + bo_ofs;
for (row = 0; row < height; row++) {
u64 addr = xe_bo_addr(bo, src_idx * XE_PAGE_SIZE, XE_PAGE_SIZE);
iosys_map_wr(map, *dpt_ofs, u64, pte | addr);
*dpt_ofs += 8;
src_idx -= src_stride;
}
/* The DE ignores the PTEs for the padding tiles */
*dpt_ofs += (dst_stride - height) * 8;
}
/* Align to next page */
*dpt_ofs = ALIGN(*dpt_ofs, 4096);
}
static unsigned int
write_dpt_padding(struct iosys_map *map, unsigned int dest, unsigned int pad)
{
/* The DE ignores the PTEs for the padding tiles */
return dest + pad * sizeof(u64);
}
static unsigned int
write_dpt_remapped_linear(struct xe_bo *bo, struct iosys_map *map,
unsigned int dest,
const struct intel_remapped_plane_info *plane)
{
struct xe_device *xe = xe_bo_device(bo);
struct xe_ggtt *ggtt = xe_device_get_root_tile(xe)->mem.ggtt;
const u64 pte = xe_ggtt_encode_pte_flags(ggtt, bo,
xe_cache_pat_idx(xe, XE_CACHE_NONE));
unsigned int offset = plane->offset * XE_PAGE_SIZE;
unsigned int size = plane->size;
while (size--) {
u64 addr = xe_bo_addr(bo, offset, XE_PAGE_SIZE);
iosys_map_wr(map, dest, u64, addr | pte);
dest += sizeof(u64);
offset += XE_PAGE_SIZE;
}
return dest;
}
static unsigned int
write_dpt_remapped_tiled(struct xe_bo *bo, struct iosys_map *map,
unsigned int dest,
const struct intel_remapped_plane_info *plane)
{
struct xe_device *xe = xe_bo_device(bo);
struct xe_ggtt *ggtt = xe_device_get_root_tile(xe)->mem.ggtt;
const u64 pte = xe_ggtt_encode_pte_flags(ggtt, bo,
xe_cache_pat_idx(xe, XE_CACHE_NONE));
unsigned int offset, column, row;
for (row = 0; row < plane->height; row++) {
offset = (plane->offset + plane->src_stride * row) *
XE_PAGE_SIZE;
for (column = 0; column < plane->width; column++) {
u64 addr = xe_bo_addr(bo, offset, XE_PAGE_SIZE);
iosys_map_wr(map, dest, u64, addr | pte);
dest += sizeof(u64);
offset += XE_PAGE_SIZE;
}
dest = write_dpt_padding(map, dest,
plane->dst_stride - plane->width);
}
return dest;
}
static void
write_dpt_remapped(struct xe_bo *bo,
const struct intel_remapped_info *remap_info,
struct iosys_map *map)
{
unsigned int i, dest = 0;
for (i = 0; i < ARRAY_SIZE(remap_info->plane); i++) {
const struct intel_remapped_plane_info *plane =
&remap_info->plane[i];
if (!plane->linear && !plane->width && !plane->height)
continue;
if (dest && remap_info->plane_alignment) {
const unsigned int index = dest / sizeof(u64);
const unsigned int pad =
ALIGN(index, remap_info->plane_alignment) -
index;
dest = write_dpt_padding(map, dest, pad);
}
if (plane->linear)
dest = write_dpt_remapped_linear(bo, map, dest, plane);
else
dest = write_dpt_remapped_tiled(bo, map, dest, plane);
}
}
static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj,
const struct intel_fb_pin_params *pin_params,
struct i915_vma *vma)
{
struct xe_device *xe = to_xe_device(obj->dev);
struct xe_tile *tile0 = xe_device_get_root_tile(xe);
struct xe_ggtt *ggtt = tile0->mem.ggtt;
const struct i915_gtt_view *view = pin_params->view;
struct xe_bo *bo = gem_to_xe_bo(obj), *dpt;
u32 dpt_size, size = bo->ttm.base.size;
if (view->type == I915_GTT_VIEW_NORMAL)
dpt_size = ALIGN(size / XE_PAGE_SIZE * 8, XE_PAGE_SIZE);
else if (view->type == I915_GTT_VIEW_REMAPPED)
dpt_size = ALIGN(intel_remapped_info_size(&view->remapped) * 8,
XE_PAGE_SIZE);
else
/* display uses 4K tiles instead of bytes here, convert to entries.. */
dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8,
XE_PAGE_SIZE);
dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
dpt_size, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_VRAM_IF_DGFX(tile0) |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_PAGETABLE |
XE_BO_FLAG_FORCE_WC,
pin_params->alignment, false);
if (IS_ERR(dpt))
return PTR_ERR(dpt);
if (view->type == I915_GTT_VIEW_NORMAL) {
u64 pte = xe_ggtt_encode_pte_flags(ggtt, bo, xe_cache_pat_idx(xe, XE_CACHE_NONE));
u32 x;
for (x = 0; x < size / XE_PAGE_SIZE; x++) {
u64 addr = xe_bo_addr(bo, x * XE_PAGE_SIZE, XE_PAGE_SIZE);
iosys_map_wr(&dpt->vmap, x * 8, u64, pte | addr);
}
} else if (view->type == I915_GTT_VIEW_REMAPPED) {
write_dpt_remapped(bo, &view->remapped, &dpt->vmap);
} else {
const struct intel_rotation_info *rot_info = &view->rotated;
u32 i, dpt_ofs = 0;
for (i = 0; i < ARRAY_SIZE(rot_info->plane); i++)
write_dpt_rotated(bo, &dpt->vmap, &dpt_ofs,
rot_info->plane[i].offset,
rot_info->plane[i].width,
rot_info->plane[i].height,
rot_info->plane[i].src_stride,
rot_info->plane[i].dst_stride);
}
vma->dpt = dpt;
vma->node = dpt->ggtt_node[tile0->id];
/* Ensure DPT writes are flushed */
xe_device_l2_flush(xe);
return 0;
}
static void
write_ggtt_rotated(struct xe_ggtt *ggtt, u32 *ggtt_ofs,
u64 pte_flags,
xe_ggtt_set_pte_fn write_pte,
struct xe_bo *bo, u32 bo_ofs,
u32 width, u32 height, u32 src_stride, u32 dst_stride)
{
u32 column, row;
for (column = 0; column < width; column++) {
u32 src_idx = src_stride * (height - 1) + column + bo_ofs;
for (row = 0; row < height; row++) {
u64 addr = xe_bo_addr(bo, src_idx * XE_PAGE_SIZE, XE_PAGE_SIZE);
write_pte(ggtt, *ggtt_ofs, pte_flags | addr);
*ggtt_ofs += XE_PAGE_SIZE;
src_idx -= src_stride;
}
/* The DE ignores the PTEs for the padding tiles */
*ggtt_ofs += (dst_stride - height) * XE_PAGE_SIZE;
}
}
struct fb_rotate_args {
const struct i915_gtt_view *view;
struct xe_bo *bo;
};
static void write_ggtt_rotated_node(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
u64 pte_flags, xe_ggtt_set_pte_fn write_pte, void *data)
{
struct fb_rotate_args *args = data;
struct xe_bo *bo = args->bo;
const struct intel_rotation_info *rot_info = &args->view->rotated;
u32 ggtt_ofs = xe_ggtt_node_addr(node);
for (u32 i = 0; i < ARRAY_SIZE(rot_info->plane); i++)
write_ggtt_rotated(ggtt, &ggtt_ofs, pte_flags, write_pte,
bo, rot_info->plane[i].offset,
rot_info->plane[i].width,
rot_info->plane[i].height,
rot_info->plane[i].src_stride,
rot_info->plane[i].dst_stride);
}
static int __xe_pin_fb_vma_ggtt(struct drm_gem_object *obj,
const struct intel_fb_pin_params *pin_params,
struct i915_vma *vma)
{
const struct i915_gtt_view *view = pin_params->view;
struct xe_bo *bo = gem_to_xe_bo(obj);
struct xe_device *xe = to_xe_device(obj->dev);
struct xe_tile *tile0 = xe_device_get_root_tile(xe);
struct xe_ggtt *ggtt = tile0->mem.ggtt;
u64 pte, size;
u32 align;
int ret = 0;
/* TODO: Consider sharing framebuffer mapping?
* embed i915_vma inside intel_framebuffer
*/
guard(xe_pm_runtime_noresume)(xe);
align = max(XE_PAGE_SIZE, pin_params->alignment);
if (xe_bo_is_vram(bo) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
align = max(align, SZ_64K);
/* Fast case, preallocated GGTT view? */
if (bo->ggtt_node[tile0->id] && view->type == I915_GTT_VIEW_NORMAL) {
vma->node = bo->ggtt_node[tile0->id];
return 0;
}
/* TODO: Consider sharing framebuffer mapping?
* embed i915_vma inside intel_framebuffer
*/
if (view->type == I915_GTT_VIEW_NORMAL)
size = xe_bo_size(bo);
else
/* display uses tiles instead of bytes here, so convert it back.. */
size = intel_rotation_info_size(&view->rotated) * XE_PAGE_SIZE;
pte = xe_ggtt_encode_pte_flags(ggtt, bo, xe_cache_pat_idx(xe, XE_CACHE_NONE));
vma->node = xe_ggtt_insert_node_transform(ggtt, bo, pte,
ALIGN(size, align), align,
view->type == I915_GTT_VIEW_NORMAL ?
NULL : write_ggtt_rotated_node,
&(struct fb_rotate_args){view, bo});
if (IS_ERR(vma->node))
ret = PTR_ERR(vma->node);
return ret;
}
static struct i915_vma *__xe_pin_fb_vma(struct drm_gem_object *obj, bool is_dpt,
const struct intel_fb_pin_params *pin_params)
{
struct xe_device *xe = to_xe_device(obj->dev);
struct i915_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL);
struct xe_bo *bo = gem_to_xe_bo(obj);
struct xe_validation_ctx ctx;
struct drm_exec exec;
int ret = 0;
/* We reject creating !SCANOUT fb's, so this is weird.. */
drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC) &&
bo->ttm.type != ttm_bo_type_sg);
if (!vma)
return ERR_PTR(-ENODEV);
refcount_set(&vma->ref, 1);
if (IS_DGFX(to_xe_device(bo->ttm.base.dev)) &&
pin_params->needs_cpu_lmem_access &&
!(bo->flags & XE_BO_FLAG_NEEDS_CPU_ACCESS)) {
struct xe_vram_region *vram = xe_device_get_root_tile(xe)->mem.vram;
/*
* If we need to able to access the clear-color value stored in
* the buffer, then we require that such buffers are also CPU
* accessible. This is important on small-bar systems where
* only some subset of VRAM is CPU accessible.
*/
if (xe_vram_region_io_size(vram) < xe_vram_region_usable_size(vram)) {
ret = -EINVAL;
goto err;
}
}
/*
* Pin the framebuffer, we can't use xe_bo_(un)pin functions as the
* assumptions are incorrect for framebuffers
*/
xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {.interruptible = true},
ret) {
ret = drm_exec_lock_obj(&exec, &bo->ttm.base);
drm_exec_retry_on_contention(&exec);
if (ret)
break;
if (IS_DGFX(xe))
ret = xe_bo_migrate(bo, XE_PL_VRAM0, NULL, &exec);
else
ret = xe_bo_validate(bo, NULL, true, &exec);
drm_exec_retry_on_contention(&exec);
xe_validation_retry_on_oom(&ctx, &ret);
if (!ret)
ttm_bo_pin(&bo->ttm);
}
if (ret)
goto err;
vma->bo = bo;
if (is_dpt)
ret = __xe_pin_fb_vma_dpt(obj, pin_params, vma);
else
ret = __xe_pin_fb_vma_ggtt(obj, pin_params, vma);
if (ret)
goto err_unpin;
return vma;
err_unpin:
ttm_bo_reserve(&bo->ttm, false, false, NULL);
ttm_bo_unpin(&bo->ttm);
ttm_bo_unreserve(&bo->ttm);
err:
kfree(vma);
return ERR_PTR(ret);
}
static void __xe_unpin_fb_vma(struct i915_vma *vma)
{
u8 tile_id = xe_device_get_root_tile(xe_bo_device(vma->bo))->id;
if (!refcount_dec_and_test(&vma->ref))
return;
if (vma->dpt)
xe_bo_unpin_map_no_vm(vma->dpt);
else if (vma->bo->ggtt_node[tile_id] != vma->node)
xe_ggtt_node_remove(vma->node, false);
ttm_bo_reserve(&vma->bo->ttm, false, false, NULL);
ttm_bo_unpin(&vma->bo->ttm);
ttm_bo_unreserve(&vma->bo->ttm);
kfree(vma);
}
int xe_fb_pin_ggtt_pin(struct drm_gem_object *obj,
const struct intel_fb_pin_params *pin_params,
struct i915_vma **out_ggtt_vma,
u32 *out_offset,
int *out_fence_id)
{
struct i915_vma *ggtt_vma;
ggtt_vma = __xe_pin_fb_vma(obj, false, pin_params);
if (IS_ERR(ggtt_vma))
return PTR_ERR(ggtt_vma);
*out_ggtt_vma = ggtt_vma;
*out_offset = xe_ggtt_node_addr(ggtt_vma->node);
if (out_fence_id)
*out_fence_id = -1;
return 0;
}
static void xe_fb_pin_ggtt_unpin(struct i915_vma *ggtt_vma,
int fence_id)
{
WARN_ON(fence_id >= 0);
__xe_unpin_fb_vma(ggtt_vma);
}
static int xe_fb_pin_dpt_pin(struct drm_gem_object *obj, struct intel_dpt *dpt,
const struct intel_fb_pin_params *pin_params,
struct i915_vma **out_dpt_vma,
struct i915_vma **out_ggtt_vma,
u32 *out_offset)
{
struct i915_vma *ggtt_vma;
WARN_ON(dpt);
ggtt_vma = __xe_pin_fb_vma(obj, true, pin_params);
if (IS_ERR(ggtt_vma))
return PTR_ERR(ggtt_vma);
*out_dpt_vma = NULL; /* not used on xe */
*out_ggtt_vma = ggtt_vma;
*out_offset = xe_ggtt_node_addr(ggtt_vma->node);
return 0;
}
static void xe_fb_pin_dpt_unpin(struct intel_dpt *dpt,
struct i915_vma *dpt_vma,
struct i915_vma *ggtt_vma)
{
WARN_ON(dpt || dpt_vma);
__xe_unpin_fb_vma(ggtt_vma);
}
static struct i915_vma *
xe_fb_pin_reuse_vma(struct i915_vma *old_ggtt_vma,
struct drm_gem_object *old_obj,
const struct i915_gtt_view *old_view,
struct drm_gem_object *new_obj,
const struct i915_gtt_view *new_view,
u32 *out_offset)
{
if (old_ggtt_vma && old_obj == new_obj &&
!memcmp(old_view, new_view, sizeof(*new_view))) {
refcount_inc(&old_ggtt_vma->ref);
*out_offset = xe_ggtt_node_addr(old_ggtt_vma->node);
return old_ggtt_vma;
}
return NULL;
}
static void xe_fb_pin_get_map(struct i915_vma *vma, struct iosys_map *map)
{
*map = vma->bo->vmap;
}
const struct intel_display_fb_pin_interface xe_display_fb_pin_interface = {
.ggtt_pin = xe_fb_pin_ggtt_pin,
.ggtt_unpin = xe_fb_pin_ggtt_unpin,
.dpt_pin = xe_fb_pin_dpt_pin,
.dpt_unpin = xe_fb_pin_dpt_unpin,
.reuse_vma = xe_fb_pin_reuse_vma,
.get_map = xe_fb_pin_get_map,
};
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef __XE_FB_PIN_H__
#define __XE_FB_PIN_H__
#include <linux/types.h>
struct drm_gem_object;
struct i915_vma;
struct intel_fb_pin_params;
int xe_fb_pin_ggtt_pin(struct drm_gem_object *obj,
const struct intel_fb_pin_params *pin_params,
struct i915_vma **out_ggtt_vma,
u32 *out_offset,
int *out_fence_id);
extern const struct intel_display_fb_pin_interface xe_display_fb_pin_interface;
#endif /* __XE_FB_PIN_H__ */
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2026 Intel Corporation */
#include <drm/drm_gem.h>
#include <drm/intel/display_parent_interface.h>
#include "intel_frontbuffer.h"
#include "xe_frontbuffer.h"
struct xe_frontbuffer {
struct intel_frontbuffer base;
struct drm_gem_object *obj;
struct kref ref;
};
static struct intel_frontbuffer *xe_frontbuffer_get(struct drm_gem_object *obj)
{
struct xe_frontbuffer *front;
front = kmalloc_obj(*front);
if (!front)
return NULL;
intel_frontbuffer_init(&front->base, obj->dev);
kref_init(&front->ref);
drm_gem_object_get(obj);
front->obj = obj;
return &front->base;
}
static void xe_frontbuffer_ref(struct intel_frontbuffer *_front)
{
struct xe_frontbuffer *front =
container_of(_front, typeof(*front), base);
kref_get(&front->ref);
}
static void frontbuffer_release(struct kref *ref)
{
struct xe_frontbuffer *front =
container_of(ref, typeof(*front), ref);
intel_frontbuffer_fini(&front->base);
drm_gem_object_put(front->obj);
kfree(front);
}
static void xe_frontbuffer_put(struct intel_frontbuffer *_front)
{
struct xe_frontbuffer *front =
container_of(_front, typeof(*front), base);
kref_put(&front->ref, frontbuffer_release);
}
static void xe_frontbuffer_flush_for_display(struct intel_frontbuffer *front)
{
}
const struct intel_display_frontbuffer_interface xe_display_frontbuffer_interface = {
.get = xe_frontbuffer_get,
.ref = xe_frontbuffer_ref,
.put = xe_frontbuffer_put,
.flush_for_display = xe_frontbuffer_flush_for_display,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef _XE_FRONTBUFFER_H_
#define _XE_FRONTBUFFER_H_
extern const struct intel_display_frontbuffer_interface xe_display_frontbuffer_interface;
#endif
@@ -0,0 +1,217 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2023, Intel Corporation.
*/
#include <linux/delay.h>
#include <drm/drm_print.h>
#include <drm/intel/display_parent_interface.h>
#include <drm/intel/i915_hdcp_interface.h>
#include "abi/gsc_command_header_abi.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_force_wake.h"
#include "xe_gsc_proxy.h"
#include "xe_gsc_submit.h"
#include "xe_hdcp_gsc.h"
#include "xe_map.h"
#include "xe_pm.h"
#include "xe_uc_fw.h"
#define HECI_MEADDRESS_HDCP 18
struct intel_hdcp_gsc_context {
struct xe_device *xe;
struct xe_bo *hdcp_bo;
u64 hdcp_cmd_in;
u64 hdcp_cmd_out;
};
#define HDCP_GSC_HEADER_SIZE sizeof(struct intel_gsc_mtl_header)
static bool intel_hdcp_gsc_check_status(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_tile *tile = xe_device_get_root_tile(xe);
struct xe_gt *gt = tile->media_gt;
struct xe_gsc *gsc;
if (!gt) {
drm_dbg_kms(&xe->drm,
"not checking GSC status for HDCP2.x: media GT not present or disabled\n");
return false;
}
gsc = &gt->uc.gsc;
if (!xe_uc_fw_is_available(&gsc->fw)) {
drm_dbg_kms(&xe->drm,
"GSC Components not ready for HDCP2.x\n");
return false;
}
guard(xe_pm_runtime)(xe);
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
if (!fw_ref.domains) {
drm_dbg_kms(&xe->drm,
"failed to get forcewake to check proxy status\n");
return false;
}
return xe_gsc_proxy_init_done(gsc);
}
/*This function helps allocate memory for the command that we will send to gsc cs */
static int intel_hdcp_gsc_initialize_message(struct xe_device *xe,
struct intel_hdcp_gsc_context *gsc_context)
{
struct xe_bo *bo = NULL;
u64 cmd_in, cmd_out;
int ret = 0;
/* allocate object of two page for HDCP command memory and store it */
bo = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), PAGE_SIZE * 2,
ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT, false);
if (IS_ERR(bo)) {
drm_err(&xe->drm, "Failed to allocate bo for HDCP streaming command!\n");
ret = PTR_ERR(bo);
goto out;
}
cmd_in = xe_bo_ggtt_addr(bo);
cmd_out = cmd_in + PAGE_SIZE;
xe_map_memset(xe, &bo->vmap, 0, 0, xe_bo_size(bo));
gsc_context->hdcp_bo = bo;
gsc_context->hdcp_cmd_in = cmd_in;
gsc_context->hdcp_cmd_out = cmd_out;
gsc_context->xe = xe;
out:
return ret;
}
static struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
struct intel_hdcp_gsc_context *gsc_context;
int ret;
gsc_context = kzalloc_obj(*gsc_context);
if (!gsc_context)
return ERR_PTR(-ENOMEM);
/*
* NOTE: No need to lock the comp mutex here as it is already
* going to be taken before this function called
*/
ret = intel_hdcp_gsc_initialize_message(xe, gsc_context);
if (ret) {
drm_err(&xe->drm, "Could not initialize gsc_context\n");
kfree(gsc_context);
gsc_context = ERR_PTR(ret);
}
return gsc_context;
}
static void intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context *gsc_context)
{
if (!gsc_context)
return;
xe_bo_unpin_map_no_vm(gsc_context->hdcp_bo);
kfree(gsc_context);
}
static int xe_gsc_send_sync(struct xe_device *xe,
struct intel_hdcp_gsc_context *gsc_context,
u32 msg_size_in, u32 msg_size_out,
u32 addr_out_off)
{
struct xe_gt *gt = gsc_context->hdcp_bo->tile->media_gt;
struct iosys_map *map = &gsc_context->hdcp_bo->vmap;
struct xe_gsc *gsc = &gt->uc.gsc;
int ret;
ret = xe_gsc_pkt_submit_kernel(gsc, gsc_context->hdcp_cmd_in, msg_size_in,
gsc_context->hdcp_cmd_out, msg_size_out);
if (ret) {
drm_err(&xe->drm, "failed to send gsc HDCP msg (%d)\n", ret);
return ret;
}
if (xe_gsc_check_and_update_pending(xe, map, 0, map, addr_out_off))
return -EAGAIN;
ret = xe_gsc_read_out_header(xe, map, addr_out_off,
sizeof(struct hdcp_cmd_header), NULL);
return ret;
}
static ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context,
void *msg_in, size_t msg_in_len,
void *msg_out, size_t msg_out_len)
{
struct xe_device *xe = gsc_context->xe;
const size_t max_msg_size = PAGE_SIZE - HDCP_GSC_HEADER_SIZE;
u64 host_session_id;
u32 msg_size_in, msg_size_out;
u32 addr_out_off, addr_in_wr_off = 0;
int ret, tries = 0;
if (msg_in_len > max_msg_size || msg_out_len > max_msg_size)
return -ENOSPC;
msg_size_in = msg_in_len + HDCP_GSC_HEADER_SIZE;
msg_size_out = msg_out_len + HDCP_GSC_HEADER_SIZE;
addr_out_off = PAGE_SIZE;
host_session_id = xe_gsc_create_host_session_id();
guard(xe_pm_runtime_noresume)(xe);
addr_in_wr_off = xe_gsc_emit_header(xe, &gsc_context->hdcp_bo->vmap,
addr_in_wr_off, HECI_MEADDRESS_HDCP,
host_session_id, msg_in_len);
xe_map_memcpy_to(xe, &gsc_context->hdcp_bo->vmap, addr_in_wr_off,
msg_in, msg_in_len);
/*
* Keep sending request in case the pending bit is set no need to add
* message handle as we are using same address hence loc. of header is
* same and it will contain the message handle. we will send the message
* 20 times each message 50 ms apart
*/
do {
ret = xe_gsc_send_sync(xe, gsc_context, msg_size_in, msg_size_out,
addr_out_off);
/* Only try again if gsc says so */
if (ret != -EAGAIN)
break;
msleep(50);
} while (++tries < 20);
if (ret)
return ret;
xe_map_memcpy_from(xe, msg_out, &gsc_context->hdcp_bo->vmap,
addr_out_off + HDCP_GSC_HEADER_SIZE,
msg_out_len);
return ret;
}
const struct intel_display_hdcp_interface xe_display_hdcp_interface = {
.gsc_msg_send = intel_hdcp_gsc_msg_send,
.gsc_check_status = intel_hdcp_gsc_check_status,
.gsc_context_alloc = intel_hdcp_gsc_context_alloc,
.gsc_context_free = intel_hdcp_gsc_context_free,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __XE_HDCP_GSC_H__
#define __XE_HDCP_GSC_H__
extern const struct intel_display_hdcp_interface xe_display_hdcp_interface;
#endif
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021 Intel Corporation
*/
#include <drm/intel/display_parent_interface.h>
#include "regs/xe_gtt_defs.h"
/* FIXME move intel_remapped_info_size() & co. */
#include "intel_fb.h"
/* FIXME move intel_initial_plane_config */
#include "intel_display_types.h"
#include "xe_bo.h"
#include "xe_display_bo.h"
#include "xe_display_vma.h"
#include "xe_fb_pin.h"
#include "xe_ggtt.h"
#include "xe_mmio.h"
#include "xe_vram_types.h"
static struct xe_bo *
initial_plane_bo(struct xe_device *xe,
struct intel_initial_plane_config *plane_config)
{
struct xe_tile *tile0 = xe_device_get_root_tile(xe);
struct xe_bo *bo;
resource_size_t phys_base;
u32 base, size, flags;
u64 page_size = xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K;
if (plane_config->size == 0)
return NULL;
flags = XE_BO_FLAG_FORCE_WC | XE_BO_FLAG_GGTT;
base = round_down(plane_config->base, page_size);
if (IS_DGFX(xe)) {
u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base);
if (!(pte & XE_GGTT_PTE_DM)) {
drm_err(&xe->drm,
"Initial plane programming missing DM bit\n");
return NULL;
}
phys_base = pte & ~(page_size - 1);
flags |= XE_BO_FLAG_VRAM0;
/*
* We don't currently expect this to ever be placed in the
* stolen portion.
*/
if (phys_base >= xe_vram_region_usable_size(tile0->mem.vram)) {
drm_err(&xe->drm,
"Initial plane programming using invalid range, phys_base=%pa\n",
&phys_base);
return NULL;
}
drm_dbg(&xe->drm,
"Using phys_base=%pa, based on initial plane programming\n",
&phys_base);
} else {
struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
if (!stolen)
return NULL;
phys_base = base;
flags |= XE_BO_FLAG_STOLEN;
if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) &&
IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) &&
!xe_display_bo_fbdev_prefer_stolen(xe, plane_config->size)) {
drm_info(&xe->drm, "Initial FB size exceeds half of stolen, discarding\n");
return NULL;
}
}
size = round_up(plane_config->base + plane_config->size,
page_size);
size -= base;
bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base,
ttm_bo_type_kernel, flags, 0, false);
if (IS_ERR(bo)) {
drm_dbg(&xe->drm,
"Failed to create bo phys_base=%pa size %u with flags %x: %li\n",
&phys_base, size, flags, PTR_ERR(bo));
return NULL;
}
return bo;
}
static struct drm_gem_object *
xe_alloc_initial_plane_obj(struct drm_device *drm,
struct intel_initial_plane_config *plane_config)
{
struct xe_device *xe = to_xe_device(drm);
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
struct drm_framebuffer *fb = plane_config->fb;
struct xe_bo *bo;
mode_cmd.pixel_format = fb->format->format;
mode_cmd.width = fb->width;
mode_cmd.height = fb->height;
mode_cmd.pitches[0] = fb->pitches[0];
mode_cmd.modifier[0] = fb->modifier;
mode_cmd.flags = DRM_MODE_FB_MODIFIERS;
bo = initial_plane_bo(xe, plane_config);
if (!bo)
return NULL;
if (intel_framebuffer_init(to_intel_framebuffer(fb),
&bo->ttm.base, fb->format, &mode_cmd)) {
drm_dbg_kms(&xe->drm, "intel fb init failed\n");
goto err_bo;
}
/* Reference handed over to fb */
xe_bo_put(bo);
return &bo->ttm.base;
err_bo:
xe_bo_unpin_map_no_vm(bo);
return NULL;
}
static int
xe_initial_plane_setup(struct drm_plane_state *_plane_state,
struct intel_initial_plane_config *plane_config,
struct drm_framebuffer *fb,
struct i915_vma *_unused)
{
struct intel_plane_state *plane_state = to_intel_plane_state(_plane_state);
struct i915_vma *vma;
struct intel_fb_pin_params pin_params = {
.view = &plane_state->view.gtt,
};
u32 offset;
int ret;
ret = xe_fb_pin_ggtt_pin(intel_fb_bo(fb), &pin_params, &vma, &offset, NULL);
if (ret)
return ret;
plane_state->ggtt_vma = vma;
plane_state->surf = offset;
plane_config->vma = vma;
return 0;
}
static void xe_plane_config_fini(struct intel_initial_plane_config *plane_config)
{
}
const struct intel_display_initial_plane_interface xe_display_initial_plane_interface = {
.alloc_obj = xe_alloc_initial_plane_obj,
.setup = xe_initial_plane_setup,
.config_fini = xe_plane_config_fini,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __XE_INITIAL_PLANE_H__
#define __XE_INITIAL_PLANE_H__
extern const struct intel_display_initial_plane_interface xe_display_initial_plane_interface;
#endif
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
#include <drm/drm_cache.h>
#include <drm/drm_panic.h>
#include <drm/intel/display_parent_interface.h>
#include "intel_display_types.h"
#include "intel_fb.h"
#include "xe_bo.h"
#include "xe_panic.h"
#include "xe_res_cursor.h"
struct intel_panic {
struct xe_res_cursor res;
struct iosys_map vmap;
int page;
};
static void xe_panic_kunmap(struct intel_panic *panic)
{
if (!panic->vmap.is_iomem && iosys_map_is_set(&panic->vmap)) {
drm_clflush_virt_range(panic->vmap.vaddr, PAGE_SIZE);
kunmap_local(panic->vmap.vaddr);
}
iosys_map_clear(&panic->vmap);
panic->page = -1;
}
/*
* 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) {
if (xe_bo_is_vram(bo)) {
/* Display is always mapped on root tile */
struct xe_vram_region *vram = xe_bo_device(bo)->mem.vram;
if (panic->page < 0 || new_page < panic->page) {
xe_res_first(bo->ttm.resource, new_page * PAGE_SIZE,
bo->ttm.base.size - new_page * PAGE_SIZE, &panic->res);
} else {
xe_res_next(&panic->res, PAGE_SIZE * (new_page - panic->page));
}
iosys_map_set_vaddr_iomem(&panic->vmap,
vram->mapping + panic->res.start);
} else {
xe_panic_kunmap(panic);
iosys_map_set_vaddr(&panic->vmap,
ttm_bo_kmap_try_from_panic(&bo->ttm,
new_page));
}
panic->page = new_page;
}
if (iosys_map_is_set(&panic->vmap))
iosys_map_wr(&panic->vmap, offset, u32, color);
}
static struct intel_panic *xe_panic_alloc(void)
{
struct intel_panic *panic;
panic = kzalloc_obj(*panic);
return panic;
}
static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
{
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo))
return -ENODEV;
panic->page = -1;
sb->set_pixel = xe_panic_page_set_pixel;
return 0;
}
const struct intel_display_panic_interface xe_display_panic_interface = {
.alloc = xe_panic_alloc,
.setup = xe_panic_setup,
.finish = xe_panic_kunmap,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __XE_PANIC_H__
#define __XE_PANIC_H__
extern const struct intel_display_panic_interface xe_display_panic_interface;
#endif
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
#include <drm/intel/display_parent_interface.h>
#include "xe_res_cursor.h"
#include "xe_stolen.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_validation.h"
struct intel_stolen_node {
struct xe_device *xe;
struct xe_bo *bo;
};
static int xe_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
unsigned int align, u64 start, u64 end)
{
struct xe_device *xe = node->xe;
struct xe_bo *bo;
int err = 0;
u32 flags = XE_BO_FLAG_PINNED | XE_BO_FLAG_STOLEN;
if (start < SZ_4K)
start = SZ_4K;
if (align) {
size = ALIGN(size, align);
start = ALIGN(start, align);
}
bo = xe_bo_create_pin_range_novm(xe, xe_device_get_root_tile(xe),
size, start, end, ttm_bo_type_kernel, flags);
if (IS_ERR(bo)) {
err = PTR_ERR(bo);
bo = NULL;
return err;
}
node->bo = bo;
return err;
}
static void xe_stolen_remove_node(struct intel_stolen_node *node)
{
xe_bo_unpin_map_no_vm(node->bo);
node->bo = NULL;
}
static bool xe_stolen_initialized(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
return ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
}
static bool xe_stolen_node_allocated(const struct intel_stolen_node *node)
{
return node->bo;
}
static u64 xe_stolen_node_offset(const struct intel_stolen_node *node)
{
struct xe_res_cursor res;
xe_res_first(node->bo->ttm.resource, 0, 4096, &res);
return res.start;
}
static u64 xe_stolen_node_address(const struct intel_stolen_node *node)
{
struct xe_device *xe = node->xe;
return xe_ttm_stolen_gpu_offset(xe) + xe_stolen_node_offset(node);
}
static u64 xe_stolen_node_size(const struct intel_stolen_node *node)
{
return xe_bo_size(node->bo);
}
static struct intel_stolen_node *xe_stolen_node_alloc(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
struct intel_stolen_node *node;
node = kzalloc_obj(*node);
if (!node)
return NULL;
node->xe = xe;
return node;
}
static void xe_stolen_node_free(const struct intel_stolen_node *node)
{
kfree(node);
}
const struct intel_display_stolen_interface xe_display_stolen_interface = {
.insert_node_in_range = xe_stolen_insert_node_in_range,
.remove_node = xe_stolen_remove_node,
.initialized = xe_stolen_initialized,
.node_allocated = xe_stolen_node_allocated,
.node_offset = xe_stolen_node_offset,
.node_address = xe_stolen_node_address,
.node_size = xe_stolen_node_size,
.node_alloc = xe_stolen_node_alloc,
.node_free = xe_stolen_node_free,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __XE_STOLEN_H__
#define __XE_STOLEN_H__
extern const struct intel_display_stolen_interface xe_display_stolen_interface;
#endif
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2024 Intel Corporation
*/
#include "intel_display_core.h"
#include "intel_tdf.h"
#include "xe_device.h"
void intel_td_flush(struct intel_display *display)
{
struct xe_device *xe = to_xe_device(display->drm);
xe_device_td_flush(xe);
}