summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/display/panel/boe,tv101wum-ll2.yaml63
-rw-r--r--Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml32
-rw-r--r--Documentation/gpu/xe/xe_mm.rst15
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/accel/drm_accel.c110
-rw-r--r--drivers/gpu/drm/Kconfig31
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c95
-rw-r--r--drivers/gpu/drm/ci/test.yml5
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology.c71
-rw-r--r--drivers/gpu/drm/drm_atomic.c6
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c2
-rw-r--r--drivers/gpu/drm/drm_crtc_internal.h4
-rw-r--r--drivers/gpu/drm/drm_drv.c100
-rw-r--r--drivers/gpu/drm/drm_file.c9
-rw-r--r--drivers/gpu/drm/drm_gem.c7
-rw-r--r--drivers/gpu/drm/drm_internal.h4
-rw-r--r--drivers/gpu/drm/drm_modes.c1
-rw-r--r--drivers/gpu/drm/drm_panic.c340
-rw-r--r--drivers/gpu/drm/drm_panic_qr.rs1003
-rw-r--r--drivers/gpu/drm/drm_print.c13
-rw-r--r--drivers/gpu/drm/drm_rect.c1
-rw-r--r--drivers/gpu/drm/drm_vblank.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.h3
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.h1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c1
-rw-r--r--drivers/gpu/drm/i915/i915_vma.h1
-rw-r--r--drivers/gpu/drm/imx/ipuv3/parallel-display.c14
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c5
-rw-r--r--drivers/gpu/drm/panel/Kconfig9
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c241
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c3
-rw-r--r--drivers/gpu/drm/panel/panel-newvision-nv3051d.c367
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt35950.c211
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-vtdr6130.c212
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/Kconfig8
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/Makefile2
-rw-r--r--drivers/gpu/drm/renesas/rz-du/Kconfig8
-rw-r--r--drivers/gpu/drm/renesas/rz-du/Makefile2
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c8
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c11
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c3
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c (renamed from drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c)0
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h (renamed from drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h)0
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c24
-rw-r--r--drivers/gpu/drm/tegra/drm.h2
-rw-r--r--drivers/gpu/drm/tegra/gr3d.c46
-rw-r--r--drivers/gpu/drm/tegra/hub.c7
-rw-r--r--drivers/gpu/drm/tegra/output.c29
-rw-r--r--drivers/gpu/drm/tiny/gm12u320.c13
-rw-r--r--drivers/gpu/drm/xe/Makefile18
-rw-r--r--drivers/gpu/drm/xe/abi/guc_klvs_abi.h1
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h7
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c94
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h4
-rw-r--r--drivers/gpu/drm/xe/display/xe_fb_pin.c50
-rw-r--r--drivers/gpu/drm/xe/regs/xe_engine_regs.h1
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h9
-rw-r--r--drivers/gpu/drm/xe/tests/xe_bo.c8
-rw-r--r--drivers/gpu/drm/xe/tests/xe_dma_buf.c2
-rw-r--r--drivers/gpu/drm/xe/tests/xe_migrate.c24
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci.c52
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci_test.h1
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c20
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h10
-rw-r--r--drivers/gpu/drm/xe/xe_bo_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.h4
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c111
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump_types.h8
-rw-r--r--drivers/gpu/drm/xe/xe_device.c5
-rw-r--r--drivers/gpu/drm/xe/xe_device.h15
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h17
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c20
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c213
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h8
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c487
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.h28
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt_types.h54
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c23
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.h12
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.c12
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_proxy.c45
-rw-r--r--drivers/gpu/drm/xe/xe_gt.c10
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.c13
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.c40
-rw-r--r--drivers/gpu/drm/xe/xe_gt_pagefault.c57
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c108
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.c44
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats.c49
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats.h29
-rw-r--r--drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_types.h24
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c4
-rw-r--r--drivers/gpu/drm/xe/xe_guc.h10
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c6
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c12
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hwconfig.c97
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hwconfig.h3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c58
-rw-r--r--drivers/gpu/drm/xe/xe_huc.c19
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c149
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.h10
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group.c372
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group.h29
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group_types.h51
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c37
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c29
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.h9
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.c5
-rw-r--r--drivers/gpu/drm/xe/xe_module.c54
-rw-r--r--drivers/gpu/drm/xe/xe_module.h2
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c3
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c12
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c90
-rw-r--r--drivers/gpu/drm/xe/xe_pm.h1
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c10
-rw-r--r--drivers/gpu/drm/xe/xe_res_cursor.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sa.c13
-rw-r--r--drivers/gpu/drm/xe/xe_sa_types.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c3
-rw-r--r--drivers/gpu/drm/xe/xe_sync.c21
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c1
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.c13
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.c28
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c90
-rw-r--r--drivers/gpu/drm/xe/xe_wa.c10
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules6
-rw-r--r--drivers/gpu/host1x/dev.c7
-rw-r--r--drivers/gpu/host1x/dev.h2
-rw-r--r--drivers/gpu/host1x/hw/intr_hw.c37
-rw-r--r--drivers/gpu/host1x/intr.c21
-rw-r--r--drivers/gpu/host1x/intr.h5
-rw-r--r--include/drm/display/drm_dp_mst_helper.h12
-rw-r--r--include/drm/drm_accel.h18
-rw-r--r--include/drm/drm_atomic.h2
-rw-r--r--include/drm/drm_file.h5
-rw-r--r--include/drm/drm_print.h54
-rw-r--r--include/drm/drm_rect.h15
-rw-r--r--include/drm/ttm/ttm_bo.h2
146 files changed, 4639 insertions, 1711 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/boe,tv101wum-ll2.yaml b/Documentation/devicetree/bindings/display/panel/boe,tv101wum-ll2.yaml
new file mode 100644
index 000000000000..dced98e1c69a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/boe,tv101wum-ll2.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/boe,tv101wum-ll2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: BOE TV101WUM-LL2 DSI Display Panel
+
+maintainers:
+ - Neil Armstrong <neil.armstrong@linaro.org>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ const: boe,tv101wum-ll2
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel
+
+ backlight: true
+ reset-gpios: true
+ vsp-supply: true
+ vsn-supply: true
+ port: true
+ rotation: true
+
+required:
+ - compatible
+ - reg
+ - reset-gpios
+ - vsp-supply
+ - vsn-supply
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "boe,tv101wum-ll2";
+ reg = <0>;
+
+ vsn-supply = <&vsn_lcd>;
+ vsp-supply = <&vsp_lcd>;
+
+ reset-gpios = <&pio 45 GPIO_ACTIVE_LOW>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&dsi_out>;
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
index 08e5b9478051..95e3d5e74b87 100644
--- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
+++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
@@ -18,6 +18,7 @@ properties:
compatible:
oneOf:
- enum:
+ - renesas,r9a07g043u-du # RZ/G2UL
- renesas,r9a07g044-du # RZ/G2{L,LC}
- items:
- enum:
@@ -60,9 +61,6 @@ properties:
$ref: /schemas/graph.yaml#/properties/port
unevaluatedProperties: false
- required:
- - port@0
-
unevaluatedProperties: false
renesas,vsps:
@@ -88,6 +86,34 @@ required:
additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: renesas,r9a07g043u-du
+ then:
+ properties:
+ ports:
+ properties:
+ port@0:
+ description: DPI
+
+ required:
+ - port@0
+ else:
+ properties:
+ ports:
+ properties:
+ port@0:
+ description: DSI
+ port@1:
+ description: DPI
+
+ required:
+ - port@0
+ - port@1
+
examples:
# RZ/G2L DU
- |
diff --git a/Documentation/gpu/xe/xe_mm.rst b/Documentation/gpu/xe/xe_mm.rst
index 6c8fd8b4a466..95864a4502dd 100644
--- a/Documentation/gpu/xe/xe_mm.rst
+++ b/Documentation/gpu/xe/xe_mm.rst
@@ -7,6 +7,21 @@ Memory Management
.. kernel-doc:: drivers/gpu/drm/xe/xe_bo_doc.h
:doc: Buffer Objects (BO)
+GGTT
+====
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_ggtt.c
+ :doc: Global Graphics Translation Table (GGTT)
+
+GGTT Internal API
+-----------------
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_ggtt_types.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_ggtt.c
+ :internal:
+
Pagetable building
==================
diff --git a/MAINTAINERS b/MAINTAINERS
index 3b9ec16eb221..a38754d1cc86 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7343,10 +7343,10 @@ F: drivers/gpu/drm/udl/
DRM DRIVER FOR VIRTUAL KERNEL MODESETTING (VKMS)
M: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
-M: Melissa Wen <melissa.srw@gmail.com>
M: Maíra Canal <mairacanal@riseup.net>
R: Haneen Mohammed <hamohammed.sa@gmail.com>
R: Daniel Vetter <daniel@ffwll.ch>
+R: Melissa Wen <melissa.srw@gmail.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
diff --git a/drivers/accel/drm_accel.c b/drivers/accel/drm_accel.c
index 16c3edb8c46e..aa826033b0ce 100644
--- a/drivers/accel/drm_accel.c
+++ b/drivers/accel/drm_accel.c
@@ -8,7 +8,7 @@
#include <linux/debugfs.h>
#include <linux/device.h>
-#include <linux/idr.h>
+#include <linux/xarray.h>
#include <drm/drm_accel.h>
#include <drm/drm_auth.h>
@@ -18,8 +18,7 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_print.h>
-static DEFINE_SPINLOCK(accel_minor_lock);
-static struct idr accel_minors_idr;
+DEFINE_XARRAY_ALLOC(accel_minors_xa);
static struct dentry *accel_debugfs_root;
@@ -118,99 +117,6 @@ void accel_set_device_instance_params(struct device *kdev, int index)
}
/**
- * accel_minor_alloc() - Allocates a new accel minor
- *
- * This function access the accel minors idr and allocates from it
- * a new id to represent a new accel minor
- *
- * Return: A new id on success or error code in case idr_alloc failed
- */
-int accel_minor_alloc(void)
-{
- unsigned long flags;
- int r;
-
- spin_lock_irqsave(&accel_minor_lock, flags);
- r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
- spin_unlock_irqrestore(&accel_minor_lock, flags);
-
- return r;
-}
-
-/**
- * accel_minor_remove() - Remove an accel minor
- * @index: The minor id to remove.
- *
- * This function access the accel minors idr and removes from
- * it the member with the id that is passed to this function.
- */
-void accel_minor_remove(int index)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&accel_minor_lock, flags);
- idr_remove(&accel_minors_idr, index);
- spin_unlock_irqrestore(&accel_minor_lock, flags);
-}
-
-/**
- * accel_minor_replace() - Replace minor pointer in accel minors idr.
- * @minor: Pointer to the new minor.
- * @index: The minor id to replace.
- *
- * This function access the accel minors idr structure and replaces the pointer
- * that is associated with an existing id. Because the minor pointer can be
- * NULL, we need to explicitly pass the index.
- *
- * Return: 0 for success, negative value for error
- */
-void accel_minor_replace(struct drm_minor *minor, int index)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&accel_minor_lock, flags);
- idr_replace(&accel_minors_idr, minor, index);
- spin_unlock_irqrestore(&accel_minor_lock, flags);
-}
-
-/*
- * Looks up the given minor-ID and returns the respective DRM-minor object. The
- * refence-count of the underlying device is increased so you must release this
- * object with accel_minor_release().
- *
- * The object can be only a drm_minor that represents an accel device.
- *
- * As long as you hold this minor, it is guaranteed that the object and the
- * minor->dev pointer will stay valid! However, the device may get unplugged and
- * unregistered while you hold the minor.
- */
-static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
-{
- struct drm_minor *minor;
- unsigned long flags;
-
- spin_lock_irqsave(&accel_minor_lock, flags);
- minor = idr_find(&accel_minors_idr, minor_id);
- if (minor)
- drm_dev_get(minor->dev);
- spin_unlock_irqrestore(&accel_minor_lock, flags);
-
- if (!minor) {
- return ERR_PTR(-ENODEV);
- } else if (drm_dev_is_unplugged(minor->dev)) {
- drm_dev_put(minor->dev);
- return ERR_PTR(-ENODEV);
- }
-
- return minor;
-}
-
-static void accel_minor_release(struct drm_minor *minor)
-{
- drm_dev_put(minor->dev);
-}
-
-/**
* accel_open - open method for ACCEL file
* @inode: device inode
* @filp: file pointer.
@@ -227,7 +133,7 @@ int accel_open(struct inode *inode, struct file *filp)
struct drm_minor *minor;
int retcode;
- minor = accel_minor_acquire(iminor(inode));
+ minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
@@ -246,7 +152,7 @@ int accel_open(struct inode *inode, struct file *filp)
err_undo:
atomic_dec(&dev->open_count);
- accel_minor_release(minor);
+ drm_minor_release(minor);
return retcode;
}
EXPORT_SYMBOL_GPL(accel_open);
@@ -257,7 +163,7 @@ static int accel_stub_open(struct inode *inode, struct file *filp)
struct drm_minor *minor;
int err;
- minor = accel_minor_acquire(iminor(inode));
+ minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
@@ -274,7 +180,7 @@ static int accel_stub_open(struct inode *inode, struct file *filp)
err = 0;
out:
- accel_minor_release(minor);
+ drm_minor_release(minor);
return err;
}
@@ -290,15 +196,13 @@ void accel_core_exit(void)
unregister_chrdev(ACCEL_MAJOR, "accel");
debugfs_remove(accel_debugfs_root);
accel_sysfs_destroy();
- idr_destroy(&accel_minors_idr);
+ WARN_ON(!xa_empty(&accel_minors_xa));
}
int __init accel_core_init(void)
{
int ret;
- idr_init(&accel_minors_idr);
-
ret = accel_sysfs_init();
if (ret < 0) {
DRM_ERROR("Cannot create ACCEL class: %d\n", ret);
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 0387143bbb39..1cb5a4f19293 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -149,6 +149,37 @@ config DRM_PANIC_SCREEN
or by writing to /sys/module/drm/parameters/panic_screen sysfs entry
Default is "user"
+config DRM_PANIC_SCREEN_QR_CODE
+ bool "Add a panic screen with a QR code"
+ depends on DRM_PANIC && RUST
+ help
+ This option adds a QR code generator, and a panic screen with a QR
+ code. The QR code will contain the last lines of kmsg and other debug
+ information. This should be easier for the user to report a kernel
+ panic, with all debug information available.
+ To use this panic screen, also set DRM_PANIC_SCREEN to "qr_code"
+
+config DRM_PANIC_SCREEN_QR_CODE_URL
+ string "Base URL of the QR code in the panic screen"
+ depends on DRM_PANIC_SCREEN_QR_CODE
+ help
+ This option sets the base URL to report the kernel panic. If it's set
+ the QR code will contain the URL and the kmsg compressed with zlib as
+ a URL parameter. If it's empty, the QR code will contain the kmsg as
+ uncompressed text only.
+ There is a demo code in javascript, to decode and uncompress the kmsg
+ data from the URL parameter at https://github.com/kdj0c/panic_report
+
+config DRM_PANIC_SCREEN_QR_VERSION
+ int "Maximum version (size) of the QR code."
+ depends on DRM_PANIC_SCREEN_QR_CODE
+ default 40
+ help
+ This option limits the version (or size) of the QR code. QR code
+ version ranges from Version 1 (21x21) to Version 40 (177x177).
+ Smaller QR code are easier to read, but will contain less debugging
+ data. Default is 40.
+
config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
bool "Enable refcount backtrace history in the DP MST helpers"
depends on STACKTRACE_SUPPORT
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 68cc9258ffc4..c62339b89d46 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -89,6 +89,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \
drm_privacy_screen_x86.o
drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o
drm-$(CONFIG_DRM_PANIC) += drm_panic.o
+drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o
obj-$(CONFIG_DRM) += drm.o
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 7d999e352df3..5f7b178ba870 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2579,9 +2579,9 @@ static int dm_late_init(void *handle)
static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
{
+ u8 buf[UUID_SIZE];
+ guid_t guid;
int ret;
- u8 guid[16];
- u64 tmp64;
mutex_lock(&mgr->lock);
if (!mgr->mst_primary)
@@ -2602,26 +2602,27 @@ static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
}
/* Some hubs forget their guids after they resume */
- ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
- if (ret != 16) {
+ ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
goto out_fail;
}
- if (memchr_inv(guid, 0, 16) == NULL) {
- tmp64 = get_jiffies_64();
- memcpy(&guid[0], &tmp64, sizeof(u64));
- memcpy(&guid[8], &tmp64, sizeof(u64));
+ import_guid(&guid, buf);
- ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16);
+ if (guid_is_null(&guid)) {
+ guid_gen(&guid);
+ export_guid(buf, &guid);
- if (ret != 16) {
+ ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, buf, sizeof(buf));
+
+ if (ret != sizeof(buf)) {
drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n");
goto out_fail;
}
}
- memcpy(mgr->mst_primary->guid, guid, 16);
+ guid_copy(&mgr->mst_primary->guid, &guid);
out_fail:
mutex_unlock(&mgr->lock);
@@ -4947,12 +4948,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
if (psr_feature_enabled)
amdgpu_dm_set_psr_caps(link);
-
- /* TODO: Fix vblank control helpers to delay PSR entry to allow this when
- * PSR is also supported.
- */
- if (link->psr_settings.psr_feature_enabled)
- adev_to_drm(adev)->vblank_disable_immediate = false;
}
}
amdgpu_set_panel_orientation(&aconnector->base);
@@ -8248,12 +8243,66 @@ static int amdgpu_dm_encoder_init(struct drm_device *dev,
static void manage_dm_interrupts(struct amdgpu_device *adev,
struct amdgpu_crtc *acrtc,
- bool enable)
+ struct dm_crtc_state *acrtc_state)
{
- if (enable)
- drm_crtc_vblank_on(&acrtc->base);
- else
+ /*
+ * We have no guarantee that the frontend index maps to the same
+ * backend index - some even map to more than one.
+ *
+ * TODO: Use a different interrupt or check DC itself for the mapping.
+ */
+ int irq_type =
+ amdgpu_display_crtc_idx_to_irq_type(
+ adev,
+ acrtc->crtc_id);
+ struct drm_vblank_crtc_config config = {0};
+ struct dc_crtc_timing *timing;
+ int offdelay;
+
+ if (acrtc_state) {
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
+ IP_VERSION(3, 5, 0) ||
+ acrtc_state->stream->link->psr_settings.psr_version <
+ DC_PSR_VERSION_UNSUPPORTED) {
+ timing = &acrtc_state->stream->timing;
+
+ /* at least 2 frames */
+ offdelay = DIV64_U64_ROUND_UP((u64)20 *
+ timing->v_total *
+ timing->h_total,
+ timing->pix_clk_100hz);
+
+ config.offdelay_ms = offdelay ?: 30;
+ } else {
+ config.disable_immediate = true;
+ }
+
+ drm_crtc_vblank_on_config(&acrtc->base,
+ &config);
+
+ amdgpu_irq_get(
+ adev,
+ &adev->pageflip_irq,
+ irq_type);
+#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
+ amdgpu_irq_get(
+ adev,
+ &adev->vline0_irq,
+ irq_type);
+#endif
+ } else {
+#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
+ amdgpu_irq_put(
+ adev,
+ &adev->vline0_irq,
+ irq_type);
+#endif
+ amdgpu_irq_put(
+ adev,
+ &adev->pageflip_irq,
+ irq_type);
drm_crtc_vblank_off(&acrtc->base);
+ }
}
static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
@@ -9305,7 +9354,7 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
if (old_crtc_state->active &&
(!new_crtc_state->active ||
drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- manage_dm_interrupts(adev, acrtc, false);
+ manage_dm_interrupts(adev, acrtc, NULL);
dc_stream_release(dm_old_crtc_state->stream);
}
}
@@ -9821,7 +9870,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
drm_atomic_crtc_needs_modeset(new_crtc_state))) {
dc_stream_retain(dm_new_crtc_state->stream);
acrtc->dm_irq_params.stream = dm_new_crtc_state->stream;
- manage_dm_interrupts(adev, acrtc, true);
+ manage_dm_interrupts(adev, acrtc, dm_new_crtc_state);
}
/* Handle vrr on->off / off->on transitions */
amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, dm_new_crtc_state);
diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml
index b6f428cdaf94..09d8447840e9 100644
--- a/drivers/gpu/drm/ci/test.yml
+++ b/drivers/gpu/drm/ci/test.yml
@@ -10,6 +10,7 @@
.lava-test:
extends:
- .test-rules
+ timeout: "1h30m"
script:
# Note: Build dir (and thus install) may be dirty due to GIT_STRATEGY
- rm -rf install
@@ -71,6 +72,7 @@
- .baremetal-test-arm64
- .use-debian/baremetal_arm64_test
- .test-rules
+ timeout: "1h30m"
variables:
FDO_CI_CONCURRENT: 10
HWCI_TEST_SCRIPT: "/install/igt_runner.sh"
@@ -215,7 +217,6 @@ panfrost:rk3399:
extends:
- .lava-igt:x86_64
stage: i915
- timeout: "1h30m"
variables:
DRIVER_NAME: i915
DTB: ""
@@ -414,6 +415,7 @@ panfrost:g12b:
virtio_gpu:none:
stage: software-driver
+ timeout: "1h30m"
variables:
CROSVM_GALLIUM_DRIVER: llvmpipe
DRIVER_NAME: virtio_gpu
@@ -436,6 +438,7 @@ virtio_gpu:none:
vkms:none:
stage: software-driver
+ timeout: "1h30m"
variables:
DRIVER_NAME: vkms
GPU_VERSION: none
diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c
index 379a449a28a2..a040d7dfced1 100644
--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
@@ -89,7 +89,7 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_branch *mstb,
struct drm_dp_mst_port *port);
static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
- u8 *guid);
+ guid_t *guid);
static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port);
static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port);
@@ -801,7 +801,7 @@ static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_
int idx = 1;
int i;
- memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+ import_guid(&repmsg->u.link_addr.guid, &raw->msg[idx]);
idx += 16;
repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
idx++;
@@ -829,7 +829,7 @@ static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_
idx++;
if (idx > raw->curlen)
goto fail_len;
- memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+ import_guid(&repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx]);
idx += 16;
if (idx > raw->curlen)
goto fail_len;
@@ -1029,7 +1029,7 @@ static bool drm_dp_sideband_parse_reply(const struct drm_dp_mst_topology_mgr *mg
msg->req_type = (raw->msg[0] & 0x7f);
if (msg->reply_type == DP_SIDEBAND_REPLY_NAK) {
- memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+ import_guid(&msg->u.nak.guid, &raw->msg[1]);
msg->u.nak.reason = raw->msg[17];
msg->u.nak.nak_data = raw->msg[18];
return false;
@@ -1078,7 +1078,7 @@ drm_dp_sideband_parse_connection_status_notify(const struct drm_dp_mst_topology_
if (idx > raw->curlen)
goto fail_len;
- memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+ import_guid(&msg->u.conn_stat.guid, &raw->msg[idx]);
idx += 16;
if (idx > raw->curlen)
goto fail_len;
@@ -1107,7 +1107,7 @@ static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst
if (idx > raw->curlen)
goto fail_len;
- memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+ import_guid(&msg->u.resource_stat.guid, &raw->msg[idx]);
idx += 16;
if (idx > raw->curlen)
goto fail_len;
@@ -2174,20 +2174,24 @@ ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
offset, size, buffer);
}
-static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
+static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, guid_t *guid)
{
int ret = 0;
- memcpy(mstb->guid, guid, 16);
+ guid_copy(&mstb->guid, guid);
+
+ if (!drm_dp_validate_guid(mstb->mgr, &mstb->guid)) {
+ u8 buf[UUID_SIZE];
+
+ export_guid(buf, &mstb->guid);
- if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) {
if (mstb->port_parent) {
ret = drm_dp_send_dpcd_write(mstb->mgr,
mstb->port_parent,
- DP_GUID, 16, mstb->guid);
+ DP_GUID, sizeof(buf), buf);
} else {
ret = drm_dp_dpcd_write(mstb->mgr->aux,
- DP_GUID, mstb->guid, 16);
+ DP_GUID, buf, sizeof(buf));
}
}
@@ -2567,9 +2571,9 @@ out:
return mstb;
}
-static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
- struct drm_dp_mst_branch *mstb,
- const uint8_t *guid)
+static struct drm_dp_mst_branch *
+get_mst_branch_device_by_guid_helper(struct drm_dp_mst_branch *mstb,
+ const guid_t *guid)
{
struct drm_dp_mst_branch *found_mstb;
struct drm_dp_mst_port *port;
@@ -2577,10 +2581,9 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
if (!mstb)
return NULL;
- if (memcmp(mstb->guid, guid, 16) == 0)
+ if (guid_equal(&mstb->guid, guid))
return mstb;
-
list_for_each_entry(port, &mstb->ports, next) {
found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
@@ -2593,7 +2596,7 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
static struct drm_dp_mst_branch *
drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
- const uint8_t *guid)
+ const guid_t *guid)
{
struct drm_dp_mst_branch *mstb;
int ret;
@@ -2695,17 +2698,12 @@ static void drm_dp_mst_queue_probe_work(struct drm_dp_mst_topology_mgr *mgr)
}
static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
- u8 *guid)
+ guid_t *guid)
{
- u64 salt;
-
- if (memchr_inv(guid, 0, 16))
+ if (!guid_is_null(guid))
return true;
- salt = get_jiffies_64();
-
- memcpy(&guid[0], &salt, sizeof(u64));
- memcpy(&guid[8], &salt, sizeof(u64));
+ guid_gen(guid);
return false;
}
@@ -2945,7 +2943,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
drm_dbg_kms(mgr->dev, "link address reply: %d\n", reply->nports);
drm_dp_dump_link_address(mgr, reply);
- ret = drm_dp_check_mstb_guid(mstb, reply->guid);
+ ret = drm_dp_check_mstb_guid(mstb, &reply->guid);
if (ret) {
char buf[64];
@@ -3799,8 +3797,9 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
bool sync)
{
+ u8 buf[UUID_SIZE];
+ guid_t guid;
int ret;
- u8 guid[16];
mutex_lock(&mgr->lock);
if (!mgr->mst_primary)
@@ -3821,13 +3820,15 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
}
/* Some hubs forget their guids after they resume */
- ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
- if (ret != 16) {
+ ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
goto out_fail;
}
- ret = drm_dp_check_mstb_guid(mgr->mst_primary, guid);
+ import_guid(&guid, buf);
+
+ ret = drm_dp_check_mstb_guid(mgr->mst_primary, &guid);
if (ret) {
drm_dbg_kms(mgr->dev, "check mstb failed - undocked during suspend?\n");
goto out_fail;
@@ -4005,12 +4006,12 @@ drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
bool hotplug = false, dowork = false;
if (hdr->broadcast) {
- const u8 *guid = NULL;
+ const guid_t *guid = NULL;
if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY)
- guid = msg->u.conn_stat.guid;
+ guid = &msg->u.conn_stat.guid;
else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
- guid = msg->u.resource_stat.guid;
+ guid = &msg->u.resource_stat.guid;
if (guid)
mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
@@ -5598,7 +5599,6 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_check_mgr);
* drm_dp_atomic_release_time_slots()
*
* Returns:
- *
* 0 if the new state is valid, negative error code otherwise.
*/
int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
@@ -5635,7 +5635,6 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
* topology object.
*
* RETURNS:
- *
* The MST topology state or error pointer.
*/
struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
@@ -5655,7 +5654,6 @@ EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
* topology object.
*
* Returns:
- *
* The old MST topology state, or NULL if there's no topology state for this MST mgr
* in the global atomic state
*/
@@ -5680,7 +5678,6 @@ EXPORT_SYMBOL(drm_atomic_get_old_mst_topology_state);
* topology object.
*
* Returns:
- *
* The new MST topology state, or NULL if there's no topology state for this MST mgr
* in the global atomic state
*/
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 6e516c39a372..0fc99da93afe 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -63,7 +63,6 @@ EXPORT_SYMBOL(__drm_crtc_commit_free);
* hardware and flipped to.
*
* Returns:
- *
* 0 on success, a negative error code otherwise.
*/
int drm_crtc_commit_wait(struct drm_crtc_commit *commit)
@@ -337,7 +336,6 @@ EXPORT_SYMBOL(__drm_atomic_state_free);
* not created by userspace through an IOCTL call.
*
* Returns:
- *
* Either the allocated state or the error code encoded into the pointer. When
* the error is EDEADLK then the w/w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted. All other errors are fatal.
@@ -518,7 +516,6 @@ static int drm_atomic_connector_check(struct drm_connector *connector,
* is consistent.
*
* Returns:
- *
* Either the allocated state or the error code encoded into the pointer. When
* the error is EDEADLK then the w/w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted. All other errors are fatal.
@@ -828,7 +825,6 @@ EXPORT_SYMBOL(drm_atomic_private_obj_fini);
* object lock to make sure that the state is consistent.
*
* RETURNS:
- *
* Either the allocated state or the error code encoded into a pointer.
*/
struct drm_private_state *
@@ -1061,7 +1057,6 @@ EXPORT_SYMBOL(drm_atomic_get_new_crtc_for_encoder);
* make sure that the state is consistent.
*
* Returns:
- *
* Either the allocated state or the error code encoded into the pointer. When
* the error is EDEADLK then the w/w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted. All other errors are fatal.
@@ -1169,7 +1164,6 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
* state is consistent.
*
* Returns:
- *
* Either the allocated state or the error code encoded into the pointer. When
* the error is EDEADLK then the w/w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted.
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index fb97b51b38f1..43cdf39019a4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -2266,7 +2266,6 @@ crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
* automatically.
*
* Returns:
- *
* 0 on success. -EBUSY when userspace schedules nonblocking commits too fast,
* -ENOMEM on allocation failures and -EINTR when a signal is pending.
*/
@@ -3009,7 +3008,6 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
* don't pass the right state structures to the callbacks.
*
* Returns:
- *
* Returns 0 on success. Can return -ERESTARTSYS when @stall is true and the
* waiting for the previous commits has been interrupted.
*/
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 251ff7bba40d..89706aa8232f 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -320,10 +320,14 @@ drm_edid_load_firmware(struct drm_connector *connector)
bool drm_panic_is_enabled(struct drm_device *dev);
void drm_panic_register(struct drm_device *dev);
void drm_panic_unregister(struct drm_device *dev);
+void drm_panic_init(void);
+void drm_panic_exit(void);
#else
static inline bool drm_panic_is_enabled(struct drm_device *dev) { return false; }
static inline void drm_panic_register(struct drm_device *dev) {}
static inline void drm_panic_unregister(struct drm_device *dev) {}
+static inline void drm_panic_init(void) {}
+static inline void drm_panic_exit(void) {}
#endif
#endif /* __DRM_CRTC_INTERNAL_H__ */
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 93543071a500..ac30b0ec9d93 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
#include <linux/pseudo_fs.h>
#include <linux/slab.h>
#include <linux/srcu.h>
+#include <linux/xarray.h>
#include <drm/drm_accel.h>
#include <drm/drm_cache.h>
@@ -54,8 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
MODULE_DESCRIPTION("DRM shared core routines");
MODULE_LICENSE("GPL and additional rights");
-static DEFINE_SPINLOCK(drm_minor_lock);
-static struct idr drm_minors_idr;
+DEFINE_XARRAY_ALLOC(drm_minors_xa);
/*
* If the drm core fails to init for whatever reason,
@@ -83,6 +83,18 @@ DEFINE_STATIC_SRCU(drm_unplug_srcu);
* registered and unregistered dynamically according to device-state.
*/
+static struct xarray *drm_minor_get_xa(enum drm_minor_type type)
+{
+ if (type == DRM_MINOR_PRIMARY || type == DRM_MINOR_RENDER)
+ return &drm_minors_xa;
+#if IS_ENABLED(CONFIG_DRM_ACCEL)
+ else if (type == DRM_MINOR_ACCEL)
+ return &accel_minors_xa;
+#endif
+ else
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
enum drm_minor_type type)
{
@@ -101,25 +113,31 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
static void drm_minor_alloc_release(struct drm_device *dev, void *data)
{
struct drm_minor *minor = data;
- unsigned long flags;
WARN_ON(dev != minor->dev);
put_device(minor->kdev);
- if (minor->type == DRM_MINOR_ACCEL) {
- accel_minor_remove(minor->index);
- } else {
- spin_lock_irqsave(&drm_minor_lock, flags);
- idr_remove(&drm_minors_idr, minor->index);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
- }
+ xa_erase(drm_minor_get_xa(minor->type), minor->index);
}
+/*
+ * DRM used to support 64 devices, for backwards compatibility we need to maintain the
+ * minor allocation scheme where minors 0-63 are primary nodes, 64-127 are control nodes,
+ * and 128-191 are render nodes.
+ * After reaching the limit, we're allocating minors dynamically - first-come, first-serve.
+ * Accel nodes are using a distinct major, so the minors are allocated in continuous 0-MAX
+ * range.
+ */
+#define DRM_MINOR_LIMIT(t) ({ \
+ typeof(t) _t = (t); \
+ _t == DRM_MINOR_ACCEL ? XA_LIMIT(0, ACCEL_MAX_MINORS) : XA_LIMIT(64 * _t, 64 * _t + 63); \
+})
+#define DRM_EXTENDED_MINOR_LIMIT XA_LIMIT(192, (1 << MINORBITS) - 1)
+
static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
{
struct drm_minor *minor;
- unsigned long flags;
int r;
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
@@ -129,25 +147,14 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
minor->type = type;
minor->dev = dev;
- idr_preload(GFP_KERNEL);
- if (type == DRM_MINOR_ACCEL) {
- r = accel_minor_alloc();
- } else {
- spin_lock_irqsave(&drm_minor_lock, flags);
- r = idr_alloc(&drm_minors_idr,
- NULL,
- 64 * type,
- 64 * (type + 1),
- GFP_NOWAIT);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
- }
- idr_preload_end();
-
+ r = xa_alloc(drm_minor_get_xa(type), &minor->index,
+ NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);
+ if (r == -EBUSY && (type == DRM_MINOR_PRIMARY || type == DRM_MINOR_RENDER))
+ r = xa_alloc(&drm_minors_xa, &minor->index,
+ NULL, DRM_EXTENDED_MINOR_LIMIT, GFP_KERNEL);
if (r < 0)
return r;
- minor->index = r;
-
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
return r;
@@ -163,7 +170,7 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
{
struct drm_minor *minor;
- unsigned long flags;
+ void *entry;
int ret;
DRM_DEBUG("\n");
@@ -186,13 +193,12 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
goto err_debugfs;
/* replace NULL with @minor so lookups will succeed from now on */
- if (minor->type == DRM_MINOR_ACCEL) {
- accel_minor_replace(minor, minor->index);
- } else {
- spin_lock_irqsave(&drm_minor_lock, flags);
- idr_replace(&drm_minors_idr, minor, minor->index);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
+ entry = xa_store(drm_minor_get_xa(type), minor->index, minor, GFP_KERNEL);
+ if (xa_is_err(entry)) {
+ ret = xa_err(entry);
+ goto err_debugfs;
}
+ WARN_ON(entry);
DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
@@ -205,20 +211,13 @@ err_debugfs:
static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type type)
{
struct drm_minor *minor;
- unsigned long flags;
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
/* replace @minor with NULL so lookups will fail from now on */
- if (minor->type == DRM_MINOR_ACCEL) {
- accel_minor_replace(NULL, minor->index);
- } else {
- spin_lock_irqsave(&drm_minor_lock, flags);
- idr_replace(&drm_minors_idr, NULL, minor->index);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
- }
+ xa_store(drm_minor_get_xa(type), minor->index, NULL, GFP_KERNEL);
device_del(minor->kdev);
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
@@ -234,16 +233,15 @@ static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type typ
* minor->dev pointer will stay valid! However, the device may get unplugged and
* unregistered while you hold the minor.
*/
-struct drm_minor *drm_minor_acquire(unsigned int minor_id)
+struct drm_minor *drm_minor_acquire(struct xarray *minor_xa, unsigned int minor_id)
{
struct drm_minor *minor;
- unsigned long flags;
- spin_lock_irqsave(&drm_minor_lock, flags);
- minor = idr_find(&drm_minors_idr, minor_id);
+ xa_lock(minor_xa);
+ minor = xa_load(minor_xa, minor_id);
if (minor)
drm_dev_get(minor->dev);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
+ xa_unlock(minor_xa);
if (!minor) {
return ERR_PTR(-ENODEV);
@@ -1036,7 +1034,7 @@ static int drm_stub_open(struct inode *inode, struct file *filp)
DRM_DEBUG("\n");
- minor = drm_minor_acquire(iminor(inode));
+ minor = drm_minor_acquire(&drm_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
@@ -1067,11 +1065,12 @@ static const struct file_operations drm_stub_fops = {
static void drm_core_exit(void)
{
drm_privacy_screen_lookup_exit();
+ drm_panic_exit();
accel_core_exit();
unregister_chrdev(DRM_MAJOR, "drm");
debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
- idr_destroy(&drm_minors_idr);
+ WARN_ON(!xa_empty(&drm_minors_xa));
drm_connector_ida_destroy();
}
@@ -1080,7 +1079,6 @@ static int __init drm_core_init(void)
int ret;
drm_connector_ida_init();
- idr_init(&drm_minors_idr);
drm_memcpy_init_early();
ret = drm_sysfs_init();
@@ -1099,6 +1097,8 @@ static int __init drm_core_init(void)
if (ret < 0)
goto error;
+ drm_panic_init();
+
drm_privacy_screen_lookup_init();
drm_core_init_complete = true;
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index e8a841e70934..01fde94fe2a9 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -347,7 +347,6 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor)
* resources for it. It also calls the &drm_driver.open driver callback.
*
* RETURNS:
- *
* 0 on success or negative errno value on failure.
*/
int drm_open(struct inode *inode, struct file *filp)
@@ -356,7 +355,7 @@ int drm_open(struct inode *inode, struct file *filp)
struct drm_minor *minor;
int retcode;
- minor = drm_minor_acquire(iminor(inode));
+ minor = drm_minor_acquire(&drm_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
@@ -406,7 +405,6 @@ static void drm_lastclose(struct drm_device *dev)
* in-kernel DRM client.
*
* RETURNS:
- *
* Always succeeds and returns 0.
*/
int drm_release(struct inode *inode, struct file *filp)
@@ -477,7 +475,6 @@ void drm_file_update_pid(struct drm_file *filp)
* then restores the active in-kernel DRM client.
*
* RETURNS:
- *
* Always succeeds and returns 0.
*/
int drm_release_noglobal(struct inode *inode, struct file *filp)
@@ -520,7 +517,6 @@ EXPORT_SYMBOL(drm_release_noglobal);
* safety.
*
* RETURNS:
- *
* Number of bytes read (always aligned to full events, and can be 0) or a
* negative error code on failure.
*/
@@ -606,7 +602,6 @@ EXPORT_SYMBOL(drm_read);
* See also drm_read().
*
* RETURNS:
- *
* Mask of POLL flags indicating the current status of the file.
*/
__poll_t drm_poll(struct file *filp, struct poll_table_struct *wait)
@@ -644,7 +639,6 @@ EXPORT_SYMBOL(drm_poll);
* already hold &drm_device.event_lock.
*
* RETURNS:
- *
* 0 on success or a negative error code on failure.
*/
int drm_event_reserve_init_locked(struct drm_device *dev,
@@ -686,7 +680,6 @@ EXPORT_SYMBOL(drm_event_reserve_init_locked);
* drm_event_reserve_init_locked() instead.
*
* RETURNS:
- *
* 0 on success or a negative error code on failure.
*/
int drm_event_reserve_init(struct drm_device *dev,
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index d4bbc5d109c8..149b8e25da5b 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -689,7 +689,6 @@ static int objects_lookup(struct drm_file *filp, u32 *handle, int count,
* For a single handle lookup, use drm_gem_object_lookup().
*
* Returns:
- *
* @objs filled in with GEM object pointers. Returned GEM objects need to be
* released with drm_gem_object_put(). -ENOENT is returned on a lookup
* failure. 0 is returned on success.
@@ -737,12 +736,11 @@ EXPORT_SYMBOL(drm_gem_objects_lookup);
* @filp: DRM file private date
* @handle: userspace handle
*
- * Returns:
+ * If looking up an array of handles, use drm_gem_objects_lookup().
*
+ * Returns:
* A reference to the object named by the handle if such exists on @filp, NULL
* otherwise.
- *
- * If looking up an array of handles, use drm_gem_objects_lookup().
*/
struct drm_gem_object *
drm_gem_object_lookup(struct drm_file *filp, u32 handle)
@@ -763,7 +761,6 @@ EXPORT_SYMBOL(drm_gem_object_lookup);
* @timeout: timeout value in jiffies or zero to return immediately
*
* Returns:
- *
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or
* greater than 0 on success.
*/
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 23c99803af44..1705bfc90b1e 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -80,10 +80,6 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv);
void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv,
uint32_t handle);
-/* drm_drv.c */
-struct drm_minor *drm_minor_acquire(unsigned int minor_id);
-void drm_minor_release(struct drm_minor *minor);
-
/* drm_managed.c */
void drm_managed_release(struct drm_device *dev);
void drmm_add_final_kfree(struct drm_device *dev, void *container);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 1a0890083aee..6ba167a33461 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -539,7 +539,6 @@ static int fill_analog_mode(struct drm_device *dev,
* to reach those resolutions.
*
* Returns:
- *
* A pointer to the mode, allocated with drm_mode_create(). Returns NULL
* on error.
*/
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index eff4598e2fc7..74412b7bf936 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -18,6 +18,8 @@
#include <linux/overflow.h>
#include <linux/printk.h>
#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/zlib.h>
#include <drm/drm_drv.h>
#include <drm/drm_fourcc.h>
@@ -26,6 +28,7 @@
#include <drm/drm_panic.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
#include "drm_crtc_internal.h"
@@ -85,7 +88,7 @@ static struct drm_panic_line panic_msg[] = {
PANIC_LINE(""), /* will be replaced by the panic description */
};
-#define PANIC_MSG_LINES ARRAY_SIZE(panic_msg)
+static const size_t panic_msg_lines = ARRAY_SIZE(panic_msg);
static const struct drm_panic_line logo_ascii[] = {
PANIC_LINE(" .--. _"),
@@ -97,7 +100,7 @@ static const struct drm_panic_line logo_ascii[] = {
PANIC_LINE(" \\___)=(___/"),
};
-#define PANIC_LOGO_LINES ARRAY_SIZE(logo_ascii)
+static const size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii);
#if defined(CONFIG_LOGO) && !defined(MODULE)
static const struct linux_logo *logo_mono;
@@ -257,20 +260,20 @@ static bool drm_panic_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, i
static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
- u16 fg16)
+ unsigned int scale, u16 fg16)
{
unsigned int y, x;
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y))
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, fg16);
}
static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
- u32 fg32)
+ unsigned int scale, u32 fg32)
{
unsigned int y, x;
@@ -278,7 +281,7 @@ static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
for (x = 0; x < width; x++) {
u32 off = y * dpitch + x * 3;
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) {
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) {
/* write blue-green-red to output in little endianness */
iosys_map_wr(dmap, off, u8, (fg32 & 0x000000FF) >> 0);
iosys_map_wr(dmap, off + 1, u8, (fg32 & 0x0000FF00) >> 8);
@@ -291,24 +294,25 @@ static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
- u32 fg32)
+ unsigned int scale, u32 fg32)
{
unsigned int y, x;
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y))
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, fg32);
}
static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- const u8 *sbuf8, unsigned int spitch, u32 fg_color)
+ const u8 *sbuf8, unsigned int spitch, unsigned int scale,
+ u32 fg_color)
{
unsigned int y, x;
for (y = 0; y < drm_rect_height(clip); y++)
for (x = 0; x < drm_rect_width(clip); x++)
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y))
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color);
}
@@ -318,18 +322,22 @@ static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect
* @clip: destination rectangle
* @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
* @spitch: source pitch in bytes
+ * @scale: integer scale, source buffer is scale time smaller than destination
+ * rectangle
* @fg_color: foreground color, in destination format
*
* This can be used to draw a font character, which is a monochrome image, to a
* framebuffer in other supported format.
*/
static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- const u8 *sbuf8, unsigned int spitch, u32 fg_color)
+ const u8 *sbuf8, unsigned int spitch,
+ unsigned int scale, u32 fg_color)
+
{
struct iosys_map map;
if (sb->set_pixel)
- return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, fg_color);
+ return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color);
map = sb->map[0];
iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
@@ -337,15 +345,15 @@ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
switch (sb->format->cpp[0]) {
case 2:
drm_panic_blit16(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), fg_color);
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
break;
case 3:
drm_panic_blit24(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), fg_color);
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
break;
case 4:
drm_panic_blit32(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), fg_color);
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
break;
default:
WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]);
@@ -485,37 +493,50 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb,
for (j = 0; j < line_len; j++) {
src = get_char_bitmap(font, msg[i].txt[j], font_pitch);
rec.x2 = rec.x1 + font->width;
- drm_panic_blit(sb, &rec, src, font_pitch, color);
+ drm_panic_blit(sb, &rec, src, font_pitch, 1, color);
rec.x1 += font->width;
}
}
}
+static void drm_panic_logo_rect(struct drm_rect *rect, const struct font_desc *font)
+{
+ if (logo_mono) {
+ drm_rect_init(rect, 0, 0, logo_mono->width, logo_mono->height);
+ } else {
+ int logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width;
+
+ drm_rect_init(rect, 0, 0, logo_width, logo_ascii_lines * font->height);
+ }
+}
+
+static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect,
+ const struct font_desc *font, u32 fg_color)
+{
+ if (logo_mono)
+ drm_panic_blit(sb, rect, logo_mono->data,
+ DIV_ROUND_UP(drm_rect_width(rect), 8), 1, fg_color);
+ else
+ draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, rect,
+ fg_color);
+}
+
static void draw_panic_static_user(struct drm_scanout_buffer *sb)
{
u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, r_logo, r_msg;
- unsigned int logo_width, logo_height;
unsigned int msg_width, msg_height;
if (!font)
return;
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+ drm_panic_logo_rect(&r_logo, font);
- if (logo_mono) {
- logo_width = logo_mono->width;
- logo_height = logo_mono->height;
- } else {
- logo_width = get_max_line_len(logo_ascii, PANIC_LOGO_LINES) * font->width;
- logo_height = PANIC_LOGO_LINES * font->height;
- }
- r_logo = DRM_RECT_INIT(0, 0, logo_width, logo_height);
-
- msg_width = min(get_max_line_len(panic_msg, PANIC_MSG_LINES) * font->width, sb->width);
- msg_height = min(PANIC_MSG_LINES * font->height, sb->height);
+ msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
+ msg_height = min(panic_msg_lines * font->height, sb->height);
r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
/* Center the panic message */
@@ -524,16 +545,10 @@ static void draw_panic_static_user(struct drm_scanout_buffer *sb)
/* Fill with the background color, and draw text on top */
drm_panic_fill(sb, &r_screen, bg_color);
- if ((r_msg.x1 >= logo_width || r_msg.y1 >= logo_height) &&
- logo_width <= sb->width && logo_height <= sb->height) {
- if (logo_mono)
- drm_panic_blit(sb, &r_logo, logo_mono->data, DIV_ROUND_UP(logo_width, 8),
- fg_color);
- else
- draw_txt_rectangle(sb, font, logo_ascii, PANIC_LOGO_LINES, false, &r_logo,
- fg_color);
- }
- draw_txt_rectangle(sb, font, panic_msg, PANIC_MSG_LINES, true, &r_msg, fg_color);
+ if (!drm_rect_overlap(&r_logo, &r_msg))
+ drm_panic_logo_draw(sb, &r_logo, font, fg_color);
+
+ draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
}
/*
@@ -615,6 +630,233 @@ static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
}
}
+#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+/*
+ * It is unwise to allocate memory in the panic callback, so the buffers are
+ * pre-allocated. Only 2 buffers and the zlib workspace are needed.
+ * Two buffers are enough, using the following buffer usage:
+ * 1) kmsg messages are dumped in buffer1
+ * 2) kmsg is zlib-compressed into buffer2
+ * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1
+ * 4) QR-code image is generated in buffer2
+ * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes for
+ * data segments.
+ *
+ * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which fits in
+ * a V40 QR-code (177x177).
+ *
+ * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be put
+ * directly in the QR code.
+ * 1) kmsg messages are dumped in buffer1
+ * 2) kmsg message is encoded as byte stream in buffer2
+ * 3) QR-code image is generated in buffer1
+ */
+
+static uint panic_qr_version = CONFIG_DRM_PANIC_SCREEN_QR_VERSION;
+module_param(panic_qr_version, uint, 0644);
+MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code");
+
+#define MAX_QR_DATA 2956
+#define MAX_ZLIB_RATIO 3
+#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4071 */
+#define QR_BUFFER2_SIZE 4096
+#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */
+
+/* Compression parameters */
+#define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
+
+static char *qrbuf1;
+static char *qrbuf2;
+static struct z_stream_s stream;
+
+static void __init drm_panic_qr_init(void)
+{
+ qrbuf1 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL);
+ qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL);
+ stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
+ GFP_KERNEL);
+}
+
+static void drm_panic_qr_exit(void)
+{
+ kfree(qrbuf1);
+ qrbuf1 = NULL;
+ kfree(qrbuf2);
+ qrbuf2 = NULL;
+ kfree(stream.workspace);
+ stream.workspace = NULL;
+}
+
+extern size_t drm_panic_qr_max_data_size(u8 version, size_t url_len);
+
+extern u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size,
+ u8 *tmp, size_t tmp_size);
+
+static int drm_panic_get_qr_code_url(u8 **qr_image)
+{
+ struct kmsg_dump_iter iter;
+ char url[256];
+ size_t kmsg_len, max_kmsg_size;
+ char *kmsg;
+ int max_qr_data_size, url_len;
+
+ url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&zl=",
+ utsname()->machine, utsname()->release);
+
+ max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
+ max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, QR_BUFFER1_SIZE);
+
+ /* get kmsg to buffer 1 */
+ kmsg_dump_rewind(&iter);
+ kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
+
+ if (!kmsg_len)
+ return -ENODATA;
+ kmsg = qrbuf1;
+
+try_again:
+ if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+ MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
+ return -EINVAL;
+
+ stream.next_in = kmsg;
+ stream.avail_in = kmsg_len;
+ stream.total_in = 0;
+ stream.next_out = qrbuf2;
+ stream.avail_out = QR_BUFFER2_SIZE;
+ stream.total_out = 0;
+
+ if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
+ return -EINVAL;
+
+ if (zlib_deflateEnd(&stream) != Z_OK)
+ return -EINVAL;
+
+ if (stream.total_out > max_qr_data_size) {
+ /* too much data for the QR code, so skip the first line and try again */
+ kmsg = strchr(kmsg, '\n');
+ if (!kmsg)
+ return -EINVAL;
+ /* skip the first \n */
+ kmsg += 1;
+ kmsg_len = strlen(kmsg);
+ goto try_again;
+ }
+ *qr_image = qrbuf2;
+
+ /* generate qr code image in buffer2 */
+ return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE,
+ qrbuf1, QR_BUFFER1_SIZE);
+}
+
+static int drm_panic_get_qr_code_raw(u8 **qr_image)
+{
+ struct kmsg_dump_iter iter;
+ size_t kmsg_len;
+ size_t max_kmsg_size = min(drm_panic_qr_max_data_size(panic_qr_version, 0),
+ QR_BUFFER1_SIZE);
+
+ kmsg_dump_rewind(&iter);
+ kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
+ if (!kmsg_len)
+ return -ENODATA;
+
+ *qr_image = qrbuf1;
+ return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, QR_BUFFER1_SIZE,
+ qrbuf2, QR_BUFFER2_SIZE);
+}
+
+static int drm_panic_get_qr_code(u8 **qr_image)
+{
+ if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
+ return drm_panic_get_qr_code_url(qr_image);
+ else
+ return drm_panic_get_qr_code_raw(qr_image);
+}
+
+/*
+ * Draw the panic message at the center of the screen, with a QR Code
+ */
+static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
+{
+ u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
+ u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
+ const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
+ struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
+ unsigned int max_qr_size, scale;
+ unsigned int msg_width, msg_height;
+ int qr_width, qr_canvas_width, qr_pitch, v_margin;
+ u8 *qr_image;
+
+ if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace)
+ return -ENOMEM;
+
+ r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+
+ drm_panic_logo_rect(&r_logo, font);
+
+ msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
+ msg_height = min(panic_msg_lines * font->height, sb->height);
+ r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
+
+ max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
+
+ qr_width = drm_panic_get_qr_code(&qr_image);
+ if (qr_width <= 0)
+ return -ENOSPC;
+
+ qr_canvas_width = qr_width + QR_MARGIN * 2;
+ scale = max_qr_size / qr_canvas_width;
+ /* QR code is not readable if not scaled at least by 2 */
+ if (scale < 2)
+ return -ENOSPC;
+
+ pr_debug("QR width %d and scale %d\n", qr_width, scale);
+ r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
+
+ v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg)) / 5;
+
+ drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
+ r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale,
+ qr_width * scale, qr_width * scale);
+
+ /* Center the panic message */
+ drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2,
+ 3 * v_margin + drm_rect_height(&r_qr_canvas));
+
+ /* Fill with the background color, and draw text on top */
+ drm_panic_fill(sb, &r_screen, bg_color);
+
+ if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr))
+ drm_panic_logo_draw(sb, &r_logo, font, fg_color);
+
+ draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
+
+ /* Draw the qr code */
+ qr_pitch = DIV_ROUND_UP(qr_width, 8);
+ drm_panic_fill(sb, &r_qr_canvas, fg_color);
+ drm_panic_fill(sb, &r_qr, bg_color);
+ drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
+ return 0;
+}
+
+static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
+{
+ if (_draw_panic_static_qr_code(sb))
+ draw_panic_static_user(sb);
+}
+#else
+static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
+{
+ draw_panic_static_user(sb);
+}
+
+static void drm_panic_qr_init(void) {};
+static void drm_panic_qr_exit(void) {};
+#endif
+
/*
* drm_panic_is_format_supported()
* @format: a fourcc color code
@@ -633,6 +875,8 @@ static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
{
if (!strcmp(drm_panic_screen, "kmsg")) {
draw_panic_static_kmsg(sb);
+ } else if (!strcmp(drm_panic_screen, "qr_code")) {
+ draw_panic_static_qr_code(sb);
} else {
draw_panic_static_user(sb);
}
@@ -643,7 +887,7 @@ static void drm_panic_set_description(const char *description)
u32 len;
if (description) {
- struct drm_panic_line *desc_line = &panic_msg[PANIC_MSG_LINES - 1];
+ struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
desc_line->txt = description;
len = strlen(description);
@@ -656,7 +900,7 @@ static void drm_panic_set_description(const char *description)
static void drm_panic_clear_description(void)
{
- struct drm_panic_line *desc_line = &panic_msg[PANIC_MSG_LINES - 1];
+ struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
desc_line->len = 0;
desc_line->txt = NULL;
@@ -802,3 +1046,19 @@ void drm_panic_unregister(struct drm_device *dev)
kmsg_dump_unregister(&plane->kmsg_panic);
}
}
+
+/**
+ * drm_panic_init() - initialize DRM panic.
+ */
+void __init drm_panic_init(void)
+{
+ drm_panic_qr_init();
+}
+
+/**
+ * drm_panic_exit() - Free the resources taken by drm_panic_exit()
+ */
+void drm_panic_exit(void)
+{
+ drm_panic_qr_exit();
+}
diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_qr.rs
new file mode 100644
index 000000000000..1ef56cb07dfb
--- /dev/null
+++ b/drivers/gpu/drm/drm_panic_qr.rs
@@ -0,0 +1,1003 @@
+// SPDX-License-Identifier: MIT
+
+//! This is a simple QR encoder for DRM panic.
+//!
+//! It is called from a panic handler, so it should't allocate memory and
+//! does all the work on the stack or on the provided buffers. For
+//! simplification, it only supports low error correction, and applies the
+//! first mask (checkerboard). It will draw the smallest QRcode that can
+//! contain the string passed as parameter. To get the most compact
+//! QR code, the start of the URL is encoded as binary, and the
+//! compressed kmsg is encoded as numeric.
+//!
+//! The binary data must be a valid URL parameter, so the easiest way is
+//! to use base64 encoding. But this wastes 25% of data space, so the
+//! whole stack trace won't fit in the QR code. So instead it encodes
+//! every 13bits of input into 4 decimal digits, and then uses the
+//! efficient numeric encoding, that encode 3 decimal digits into
+//! 10bits. This makes 39bits of compressed data into 12 decimal digits,
+//! into 40bits in the QR code, so wasting only 2.5%. And the numbers are
+//! valid URL parameter, so the website can do the reverse, to get the
+//! binary data.
+//!
+//! Inspired by these 3 projects, all under MIT license:
+//!
+//! * <https://github.com/kennytm/qrcode-rust>
+//! * <https://github.com/erwanvivien/fast_qr>
+//! * <https://github.com/bjguillot/qr>
+
+use core::cmp;
+use kernel::str::CStr;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
+struct Version(usize);
+
+// Generator polynomials for ECC, only those that are needed for low quality.
+const P7: [u8; 7] = [87, 229, 146, 149, 238, 102, 21];
+const P10: [u8; 10] = [251, 67, 46, 61, 118, 70, 64, 94, 32, 45];
+const P15: [u8; 15] = [
+ 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105,
+];
+const P18: [u8; 18] = [
+ 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153,
+];
+const P20: [u8; 20] = [
+ 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190,
+];
+const P22: [u8; 22] = [
+ 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 219, 134, 160, 105,
+ 165, 231,
+];
+const P24: [u8; 24] = [
+ 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117,
+ 232, 87, 96, 227, 21,
+];
+const P26: [u8; 26] = [
+ 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 245, 142, 13, 102,
+ 48, 227, 153, 145, 218, 70,
+];
+const P28: [u8; 28] = [
+ 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87,
+ 42, 195, 212, 119, 242, 37, 9, 123,
+];
+const P30: [u8; 30] = [
+ 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193,
+ 224, 130, 156, 37, 251, 216, 238, 40, 192, 180,
+];
+
+/// QR Code parameters for Low quality ECC:
+/// - Error Correction polynomial.
+/// - Number of blocks in group 1.
+/// - Number of blocks in group 2.
+/// - Block size in group 1.
+///
+/// (Block size in group 2 is one more than group 1).
+struct VersionParameter(&'static [u8], u8, u8, u8);
+const VPARAM: [VersionParameter; 40] = [
+ VersionParameter(&P7, 1, 0, 19), // V1
+ VersionParameter(&P10, 1, 0, 34), // V2
+ VersionParameter(&P15, 1, 0, 55), // V3
+ VersionParameter(&P20, 1, 0, 80), // V4
+ VersionParameter(&P26, 1, 0, 108), // V5
+ VersionParameter(&P18, 2, 0, 68), // V6
+ VersionParameter(&P20, 2, 0, 78), // V7
+ VersionParameter(&P24, 2, 0, 97), // V8
+ VersionParameter(&P30, 2, 0, 116), // V9
+ VersionParameter(&P18, 2, 2, 68), // V10
+ VersionParameter(&P20, 4, 0, 81), // V11
+ VersionParameter(&P24, 2, 2, 92), // V12
+ VersionParameter(&P26, 4, 0, 107), // V13
+ VersionParameter(&P30, 3, 1, 115), // V14
+ VersionParameter(&P22, 5, 1, 87), // V15
+ VersionParameter(&P24, 5, 1, 98), // V16
+ VersionParameter(&P28, 1, 5, 107), // V17
+ VersionParameter(&P30, 5, 1, 120), // V18
+ VersionParameter(&P28, 3, 4, 113), // V19
+ VersionParameter(&P28, 3, 5, 107), // V20
+ VersionParameter(&P28, 4, 4, 116), // V21
+ VersionParameter(&P28, 2, 7, 111), // V22
+ VersionParameter(&P30, 4, 5, 121), // V23
+ VersionParameter(&P30, 6, 4, 117), // V24
+ VersionParameter(&P26, 8, 4, 106), // V25
+ VersionParameter(&P28, 10, 2, 114), // V26
+ VersionParameter(&P30, 8, 4, 122), // V27
+ VersionParameter(&P30, 3, 10, 117), // V28
+ VersionParameter(&P30, 7, 7, 116), // V29
+ VersionParameter(&P30, 5, 10, 115), // V30
+ VersionParameter(&P30, 13, 3, 115), // V31
+ VersionParameter(&P30, 17, 0, 115), // V32
+ VersionParameter(&P30, 17, 1, 115), // V33
+ VersionParameter(&P30, 13, 6, 115), // V34
+ VersionParameter(&P30, 12, 7, 121), // V35
+ VersionParameter(&P30, 6, 14, 121), // V36
+ VersionParameter(&P30, 17, 4, 122), // V37
+ VersionParameter(&P30, 4, 18, 122), // V38
+ VersionParameter(&P30, 20, 4, 117), // V39
+ VersionParameter(&P30, 19, 6, 118), // V40
+];
+
+const MAX_EC_SIZE: usize = 30;
+const MAX_BLK_SIZE: usize = 123;
+
+/// Position of the alignment pattern grid.
+const ALIGNMENT_PATTERNS: [&[u8]; 40] = [
+ &[],
+ &[6, 18],
+ &[6, 22],
+ &[6, 26],
+ &[6, 30],
+ &[6, 34],
+ &[6, 22, 38],
+ &[6, 24, 42],
+ &[6, 26, 46],
+ &[6, 28, 50],
+ &[6, 30, 54],
+ &[6, 32, 58],
+ &[6, 34, 62],
+ &[6, 26, 46, 66],
+ &[6, 26, 48, 70],
+ &[6, 26, 50, 74],
+ &[6, 30, 54, 78],
+ &[6, 30, 56, 82],
+ &[6, 30, 58, 86],
+ &[6, 34, 62, 90],
+ &[6, 28, 50, 72, 94],
+ &[6, 26, 50, 74, 98],
+ &[6, 30, 54, 78, 102],
+ &[6, 28, 54, 80, 106],
+ &[6, 32, 58, 84, 110],
+ &[6, 30, 58, 86, 114],
+ &[6, 34, 62, 90, 118],
+ &[6, 26, 50, 74, 98, 122],
+ &[6, 30, 54, 78, 102, 126],
+ &[6, 26, 52, 78, 104, 130],
+ &[6, 30, 56, 82, 108, 134],
+ &[6, 34, 60, 86, 112, 138],
+ &[6, 30, 58, 86, 114, 142],
+ &[6, 34, 62, 90, 118, 146],
+ &[6, 30, 54, 78, 102, 126, 150],
+ &[6, 24, 50, 76, 102, 128, 154],
+ &[6, 28, 54, 80, 106, 132, 158],
+ &[6, 32, 58, 84, 110, 136, 162],
+ &[6, 26, 54, 82, 110, 138, 166],
+ &[6, 30, 58, 86, 114, 142, 170],
+];
+
+/// Version information for format V7-V40.
+const VERSION_INFORMATION: [u32; 34] = [
+ 0b00_0111_1100_1001_0100,
+ 0b00_1000_0101_1011_1100,
+ 0b00_1001_1010_1001_1001,
+ 0b00_1010_0100_1101_0011,
+ 0b00_1011_1011_1111_0110,
+ 0b00_1100_0111_0110_0010,
+ 0b00_1101_1000_0100_0111,
+ 0b00_1110_0110_0000_1101,
+ 0b00_1111_1001_0010_1000,
+ 0b01_0000_1011_0111_1000,
+ 0b01_0001_0100_0101_1101,
+ 0b01_0010_1010_0001_0111,
+ 0b01_0011_0101_0011_0010,
+ 0b01_0100_1001_1010_0110,
+ 0b01_0101_0110_1000_0011,
+ 0b01_0110_1000_1100_1001,
+ 0b01_0111_0111_1110_1100,
+ 0b01_1000_1110_1100_0100,
+ 0b01_1001_0001_1110_0001,
+ 0b01_1010_1111_1010_1011,
+ 0b01_1011_0000_1000_1110,
+ 0b01_1100_1100_0001_1010,
+ 0b01_1101_0011_0011_1111,
+ 0b01_1110_1101_0111_0101,
+ 0b01_1111_0010_0101_0000,
+ 0b10_0000_1001_1101_0101,
+ 0b10_0001_0110_1111_0000,
+ 0b10_0010_1000_1011_1010,
+ 0b10_0011_0111_1001_1111,
+ 0b10_0100_1011_0000_1011,
+ 0b10_0101_0100_0010_1110,
+ 0b10_0110_1010_0110_0100,
+ 0b10_0111_0101_0100_0001,
+ 0b10_1000_1100_0110_1001,
+];
+
+/// Format info for low quality ECC.
+const FORMAT_INFOS_QR_L: [u16; 8] = [
+ 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976,
+];
+
+impl Version {
+ /// Returns the smallest QR version than can hold these segments.
+ fn from_segments(segments: &[&Segment<'_>]) -> Option<Version> {
+ for v in (1..=40).map(|k| Version(k)) {
+ if v.max_data() * 8 >= segments.iter().map(|s| s.total_size_bits(v)).sum() {
+ return Some(v);
+ }
+ }
+ None
+ }
+
+ fn width(&self) -> u8 {
+ (self.0 as u8) * 4 + 17
+ }
+
+ fn max_data(&self) -> usize {
+ self.g1_blk_size() * self.g1_blocks() + (self.g1_blk_size() + 1) * self.g2_blocks()
+ }
+
+ fn ec_size(&self) -> usize {
+ VPARAM[self.0 - 1].0.len()
+ }
+
+ fn g1_blocks(&self) -> usize {
+ VPARAM[self.0 - 1].1 as usize
+ }
+
+ fn g2_blocks(&self) -> usize {
+ VPARAM[self.0 - 1].2 as usize
+ }
+
+ fn g1_blk_size(&self) -> usize {
+ VPARAM[self.0 - 1].3 as usize
+ }
+
+ fn alignment_pattern(&self) -> &'static [u8] {
+ &ALIGNMENT_PATTERNS[self.0 - 1]
+ }
+
+ fn poly(&self) -> &'static [u8] {
+ VPARAM[self.0 - 1].0
+ }
+
+ fn version_info(&self) -> u32 {
+ if *self >= Version(7) {
+ VERSION_INFORMATION[self.0 - 7]
+ } else {
+ 0
+ }
+ }
+}
+
+/// Exponential table for Galois Field GF(256).
+const EXP_TABLE: [u8; 256] = [
+ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117,
+ 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181,
+ 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161,
+ 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187,
+ 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136,
+ 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197,
+ 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168,
+ 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198,
+ 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149,
+ 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167,
+ 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72,
+ 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207,
+ 131, 27, 54, 108, 216, 173, 71, 142, 1,
+];
+
+/// Reverse exponential table for Galois Field GF(256).
+const LOG_TABLE: [u8; 256] = [
+ 175, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141,
+ 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142,
+ 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114,
+ 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148,
+ 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126,
+ 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172,
+ 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
+ 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149,
+ 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132,
+ 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12,
+ 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95,
+ 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173,
+ 232, 116, 214, 244, 234, 168, 80, 88, 175,
+];
+
+// 4 bits segment header.
+const MODE_STOP: u16 = 0;
+const MODE_NUMERIC: u16 = 1;
+const MODE_BINARY: u16 = 4;
+/// Padding bytes.
+const PADDING: [u8; 2] = [236, 17];
+
+/// Get the next 13 bits of data, starting at specified offset (in bits).
+fn get_next_13b(data: &[u8], offset: usize) -> Option<(u16, usize)> {
+ if offset < data.len() * 8 {
+ let size = cmp::min(13, data.len() * 8 - offset);
+ let byte_off = offset / 8;
+ let bit_off = offset % 8;
+ // `b` is 20 at max (`bit_off` <= 7 and `size` <= 13).
+ let b = (bit_off + size) as u16;
+
+ let first_byte = (data[byte_off] << bit_off >> bit_off) as u16;
+
+ let number = match b {
+ 0..=8 => first_byte >> (8 - b),
+ 9..=16 => (first_byte << (b - 8)) + (data[byte_off + 1] >> (16 - b)) as u16,
+ _ => {
+ (first_byte << (b - 8))
+ + ((data[byte_off + 1] as u16) << (b - 16))
+ + (data[byte_off + 2] >> (24 - b)) as u16
+ }
+ };
+ Some((number, size))
+ } else {
+ None
+ }
+}
+
+/// Number of bits to encode characters in numeric mode.
+const NUM_CHARS_BITS: [usize; 4] = [0, 4, 7, 10];
+const POW10: [u16; 4] = [1, 10, 100, 1000];
+
+enum Segment<'a> {
+ Numeric(&'a [u8]),
+ Binary(&'a [u8]),
+}
+
+impl Segment<'_> {
+ fn get_header(&self) -> (u16, usize) {
+ match self {
+ Segment::Binary(_) => (MODE_BINARY, 4),
+ Segment::Numeric(_) => (MODE_NUMERIC, 4),
+ }
+ }
+
+ // Returns the size of the length field in bits, depending on QR Version.
+ fn length_bits_count(&self, version: Version) -> usize {
+ let Version(v) = version;
+ match self {
+ Segment::Binary(_) => match v {
+ 1..=9 => 8,
+ _ => 16,
+ },
+ Segment::Numeric(_) => match v {
+ 1..=9 => 10,
+ 10..=26 => 12,
+ _ => 14,
+ },
+ }
+ }
+
+ // Number of characters in the segment.
+ fn character_count(&self) -> usize {
+ match self {
+ Segment::Binary(data) => data.len(),
+ Segment::Numeric(data) => {
+ let data_bits = data.len() * 8;
+ let last_chars = match data_bits % 13 {
+ 1 => 1,
+ k => (k + 1) / 3,
+ };
+ // 4 decimal numbers per 13bits + remainder.
+ 4 * (data_bits / 13) + last_chars
+ }
+ }
+ }
+
+ fn get_length_field(&self, version: Version) -> (u16, usize) {
+ (
+ self.character_count() as u16,
+ self.length_bits_count(version),
+ )
+ }
+
+ fn total_size_bits(&self, version: Version) -> usize {
+ let data_size = match self {
+ Segment::Binary(data) => data.len() * 8,
+ Segment::Numeric(_) => {
+ let digits = self.character_count();
+ 10 * (digits / 3) + NUM_CHARS_BITS[digits % 3]
+ }
+ };
+ // header + length + data.
+ 4 + self.length_bits_count(version) + data_size
+ }
+
+ fn iter(&self) -> SegmentIterator<'_> {
+ SegmentIterator {
+ segment: self,
+ offset: 0,
+ carry: 0,
+ carry_len: 0,
+ }
+ }
+}
+
+struct SegmentIterator<'a> {
+ segment: &'a Segment<'a>,
+ offset: usize,
+ carry: u16,
+ carry_len: usize,
+}
+
+impl Iterator for SegmentIterator<'_> {
+ type Item = (u16, usize);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.segment {
+ Segment::Binary(data) => {
+ if self.offset < data.len() {
+ let byte = data[self.offset] as u16;
+ self.offset += 1;
+ Some((byte, 8))
+ } else {
+ None
+ }
+ }
+ Segment::Numeric(data) => {
+ if self.carry_len == 3 {
+ let out = (self.carry, NUM_CHARS_BITS[self.carry_len]);
+ self.carry_len = 0;
+ self.carry = 0;
+ Some(out)
+ } else if let Some((bits, size)) = get_next_13b(data, self.offset) {
+ self.offset += size;
+ let new_chars = match size {
+ 1 => 1,
+ k => (k + 1) / 3,
+ };
+ if self.carry_len + new_chars > 3 {
+ self.carry_len = new_chars + self.carry_len - 3;
+ let out = (
+ self.carry * POW10[new_chars - self.carry_len]
+ + bits / POW10[self.carry_len],
+ NUM_CHARS_BITS[3],
+ );
+ self.carry = bits % POW10[self.carry_len];
+ Some(out)
+ } else {
+ let out = (
+ self.carry * POW10[new_chars] + bits,
+ NUM_CHARS_BITS[self.carry_len + new_chars],
+ );
+ self.carry_len = 0;
+ Some(out)
+ }
+ } else if self.carry_len > 0 {
+ let out = (self.carry, NUM_CHARS_BITS[self.carry_len]);
+ self.carry_len = 0;
+ Some(out)
+ } else {
+ None
+ }
+ }
+ }
+ }
+}
+
+struct EncodedMsg<'a> {
+ data: &'a mut [u8],
+ ec_size: usize,
+ g1_blocks: usize,
+ g2_blocks: usize,
+ g1_blk_size: usize,
+ g2_blk_size: usize,
+ poly: &'static [u8],
+ version: Version,
+}
+
+/// Data to be put in the QR code, with correct segment encoding, padding, and
+/// Error Code Correction.
+impl EncodedMsg<'_> {
+ fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> Option<EncodedMsg<'a>> {
+ let version = Version::from_segments(segments)?;
+ let ec_size = version.ec_size();
+ let g1_blocks = version.g1_blocks();
+ let g2_blocks = version.g2_blocks();
+ let g1_blk_size = version.g1_blk_size();
+ let g2_blk_size = g1_blk_size + 1;
+ let poly = version.poly();
+
+ // clear the output.
+ data.fill(0);
+
+ let mut em = EncodedMsg {
+ data: data,
+ ec_size,
+ g1_blocks,
+ g2_blocks,
+ g1_blk_size,
+ g2_blk_size,
+ poly,
+ version,
+ };
+ em.encode(segments);
+ Some(em)
+ }
+
+ /// Push bits of data at an offset (in bits).
+ fn push(&mut self, offset: &mut usize, bits: (u16, usize)) {
+ let (number, len_bits) = bits;
+ let byte_off = *offset / 8;
+ let bit_off = *offset % 8;
+ let b = bit_off + len_bits;
+
+ match (bit_off, b) {
+ (0, 0..=8) => {
+ self.data[byte_off] = (number << (8 - b)) as u8;
+ }
+ (0, _) => {
+ self.data[byte_off] = (number >> (b - 8)) as u8;
+ self.data[byte_off + 1] = (number << (16 - b)) as u8;
+ }
+ (_, 0..=8) => {
+ self.data[byte_off] |= (number << (8 - b)) as u8;
+ }
+ (_, 9..=16) => {
+ self.data[byte_off] |= (number >> (b - 8)) as u8;
+ self.data[byte_off + 1] = (number << (16 - b)) as u8;
+ }
+ _ => {
+ self.data[byte_off] |= (number >> (b - 8)) as u8;
+ self.data[byte_off + 1] = (number >> (b - 16)) as u8;
+ self.data[byte_off + 2] = (number << (24 - b)) as u8;
+ }
+ }
+ *offset += len_bits;
+ }
+
+ fn add_segments(&mut self, segments: &[&Segment<'_>]) {
+ let mut offset: usize = 0;
+
+ for s in segments.iter() {
+ self.push(&mut offset, s.get_header());
+ self.push(&mut offset, s.get_length_field(self.version));
+ for bits in s.iter() {
+ self.push(&mut offset, bits);
+ }
+ }
+ self.push(&mut offset, (MODE_STOP, 4));
+
+ let pad_offset = (offset + 7) / 8;
+ for i in pad_offset..self.version.max_data() {
+ self.data[i] = PADDING[(i & 1) ^ (pad_offset & 1)];
+ }
+ }
+
+ fn error_code_for_blocks(&mut self, offset: usize, size: usize, ec_offset: usize) {
+ let mut tmp: [u8; MAX_BLK_SIZE + MAX_EC_SIZE] = [0; MAX_BLK_SIZE + MAX_EC_SIZE];
+
+ tmp[0..size].copy_from_slice(&self.data[offset..offset + size]);
+ for i in 0..size {
+ let lead_coeff = tmp[i] as usize;
+ if lead_coeff == 0 {
+ continue;
+ }
+ let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]);
+ for (u, &v) in tmp[i + 1..].iter_mut().zip(self.poly.iter()) {
+ *u ^= EXP_TABLE[(usize::from(v) + log_lead_coeff) % 255];
+ }
+ }
+ self.data[ec_offset..ec_offset + self.ec_size]
+ .copy_from_slice(&tmp[size..size + self.ec_size]);
+ }
+
+ fn compute_error_code(&mut self) {
+ let mut offset = 0;
+ let mut ec_offset = self.g1_blocks * self.g1_blk_size + self.g2_blocks * self.g2_blk_size;
+
+ for _ in 0..self.g1_blocks {
+ self.error_code_for_blocks(offset, self.g1_blk_size, ec_offset);
+ offset += self.g1_blk_size;
+ ec_offset += self.ec_size;
+ }
+ for _ in 0..self.g2_blocks {
+ self.error_code_for_blocks(offset, self.g2_blk_size, ec_offset);
+ offset += self.g2_blk_size;
+ ec_offset += self.ec_size;
+ }
+ }
+
+ fn encode(&mut self, segments: &[&Segment<'_>]) {
+ self.add_segments(segments);
+ self.compute_error_code();
+ }
+
+ fn iter(&self) -> EncodedMsgIterator<'_> {
+ EncodedMsgIterator {
+ em: self,
+ offset: 0,
+ }
+ }
+}
+
+/// Iterator, to retrieve the data in the interleaved order needed by QR code.
+struct EncodedMsgIterator<'a> {
+ em: &'a EncodedMsg<'a>,
+ offset: usize,
+}
+
+impl Iterator for EncodedMsgIterator<'_> {
+ type Item = u8;
+
+ // Send the bytes in interleaved mode, first byte of first block of group1,
+ // then first byte of second block of group1, ...
+ fn next(&mut self) -> Option<Self::Item> {
+ let em = self.em;
+ let blocks = em.g1_blocks + em.g2_blocks;
+ let g1_end = em.g1_blocks * em.g1_blk_size;
+ let g2_end = g1_end + em.g2_blocks * em.g2_blk_size;
+ let ec_end = g2_end + em.ec_size * blocks;
+
+ if self.offset >= ec_end {
+ return None;
+ }
+
+ let offset = if self.offset < em.g1_blk_size * blocks {
+ // group1 and group2 interleaved
+ let blk = self.offset % blocks;
+ let blk_off = self.offset / blocks;
+ if blk < em.g1_blocks {
+ blk * em.g1_blk_size + blk_off
+ } else {
+ g1_end + em.g2_blk_size * (blk - em.g1_blocks) + blk_off
+ }
+ } else if self.offset < g2_end {
+ // last byte of group2 blocks
+ let blk2 = self.offset - blocks * em.g1_blk_size;
+ em.g1_blk_size * em.g1_blocks + blk2 * em.g2_blk_size + em.g2_blk_size - 1
+ } else {
+ // EC blocks
+ let ec_offset = self.offset - g2_end;
+ let blk = ec_offset % blocks;
+ let blk_off = ec_offset / blocks;
+
+ g2_end + blk * em.ec_size + blk_off
+ };
+ self.offset += 1;
+ Some(em.data[offset])
+ }
+}
+
+/// A QR code image, encoded as a linear binary framebuffer.
+/// 1 bit per module (pixel), each new line start at next byte boundary.
+/// Max width is 177 for V40 QR code, so `u8` is enough for coordinate.
+struct QrImage<'a> {
+ data: &'a mut [u8],
+ width: u8,
+ stride: u8,
+ version: Version,
+}
+
+impl QrImage<'_> {
+ fn new<'a, 'b>(em: &'b EncodedMsg<'b>, qrdata: &'a mut [u8]) -> QrImage<'a> {
+ let width = em.version.width();
+ let stride = (width + 7) / 8;
+ let data = qrdata;
+
+ let mut qr_image = QrImage {
+ data,
+ width,
+ stride,
+ version: em.version,
+ };
+ qr_image.draw_all(em.iter());
+ qr_image
+ }
+
+ fn clear(&mut self) {
+ self.data.fill(0);
+ }
+
+ // Set pixel to light color.
+ fn set(&mut self, x: u8, y: u8) {
+ let off = y as usize * self.stride as usize + x as usize / 8;
+ let mut v = self.data[off];
+ v |= 0x80 >> (x % 8);
+ self.data[off] = v;
+ }
+
+ // Invert a module color.
+ fn xor(&mut self, x: u8, y: u8) {
+ let off = y as usize * self.stride as usize + x as usize / 8;
+ self.data[off] ^= 0x80 >> (x % 8);
+ }
+
+ // Draw a light square at (x, y) top left corner.
+ fn draw_square(&mut self, x: u8, y: u8, size: u8) {
+ for k in 0..size {
+ self.set(x + k, y);
+ self.set(x, y + k + 1);
+ self.set(x + size, y + k);
+ self.set(x + k + 1, y + size);
+ }
+ }
+
+ // Finder pattern: 3 8x8 square at the corners.
+ fn draw_finders(&mut self) {
+ self.draw_square(1, 1, 4);
+ self.draw_square(self.width - 6, 1, 4);
+ self.draw_square(1, self.width - 6, 4);
+ for k in 0..8 {
+ self.set(k, 7);
+ self.set(self.width - k - 1, 7);
+ self.set(k, self.width - 8);
+ }
+ for k in 0..7 {
+ self.set(7, k);
+ self.set(self.width - 8, k);
+ self.set(7, self.width - 1 - k);
+ }
+ }
+
+ fn is_finder(&self, x: u8, y: u8) -> bool {
+ let end = self.width - 8;
+ (x < 8 && y < 8) || (x < 8 && y >= end) || (x >= end && y < 8)
+ }
+
+ // Alignment pattern: 5x5 squares in a grid.
+ fn draw_alignments(&mut self) {
+ let positions = self.version.alignment_pattern();
+ for &x in positions.iter() {
+ for &y in positions.iter() {
+ if !self.is_finder(x, y) {
+ self.draw_square(x - 1, y - 1, 2);
+ }
+ }
+ }
+ }
+
+ fn is_alignment(&self, x: u8, y: u8) -> bool {
+ let positions = self.version.alignment_pattern();
+ for &ax in positions.iter() {
+ for &ay in positions.iter() {
+ if self.is_finder(ax, ay) {
+ continue;
+ }
+ if x >= ax - 2 && x <= ax + 2 && y >= ay - 2 && y <= ay + 2 {
+ return true;
+ }
+ }
+ }
+ false
+ }
+
+ // Timing pattern: 2 dotted line between the finder patterns.
+ fn draw_timing_patterns(&mut self) {
+ let end = self.width - 8;
+
+ for x in (9..end).step_by(2) {
+ self.set(x, 6);
+ self.set(6, x);
+ }
+ }
+
+ fn is_timing(&self, x: u8, y: u8) -> bool {
+ x == 6 || y == 6
+ }
+
+ // Mask info: 15 bits around the finders, written twice for redundancy.
+ fn draw_maskinfo(&mut self) {
+ let info: u16 = FORMAT_INFOS_QR_L[0];
+ let mut skip = 0;
+
+ for k in 0..7 {
+ if k == 6 {
+ skip = 1;
+ }
+ if info & (1 << (14 - k)) == 0 {
+ self.set(k + skip, 8);
+ self.set(8, self.width - 1 - k);
+ }
+ }
+ skip = 0;
+ for k in 0..8 {
+ if k == 2 {
+ skip = 1;
+ }
+ if info & (1 << (7 - k)) == 0 {
+ self.set(8, 8 - skip - k);
+ self.set(self.width - 8 + k, 8);
+ }
+ }
+ }
+
+ fn is_maskinfo(&self, x: u8, y: u8) -> bool {
+ let end = self.width - 8;
+ // Count the dark module as mask info.
+ (x <= 8 && y == 8) || (y <= 8 && x == 8) || (x == 8 && y >= end) || (x >= end && y == 8)
+ }
+
+ // Version info: 18bits written twice, close to the finders.
+ fn draw_version_info(&mut self) {
+ let vinfo = self.version.version_info();
+ let pos = self.width - 11;
+
+ if vinfo != 0 {
+ for x in 0..3 {
+ for y in 0..6 {
+ if vinfo & (1 << (x + y * 3)) == 0 {
+ self.set(x + pos, y);
+ self.set(y, x + pos);
+ }
+ }
+ }
+ }
+ }
+
+ fn is_version_info(&self, x: u8, y: u8) -> bool {
+ let vinfo = self.version.version_info();
+ let pos = self.width - 11;
+
+ vinfo != 0 && ((x >= pos && x < pos + 3 && y < 6) || (y >= pos && y < pos + 3 && x < 6))
+ }
+
+ // Returns true if the module is reserved (Not usable for data and EC).
+ fn is_reserved(&self, x: u8, y: u8) -> bool {
+ self.is_alignment(x, y)
+ || self.is_finder(x, y)
+ || self.is_timing(x, y)
+ || self.is_maskinfo(x, y)
+ || self.is_version_info(x, y)
+ }
+
+ // Last module to draw, at bottom left corner.
+ fn is_last(&self, x: u8, y: u8) -> bool {
+ x == 0 && y == self.width - 1
+ }
+
+ // Move to the next module according to QR code order.
+ // From bottom right corner, to bottom left corner.
+ fn next(&self, x: u8, y: u8) -> (u8, u8) {
+ let x_adj = if x <= 6 { x + 1 } else { x };
+ let column_type = (self.width - x_adj) % 4;
+
+ match column_type {
+ 2 if y > 0 => (x + 1, y - 1),
+ 0 if y < self.width - 1 => (x + 1, y + 1),
+ 0 | 2 if x == 7 => (x - 2, y),
+ _ => (x - 1, y),
+ }
+ }
+
+ // Find next module that can hold data.
+ fn next_available(&self, x: u8, y: u8) -> (u8, u8) {
+ let (mut x, mut y) = self.next(x, y);
+ while self.is_reserved(x, y) && !self.is_last(x, y) {
+ (x, y) = self.next(x, y);
+ }
+ (x, y)
+ }
+
+ fn draw_data(&mut self, data: impl Iterator<Item = u8>) {
+ let (mut x, mut y) = (self.width - 1, self.width - 1);
+ for byte in data {
+ for s in 0..8 {
+ if byte & (0x80 >> s) == 0 {
+ self.set(x, y);
+ }
+ (x, y) = self.next_available(x, y);
+ }
+ }
+ // Set the remaining modules (0, 3 or 7 depending on version).
+ // because 0 correspond to a light module.
+ while !self.is_last(x, y) {
+ if !self.is_reserved(x, y) {
+ self.set(x, y);
+ }
+ (x, y) = self.next(x, y);
+ }
+ }
+
+ // Apply checkerboard mask to all non-reserved modules.
+ fn apply_mask(&mut self) {
+ for x in 0..self.width {
+ for y in 0..self.width {
+ if (x ^ y) % 2 == 0 && !self.is_reserved(x, y) {
+ self.xor(x, y);
+ }
+ }
+ }
+ }
+
+ // Draw the QR code with the provided data iterator.
+ fn draw_all(&mut self, data: impl Iterator<Item = u8>) {
+ // First clear the table, as it may have already some data.
+ self.clear();
+ self.draw_finders();
+ self.draw_alignments();
+ self.draw_timing_patterns();
+ self.draw_version_info();
+ self.draw_data(data);
+ self.draw_maskinfo();
+ self.apply_mask();
+ }
+}
+
+/// C entry point for the rust QR Code generator.
+///
+/// Write the QR code image in the data buffer, and return the QR code width,
+/// or 0, if the data doesn't fit in a QR code.
+///
+/// * `url`: The base URL of the QR code. It will be encoded as Binary segment.
+/// * `data`: A pointer to the binary data, to be encoded. if URL is NULL, it
+/// will be encoded as binary segment, otherwise it will be encoded
+/// efficiently as a numeric segment, and appended to the URL.
+/// * `data_len`: Length of the data, that needs to be encoded, must be less
+/// than data_size.
+/// * `data_size`: Size of data buffer, it should be at least 4071 bytes to hold
+/// a V40 QR code. It will then be overwritten with the QR code image.
+/// * `tmp`: A temporary buffer that the QR code encoder will use, to write the
+/// segments and ECC.
+/// * `tmp_size`: Size of the temporary buffer, it must be at least 3706 bytes
+/// long for V40.
+///
+/// # Safety
+///
+/// * `url` must be null or point at a nul-terminated string.
+/// * `data` must be valid for reading and writing for `data_size` bytes.
+/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.
+///
+/// They must remain valid for the duration of the function call.
+
+#[no_mangle]
+pub unsafe extern "C" fn drm_panic_qr_generate(
+ url: *const i8,
+ data: *mut u8,
+ data_len: usize,
+ data_size: usize,
+ tmp: *mut u8,
+ tmp_size: usize,
+) -> u8 {
+ if data_size < 4071 || tmp_size < 3706 || data_len > data_size {
+ return 0;
+ }
+ // SAFETY: The caller ensures that `data` is a valid pointer for reading and
+ // writing `data_size` bytes.
+ let data_slice: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(data, data_size) };
+ // SAFETY: The caller ensures that `tmp` is a valid pointer for reading and
+ // writing `tmp_size` bytes.
+ let tmp_slice: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(tmp, tmp_size) };
+ if url.is_null() {
+ match EncodedMsg::new(&[&Segment::Binary(&data_slice[0..data_len])], tmp_slice) {
+ None => 0,
+ Some(em) => {
+ let qr_image = QrImage::new(&em, data_slice);
+ qr_image.width
+ }
+ }
+ } else {
+ // SAFETY: The caller ensures that `url` is a valid pointer to a
+ // nul-terminated string.
+ let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) };
+ let segments = &[
+ &Segment::Binary(url_cstr.as_bytes()),
+ &Segment::Numeric(&data_slice[0..data_len]),
+ ];
+ match EncodedMsg::new(segments, tmp_slice) {
+ None => 0,
+ Some(em) => {
+ let qr_image = QrImage::new(&em, data_slice);
+ qr_image.width
+ }
+ }
+ }
+}
+
+/// Returns the maximum data size that can fit in a QR code of this version.
+/// * `version`: QR code version, between 1-40.
+/// * `url_len`: Length of the URL.
+///
+/// * If `url_len` > 0, remove the 2 segments header/length and also count the
+/// conversion to numeric segments.
+/// * If `url_len` = 0, only removes 3 bytes for 1 binary segment.
+#[no_mangle]
+pub extern "C" fn drm_panic_qr_max_data_size(version: u8, url_len: usize) -> usize {
+ if version < 1 || version > 40 {
+ return 0;
+ }
+ let max_data = Version(version as usize).max_data();
+
+ if url_len > 0 {
+ // Binary segment (URL) 4 + 16 bits, numeric segment (kmsg) 4 + 12 bits => 5 bytes.
+ if url_len + 5 >= max_data {
+ 0
+ } else {
+ let max = max_data - url_len - 5;
+ (max * 39) / 40
+ }
+ } else {
+ // Remove 3 bytes for the binary segment (header 4 bits, length 16 bits, stop 4bits).
+ max_data - 3
+ }
+}
diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c
index cf24dfdeb6b2..0081190201a7 100644
--- a/drivers/gpu/drm/drm_print.c
+++ b/drivers/gpu/drm/drm_print.c
@@ -100,8 +100,9 @@ void __drm_puts_coredump(struct drm_printer *p, const char *str)
copy = iterator->remain;
/* Copy out the bit of the string that we need */
- memcpy(iterator->data,
- str + (iterator->start - iterator->offset), copy);
+ if (iterator->data)
+ memcpy(iterator->data,
+ str + (iterator->start - iterator->offset), copy);
iterator->offset = iterator->start + copy;
iterator->remain -= copy;
@@ -110,7 +111,8 @@ void __drm_puts_coredump(struct drm_printer *p, const char *str)
len = min_t(ssize_t, strlen(str), iterator->remain);
- memcpy(iterator->data + pos, str, len);
+ if (iterator->data)
+ memcpy(iterator->data + pos, str, len);
iterator->offset += len;
iterator->remain -= len;
@@ -140,8 +142,9 @@ void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
if ((iterator->offset >= iterator->start) && (len < iterator->remain)) {
ssize_t pos = iterator->offset - iterator->start;
- snprintf(((char *) iterator->data) + pos,
- iterator->remain, "%pV", vaf);
+ if (iterator->data)
+ snprintf(((char *) iterator->data) + pos,
+ iterator->remain, "%pV", vaf);
iterator->offset += len;
iterator->remain -= len;
diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c
index 85c79a38c13a..492acce0516f 100644
--- a/drivers/gpu/drm/drm_rect.c
+++ b/drivers/gpu/drm/drm_rect.c
@@ -85,7 +85,6 @@ static u32 clip_scaled(int src, int dst, int *clip)
* factors from @src to @dst.
*
* RETURNS:
- *
* %true if rectangle @dst is still visible after being clipped,
* %false otherwise.
*/
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index c6b4cd77df72..94e45ed6869d 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -686,7 +686,6 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* drm_atomic_helper_calc_timestamping_constants().
*
* Returns:
- *
* Returns true on success, and false on failure, i.e. when no accurate
* timestamp could be acquired.
*/
@@ -831,7 +830,6 @@ EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_internal);
* drm_atomic_helper_calc_timestamping_constants().
*
* Returns:
- *
* Returns true on success, and false on failure, i.e. when no accurate
* timestamp could be acquired.
*/
diff --git a/drivers/gpu/drm/i915/display/intel_dpt.c b/drivers/gpu/drm/i915/display/intel_dpt.c
index 73a1918e2537..3a6d99044828 100644
--- a/drivers/gpu/drm/i915/display/intel_dpt.c
+++ b/drivers/gpu/drm/i915/display/intel_dpt.c
@@ -317,3 +317,7 @@ void intel_dpt_destroy(struct i915_address_space *vm)
i915_vm_put(&dpt->vm);
}
+u64 intel_dpt_offset(struct i915_vma *dpt_vma)
+{
+ return dpt_vma->node.start;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dpt.h b/drivers/gpu/drm/i915/display/intel_dpt.h
index ff18a525bfbe..1f88b0ee17e7 100644
--- a/drivers/gpu/drm/i915/display/intel_dpt.h
+++ b/drivers/gpu/drm/i915/display/intel_dpt.h
@@ -6,6 +6,8 @@
#ifndef __INTEL_DPT_H__
#define __INTEL_DPT_H__
+#include <linux/types.h>
+
struct drm_i915_private;
struct i915_address_space;
@@ -20,5 +22,6 @@ void intel_dpt_suspend(struct drm_i915_private *i915);
void intel_dpt_resume(struct drm_i915_private *i915);
struct i915_address_space *
intel_dpt_create(struct intel_framebuffer *fb);
+u64 intel_dpt_offset(struct i915_vma *dpt_vma);
#endif /* __INTEL_DPT_H__ */
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index 0e81d540ecc9..9452cad41d07 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -14,6 +14,7 @@
#include "intel_de.h"
#include "intel_display_irq.h"
#include "intel_display_types.h"
+#include "intel_dpt.h"
#include "intel_fb.h"
#include "intel_fbc.h"
#include "intel_frontbuffer.h"
@@ -1162,7 +1163,7 @@ static u32 skl_surf_address(const struct intel_plane_state *plane_state,
* within the DPT is always 0.
*/
drm_WARN_ON(&i915->drm, plane_state->dpt_vma &&
- plane_state->dpt_vma->node.start);
+ intel_dpt_offset(plane_state->dpt_vma));
drm_WARN_ON(&i915->drm, offset & 0x1fffff);
return offset >> 9;
} else {
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 5d7446a48ae7..3dc61cbd2e11 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -89,7 +89,6 @@ __i915_gem_object_unset_pages(struct drm_i915_gem_object *obj);
* @handle: userspace handle
*
* Returns:
- *
* A pointer to the object named by the handle if such exists on @filp, NULL
* otherwise. This object is only valid whilst under the RCU read lock, and
* note carefully the object may be in the process of being destroyed.
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
index 93bc1cc1ee7e..0ffba50981e3 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
@@ -418,7 +418,6 @@ out_unpin:
* For an untiled surface, this removes any existing fence.
*
* Returns:
- *
* 0 on success, negative error code on failure.
*/
int i915_vma_pin_fence(struct i915_vma *vma)
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index e356dfb883d3..6a6be8048aa8 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -389,7 +389,6 @@ void i915_vma_unpin_iomap(struct i915_vma *vma);
* i915_vma_unpin_fence().
*
* Returns:
- *
* True if the vma has a fence, false otherwise.
*/
int __must_check i915_vma_pin_fence(struct i915_vma *vma);
diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
index 55dedd73f528..91d7808a2d8d 100644
--- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c
+++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
@@ -34,7 +34,7 @@ struct imx_parallel_display_encoder {
struct imx_parallel_display {
struct device *dev;
- void *edid;
+ const struct drm_edid *drm_edid;
u32 bus_format;
u32 bus_flags;
struct drm_display_mode mode;
@@ -62,9 +62,9 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
if (num_modes > 0)
return num_modes;
- if (imxpd->edid) {
- drm_connector_update_edid_property(connector, imxpd->edid);
- num_modes = drm_add_edid_modes(connector, imxpd->edid);
+ if (imxpd->drm_edid) {
+ drm_edid_connector_update(connector, imxpd->drm_edid);
+ num_modes = drm_edid_connector_add_modes(connector);
}
if (np) {
@@ -331,7 +331,7 @@ static int imx_pd_probe(struct platform_device *pdev)
edidp = of_get_property(np, "edid", &edid_len);
if (edidp)
- imxpd->edid = devm_kmemdup(dev, edidp, edid_len, GFP_KERNEL);
+ imxpd->drm_edid = drm_edid_alloc(edidp, edid_len);
ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
if (!ret) {
@@ -355,7 +355,11 @@ static int imx_pd_probe(struct platform_device *pdev)
static void imx_pd_remove(struct platform_device *pdev)
{
+ struct imx_parallel_display *imxpd = platform_get_drvdata(pdev);
+
component_del(&pdev->dev, &imx_pd_ops);
+
+ drm_edid_free(imxpd->drm_edid);
}
static const struct of_device_id imx_pd_dt_ids[] = {
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 6598c9c08ba1..d3eac4817d76 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -695,6 +695,10 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
soc = soc_device_match(omapdrm_soc_devices);
priv->omaprev = soc ? (uintptr_t)soc->data : 0;
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
+ if (!priv->wq) {
+ ret = -ENOMEM;
+ goto err_alloc_workqueue;
+ }
mutex_init(&priv->list_lock);
INIT_LIST_HEAD(&priv->obj_list);
@@ -753,6 +757,7 @@ err_gem_deinit:
drm_mode_config_cleanup(ddev);
omap_gem_deinit(ddev);
destroy_workqueue(priv->wq);
+err_alloc_workqueue:
omap_disconnect_pipelines(ddev);
drm_dev_put(ddev);
return ret;
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 5d83ddc06ece..d3a9a9fafe4e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -87,6 +87,15 @@ config DRM_PANEL_BOE_TV101WUM_NL6
Say Y here if you want to support for BOE TV101WUM and AUO KD101N80
45NA WUXGA PANEL DSI Video Mode panel
+config DRM_PANEL_BOE_TV101WUM_LL2
+ tristate "BOE TV101WUM LL2 1200x1920 panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to support for BOE TV101WUM-LL2
+ WUXGA PANEL DSI Video Mode panel
+
config DRM_PANEL_EBBG_FT8719
tristate "EBBG FT8719 panel driver"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 5581387707c6..987a08702410 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o
obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o
obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o
obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o
+obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o
obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c
new file mode 100644
index 000000000000..50e4a5341bc6
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
+// Copyright (c) 2013, The Linux Foundation. All rights reserved.
+// Copyright (c) 2024, Neil Armstrong <neil.armstrong@linaro.org>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct boe_tv101wum_ll2 {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *supplies;
+};
+
+static const struct regulator_bulk_data boe_tv101wum_ll2_supplies[] = {
+ { .supply = "vsp" },
+ { .supply = "vsn" },
+};
+
+static inline struct boe_tv101wum_ll2 *to_boe_tv101wum_ll2(struct drm_panel *panel)
+{
+ return container_of(panel, struct boe_tv101wum_ll2, panel);
+}
+
+static void boe_tv101wum_ll2_reset(struct boe_tv101wum_ll2 *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+
+ msleep(120);
+}
+
+static int boe_tv101wum_ll2_on(struct boe_tv101wum_ll2 *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x0e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0xff, 0x81, 0x68, 0x6c, 0x22,
+ 0x6d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x94, 0x2c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa2, 0x38);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x0c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x80, 0xfd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x50, 0x00);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ return dsi_ctx.accum_err;
+}
+
+static void boe_tv101wum_ll2_off(struct boe_tv101wum_ll2 *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+
+ mipi_dsi_msleep(&dsi_ctx, 70);
+
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5a);
+
+ mipi_dsi_msleep(&dsi_ctx, 150);
+}
+
+static int boe_tv101wum_ll2_prepare(struct drm_panel *panel)
+{
+ struct boe_tv101wum_ll2 *ctx = to_boe_tv101wum_ll2(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(boe_tv101wum_ll2_supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ boe_tv101wum_ll2_reset(ctx);
+
+ ret = boe_tv101wum_ll2_on(ctx);
+ if (ret < 0) {
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(boe_tv101wum_ll2_supplies),
+ ctx->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int boe_tv101wum_ll2_unprepare(struct drm_panel *panel)
+{
+ struct boe_tv101wum_ll2 *ctx = to_boe_tv101wum_ll2(panel);
+
+ /* Ignore errors on failure, in any case set gpio and disable regulators */
+ boe_tv101wum_ll2_off(ctx);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(boe_tv101wum_ll2_supplies),
+ ctx->supplies);
+
+ return 0;
+}
+
+static const struct drm_display_mode boe_tv101wum_ll2_mode = {
+ .clock = (1200 + 27 + 8 + 12) * (1920 + 155 + 8 + 32) * 60 / 1000,
+ .hdisplay = 1200,
+ .hsync_start = 1200 + 27,
+ .hsync_end = 1200 + 27 + 8,
+ .htotal = 1200 + 27 + 8 + 12,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 155,
+ .vsync_end = 1920 + 155 + 8,
+ .vtotal = 1920 + 155 + 8 + 32,
+ .width_mm = 136,
+ .height_mm = 217,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+static int boe_tv101wum_ll2_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ /* We do not set display_info.bpc since unset value is bpc=8 by default */
+ return drm_connector_helper_get_modes_fixed(connector, &boe_tv101wum_ll2_mode);
+}
+
+static const struct drm_panel_funcs boe_tv101wum_ll2_panel_funcs = {
+ .prepare = boe_tv101wum_ll2_prepare,
+ .unprepare = boe_tv101wum_ll2_unprepare,
+ .get_modes = boe_tv101wum_ll2_get_modes,
+};
+
+static int boe_tv101wum_ll2_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct boe_tv101wum_ll2 *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ret = devm_regulator_bulk_get_const(&dsi->dev,
+ ARRAY_SIZE(boe_tv101wum_ll2_supplies),
+ boe_tv101wum_ll2_supplies,
+ &ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_VIDEO_HSE;
+
+ drm_panel_init(&ctx->panel, dev, &boe_tv101wum_ll2_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ ctx->panel.prepare_prev_first = true;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ drm_panel_remove(&ctx->panel);
+ return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
+ }
+
+ return 0;
+}
+
+static void boe_tv101wum_ll2_remove(struct mipi_dsi_device *dsi)
+{
+ struct boe_tv101wum_ll2 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct of_device_id boe_tv101wum_ll2_of_match[] = {
+ { .compatible = "boe,tv101wum-ll2" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, boe_tv101wum_ll2_of_match);
+
+static struct mipi_dsi_driver boe_tv101wum_ll2_driver = {
+ .probe = boe_tv101wum_ll2_probe,
+ .remove = boe_tv101wum_ll2_remove,
+ .driver = {
+ .name = "panel-boe-tv101wum_ll2",
+ .of_match_table = boe_tv101wum_ll2_of_match,
+ },
+};
+module_mipi_dsi_driver(boe_tv101wum_ll2_driver);
+
+MODULE_DESCRIPTION("DRM driver for BOE TV101WUM-LL2 Panel");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 7183df267777..767e47a2b0c1 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -1911,6 +1911,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80, "NV122WUM-N41"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b43, &delay_200_500_e200, "NV140FHM-T09"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b66, &delay_200_500_e80, "NE140WUM-N6G"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cb6, &delay_200_500_e200, "NT116WHM-N44"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"),
@@ -1977,8 +1978,6 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05af, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05f1, &delay_200_500_e200_d200, "Unknown"),
- EDP_PANEL_ENTRY('S', 'D', 'C', 0x416d, &delay_100_500_e200, "ATNA45AF01"),
-
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1511, &delay_200_500_e50, "LQ140M1JW48"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &delay_80_500_e50, "LQ140M1JW46"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x153a, &delay_200_500_e50, "LQ140T1JH01"),
diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c
index 94d89ffd596b..5d115ecd5dd4 100644
--- a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c
+++ b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c
@@ -47,195 +47,196 @@ static inline struct panel_nv3051d *panel_to_panelnv3051d(struct drm_panel *pane
static int panel_nv3051d_init_sequence(struct panel_nv3051d *ctx)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
/*
* Init sequence was supplied by device vendor with no
* documentation.
*/
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xE3, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x03, 0x40);
- mipi_dsi_dcs_write_seq(dsi, 0x04, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x05, 0x03);
- mipi_dsi_dcs_write_seq(dsi, 0x24, 0x12);
- mipi_dsi_dcs_write_seq(dsi, 0x25, 0x1E);
- mipi_dsi_dcs_write_seq(dsi, 0x26, 0x28);
- mipi_dsi_dcs_write_seq(dsi, 0x27, 0x52);
- mipi_dsi_dcs_write_seq(dsi, 0x28, 0x57);
- mipi_dsi_dcs_write_seq(dsi, 0x29, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0x2A, 0xDF);
- mipi_dsi_dcs_write_seq(dsi, 0x38, 0x9C);
- mipi_dsi_dcs_write_seq(dsi, 0x39, 0xA7);
- mipi_dsi_dcs_write_seq(dsi, 0x3A, 0x53);
- mipi_dsi_dcs_write_seq(dsi, 0x44, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x49, 0x3C);
- mipi_dsi_dcs_write_seq(dsi, 0x59, 0xFE);
- mipi_dsi_dcs_write_seq(dsi, 0x5C, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x91, 0x77);
- mipi_dsi_dcs_write_seq(dsi, 0x92, 0x77);
- mipi_dsi_dcs_write_seq(dsi, 0xA0, 0x55);
- mipi_dsi_dcs_write_seq(dsi, 0xA1, 0x50);
- mipi_dsi_dcs_write_seq(dsi, 0xA4, 0x9C);
- mipi_dsi_dcs_write_seq(dsi, 0xA7, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0xA8, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xA9, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xAA, 0xFC);
- mipi_dsi_dcs_write_seq(dsi, 0xAB, 0x28);
- mipi_dsi_dcs_write_seq(dsi, 0xAC, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xAD, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xAE, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xAF, 0x03);
- mipi_dsi_dcs_write_seq(dsi, 0xB0, 0x08);
- mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x26);
- mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x28);
- mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x28);
- mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x33);
- mipi_dsi_dcs_write_seq(dsi, 0xB5, 0x08);
- mipi_dsi_dcs_write_seq(dsi, 0xB6, 0x26);
- mipi_dsi_dcs_write_seq(dsi, 0xB7, 0x08);
- mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x26);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x0E);
- mipi_dsi_dcs_write_seq(dsi, 0xD1, 0x0E);
- mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x29);
- mipi_dsi_dcs_write_seq(dsi, 0xD4, 0x2B);
- mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x0C);
- mipi_dsi_dcs_write_seq(dsi, 0xD2, 0x0A);
- mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x28);
- mipi_dsi_dcs_write_seq(dsi, 0xD3, 0x28);
- mipi_dsi_dcs_write_seq(dsi, 0xB6, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0xD6, 0x0D);
- mipi_dsi_dcs_write_seq(dsi, 0xB7, 0x32);
- mipi_dsi_dcs_write_seq(dsi, 0xD7, 0x30);
- mipi_dsi_dcs_write_seq(dsi, 0xC1, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0xE1, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x0A);
- mipi_dsi_dcs_write_seq(dsi, 0xD8, 0x0A);
- mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xD9, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xBD, 0x13);
- mipi_dsi_dcs_write_seq(dsi, 0xDD, 0x13);
- mipi_dsi_dcs_write_seq(dsi, 0xBC, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0xDC, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0xBB, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xDB, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xBA, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xDA, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xBE, 0x18);
- mipi_dsi_dcs_write_seq(dsi, 0xDE, 0x18);
- mipi_dsi_dcs_write_seq(dsi, 0xBF, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xDF, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xC0, 0x17);
- mipi_dsi_dcs_write_seq(dsi, 0xE0, 0x17);
- mipi_dsi_dcs_write_seq(dsi, 0xB5, 0x3B);
- mipi_dsi_dcs_write_seq(dsi, 0xD5, 0x3C);
- mipi_dsi_dcs_write_seq(dsi, 0xB0, 0x0B);
- mipi_dsi_dcs_write_seq(dsi, 0xD0, 0x0C);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x03);
- mipi_dsi_dcs_write_seq(dsi, 0x00, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x01, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x02, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x03, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x04, 0x61);
- mipi_dsi_dcs_write_seq(dsi, 0x05, 0x80);
- mipi_dsi_dcs_write_seq(dsi, 0x06, 0xC7);
- mipi_dsi_dcs_write_seq(dsi, 0x07, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0x08, 0x82);
- mipi_dsi_dcs_write_seq(dsi, 0x09, 0x83);
- mipi_dsi_dcs_write_seq(dsi, 0x30, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x31, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x32, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x33, 0x2A);
- mipi_dsi_dcs_write_seq(dsi, 0x34, 0x61);
- mipi_dsi_dcs_write_seq(dsi, 0x35, 0xC5);
- mipi_dsi_dcs_write_seq(dsi, 0x36, 0x80);
- mipi_dsi_dcs_write_seq(dsi, 0x37, 0x23);
- mipi_dsi_dcs_write_seq(dsi, 0x40, 0x82);
- mipi_dsi_dcs_write_seq(dsi, 0x41, 0x83);
- mipi_dsi_dcs_write_seq(dsi, 0x42, 0x80);
- mipi_dsi_dcs_write_seq(dsi, 0x43, 0x81);
- mipi_dsi_dcs_write_seq(dsi, 0x44, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0x45, 0xF2);
- mipi_dsi_dcs_write_seq(dsi, 0x46, 0xF1);
- mipi_dsi_dcs_write_seq(dsi, 0x47, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0x48, 0xF4);
- mipi_dsi_dcs_write_seq(dsi, 0x49, 0xF3);
- mipi_dsi_dcs_write_seq(dsi, 0x50, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0x51, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0x52, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0x53, 0x03);
- mipi_dsi_dcs_write_seq(dsi, 0x54, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0x55, 0xF6);
- mipi_dsi_dcs_write_seq(dsi, 0x56, 0xF5);
- mipi_dsi_dcs_write_seq(dsi, 0x57, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0x58, 0xF8);
- mipi_dsi_dcs_write_seq(dsi, 0x59, 0xF7);
- mipi_dsi_dcs_write_seq(dsi, 0x7E, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0x7F, 0x80);
- mipi_dsi_dcs_write_seq(dsi, 0xE0, 0x5A);
- mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x0E);
- mipi_dsi_dcs_write_seq(dsi, 0xB5, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xB6, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0xB7, 0x07);
- mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x05);
- mipi_dsi_dcs_write_seq(dsi, 0xBA, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xC7, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xCA, 0x0E);
- mipi_dsi_dcs_write_seq(dsi, 0xCB, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0xCC, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0xCD, 0x07);
- mipi_dsi_dcs_write_seq(dsi, 0xCE, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xCF, 0x05);
- mipi_dsi_dcs_write_seq(dsi, 0xD0, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0x81, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0x84, 0x0E);
- mipi_dsi_dcs_write_seq(dsi, 0x85, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0x86, 0x07);
- mipi_dsi_dcs_write_seq(dsi, 0x87, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0x88, 0x05);
- mipi_dsi_dcs_write_seq(dsi, 0x89, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0x8A, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x97, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0x9A, 0x0E);
- mipi_dsi_dcs_write_seq(dsi, 0x9B, 0x0F);
- mipi_dsi_dcs_write_seq(dsi, 0x9C, 0x07);
- mipi_dsi_dcs_write_seq(dsi, 0x9D, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0x9E, 0x05);
- mipi_dsi_dcs_write_seq(dsi, 0x9F, 0x06);
- mipi_dsi_dcs_write_seq(dsi, 0xA0, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0x01, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0x02, 0xDA);
- mipi_dsi_dcs_write_seq(dsi, 0x03, 0xBA);
- mipi_dsi_dcs_write_seq(dsi, 0x04, 0xA8);
- mipi_dsi_dcs_write_seq(dsi, 0x05, 0x9A);
- mipi_dsi_dcs_write_seq(dsi, 0x06, 0x70);
- mipi_dsi_dcs_write_seq(dsi, 0x07, 0xFF);
- mipi_dsi_dcs_write_seq(dsi, 0x08, 0x91);
- mipi_dsi_dcs_write_seq(dsi, 0x09, 0x90);
- mipi_dsi_dcs_write_seq(dsi, 0x0A, 0xFF);
- mipi_dsi_dcs_write_seq(dsi, 0x0B, 0x8F);
- mipi_dsi_dcs_write_seq(dsi, 0x0C, 0x60);
- mipi_dsi_dcs_write_seq(dsi, 0x0D, 0x58);
- mipi_dsi_dcs_write_seq(dsi, 0x0E, 0x48);
- mipi_dsi_dcs_write_seq(dsi, 0x0F, 0x38);
- mipi_dsi_dcs_write_seq(dsi, 0x10, 0x2B);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52);
- mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x36, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0x3A, 0x70);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE3, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x12);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x1E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0xDF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x9C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0xA7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x3C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xFE);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x91, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x92, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA0, 0x55);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA1, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA4, 0x9C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA7, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA8, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA9, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAA, 0xFC);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAB, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAC, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAD, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAE, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAF, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB0, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB1, 0x26);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB2, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB3, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB4, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB5, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB6, 0x26);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB7, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB8, 0x26);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB1, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD1, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB4, 0x29);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD4, 0x2B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB2, 0x0C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD2, 0x0A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB3, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD3, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB6, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD6, 0x0D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB7, 0x32);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD7, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xC1, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB8, 0x0A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD8, 0x0A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB9, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD9, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBD, 0x13);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDD, 0x13);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBC, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDC, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBB, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDB, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBA, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDA, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBE, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDE, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBF, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDF, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xC0, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB5, 0x3B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD5, 0x3C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB0, 0x0B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD0, 0x0C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x61);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0xC7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x82);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x83);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x2A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x61);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0xC5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x82);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x83);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x81);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0xF2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0xF1);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0xF4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0xF3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0xF6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0xF5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xF8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xF7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x5A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB1, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB4, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB5, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB6, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB7, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB8, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB9, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBA, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xC7, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCA, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCB, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCC, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCD, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCE, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCF, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD0, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x84, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x86, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x89, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8A, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x97, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9A, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9B, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9C, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9D, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9E, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9F, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA0, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xDA);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0xBA);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0xA8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x9A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x70);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0xFF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x91);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x90);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0xFF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x8F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x60);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x58);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x2B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x70);
dev_dbg(ctx->dev, "Panel init sequence done\n");
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c
index 028fdac293f7..b036208f9356 100644
--- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c
+++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c
@@ -100,106 +100,87 @@ static void nt35950_reset(struct nt35950 *nt)
/*
* nt35950_set_cmd2_page - Select manufacturer control (CMD2) page
+ * @dsi_ctx: context for mipi_dsi functions
* @nt: Main driver structure
* @page: Page number (0-7)
- *
- * Return: Number of transferred bytes or negative number on error
*/
-static int nt35950_set_cmd2_page(struct nt35950 *nt, u8 page)
+static void nt35950_set_cmd2_page(struct mipi_dsi_multi_context *dsi_ctx,
+ struct nt35950 *nt, u8 page)
{
const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52,
0x08, page };
- int ret;
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], mauc_cmd2_page,
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, mauc_cmd2_page,
ARRAY_SIZE(mauc_cmd2_page));
- if (ret < 0)
- return ret;
-
- nt->last_page = page;
- return 0;
+ if (!dsi_ctx->accum_err)
+ nt->last_page = page;
}
/*
* nt35950_set_data_compression - Set data compression mode
+ * @dsi_ctx: context for mipi_dsi functions
* @nt: Main driver structure
* @comp_mode: Compression mode
- *
- * Return: Number of transferred bytes or negative number on error
*/
-static int nt35950_set_data_compression(struct nt35950 *nt, u8 comp_mode)
+static void nt35950_set_data_compression(struct mipi_dsi_multi_context *dsi_ctx,
+ struct nt35950 *nt, u8 comp_mode)
{
u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode };
u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode };
u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 };
u8 last_page = nt->last_page;
- int ret;
/* Set CMD2 Page 0 if we're not there yet */
- if (last_page != 0) {
- ret = nt35950_set_cmd2_page(nt, 0);
- if (ret < 0)
- return ret;
- }
+ if (last_page != 0)
+ nt35950_set_cmd2_page(dsi_ctx, nt, 0);
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_data_compression,
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_data_compression,
ARRAY_SIZE(cmd_data_compression));
- if (ret < 0)
- return ret;
-
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_on,
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_on,
ARRAY_SIZE(cmd_vesa_dsc_on));
- if (ret < 0)
- return ret;
/* Set the vesa dsc setting on Page 4 */
- ret = nt35950_set_cmd2_page(nt, 4);
- if (ret < 0)
- return ret;
+ nt35950_set_cmd2_page(dsi_ctx, nt, 4);
/* Display Stream Compression setting, always 0x03 */
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_setting,
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_setting,
ARRAY_SIZE(cmd_vesa_dsc_setting));
- if (ret < 0)
- return ret;
/* Get back to the previously set page */
- return nt35950_set_cmd2_page(nt, last_page);
+ nt35950_set_cmd2_page(dsi_ctx, nt, last_page);
}
/*
* nt35950_set_scaler - Enable/disable resolution upscaling
- * @nt: Main driver structure
+ * @dsi_ctx: context for mipi_dsi functions
* @scale_up: Scale up function control
- *
- * Return: Number of transferred bytes or negative number on error
*/
-static int nt35950_set_scaler(struct nt35950 *nt, u8 scale_up)
+static void nt35950_set_scaler(struct mipi_dsi_multi_context *dsi_ctx,
+ u8 scale_up)
{
u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up };
- return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler,
- ARRAY_SIZE(cmd_scaler));
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler,
+ ARRAY_SIZE(cmd_scaler));
}
/*
* nt35950_set_scale_mode - Resolution upscaling mode
- * @nt: Main driver structure
+ * @dsi_ctx: context for mipi_dsi functions
* @mode: Scaler mode (MCS_DATA_COMPRESSION_*)
- *
- * Return: Number of transferred bytes or negative number on error
*/
-static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode)
+static void nt35950_set_scale_mode(struct mipi_dsi_multi_context *dsi_ctx,
+ u8 mode)
{
u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode };
- return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler,
- ARRAY_SIZE(cmd_scaler));
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler,
+ ARRAY_SIZE(cmd_scaler));
}
/*
* nt35950_inject_black_image - Display a completely black image
- * @nt: Main driver structure
+ * @dsi_ctx: context for mipi_dsi functions
*
* After IC setup, the attached panel may show random data
* due to driveric behavior changes (resolution, compression,
@@ -208,43 +189,34 @@ static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode)
* the display.
* It makes sense to push a black image before sending the sleep-out
* and display-on commands.
- *
- * Return: Number of transferred bytes or negative number on error
*/
-static int nt35950_inject_black_image(struct nt35950 *nt)
+static void nt35950_inject_black_image(struct mipi_dsi_multi_context *dsi_ctx)
{
const u8 cmd0_black_img[] = { 0x6f, 0x01 };
const u8 cmd1_black_img[] = { 0xf3, 0x10 };
u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 };
- int ret;
/* Enable test command */
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test));
- if (ret < 0)
- return ret;
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test));
/* Send a black image */
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd0_black_img,
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd0_black_img,
ARRAY_SIZE(cmd0_black_img));
- if (ret < 0)
- return ret;
- ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd1_black_img,
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd1_black_img,
ARRAY_SIZE(cmd1_black_img));
- if (ret < 0)
- return ret;
/* Disable test command */
cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00;
- return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test));
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test));
}
/*
* nt35950_set_dispout - Set Display Output register parameters
* @nt: Main driver structure
- *
- * Return: Number of transferred bytes or negative number on error
+ * @dsi_ctx: context for mipi_dsi functions
*/
-static int nt35950_set_dispout(struct nt35950 *nt)
+static void nt35950_set_dispout(struct mipi_dsi_multi_context *dsi_ctx,
+ struct nt35950 *nt)
{
u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 };
const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
@@ -254,8 +226,8 @@ static int nt35950_set_dispout(struct nt35950 *nt)
if (mode_data[nt->cur_mode].enable_sram)
cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN;
- return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_dispout,
- ARRAY_SIZE(cmd_dispout));
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_dispout,
+ ARRAY_SIZE(cmd_dispout));
}
static int nt35950_get_current_mode(struct nt35950 *nt)
@@ -284,78 +256,47 @@ static int nt35950_on(struct nt35950 *nt)
{
const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
struct mipi_dsi_device *dsi = nt->dsi[0];
- struct device *dev = &dsi->dev;
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
nt->cur_mode = nt35950_get_current_mode(nt);
nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
- ret = nt35950_set_cmd2_page(nt, 0);
- if (ret < 0)
- return ret;
+ nt35950_set_cmd2_page(&dsi_ctx, nt, 0);
+ nt35950_set_data_compression(&dsi_ctx, nt, mode_data[nt->cur_mode].compression);
+ nt35950_set_scale_mode(&dsi_ctx, mode_data[nt->cur_mode].scaler_mode);
+ nt35950_set_scaler(&dsi_ctx, mode_data[nt->cur_mode].scaler_on);
+ nt35950_set_dispout(&dsi_ctx, nt);
- ret = nt35950_set_data_compression(nt, mode_data[nt->cur_mode].compression);
- if (ret < 0)
- return ret;
-
- ret = nt35950_set_scale_mode(nt, mode_data[nt->cur_mode].scaler_mode);
- if (ret < 0)
- return ret;
-
- ret = nt35950_set_scaler(nt, mode_data[nt->cur_mode].scaler_on);
- if (ret < 0)
- return ret;
-
- ret = nt35950_set_dispout(nt);
- if (ret < 0)
- return ret;
-
- ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
- if (ret < 0) {
- dev_err(dev, "Failed to set tear on: %d\n", ret);
- return ret;
- }
-
- ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0);
- if (ret < 0) {
- dev_err(dev, "Failed to set tear scanline: %d\n", ret);
- return ret;
- }
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0);
/* CMD2 Page 1 */
- ret = nt35950_set_cmd2_page(nt, 1);
- if (ret < 0)
- return ret;
+ nt35950_set_cmd2_page(&dsi_ctx, nt, 1);
/* Unknown command */
- mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x88, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x88, 0x88);
/* CMD2 Page 7 */
- ret = nt35950_set_cmd2_page(nt, 7);
- if (ret < 0)
- return ret;
+ nt35950_set_cmd2_page(&dsi_ctx, nt, 7);
/* Enable SubPixel Rendering */
- mipi_dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_EN, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_EN, 0x01);
/* SPR Mode: YYG Rainbow-RGB */
- mipi_dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_MODE, MCS_SPR_MODE_YYG_RAINBOW_RGB);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_MODE,
+ MCS_SPR_MODE_YYG_RAINBOW_RGB);
/* CMD3 */
- ret = nt35950_inject_black_image(nt);
- if (ret < 0)
- return ret;
+ nt35950_inject_black_image(&dsi_ctx);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
- ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
- if (ret < 0)
- return ret;
- msleep(120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
- ret = mipi_dsi_dcs_set_display_on(dsi);
- if (ret < 0)
- return ret;
- msleep(120);
+ if (dsi_ctx.accum_err)
+ return dsi_ctx.accum_err;
nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM;
nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;
@@ -363,30 +304,19 @@ static int nt35950_on(struct nt35950 *nt)
return 0;
}
-static int nt35950_off(struct nt35950 *nt)
+static void nt35950_off(struct nt35950 *nt)
{
- struct device *dev = &nt->dsi[0]->dev;
- int ret;
+ struct mipi_dsi_device *dsi = nt->dsi[0];
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- ret = mipi_dsi_dcs_set_display_off(nt->dsi[0]);
- if (ret < 0) {
- dev_err(dev, "Failed to set display off: %d\n", ret);
- goto set_lpm;
- }
- usleep_range(10000, 11000);
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
- ret = mipi_dsi_dcs_enter_sleep_mode(nt->dsi[0]);
- if (ret < 0) {
- dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
- goto set_lpm;
- }
- msleep(150);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 150);
-set_lpm:
nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
-
- return 0;
}
static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)
@@ -427,7 +357,6 @@ static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)
static int nt35950_prepare(struct drm_panel *panel)
{
struct nt35950 *nt = to_nt35950(panel);
- struct device *dev = &nt->dsi[0]->dev;
int ret;
ret = regulator_enable(nt->vregs[0].consumer);
@@ -452,10 +381,6 @@ static int nt35950_prepare(struct drm_panel *panel)
nt35950_reset(nt);
ret = nt35950_on(nt);
- if (ret < 0) {
- dev_err(dev, "Failed to initialize panel: %d\n", ret);
- goto end;
- }
end:
if (ret < 0) {
@@ -469,12 +394,8 @@ end:
static int nt35950_unprepare(struct drm_panel *panel)
{
struct nt35950 *nt = to_nt35950(panel);
- struct device *dev = &nt->dsi[0]->dev;
- int ret;
- ret = nt35950_off(nt);
- if (ret < 0)
- dev_err(dev, "Failed to deinitialize panel: %d\n", ret);
+ nt35950_off(nt);
gpiod_set_value_cansleep(nt->reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);
diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
index 540099253e1b..17b8defe79c1 100644
--- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
+++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
@@ -19,7 +19,13 @@ struct visionox_vtdr6130 {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct gpio_desc *reset_gpio;
- struct regulator_bulk_data supplies[3];
+ struct regulator_bulk_data *supplies;
+};
+
+static const struct regulator_bulk_data visionox_vtdr6130_supplies[] = {
+ { .supply = "vddio" },
+ { .supply = "vci" },
+ { .supply = "vdd" },
};
static inline struct visionox_vtdr6130 *to_visionox_vtdr6130(struct drm_panel *panel)
@@ -40,123 +46,106 @@ static void visionox_vtdr6130_reset(struct visionox_vtdr6130 *ctx)
static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
- struct device *dev = &dsi->dev;
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
- ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
- if (ret)
- return ret;
-
- mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
- mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x00, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x59, 0x09);
- mipi_dsi_dcs_write_seq(dsi, 0x6c, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0x6d, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x6f, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0x70,
- 0x12, 0x00, 0x00, 0xab, 0x30, 0x80, 0x09, 0x60, 0x04,
- 0x38, 0x00, 0x28, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00,
- 0x02, 0x0e, 0x00, 0x20, 0x03, 0xdd, 0x00, 0x07, 0x00,
- 0x0c, 0x02, 0x77, 0x02, 0x8b, 0x18, 0x00, 0x10, 0xf0,
- 0x07, 0x10, 0x20, 0x00, 0x06, 0x0f, 0x0f, 0x33, 0x0e,
- 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70, 0x77,
- 0x79, 0x7b, 0x7d, 0x7e, 0x02, 0x02, 0x22, 0x00, 0x2a,
- 0x40, 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
- 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0xb6, 0x4b, 0xb6, 0x4b,
- 0xf4, 0x4b, 0xf4, 0x6c, 0x34, 0x84, 0x74, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x10);
- mipi_dsi_dcs_write_seq(dsi, 0xb1,
- 0x01, 0x38, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x01, 0x66,
- 0x00, 0x14, 0x00, 0x14, 0x00, 0x01, 0x66, 0x00, 0x14,
- 0x05, 0xcc, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x13);
- mipi_dsi_dcs_write_seq(dsi, 0xce,
- 0x09, 0x11, 0x09, 0x11, 0x08, 0xc1, 0x07, 0xfa, 0x05,
- 0xa4, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x24, 0x00, 0x0c,
- 0x00, 0x0c, 0x04, 0x00, 0x35);
- mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x14);
- mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x03, 0x33);
- mipi_dsi_dcs_write_seq(dsi, 0xb4,
- 0x00, 0x33, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
- 0x3e, 0x00, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xb5,
- 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0x00, 0x08, 0x09, 0x09, 0x09);
- mipi_dsi_dcs_write_seq(dsi, 0xbc,
- 0x10, 0x00, 0x00, 0x06, 0x11, 0x09, 0x3b, 0x09, 0x47,
- 0x09, 0x47, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xbe,
- 0x10, 0x10, 0x00, 0x08, 0x22, 0x09, 0x19, 0x09, 0x25,
- 0x09, 0x25, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x80);
- mipi_dsi_dcs_write_seq(dsi, 0x65, 0x14);
- mipi_dsi_dcs_write_seq(dsi, 0xfa, 0x08, 0x08, 0x08);
- mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x81);
- mipi_dsi_dcs_write_seq(dsi, 0x65, 0x05);
- mipi_dsi_dcs_write_seq(dsi, 0xf3, 0x0f);
- mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x82);
- mipi_dsi_dcs_write_seq(dsi, 0xf9, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xff, 0x51, 0x83);
- mipi_dsi_dcs_write_seq(dsi, 0x65, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0xf8, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0x65, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xf4, 0x9a);
- mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x00);
-
- ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
- return ret;
- }
- msleep(120);
-
- ret = mipi_dsi_dcs_set_display_on(dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to set display on: %d\n", ret);
- return ret;
- }
- msleep(20);
-
- return 0;
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx,
+ MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx,
+ MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x00,
+ 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x12, 0x00, 0x00, 0xab,
+ 0x30, 0x80, 0x09, 0x60, 0x04, 0x38, 0x00,
+ 0x28, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00,
+ 0x02, 0x0e, 0x00, 0x20, 0x03, 0xdd, 0x00,
+ 0x07, 0x00, 0x0c, 0x02, 0x77, 0x02, 0x8b,
+ 0x18, 0x00, 0x10, 0xf0, 0x07, 0x10, 0x20,
+ 0x00, 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c,
+ 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70,
+ 0x77, 0x79, 0x7b, 0x7d, 0x7e, 0x02, 0x02,
+ 0x22, 0x00, 0x2a, 0x40, 0x2a, 0xbe, 0x3a,
+ 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, 0x3b, 0x38,
+ 0x3b, 0x78, 0x3b, 0xb6, 0x4b, 0xb6, 0x4b,
+ 0xf4, 0x4b, 0xf4, 0x6c, 0x34, 0x84, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x01, 0x38, 0x00, 0x14,
+ 0x00, 0x1c, 0x00, 0x01, 0x66, 0x00, 0x14,
+ 0x00, 0x14, 0x00, 0x01, 0x66, 0x00, 0x14,
+ 0x05, 0xcc, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x13);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xce, 0x09, 0x11, 0x09, 0x11,
+ 0x08, 0xc1, 0x07, 0xfa, 0x05, 0xa4, 0x00,
+ 0x3c, 0x00, 0x34, 0x00, 0x24, 0x00, 0x0c,
+ 0x00, 0x0c, 0x04, 0x00, 0x35);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x03, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x00, 0x33, 0x00, 0x00,
+ 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3e, 0x00,
+ 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x00, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x06, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00, 0x00, 0x08, 0x09,
+ 0x09, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc, 0x10, 0x00, 0x00, 0x06,
+ 0x11, 0x09, 0x3b, 0x09, 0x47, 0x09, 0x47,
+ 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x10, 0x10, 0x00, 0x08,
+ 0x22, 0x09, 0x19, 0x09, 0x25, 0x09, 0x25,
+ 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x08, 0x08, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x81);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x82);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x51, 0x83);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x9a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ return dsi_ctx.accum_err;
}
-static int visionox_vtdr6130_off(struct visionox_vtdr6130 *ctx)
+static void visionox_vtdr6130_off(struct visionox_vtdr6130 *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
- struct device *dev = &dsi->dev;
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
- ret = mipi_dsi_dcs_set_display_off(dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to set display off: %d\n", ret);
- return ret;
- }
- msleep(20);
-
- ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
- return ret;
- }
- msleep(120);
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
- return 0;
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
}
static int visionox_vtdr6130_prepare(struct drm_panel *panel)
{
struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel);
- struct device *dev = &ctx->dsi->dev;
int ret;
- ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies),
+ ret = regulator_bulk_enable(ARRAY_SIZE(visionox_vtdr6130_supplies),
ctx->supplies);
if (ret < 0)
return ret;
@@ -165,9 +154,9 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel)
ret = visionox_vtdr6130_on(ctx);
if (ret < 0) {
- dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
- regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies),
+ ctx->supplies);
return ret;
}
@@ -177,16 +166,13 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel)
static int visionox_vtdr6130_unprepare(struct drm_panel *panel)
{
struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel);
- struct device *dev = &ctx->dsi->dev;
- int ret;
- ret = visionox_vtdr6130_off(ctx);
- if (ret < 0)
- dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+ visionox_vtdr6130_off(ctx);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
- regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies),
+ ctx->supplies);
return 0;
}
@@ -266,12 +252,10 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
if (!ctx)
return -ENOMEM;
- ctx->supplies[0].supply = "vddio";
- ctx->supplies[1].supply = "vci";
- ctx->supplies[2].supply = "vdd";
-
- ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies),
- ctx->supplies);
+ ret = devm_regulator_bulk_get_const(&dsi->dev,
+ ARRAY_SIZE(visionox_vtdr6130_supplies),
+ visionox_vtdr6130_supplies,
+ &ctx->supplies);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/renesas/rcar-du/Kconfig b/drivers/gpu/drm/renesas/rcar-du/Kconfig
index c17e7c50492c..be86ecb9f559 100644
--- a/drivers/gpu/drm/renesas/rcar-du/Kconfig
+++ b/drivers/gpu/drm/renesas/rcar-du/Kconfig
@@ -60,14 +60,6 @@ config DRM_RCAR_MIPI_DSI
select DRM_MIPI_DSI
select RESET_CONTROLLER
-config DRM_RZG2L_MIPI_DSI
- tristate "RZ/G2L MIPI DSI Encoder Support"
- depends on DRM && DRM_BRIDGE && OF
- depends on ARCH_RENESAS || COMPILE_TEST
- select DRM_MIPI_DSI
- help
- Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders.
-
config DRM_RCAR_VSP
bool "R-Car DU VSP Compositor Support" if ARM
default y if ARM64
diff --git a/drivers/gpu/drm/renesas/rcar-du/Makefile b/drivers/gpu/drm/renesas/rcar-du/Makefile
index b8f2c82651d9..6f132325c8b7 100644
--- a/drivers/gpu/drm/renesas/rcar-du/Makefile
+++ b/drivers/gpu/drm/renesas/rcar-du/Makefile
@@ -14,5 +14,3 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
obj-$(CONFIG_DRM_RCAR_MIPI_DSI) += rcar_mipi_dsi.o
-
-obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o
diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig
index 5f0db2c5fee6..8ec14271ebba 100644
--- a/drivers/gpu/drm/renesas/rz-du/Kconfig
+++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
@@ -10,3 +10,11 @@ config DRM_RZG2L_DU
help
Choose this option if you have an RZ/G2L alike chipset.
If M is selected the module will be called rzg2l-du-drm.
+
+config DRM_RZG2L_MIPI_DSI
+ tristate "RZ/G2L MIPI DSI Encoder Support"
+ depends on DRM && DRM_BRIDGE && OF
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select DRM_MIPI_DSI
+ help
+ Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders.
diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile
index 663b82a2577f..2987900ea6b6 100644
--- a/drivers/gpu/drm/renesas/rz-du/Makefile
+++ b/drivers/gpu/drm/renesas/rz-du/Makefile
@@ -6,3 +6,5 @@ rzg2l-du-drm-y := rzg2l_du_crtc.o \
rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1) += rzg2l_du_vsp.o
obj-$(CONFIG_DRM_RZG2L_DU) += rzg2l-du-drm.o
+
+obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
index 6e7aac6219be..c4c1474d487e 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
@@ -28,6 +28,7 @@
#include "rzg2l_du_vsp.h"
#define DU_MCR0 0x00
+#define DU_MCR0_DPI_OE BIT(0)
#define DU_MCR0_DI_EN BIT(8)
#define DU_DITR0 0x10
@@ -216,9 +217,14 @@ static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc)
static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start)
{
+ struct rzg2l_du_crtc_state *rstate = to_rzg2l_crtc_state(rcrtc->crtc.state);
struct rzg2l_du_device *rcdu = rcrtc->dev;
+ u32 val = DU_MCR0_DI_EN;
- writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0);
+ if (rstate->outputs & BIT(RZG2L_DU_OUTPUT_DPAD0))
+ val |= DU_MCR0_DPI_OE;
+
+ writel(start ? val : 0, rcdu->mmio + DU_MCR0);
}
static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc)
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
index e5eca8691a33..bc7c381f92ac 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
@@ -25,6 +25,16 @@
* Device Information
*/
+static const struct rzg2l_du_device_info rzg2l_du_r9a07g043u_info = {
+ .channels_mask = BIT(0),
+ .routes = {
+ [RZG2L_DU_OUTPUT_DPAD0] = {
+ .possible_outputs = BIT(0),
+ .port = 0,
+ },
+ },
+};
+
static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
.channels_mask = BIT(0),
.routes = {
@@ -40,6 +50,7 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
};
static const struct of_device_id rzg2l_du_of_table[] = {
+ { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info },
{ .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
index 07b312b6f81e..b99217b4e05d 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
@@ -183,7 +183,8 @@ static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu)
/* Find the output route corresponding to the port number. */
for (i = 0; i < RZG2L_DU_OUTPUT_MAX; ++i) {
- if (rcdu->info->routes[i].port == ep.port) {
+ if (rcdu->info->routes[i].possible_outputs &&
+ rcdu->info->routes[i].port == ep.port) {
output = i;
break;
}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
index 10febea473cd..10febea473cd 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h
index 1dbc16ec64a4..1dbc16ec64a4 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index b0d84440a87b..847470f747c0 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -974,28 +974,32 @@ static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = {
static int sti_hdmi_connector_get_modes(struct drm_connector *connector)
{
+ const struct drm_display_info *info = &connector->display_info;
struct sti_hdmi_connector *hdmi_connector
= to_sti_hdmi_connector(connector);
struct sti_hdmi *hdmi = hdmi_connector->hdmi;
- struct edid *edid;
+ const struct drm_edid *drm_edid;
int count;
DRM_DEBUG_DRIVER("\n");
- edid = drm_get_edid(connector, hdmi->ddc_adapt);
- if (!edid)
- goto fail;
+ drm_edid = drm_edid_read(connector);
+
+ drm_edid_connector_update(connector, drm_edid);
- cec_notifier_set_phys_addr_from_edid(hdmi->notifier, edid);
+ cec_notifier_set_phys_addr(hdmi->notifier,
+ connector->display_info.source_physical_address);
+
+ if (!drm_edid)
+ goto fail;
- count = drm_add_edid_modes(connector, edid);
- drm_connector_update_edid_property(connector, edid);
+ count = drm_edid_connector_add_modes(connector);
DRM_DEBUG_KMS("%s : %dx%d cm\n",
- (connector->display_info.is_hdmi ? "hdmi monitor" : "dvi monitor"),
- edid->width_cm, edid->height_cm);
+ info->is_hdmi ? "hdmi monitor" : "dvi monitor",
+ info->width_mm / 10, info->height_mm / 10);
- kfree(edid);
+ drm_edid_free(drm_edid);
return count;
fail:
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 682011166a8f..2f3781e04b0a 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -133,7 +133,7 @@ struct tegra_output {
struct drm_bridge *bridge;
struct drm_panel *panel;
struct i2c_adapter *ddc;
- const struct edid *edid;
+ const struct drm_edid *drm_edid;
struct cec_notifier *cec;
unsigned int hpd_irq;
struct gpio_desc *hpd_gpio;
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 00c8564520e7..4de1ea0fc7c0 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -46,6 +46,7 @@ struct gr3d {
unsigned int nclocks;
struct reset_control_bulk_data resets[RST_GR3D_MAX];
unsigned int nresets;
+ struct dev_pm_domain_list *pd_list;
DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
};
@@ -369,18 +370,12 @@ static int gr3d_power_up_legacy_domain(struct device *dev, const char *name,
return 0;
}
-static void gr3d_del_link(void *link)
-{
- device_link_del(link);
-}
-
static int gr3d_init_power(struct device *dev, struct gr3d *gr3d)
{
- static const char * const opp_genpd_names[] = { "3d0", "3d1", NULL };
- const u32 link_flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME;
- struct device **opp_virt_devs, *pd_dev;
- struct device_link *link;
- unsigned int i;
+ struct dev_pm_domain_attach_data pd_data = {
+ .pd_names = (const char *[]) { "3d0", "3d1" },
+ .num_pd_names = 2,
+ };
int err;
err = of_count_phandle_with_args(dev->of_node, "power-domains",
@@ -414,29 +409,10 @@ static int gr3d_init_power(struct device *dev, struct gr3d *gr3d)
if (dev->pm_domain)
return 0;
- err = devm_pm_opp_attach_genpd(dev, opp_genpd_names, &opp_virt_devs);
- if (err)
+ err = dev_pm_domain_attach_list(dev, &pd_data, &gr3d->pd_list);
+ if (err < 0)
return err;
- for (i = 0; opp_genpd_names[i]; i++) {
- pd_dev = opp_virt_devs[i];
- if (!pd_dev) {
- dev_err(dev, "failed to get %s power domain\n",
- opp_genpd_names[i]);
- return -EINVAL;
- }
-
- link = device_link_add(dev, pd_dev, link_flags);
- if (!link) {
- dev_err(dev, "failed to link to %s\n", dev_name(pd_dev));
- return -EINVAL;
- }
-
- err = devm_add_action_or_reset(dev, gr3d_del_link, link);
- if (err)
- return err;
- }
-
return 0;
}
@@ -527,13 +503,13 @@ static int gr3d_probe(struct platform_device *pdev)
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
- return err;
+ goto err;
err = host1x_client_register(&gr3d->client.base);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
- return err;
+ goto err;
}
/* initialize address register map */
@@ -541,6 +517,9 @@ static int gr3d_probe(struct platform_device *pdev)
set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
return 0;
+err:
+ dev_pm_domain_detach_list(gr3d->pd_list);
+ return err;
}
static void gr3d_remove(struct platform_device *pdev)
@@ -549,6 +528,7 @@ static void gr3d_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
host1x_client_unregister(&gr3d->client.base);
+ dev_pm_domain_detach_list(gr3d->pd_list);
}
static int __maybe_unused gr3d_runtime_suspend(struct device *dev)
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
index f21e57e8599e..e0c2019a591b 100644
--- a/drivers/gpu/drm/tegra/hub.c
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -521,12 +521,11 @@ static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
static inline u32 compute_phase_incr(fixed20_12 in, unsigned int out)
{
- u64 tmp, tmp1, tmp2;
+ u64 tmp, tmp1;
tmp = (u64)dfixed_trunc(in);
- tmp2 = (u64)out;
- tmp1 = (tmp << NFB) + (tmp2 >> 1);
- do_div(tmp1, tmp2);
+ tmp1 = (tmp << NFB) + ((u64)out >> 1);
+ do_div(tmp1, out);
return lower_32_bits(tmp1);
}
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 4da3c3d1abbc..e6b5863fec71 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -21,7 +21,7 @@
int tegra_output_connector_get_modes(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
- struct edid *edid = NULL;
+ const struct drm_edid *drm_edid;
int err = 0;
/*
@@ -34,18 +34,17 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
return err;
}
- if (output->edid)
- edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
+ if (output->drm_edid)
+ drm_edid = drm_edid_dup(output->drm_edid);
else if (output->ddc)
- edid = drm_get_edid(connector, output->ddc);
+ drm_edid = drm_edid_read_ddc(connector, output->ddc);
- cec_notifier_set_phys_addr_from_edid(output->cec, edid);
- drm_connector_update_edid_property(connector, edid);
+ drm_edid_connector_update(connector, drm_edid);
+ cec_notifier_set_phys_addr(output->cec,
+ connector->display_info.source_physical_address);
- if (edid) {
- err = drm_add_edid_modes(connector, edid);
- kfree(edid);
- }
+ err = drm_edid_connector_add_modes(connector);
+ drm_edid_free(drm_edid);
return err;
}
@@ -98,6 +97,7 @@ static irqreturn_t hpd_irq(int irq, void *data)
int tegra_output_probe(struct tegra_output *output)
{
struct device_node *ddc, *panel;
+ const void *edid;
unsigned long flags;
int err, size;
@@ -124,8 +124,6 @@ int tegra_output_probe(struct tegra_output *output)
return PTR_ERR(output->panel);
}
- output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
-
ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
if (ddc) {
output->ddc = of_get_i2c_adapter_by_node(ddc);
@@ -137,6 +135,9 @@ int tegra_output_probe(struct tegra_output *output)
}
}
+ edid = of_get_property(output->of_node, "nvidia,edid", &size);
+ output->drm_edid = drm_edid_alloc(edid, size);
+
output->hpd_gpio = devm_fwnode_gpiod_get(output->dev,
of_fwnode_handle(output->of_node),
"nvidia,hpd",
@@ -187,6 +188,8 @@ put_i2c:
if (output->ddc)
i2c_put_adapter(output->ddc);
+ drm_edid_free(output->drm_edid);
+
return err;
}
@@ -197,6 +200,8 @@ void tegra_output_remove(struct tegra_output *output)
if (output->ddc)
i2c_put_adapter(output->ddc);
+
+ drm_edid_free(output->drm_edid);
}
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index e0defb1d134f..0bd7707c053e 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -464,7 +464,7 @@ static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
* Note this assumes this driver is only ever used with the Acer C120, if we
* add support for other devices the vendor and model should be parameterized.
*/
-static struct edid gm12u320_edid = {
+static const struct edid gm12u320_edid = {
.header = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
.mfg_id = { 0x04, 0x72 }, /* "ACR" */
.prod_code = { 0x20, 0xc1 }, /* C120h */
@@ -523,8 +523,15 @@ static struct edid gm12u320_edid = {
static int gm12u320_conn_get_modes(struct drm_connector *connector)
{
- drm_connector_update_edid_property(connector, &gm12u320_edid);
- return drm_add_edid_modes(connector, &gm12u320_edid);
+ const struct drm_edid *drm_edid;
+ int count;
+
+ drm_edid = drm_edid_alloc(&gm12u320_edid, sizeof(gm12u320_edid));
+ drm_edid_connector_update(connector, drm_edid);
+ count = drm_edid_connector_add_modes(connector);
+ drm_edid_free(drm_edid);
+
+ return count;
}
static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = {
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 1ff9602a52f6..b9670ae09a9e 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -28,7 +28,6 @@ $(obj)/generated/%_wa_oob.c $(obj)/generated/%_wa_oob.h: $(obj)/xe_gen_wa_oob \
xe-y += xe_bb.o \
xe_bo.o \
xe_bo_evict.o \
- xe_debugfs.o \
xe_devcoredump.o \
xe_device.o \
xe_device_sysfs.o \
@@ -46,7 +45,6 @@ xe-y += xe_bb.o \
xe_gt.o \
xe_gt_ccs_mode.o \
xe_gt_clock.o \
- xe_gt_debugfs.o \
xe_gt_freq.o \
xe_gt_idle.o \
xe_gt_mcr.o \
@@ -59,7 +57,6 @@ xe-y += xe_bb.o \
xe_guc_ads.o \
xe_guc_ct.o \
xe_guc_db_mgr.o \
- xe_guc_debugfs.o \
xe_guc_hwconfig.o \
xe_guc_id_mgr.o \
xe_guc_klv_helpers.o \
@@ -69,9 +66,9 @@ xe-y += xe_bb.o \
xe_heci_gsc.o \
xe_hw_engine.o \
xe_hw_engine_class_sysfs.o \
+ xe_hw_engine_group.o \
xe_hw_fence.o \
xe_huc.o \
- xe_huc_debugfs.o \
xe_irq.o \
xe_lrc.o \
xe_migrate.o \
@@ -107,7 +104,6 @@ xe-y += xe_bb.o \
xe_ttm_vram_mgr.o \
xe_tuning.o \
xe_uc.o \
- xe_uc_debugfs.o \
xe_uc_fw.o \
xe_vm.o \
xe_vram.o \
@@ -124,7 +120,6 @@ xe-$(CONFIG_HWMON) += xe_hwmon.o
# graphics virtualization (SR-IOV) support
xe-y += \
xe_gt_sriov_vf.o \
- xe_gt_sriov_vf_debugfs.o \
xe_guc_relay.o \
xe_memirq.o \
xe_sriov.o
@@ -133,7 +128,6 @@ xe-$(CONFIG_PCI_IOV) += \
xe_gt_sriov_pf.o \
xe_gt_sriov_pf_config.o \
xe_gt_sriov_pf_control.o \
- xe_gt_sriov_pf_debugfs.o \
xe_gt_sriov_pf_monitor.o \
xe_gt_sriov_pf_policy.o \
xe_gt_sriov_pf_service.o \
@@ -281,6 +275,16 @@ ifeq ($(CONFIG_DRM_FBDEV_EMULATION),y)
endif
ifeq ($(CONFIG_DEBUG_FS),y)
+ xe-y += xe_debugfs.o \
+ xe_gt_debugfs.o \
+ xe_gt_sriov_vf_debugfs.o \
+ xe_gt_stats.o \
+ xe_guc_debugfs.o \
+ xe_huc_debugfs.o \
+ xe_uc_debugfs.o
+
+ xe-$(CONFIG_PCI_IOV) += xe_gt_sriov_pf_debugfs.o
+
xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_display_debugfs.o \
i915-display/intel_display_debugfs_params.o \
diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
index 8f9f60b28306..6b30743a2f6c 100644
--- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
@@ -351,6 +351,7 @@ enum xe_guc_klv_ids {
GUC_WORKAROUND_KLV_ID_GAM_PFQ_SHADOW_TAIL_POLLING = 0x9005,
GUC_WORKAROUND_KLV_ID_DISABLE_MTP_DURING_ASYNC_COMPUTE = 0x9007,
GUC_WA_KLV_NP_RD_WRITE_TO_CLEAR_RCSM_AT_CGP_LATE_RESTORE = 0x9008,
+ GUC_WORKAROUND_KLV_ID_BACK_TO_BACK_RCS_ENGINE_RESET = 0x9009,
};
#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
index a20d2638ea7a..bdae8392e125 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
@@ -7,7 +7,8 @@
#define I915_VMA_H
#include <uapi/drm/i915_drm.h>
-#include <drm/drm_mm.h>
+
+#include "xe_ggtt_types.h"
/* We don't want these from i915_drm.h in case of Xe */
#undef I915_TILING_X
@@ -19,7 +20,7 @@ struct xe_bo;
struct i915_vma {
struct xe_bo *bo, *dpt;
- struct drm_mm_node node;
+ struct xe_ggtt_node *node;
};
#define i915_ggtt_clear_scanout(bo) do { } while (0)
@@ -28,7 +29,7 @@ struct i915_vma {
static inline u32 i915_ggtt_offset(const struct i915_vma *vma)
{
- return vma->node.start;
+ return vma->node->base.start;
}
#endif
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index 1188ab83cfae..78a884ddd499 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -46,7 +46,7 @@ static bool has_display(struct xe_device *xe)
*/
bool xe_display_driver_probe_defer(struct pci_dev *pdev)
{
- if (!xe_modparam.enable_display)
+ if (!xe_modparam.probe_display)
return 0;
return intel_display_driver_probe_defer(pdev);
@@ -62,7 +62,7 @@ bool xe_display_driver_probe_defer(struct pci_dev *pdev)
*/
void xe_display_driver_set_hooks(struct drm_driver *driver)
{
- if (!xe_modparam.enable_display)
+ if (!xe_modparam.probe_display)
return;
driver->driver_features |= DRIVER_MODESET | DRIVER_ATOMIC;
@@ -104,7 +104,7 @@ static void xe_display_fini_nommio(struct drm_device *dev, void *dummy)
{
struct xe_device *xe = to_xe_device(dev);
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_power_domains_cleanup(xe);
@@ -112,7 +112,7 @@ static void xe_display_fini_nommio(struct drm_device *dev, void *dummy)
int xe_display_init_nommio(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return 0;
/* Fake uncore lock */
@@ -129,7 +129,7 @@ static void xe_display_fini_noirq(void *arg)
struct xe_device *xe = arg;
struct intel_display *display = &xe->display;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_display_driver_remove_noirq(xe);
@@ -141,7 +141,7 @@ int xe_display_init_noirq(struct xe_device *xe)
struct intel_display *display = &xe->display;
int err;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return 0;
intel_display_driver_early_probe(xe);
@@ -172,7 +172,7 @@ static void xe_display_fini_noaccel(void *arg)
{
struct xe_device *xe = arg;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_display_driver_remove_nogem(xe);
@@ -182,7 +182,7 @@ int xe_display_init_noaccel(struct xe_device *xe)
{
int err;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return 0;
err = intel_display_driver_probe_nogem(xe);
@@ -194,7 +194,7 @@ int xe_display_init_noaccel(struct xe_device *xe)
int xe_display_init(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return 0;
return intel_display_driver_probe(xe);
@@ -202,7 +202,7 @@ int xe_display_init(struct xe_device *xe)
void xe_display_fini(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_hpd_poll_fini(xe);
@@ -213,7 +213,7 @@ void xe_display_fini(struct xe_device *xe)
void xe_display_register(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_display_driver_register(xe);
@@ -223,7 +223,7 @@ void xe_display_register(struct xe_device *xe)
void xe_display_unregister(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_unregister_dsm_handler();
@@ -233,7 +233,7 @@ void xe_display_unregister(struct xe_device *xe)
void xe_display_driver_remove(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_display_driver_remove(xe);
@@ -243,7 +243,7 @@ void xe_display_driver_remove(struct xe_device *xe)
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
if (master_ctl & DISPLAY_IRQ)
@@ -254,7 +254,7 @@ void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir)
{
struct intel_display *display = &xe->display;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
if (gu_misc_iir & GU_MISC_GSE)
@@ -263,7 +263,7 @@ void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir)
void xe_display_irq_reset(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
gen11_display_irq_reset(xe);
@@ -271,7 +271,7 @@ void xe_display_irq_reset(struct xe_device *xe)
void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
if (gt->info.id == XE_GT0)
@@ -308,11 +308,23 @@ static void xe_display_flush_cleanup_work(struct xe_device *xe)
}
}
+/* TODO: System and runtime suspend/resume sequences will be sanitized as a follow-up. */
+void xe_display_pm_runtime_suspend(struct xe_device *xe)
+{
+ if (!xe->info.probe_display)
+ return;
+
+ if (xe->d3cold.allowed)
+ xe_display_pm_suspend(xe, true);
+
+ intel_hpd_poll_enable(xe);
+}
+
void xe_display_pm_suspend(struct xe_device *xe, bool runtime)
{
struct intel_display *display = &xe->display;
bool s2idle = suspend_to_idle();
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
/*
@@ -320,11 +332,14 @@ void xe_display_pm_suspend(struct xe_device *xe, bool runtime)
* properly.
*/
intel_power_domains_disable(xe);
- if (has_display(xe))
+ intel_fbdev_set_suspend(&xe->drm, FBINFO_STATE_SUSPENDED, true);
+ if (!runtime && has_display(xe)) {
drm_kms_helper_poll_disable(&xe->drm);
-
- if (!runtime)
+ intel_display_driver_disable_user_access(xe);
intel_display_driver_suspend(xe);
+ }
+
+ xe_display_flush_cleanup_work(xe);
xe_display_flush_cleanup_work(xe);
@@ -332,19 +347,20 @@ void xe_display_pm_suspend(struct xe_device *xe, bool runtime)
intel_hpd_cancel_work(xe);
+ if (!runtime && has_display(xe))
+ intel_display_driver_suspend_access(xe);
+
intel_encoder_suspend_all(&xe->display);
intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold);
- intel_fbdev_set_suspend(&xe->drm, FBINFO_STATE_SUSPENDED, true);
-
intel_dmc_suspend(xe);
}
void xe_display_pm_suspend_late(struct xe_device *xe)
{
bool s2idle = suspend_to_idle();
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_power_domains_suspend(xe, s2idle);
@@ -352,9 +368,20 @@ void xe_display_pm_suspend_late(struct xe_device *xe)
intel_display_power_suspend_late(xe);
}
+void xe_display_pm_runtime_resume(struct xe_device *xe)
+{
+ if (!xe->info.probe_display)
+ return;
+
+ intel_hpd_poll_disable(xe);
+
+ if (xe->d3cold.allowed)
+ xe_display_pm_resume(xe, true);
+}
+
void xe_display_pm_resume_early(struct xe_device *xe)
{
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_display_power_resume_early(xe);
@@ -366,7 +393,7 @@ void xe_display_pm_resume(struct xe_device *xe, bool runtime)
{
struct intel_display *display = &xe->display;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
return;
intel_dmc_resume(xe);
@@ -377,14 +404,17 @@ void xe_display_pm_resume(struct xe_device *xe, bool runtime)
intel_display_driver_init_hw(xe);
intel_hpd_init(xe);
+ if (!runtime && has_display(xe))
+ intel_display_driver_resume_access(xe);
+
/* MST sideband requires HPD interrupts enabled */
intel_dp_mst_resume(xe);
- if (!runtime)
+ if (!runtime && has_display(xe)) {
intel_display_driver_resume(xe);
-
- intel_hpd_poll_disable(xe);
- if (has_display(xe))
drm_kms_helper_poll_enable(&xe->drm);
+ intel_display_driver_enable_user_access(xe);
+ intel_hpd_poll_disable(xe);
+ }
intel_opregion_resume(display);
@@ -404,7 +434,7 @@ int xe_display_probe(struct xe_device *xe)
{
int err;
- if (!xe->info.enable_display)
+ if (!xe->info.probe_display)
goto no_display;
intel_display_device_probe(xe);
@@ -417,7 +447,7 @@ int xe_display_probe(struct xe_device *xe)
return 0;
no_display:
- xe->info.enable_display = false;
+ xe->info.probe_display = false;
unset_display_features(xe);
return 0;
}
diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h
index 000fb5799df5..53d727fd792b 100644
--- a/drivers/gpu/drm/xe/display/xe_display.h
+++ b/drivers/gpu/drm/xe/display/xe_display.h
@@ -38,6 +38,8 @@ void xe_display_pm_suspend(struct xe_device *xe, bool runtime);
void xe_display_pm_suspend_late(struct xe_device *xe);
void xe_display_pm_resume_early(struct xe_device *xe);
void xe_display_pm_resume(struct xe_device *xe, bool runtime);
+void xe_display_pm_runtime_suspend(struct xe_device *xe);
+void xe_display_pm_runtime_resume(struct xe_device *xe);
#else
@@ -67,6 +69,8 @@ static inline void xe_display_pm_suspend(struct xe_device *xe, bool runtime) {}
static inline void xe_display_pm_suspend_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, bool runtime) {}
+static inline void xe_display_pm_runtime_suspend(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_ */
diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c
index d7db44e79eaf..d650c5ac41a4 100644
--- a/drivers/gpu/drm/xe/display/xe_fb_pin.c
+++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c
@@ -204,21 +204,28 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K)
align = max_t(u32, align, SZ_64K);
- if (bo->ggtt_node.size && view->type == I915_GTT_VIEW_NORMAL) {
+ if (bo->ggtt_node && view->type == I915_GTT_VIEW_NORMAL) {
vma->node = bo->ggtt_node;
} else if (view->type == I915_GTT_VIEW_NORMAL) {
u32 x, size = bo->ttm.base.size;
- ret = xe_ggtt_insert_special_node_locked(ggtt, &vma->node, size,
- align, 0);
- if (ret)
+ vma->node = xe_ggtt_node_init(ggtt);
+ if (IS_ERR(vma->node)) {
+ ret = PTR_ERR(vma->node);
goto out_unlock;
+ }
+
+ ret = xe_ggtt_node_insert_locked(vma->node, size, align, 0);
+ if (ret) {
+ xe_ggtt_node_fini(vma->node);
+ goto out_unlock;
+ }
for (x = 0; x < size; x += XE_PAGE_SIZE) {
u64 pte = ggtt->pt_ops->pte_encode_bo(bo, x,
xe->pat.idx[XE_CACHE_NONE]);
- ggtt->pt_ops->ggtt_set_pte(ggtt, vma->node.start + x, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, vma->node->base.start + x, pte);
}
} else {
u32 i, ggtt_ofs;
@@ -227,12 +234,19 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
/* display seems to use tiles instead of bytes here, so convert it back.. */
u32 size = intel_rotation_info_size(rot_info) * XE_PAGE_SIZE;
- ret = xe_ggtt_insert_special_node_locked(ggtt, &vma->node, size,
- align, 0);
- if (ret)
+ vma->node = xe_ggtt_node_init(ggtt);
+ if (IS_ERR(vma->node)) {
+ ret = PTR_ERR(vma->node);
+ goto out_unlock;
+ }
+
+ ret = xe_ggtt_node_insert_locked(vma->node, size, align, 0);
+ if (ret) {
+ xe_ggtt_node_fini(vma->node);
goto out_unlock;
+ }
- ggtt_ofs = vma->node.start;
+ ggtt_ofs = vma->node->base.start;
for (i = 0; i < ARRAY_SIZE(rot_info->plane); i++)
write_ggtt_rotated(bo, ggtt, &ggtt_ofs,
@@ -320,14 +334,11 @@ err:
static void __xe_unpin_fb_vma(struct i915_vma *vma)
{
- struct xe_device *xe = to_xe_device(vma->bo->ttm.base.dev);
- struct xe_ggtt *ggtt = xe_device_get_root_tile(xe)->mem.ggtt;
-
if (vma->dpt)
xe_bo_unpin_map_no_vm(vma->dpt);
- else if (!drm_mm_node_allocated(&vma->bo->ggtt_node) ||
- vma->bo->ggtt_node.start != vma->node.start)
- xe_ggtt_remove_node(ggtt, &vma->node, false);
+ else if (!xe_ggtt_node_allocated(vma->bo->ggtt_node) ||
+ vma->bo->ggtt_node->base.start != vma->node->base.start)
+ xe_ggtt_node_remove(vma->node, false);
ttm_bo_reserve(&vma->bo->ttm, false, false, NULL);
ttm_bo_unpin(&vma->bo->ttm);
@@ -377,8 +388,8 @@ void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state)
}
/*
- * For Xe introduce dummy intel_dpt_create which just return NULL and
- * intel_dpt_destroy which does nothing.
+ * For Xe introduce dummy intel_dpt_create which just return NULL,
+ * intel_dpt_destroy which does nothing, and fake intel_dpt_ofsset returning 0;
*/
struct i915_address_space *intel_dpt_create(struct intel_framebuffer *fb)
{
@@ -389,3 +400,8 @@ void intel_dpt_destroy(struct i915_address_space *vm)
{
return;
}
+
+u64 intel_dpt_offset(struct i915_vma *dpt_vma)
+{
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index c38db2a74614..81b71903675e 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -104,6 +104,7 @@
#define CSFE_CHICKEN1(base) XE_REG((base) + 0xd4, XE_REG_OPTION_MASKED)
#define GHWSP_CSB_REPORT_DIS REG_BIT(15)
#define PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS REG_BIT(14)
+#define CS_PRIORITY_MEM_READ REG_BIT(7)
#define FF_SLICE_CS_CHICKEN1(base) XE_REG((base) + 0xe0, XE_REG_OPTION_MASKED)
#define FFSC_PERCTX_PREEMPT_CTRL REG_BIT(14)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 8cd4a9589410..0d1a4a9f4e11 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -80,7 +80,10 @@
#define LE_CACHEABILITY_MASK REG_GENMASK(1, 0)
#define LE_CACHEABILITY(value) REG_FIELD_PREP(LE_CACHEABILITY_MASK, value)
-#define XE2_GAMREQSTRM_CTRL XE_REG(0x4194)
+#define STATELESS_COMPRESSION_CTRL XE_REG_MCR(0x4148)
+#define UNIFIED_COMPRESSION_FORMAT REG_GENMASK(3, 0)
+
+#define XE2_GAMREQSTRM_CTRL XE_REG_MCR(0x4194)
#define CG_DIS_CNTLBUS REG_BIT(6)
#define CCS_AUX_INV XE_REG(0x4208)
@@ -193,6 +196,7 @@
#define GSCPSMI_BASE XE_REG(0x880c)
#define CCCHKNREG1 XE_REG_MCR(0x8828)
+#define L3CMPCTRL REG_BIT(23)
#define ENCOMPPERFFIX REG_BIT(18)
/* Fuse readout registers for GT */
@@ -367,6 +371,9 @@
#define XEHP_L3NODEARBCFG XE_REG_MCR(0xb0b4)
#define XEHP_LNESPARE REG_BIT(19)
+#define L3SQCREG2 XE_REG_MCR(0xb104)
+#define COMPMEMRD256BOVRFETCHEN REG_BIT(20)
+
#define L3SQCREG3 XE_REG_MCR(0xb108)
#define COMPPWOVERFETCHEN REG_BIT(28)
diff --git a/drivers/gpu/drm/xe/tests/xe_bo.c b/drivers/gpu/drm/xe/tests/xe_bo.c
index 1768483da1b7..8dac069483e8 100644
--- a/drivers/gpu/drm/xe/tests/xe_bo.c
+++ b/drivers/gpu/drm/xe/tests/xe_bo.c
@@ -36,7 +36,8 @@ static int ccs_test_migrate(struct xe_tile *tile, struct xe_bo *bo,
/* Optionally clear bo *and* CCS data in VRAM. */
if (clear) {
- fence = xe_migrate_clear(tile->migrate, bo, bo->ttm.resource);
+ fence = xe_migrate_clear(tile->migrate, bo, bo->ttm.resource,
+ XE_MIGRATE_CLEAR_FLAG_FULL);
if (IS_ERR(fence)) {
KUNIT_FAIL(test, "Failed to submit bo clear.\n");
return PTR_ERR(fence);
@@ -124,7 +125,7 @@ static void ccs_test_run_tile(struct xe_device *xe, struct xe_tile *tile,
kunit_info(test, "Testing system memory\n");
bo = xe_bo_create_user(xe, NULL, NULL, SZ_1M, DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device, bo_flags);
+ bo_flags);
if (IS_ERR(bo)) {
KUNIT_FAIL(test, "Failed to create bo.\n");
return;
@@ -205,7 +206,6 @@ static int evict_test_run_tile(struct xe_device *xe, struct xe_tile *tile, struc
xe_vm_lock(vm, false);
bo = xe_bo_create_user(xe, NULL, vm, 0x10000,
DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device,
bo_flags);
xe_vm_unlock(vm);
if (IS_ERR(bo)) {
@@ -215,7 +215,7 @@ static int evict_test_run_tile(struct xe_device *xe, struct xe_tile *tile, struc
external = xe_bo_create_user(xe, NULL, NULL, 0x10000,
DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device, bo_flags);
+ bo_flags);
if (IS_ERR(external)) {
KUNIT_FAIL(test, "external bo create err=%pe\n", external);
goto cleanup_bo;
diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
index c24c8509227e..13db6c0530b3 100644
--- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
@@ -126,7 +126,7 @@ static void xe_test_dmabuf_import_same_driver(struct xe_device *xe)
kunit_info(test, "running %s\n", __func__);
bo = xe_bo_create_user(xe, NULL, NULL, size, DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device, params->mem_mask);
+ params->mem_mask);
if (IS_ERR(bo)) {
KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
PTR_ERR(bo));
diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c
index 4344a1724029..1a192a2a941b 100644
--- a/drivers/gpu/drm/xe/tests/xe_migrate.c
+++ b/drivers/gpu/drm/xe/tests/xe_migrate.c
@@ -105,7 +105,8 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
}
xe_map_memset(xe, &remote->vmap, 0, 0xd0, remote->size);
- fence = xe_migrate_clear(m, remote, remote->ttm.resource);
+ fence = xe_migrate_clear(m, remote, remote->ttm.resource,
+ XE_MIGRATE_CLEAR_FLAG_FULL);
if (!sanity_fence_failed(xe, fence, big ? "Clearing remote big bo" :
"Clearing remote small bo", test)) {
retval = xe_map_rd(xe, &remote->vmap, 0, u64);
@@ -279,7 +280,8 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
kunit_info(test, "Clearing small buffer object\n");
xe_map_memset(xe, &tiny->vmap, 0, 0x22, tiny->size);
expected = 0;
- fence = xe_migrate_clear(m, tiny, tiny->ttm.resource);
+ fence = xe_migrate_clear(m, tiny, tiny->ttm.resource,
+ XE_MIGRATE_CLEAR_FLAG_FULL);
if (sanity_fence_failed(xe, fence, "Clearing small bo", test))
goto out;
@@ -300,7 +302,8 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
kunit_info(test, "Clearing big buffer object\n");
xe_map_memset(xe, &big->vmap, 0, 0x11, big->size);
expected = 0;
- fence = xe_migrate_clear(m, big, big->ttm.resource);
+ fence = xe_migrate_clear(m, big, big->ttm.resource,
+ XE_MIGRATE_CLEAR_FLAG_FULL);
if (sanity_fence_failed(xe, fence, "Clearing big bo", test))
goto out;
@@ -603,7 +606,8 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
kunit_info(test, "Clear vram buffer object\n");
expected = 0x0000000000000000;
- fence = xe_migrate_clear(tile->migrate, vram_bo, vram_bo->ttm.resource);
+ fence = xe_migrate_clear(tile->migrate, vram_bo, vram_bo->ttm.resource,
+ XE_MIGRATE_CLEAR_FLAG_FULL);
if (sanity_fence_failed(xe, fence, "Clear vram_bo", test))
return;
dma_fence_put(fence);
@@ -637,7 +641,7 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
long ret;
sys_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M,
- DRM_XE_GEM_CPU_CACHING_WC, ttm_bo_type_device,
+ DRM_XE_GEM_CPU_CACHING_WC,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS);
if (IS_ERR(sys_bo)) {
@@ -660,8 +664,9 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
}
xe_bo_unlock(sys_bo);
- ccs_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M, DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device, bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS);
+ ccs_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M,
+ DRM_XE_GEM_CPU_CACHING_WC,
+ bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS);
if (IS_ERR(ccs_bo)) {
KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
@@ -683,8 +688,9 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
}
xe_bo_unlock(ccs_bo);
- vram_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M, DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device, bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS);
+ vram_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M,
+ DRM_XE_GEM_CPU_CACHING_WC,
+ bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS);
if (IS_ERR(vram_bo)) {
KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
PTR_ERR(vram_bo));
diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c
index 577ee7d14381..67404863087e 100644
--- a/drivers/gpu/drm/xe/tests/xe_pci.c
+++ b/drivers/gpu/drm/xe/tests/xe_pci.c
@@ -12,58 +12,6 @@
#include <kunit/test-bug.h>
#include <kunit/visibility.h>
-struct kunit_test_data {
- int ndevs;
- xe_device_fn xe_fn;
-};
-
-static int dev_to_xe_device_fn(struct device *dev, void *__data)
-
-{
- struct drm_device *drm = dev_get_drvdata(dev);
- struct kunit_test_data *data = __data;
- int ret = 0;
- int idx;
-
- data->ndevs++;
-
- if (drm_dev_enter(drm, &idx))
- ret = data->xe_fn(to_xe_device(dev_get_drvdata(dev)));
- drm_dev_exit(idx);
-
- return ret;
-}
-
-/**
- * xe_call_for_each_device - Iterate over all devices this driver binds to
- * @xe_fn: Function to call for each device.
- *
- * This function iterated over all devices this driver binds to, and calls
- * @xe_fn: for each one of them. If the called function returns anything else
- * than 0, iteration is stopped and the return value is returned by this
- * function. Across each function call, drm_dev_enter() / drm_dev_exit() is
- * called for the corresponding drm device.
- *
- * Return: Number of devices iterated or
- * the error code of a call to @xe_fn returning an error code.
- */
-int xe_call_for_each_device(xe_device_fn xe_fn)
-{
- int ret;
- struct kunit_test_data data = {
- .xe_fn = xe_fn,
- .ndevs = 0,
- };
-
- ret = driver_for_each_device(&xe_pci_driver.driver, NULL,
- &data, dev_to_xe_device_fn);
-
- if (!data.ndevs)
- kunit_skip(current->kunit_test, "test runs only on hardware\n");
-
- return ret ?: data.ndevs;
-}
-
/**
* xe_call_for_each_graphics_ip - Iterate over all recognized graphics IPs
* @xe_fn: Function to call for each device.
diff --git a/drivers/gpu/drm/xe/tests/xe_pci_test.h b/drivers/gpu/drm/xe/tests/xe_pci_test.h
index 3e2558bc3c90..ede46800aff1 100644
--- a/drivers/gpu/drm/xe/tests/xe_pci_test.h
+++ b/drivers/gpu/drm/xe/tests/xe_pci_test.h
@@ -19,7 +19,6 @@ typedef int (*xe_device_fn)(struct xe_device *);
typedef void (*xe_graphics_fn)(const struct xe_graphics_desc *);
typedef void (*xe_media_fn)(const struct xe_media_desc *);
-int xe_call_for_each_device(xe_device_fn xe_fn);
void xe_call_for_each_graphics_ip(xe_graphics_fn xe_fn);
void xe_call_for_each_media_ip(xe_media_fn xe_fn);
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 4b6834653810..25d0c939ba31 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -793,8 +793,16 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
}
}
} else {
- if (move_lacks_source)
- fence = xe_migrate_clear(migrate, bo, new_mem);
+ if (move_lacks_source) {
+ u32 flags = 0;
+
+ if (mem_type_is_vram(new_mem->mem_type))
+ flags |= XE_MIGRATE_CLEAR_FLAG_FULL;
+ else if (handle_system_ccs)
+ flags |= XE_MIGRATE_CLEAR_FLAG_CCS_DATA;
+
+ fence = xe_migrate_clear(migrate, bo, new_mem, flags);
+ }
else
fence = xe_migrate_copy(migrate, bo, bo, old_mem,
new_mem, handle_system_ccs);
@@ -1090,7 +1098,7 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list));
- if (bo->ggtt_node.size)
+ if (bo->ggtt_node && bo->ggtt_node->base.size)
xe_ggtt_remove_bo(bo->tile->mem.ggtt, bo);
#ifdef CONFIG_PROC_FS
@@ -1491,11 +1499,10 @@ struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile,
struct xe_bo *xe_bo_create_user(struct xe_device *xe, struct xe_tile *tile,
struct xe_vm *vm, size_t size,
u16 cpu_caching,
- enum ttm_bo_type type,
u32 flags)
{
struct xe_bo *bo = __xe_bo_create_locked(xe, tile, vm, size, 0, ~0ULL,
- cpu_caching, type,
+ cpu_caching, ttm_bo_type_device,
flags | XE_BO_FLAG_USER);
if (!IS_ERR(bo))
xe_bo_unlock_vm_held(bo);
@@ -2026,7 +2033,7 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
}
bo = xe_bo_create_user(xe, NULL, vm, args->size, args->cpu_caching,
- ttm_bo_type_device, bo_flags);
+ bo_flags);
if (vm)
xe_vm_unlock(vm);
@@ -2332,7 +2339,6 @@ int xe_bo_dumb_create(struct drm_file *file_priv,
bo = xe_bo_create_user(xe, NULL, NULL, args->size,
DRM_XE_GEM_CPU_CACHING_WC,
- ttm_bo_type_device,
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_SCANOUT |
XE_BO_FLAG_NEEDS_CPU_ACCESS);
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 1c9dc8adaaa3..dbfb3209615d 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -87,7 +87,6 @@ struct xe_bo *xe_bo_create(struct xe_device *xe, struct xe_tile *tile,
struct xe_bo *xe_bo_create_user(struct xe_device *xe, struct xe_tile *tile,
struct xe_vm *vm, size_t size,
u16 cpu_caching,
- enum ttm_bo_type type,
u32 flags);
struct xe_bo *xe_bo_create_pin_map(struct xe_device *xe, struct xe_tile *tile,
struct xe_vm *vm, size_t size,
@@ -195,9 +194,12 @@ xe_bo_main_addr(struct xe_bo *bo, size_t page_size)
static inline u32
xe_bo_ggtt_addr(struct xe_bo *bo)
{
- XE_WARN_ON(bo->ggtt_node.size > bo->size);
- XE_WARN_ON(bo->ggtt_node.start + bo->ggtt_node.size > (1ull << 32));
- return bo->ggtt_node.start;
+ if (XE_WARN_ON(!bo->ggtt_node))
+ return 0;
+
+ XE_WARN_ON(bo->ggtt_node->base.size > bo->size);
+ XE_WARN_ON(bo->ggtt_node->base.start + bo->ggtt_node->base.size > (1ull << 32));
+ return bo->ggtt_node->base.start;
}
int xe_bo_vmap(struct xe_bo *bo);
diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h
index ebc8abf7930a..2ed558ac2264 100644
--- a/drivers/gpu/drm/xe/xe_bo_types.h
+++ b/drivers/gpu/drm/xe/xe_bo_types.h
@@ -8,12 +8,13 @@
#include <linux/iosys-map.h>
-#include <drm/drm_mm.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_device.h>
#include <drm/ttm/ttm_execbuf_util.h>
#include <drm/ttm/ttm_placement.h>
+#include "xe_ggtt_types.h"
+
struct xe_device;
struct xe_vm;
@@ -39,7 +40,7 @@ struct xe_bo {
/** @placement: current placement for this BO */
struct ttm_placement placement;
/** @ggtt_node: GGTT node if this BO is mapped in the GGTT */
- struct drm_mm_node ggtt_node;
+ struct xe_ggtt_node *ggtt_node;
/** @vmap: iosys map of this buffer */
struct iosys_map vmap;
/** @ttm_kmap: TTM bo kmap object for internal use only. Keep off. */
diff --git a/drivers/gpu/drm/xe/xe_debugfs.h b/drivers/gpu/drm/xe/xe_debugfs.h
index 715b8e2e0bd9..17f4c2f1b5e4 100644
--- a/drivers/gpu/drm/xe/xe_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_debugfs.h
@@ -8,6 +8,10 @@
struct xe_device;
+#ifdef CONFIG_DEBUG_FS
void xe_debugfs_register(struct xe_device *xe);
+#else
+static inline void xe_debugfs_register(struct xe_device *xe) { }
+#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index d8d8ca2c19d3..bdb76e834e4c 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -66,22 +66,9 @@ static struct xe_guc *exec_queue_to_guc(struct xe_exec_queue *q)
return &q->gt->uc.guc;
}
-static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
-{
- struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
-
- /* keep going if fw fails as we still want to save the memory and SW data */
- if (xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL))
- xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
- xe_vm_snapshot_capture_delayed(ss->vm);
- xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
- xe_force_wake_put(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
-}
-
-static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
- size_t count, void *data, size_t datalen)
+static ssize_t __xe_devcoredump_read(char *buffer, size_t count,
+ struct xe_devcoredump *coredump)
{
- struct xe_devcoredump *coredump = data;
struct xe_device *xe;
struct xe_devcoredump_snapshot *ss;
struct drm_printer p;
@@ -89,18 +76,11 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
struct timespec64 ts;
int i;
- if (!coredump)
- return -ENODEV;
-
xe = coredump_to_xe(coredump);
ss = &coredump->snapshot;
- /* Ensure delayed work is captured before continuing */
- flush_work(&ss->work);
-
iter.data = buffer;
- iter.offset = 0;
- iter.start = offset;
+ iter.start = 0;
iter.remain = count;
p = drm_coredump_printer(&iter);
@@ -134,10 +114,83 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
return count - iter.remain;
}
+static void xe_devcoredump_snapshot_free(struct xe_devcoredump_snapshot *ss)
+{
+ int i;
+
+ xe_guc_ct_snapshot_free(ss->ct);
+ ss->ct = NULL;
+
+ xe_guc_exec_queue_snapshot_free(ss->ge);
+ ss->ge = NULL;
+
+ xe_sched_job_snapshot_free(ss->job);
+ ss->job = NULL;
+
+ for (i = 0; i < XE_NUM_HW_ENGINES; i++)
+ if (ss->hwe[i]) {
+ xe_hw_engine_snapshot_free(ss->hwe[i]);
+ ss->hwe[i] = NULL;
+ }
+
+ xe_vm_snapshot_free(ss->vm);
+ ss->vm = NULL;
+}
+
+static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
+{
+ struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
+ struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot);
+
+ /* keep going if fw fails as we still want to save the memory and SW data */
+ if (xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL))
+ xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
+ xe_vm_snapshot_capture_delayed(ss->vm);
+ xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
+ xe_force_wake_put(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
+
+ /* Calculate devcoredump size */
+ ss->read.size = __xe_devcoredump_read(NULL, INT_MAX, coredump);
+
+ ss->read.buffer = kvmalloc(ss->read.size, GFP_USER);
+ if (!ss->read.buffer)
+ return;
+
+ __xe_devcoredump_read(ss->read.buffer, ss->read.size, coredump);
+ xe_devcoredump_snapshot_free(ss);
+}
+
+static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
+ size_t count, void *data, size_t datalen)
+{
+ struct xe_devcoredump *coredump = data;
+ struct xe_devcoredump_snapshot *ss;
+ ssize_t byte_copied;
+
+ if (!coredump)
+ return -ENODEV;
+
+ ss = &coredump->snapshot;
+
+ /* Ensure delayed work is captured before continuing */
+ flush_work(&ss->work);
+
+ if (!ss->read.buffer)
+ return -ENODEV;
+
+ if (offset >= ss->read.size)
+ return 0;
+
+ byte_copied = count < ss->read.size - offset ? count :
+ ss->read.size - offset;
+ memcpy(buffer, ss->read.buffer + offset, byte_copied);
+
+ return byte_copied;
+}
+
static void xe_devcoredump_free(void *data)
{
struct xe_devcoredump *coredump = data;
- int i;
/* Our device is gone. Nothing to do... */
if (!data || !coredump_to_xe(coredump))
@@ -145,13 +198,8 @@ static void xe_devcoredump_free(void *data)
cancel_work_sync(&coredump->snapshot.work);
- xe_guc_ct_snapshot_free(coredump->snapshot.ct);
- xe_guc_exec_queue_snapshot_free(coredump->snapshot.ge);
- xe_sched_job_snapshot_free(coredump->snapshot.job);
- for (i = 0; i < XE_NUM_HW_ENGINES; i++)
- if (coredump->snapshot.hwe[i])
- xe_hw_engine_snapshot_free(coredump->snapshot.hwe[i]);
- xe_vm_snapshot_free(coredump->snapshot.vm);
+ xe_devcoredump_snapshot_free(&coredump->snapshot);
+ kvfree(coredump->snapshot.read.buffer);
/* To prevent stale data on next snapshot, clear everything */
memset(&coredump->snapshot, 0, sizeof(coredump->snapshot));
@@ -260,4 +308,5 @@ int xe_devcoredump_init(struct xe_device *xe)
{
return devm_add_action_or_reset(xe->drm.dev, xe_driver_devcoredump_fini, &xe->drm);
}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_devcoredump_types.h b/drivers/gpu/drm/xe/xe_devcoredump_types.h
index 923cdf72a816..440d05d77a5a 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump_types.h
+++ b/drivers/gpu/drm/xe/xe_devcoredump_types.h
@@ -46,6 +46,14 @@ struct xe_devcoredump_snapshot {
struct xe_sched_job_snapshot *job;
/** @vm: Snapshot of VM state */
struct xe_vm_snapshot *vm;
+
+ /** @read: devcoredump in human readable format */
+ struct {
+ /** @read.size: size of devcoredump in human readable format */
+ ssize_t size;
+ /** @read.buffer: buffer of devcoredump in human readable format */
+ char *buffer;
+ } read;
};
/**
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 1aba6f9eaa19..b6db7e082d88 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -37,6 +37,7 @@
#include "xe_gt_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_guc.h"
+#include "xe_hw_engine_group.h"
#include "xe_hwmon.h"
#include "xe_irq.h"
#include "xe_memirq.h"
@@ -165,6 +166,8 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
* vm->lock taken during xe_exec_queue_kill().
*/
xa_for_each(&xef->exec_queue.xa, idx, q) {
+ if (q->vm && q->hwe->hw_engine_group)
+ xe_hw_engine_group_del_exec_queue(q->hwe->hw_engine_group, q);
xe_exec_queue_kill(q);
xe_exec_queue_put(q);
}
@@ -543,7 +546,7 @@ static void update_device_info(struct xe_device *xe)
{
/* disable features that are not available/applicable to VFs */
if (IS_SRIOV_VF(xe)) {
- xe->info.enable_display = 0;
+ xe->info.probe_display = 0;
xe->info.has_heci_gscfi = 0;
xe->info.skip_guc_pc = 1;
xe->info.skip_pcode = 1;
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index db6cc8d0d6b8..f052c06a2d2f 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -15,6 +15,11 @@ static inline struct xe_device *to_xe_device(const struct drm_device *dev)
return container_of(dev, struct xe_device, drm);
}
+static inline struct xe_device *kdev_to_xe_device(struct device *kdev)
+{
+ return dev_get_drvdata(kdev);
+}
+
static inline struct xe_device *pdev_to_xe_device(struct pci_dev *pdev)
{
return pci_get_drvdata(pdev);
@@ -134,16 +139,6 @@ static inline struct xe_force_wake *gt_to_fw(struct xe_gt *gt)
void xe_device_assert_mem_access(struct xe_device *xe);
-static inline bool xe_device_in_fault_mode(struct xe_device *xe)
-{
- return xe->usm.num_vm_in_fault_mode != 0;
-}
-
-static inline bool xe_device_in_non_fault_mode(struct xe_device *xe)
-{
- return xe->usm.num_vm_in_non_fault_mode != 0;
-}
-
static inline bool xe_device_has_flat_ccs(struct xe_device *xe)
{
return xe->info.has_flat_ccs;
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 6e0f53ab2eb4..e73fb0c23932 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -204,7 +204,7 @@ struct xe_tile {
struct xe_memirq memirq;
/** @sriov.vf.ggtt_balloon: GGTT regions excluded from use. */
- struct drm_mm_node ggtt_balloon[2];
+ struct xe_ggtt_node *ggtt_balloon[2];
} vf;
} sriov;
@@ -282,8 +282,15 @@ struct xe_device {
u8 has_sriov:1;
/** @info.has_usm: Device has unified shared memory support */
u8 has_usm:1;
- /** @info.enable_display: display enabled */
- u8 enable_display:1;
+ /**
+ * @info.probe_display: Probe display hardware. If set to
+ * false, the driver will behave as if there is no display
+ * hardware present and will not try to read/write to it in any
+ * way. The display hardware, if it exists, will not be
+ * exposed to userspace and will be left untouched in whatever
+ * state the firmware or bootloader left it in.
+ */
+ u8 probe_display:1;
/** @info.skip_mtcfg: skip Multi-Tile configuration from MTCFG register */
u8 skip_mtcfg:1;
/** @info.skip_pcode: skip access to PCODE uC */
@@ -355,10 +362,6 @@ struct xe_device {
struct xarray asid_to_vm;
/** @usm.next_asid: next ASID, used to cyclical alloc asids */
u32 next_asid;
- /** @usm.num_vm_in_fault_mode: number of VM in fault mode */
- u32 num_vm_in_fault_mode;
- /** @usm.num_vm_in_non_fault_mode: number of VM in non-fault mode */
- u32 num_vm_in_non_fault_mode;
/** @usm.lock: protects UM state */
struct mutex lock;
} usm;
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index f36980aa26e6..484acfbe0e61 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -14,6 +14,7 @@
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
+#include "xe_hw_engine_group.h"
#include "xe_macros.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
@@ -124,6 +125,8 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
bool write_locked, skip_retry = false;
ktime_t end = 0;
int err = 0;
+ struct xe_hw_engine_group *group;
+ enum xe_hw_engine_group_execution_mode mode, previous_mode;
if (XE_IOCTL_DBG(xe, args->extensions) ||
XE_IOCTL_DBG(xe, args->pad[0] || args->pad[1] || args->pad[2]) ||
@@ -182,6 +185,15 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
}
+ group = q->hwe->hw_engine_group;
+ mode = xe_hw_engine_group_find_exec_mode(q);
+
+ if (mode == EXEC_MODE_DMA_FENCE) {
+ err = xe_hw_engine_group_get_mode(group, mode, &previous_mode);
+ if (err)
+ goto err_syncs;
+ }
+
retry:
if (!xe_vm_in_lr_mode(vm) && xe_vm_userptr_check_repin(vm)) {
err = down_write_killable(&vm->lock);
@@ -199,7 +211,7 @@ retry:
downgrade_write(&vm->lock);
write_locked = false;
if (err)
- goto err_unlock_list;
+ goto err_hw_exec_mode;
}
if (!args->num_batch_buffer) {
@@ -312,6 +324,9 @@ retry:
spin_unlock(&xe->ttm.lru_lock);
}
+ if (mode == EXEC_MODE_LR)
+ xe_hw_engine_group_resume_faulting_lr_jobs(group);
+
err_repin:
if (!xe_vm_in_lr_mode(vm))
up_read(&vm->userptr.notifier_lock);
@@ -324,6 +339,9 @@ err_unlock_list:
up_read(&vm->lock);
if (err == -EAGAIN && !skip_retry)
goto retry;
+err_hw_exec_mode:
+ if (mode == EXEC_MODE_DMA_FENCE)
+ xe_hw_engine_group_put(group);
err_syncs:
while (num_syncs--)
xe_sync_entry_cleanup(&syncs[num_syncs]);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 017d939659b5..e53937fafd14 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -14,6 +14,7 @@
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_hw_engine_class_sysfs.h"
+#include "xe_hw_engine_group.h"
#include "xe_hw_fence.h"
#include "xe_lrc.h"
#include "xe_macros.h"
@@ -73,6 +74,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
q->ops = gt->exec_queue_ops;
INIT_LIST_HEAD(&q->lr.link);
INIT_LIST_HEAD(&q->multi_gt_link);
+ INIT_LIST_HEAD(&q->hw_engine_group_link);
q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us;
q->sched_props.preempt_timeout_us =
@@ -166,7 +168,8 @@ err_post_alloc:
struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe_gt *gt,
struct xe_vm *vm,
- enum xe_engine_class class, u32 flags)
+ enum xe_engine_class class,
+ u32 flags, u64 extensions)
{
struct xe_hw_engine *hwe, *hwe0 = NULL;
enum xe_hw_engine_id id;
@@ -186,7 +189,54 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
if (!logical_mask)
return ERR_PTR(-ENODEV);
- return xe_exec_queue_create(xe, vm, logical_mask, 1, hwe0, flags, 0);
+ return xe_exec_queue_create(xe, vm, logical_mask, 1, hwe0, flags, extensions);
+}
+
+/**
+ * xe_exec_queue_create_bind() - Create bind exec queue.
+ * @xe: Xe device.
+ * @tile: tile which bind exec queue belongs to.
+ * @flags: exec queue creation flags
+ * @extensions: exec queue creation extensions
+ *
+ * Normalize bind exec queue creation. Bind exec queue is tied to migration VM
+ * for access to physical memory required for page table programming. On a
+ * faulting devices the reserved copy engine instance must be used to avoid
+ * deadlocking (user binds cannot get stuck behind faults as kernel binds which
+ * resolve faults depend on user binds). On non-faulting devices any copy engine
+ * can be used.
+ *
+ * Returns exec queue on success, ERR_PTR on failure
+ */
+struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
+ struct xe_tile *tile,
+ u32 flags, u64 extensions)
+{
+ struct xe_gt *gt = tile->primary_gt;
+ struct xe_exec_queue *q;
+ struct xe_vm *migrate_vm;
+
+ migrate_vm = xe_migrate_get_vm(tile->migrate);
+ if (xe->info.has_usm) {
+ struct xe_hw_engine *hwe = xe_gt_hw_engine(gt,
+ XE_ENGINE_CLASS_COPY,
+ gt->usm.reserved_bcs_instance,
+ false);
+
+ if (!hwe)
+ return ERR_PTR(-EINVAL);
+
+ q = xe_exec_queue_create(xe, migrate_vm,
+ BIT(hwe->logical_instance), 1, hwe,
+ flags, extensions);
+ } else {
+ q = xe_exec_queue_create_class(xe, gt, migrate_vm,
+ XE_ENGINE_CLASS_COPY, flags,
+ extensions);
+ }
+ xe_vm_put(migrate_vm);
+
+ return q;
}
void xe_exec_queue_destroy(struct kref *ref)
@@ -418,63 +468,6 @@ static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue
return 0;
}
-static const enum xe_engine_class user_to_xe_engine_class[] = {
- [DRM_XE_ENGINE_CLASS_RENDER] = XE_ENGINE_CLASS_RENDER,
- [DRM_XE_ENGINE_CLASS_COPY] = XE_ENGINE_CLASS_COPY,
- [DRM_XE_ENGINE_CLASS_VIDEO_DECODE] = XE_ENGINE_CLASS_VIDEO_DECODE,
- [DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE] = XE_ENGINE_CLASS_VIDEO_ENHANCE,
- [DRM_XE_ENGINE_CLASS_COMPUTE] = XE_ENGINE_CLASS_COMPUTE,
-};
-
-static struct xe_hw_engine *
-find_hw_engine(struct xe_device *xe,
- struct drm_xe_engine_class_instance eci)
-{
- u32 idx;
-
- if (eci.engine_class >= ARRAY_SIZE(user_to_xe_engine_class))
- return NULL;
-
- if (eci.gt_id >= xe->info.gt_count)
- return NULL;
-
- idx = array_index_nospec(eci.engine_class,
- ARRAY_SIZE(user_to_xe_engine_class));
-
- return xe_gt_hw_engine(xe_device_get_gt(xe, eci.gt_id),
- user_to_xe_engine_class[idx],
- eci.engine_instance, true);
-}
-
-static u32 bind_exec_queue_logical_mask(struct xe_device *xe, struct xe_gt *gt,
- struct drm_xe_engine_class_instance *eci,
- u16 width, u16 num_placements)
-{
- struct xe_hw_engine *hwe;
- enum xe_hw_engine_id id;
- u32 logical_mask = 0;
-
- if (XE_IOCTL_DBG(xe, width != 1))
- return 0;
- if (XE_IOCTL_DBG(xe, num_placements != 1))
- return 0;
- if (XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
- return 0;
-
- eci[0].engine_class = DRM_XE_ENGINE_CLASS_COPY;
-
- for_each_hw_engine(hwe, gt, id) {
- if (xe_hw_engine_is_reserved(hwe))
- continue;
-
- if (hwe->class ==
- user_to_xe_engine_class[DRM_XE_ENGINE_CLASS_COPY])
- logical_mask |= BIT(hwe->logical_instance);
- }
-
- return logical_mask;
-}
-
static u32 calc_validate_logical_mask(struct xe_device *xe, struct xe_gt *gt,
struct drm_xe_engine_class_instance *eci,
u16 width, u16 num_placements)
@@ -497,7 +490,7 @@ static u32 calc_validate_logical_mask(struct xe_device *xe, struct xe_gt *gt,
n = j * width + i;
- hwe = find_hw_engine(xe, eci[n]);
+ hwe = xe_hw_engine_lookup(xe, eci[n]);
if (XE_IOCTL_DBG(xe, !hwe))
return 0;
@@ -536,8 +529,9 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
struct drm_xe_engine_class_instance __user *user_eci =
u64_to_user_ptr(args->instances);
struct xe_hw_engine *hwe;
- struct xe_vm *vm, *migrate_vm;
+ struct xe_vm *vm;
struct xe_gt *gt;
+ struct xe_tile *tile;
struct xe_exec_queue *q = NULL;
u32 logical_mask;
u32 id;
@@ -562,37 +556,20 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
if (eci[0].engine_class == DRM_XE_ENGINE_CLASS_VM_BIND) {
- for_each_gt(gt, xe, id) {
- struct xe_exec_queue *new;
- u32 flags;
-
- if (xe_gt_is_media_type(gt))
- continue;
-
- eci[0].gt_id = gt->info.id;
- logical_mask = bind_exec_queue_logical_mask(xe, gt, eci,
- args->width,
- args->num_placements);
- if (XE_IOCTL_DBG(xe, !logical_mask))
- return -EINVAL;
-
- hwe = find_hw_engine(xe, eci[0]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return -EINVAL;
-
- /* The migration vm doesn't hold rpm ref */
- xe_pm_runtime_get_noresume(xe);
-
- flags = EXEC_QUEUE_FLAG_VM | (id ? EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD : 0);
+ if (XE_IOCTL_DBG(xe, args->width != 1) ||
+ XE_IOCTL_DBG(xe, args->num_placements != 1) ||
+ XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
+ return -EINVAL;
- migrate_vm = xe_migrate_get_vm(gt_to_tile(gt)->migrate);
- new = xe_exec_queue_create(xe, migrate_vm, logical_mask,
- args->width, hwe, flags,
- args->extensions);
+ for_each_tile(tile, xe, id) {
+ struct xe_exec_queue *new;
+ u32 flags = EXEC_QUEUE_FLAG_VM;
- xe_pm_runtime_put(xe); /* now held by engine */
+ if (id)
+ flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
- xe_vm_put(migrate_vm);
+ new = xe_exec_queue_create_bind(xe, tile, flags,
+ args->extensions);
if (IS_ERR(new)) {
err = PTR_ERR(new);
if (q)
@@ -613,7 +590,7 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, !logical_mask))
return -EINVAL;
- hwe = find_hw_engine(xe, eci[0]);
+ hwe = xe_hw_engine_lookup(xe, eci[0]);
if (XE_IOCTL_DBG(xe, !hwe))
return -EINVAL;
@@ -648,6 +625,12 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, err))
goto put_exec_queue;
}
+
+ if (q->vm && q->hwe->hw_engine_group) {
+ err = xe_hw_engine_group_add_exec_queue(q->hwe->hw_engine_group, q);
+ if (err)
+ goto put_exec_queue;
+ }
}
mutex_lock(&xef->exec_queue.lock);
@@ -798,6 +781,15 @@ void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q)
xef->run_ticks[q->class] += (new_ts - old_ts) * q->width;
}
+/**
+ * xe_exec_queue_kill - permanently stop all execution from an exec queue
+ * @q: The exec queue
+ *
+ * This function permanently stops all activity on an exec queue. If the queue
+ * is actively executing on the HW, it will be kicked off the engine; any
+ * pending jobs are discarded and all future submissions are rejected.
+ * This function is safe to call multiple times.
+ */
void xe_exec_queue_kill(struct xe_exec_queue *q)
{
struct xe_exec_queue *eq = q, *next;
@@ -830,6 +822,9 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, !q))
return -ENOENT;
+ if (q->vm && q->hwe->hw_engine_group)
+ xe_hw_engine_group_del_exec_queue(q->hwe->hw_engine_group, q);
+
xe_exec_queue_kill(q);
trace_xe_exec_queue_close(q);
@@ -841,10 +836,12 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
static void xe_exec_queue_last_fence_lockdep_assert(struct xe_exec_queue *q,
struct xe_vm *vm)
{
- if (q->flags & EXEC_QUEUE_FLAG_VM)
+ if (q->flags & EXEC_QUEUE_FLAG_VM) {
lockdep_assert_held(&vm->lock);
- else
+ } else {
xe_vm_assert_held(vm);
+ lockdep_assert_held(&q->hwe->hw_engine_group->mode_sem);
+ }
}
/**
@@ -856,10 +853,7 @@ void xe_exec_queue_last_fence_put(struct xe_exec_queue *q, struct xe_vm *vm)
{
xe_exec_queue_last_fence_lockdep_assert(q, vm);
- if (q->last_fence) {
- dma_fence_put(q->last_fence);
- q->last_fence = NULL;
- }
+ xe_exec_queue_last_fence_put_unlocked(q);
}
/**
@@ -902,6 +896,33 @@ struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *q,
}
/**
+ * xe_exec_queue_last_fence_get_for_resume() - Get last fence
+ * @q: The exec queue
+ * @vm: The VM the engine does a bind or exec for
+ *
+ * Get last fence, takes a ref. Only safe to be called in the context of
+ * resuming the hw engine group's long-running exec queue, when the group
+ * semaphore is held.
+ *
+ * Returns: last fence if not signaled, dma fence stub if signaled
+ */
+struct dma_fence *xe_exec_queue_last_fence_get_for_resume(struct xe_exec_queue *q,
+ struct xe_vm *vm)
+{
+ struct dma_fence *fence;
+
+ lockdep_assert_held_write(&q->hwe->hw_engine_group->mode_sem);
+
+ if (q->last_fence &&
+ test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &q->last_fence->flags))
+ xe_exec_queue_last_fence_put_unlocked(q);
+
+ fence = q->last_fence ? q->last_fence : dma_fence_get_stub();
+ dma_fence_get(fence);
+ return fence;
+}
+
+/**
* xe_exec_queue_last_fence_set() - Set last fence
* @q: The exec queue
* @vm: The VM the engine does a bind or exec for
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index ded77b0f3b90..90c7f73eab88 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -20,7 +20,11 @@ struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *v
u64 extensions);
struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe_gt *gt,
struct xe_vm *vm,
- enum xe_engine_class class, u32 flags);
+ enum xe_engine_class class,
+ u32 flags, u64 extensions);
+struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
+ struct xe_tile *tile,
+ u32 flags, u64 extensions);
void xe_exec_queue_fini(struct xe_exec_queue *q);
void xe_exec_queue_destroy(struct kref *ref);
@@ -73,6 +77,8 @@ void xe_exec_queue_last_fence_put(struct xe_exec_queue *e, struct xe_vm *vm);
void xe_exec_queue_last_fence_put_unlocked(struct xe_exec_queue *e);
struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *e,
struct xe_vm *vm);
+struct dma_fence *xe_exec_queue_last_fence_get_for_resume(struct xe_exec_queue *e,
+ struct xe_vm *vm);
void xe_exec_queue_last_fence_set(struct xe_exec_queue *e, struct xe_vm *vm,
struct dma_fence *fence);
int xe_exec_queue_last_fence_test_dep(struct xe_exec_queue *q,
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index fc2a1a20b7e4..7deb480e26af 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -140,6 +140,8 @@ struct xe_exec_queue {
* Protected by @vm's resv. Unused if @vm == NULL.
*/
u64 tlb_flush_seqno;
+ /** @hw_engine_group_link: link into exec queues in the same hw engine group */
+ struct list_head hw_engine_group_link;
/** @lrc: logical ring context for this exec queue */
struct xe_lrc *lrc[];
};
diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c
index 0cdbc1296e88..f3fca5565d32 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@ -30,6 +30,39 @@
#include "xe_wa.h"
#include "xe_wopcm.h"
+/**
+ * DOC: Global Graphics Translation Table (GGTT)
+ *
+ * Xe GGTT implements the support for a Global Virtual Address space that is used
+ * for resources that are accessible to privileged (i.e. kernel-mode) processes,
+ * and not tied to a specific user-level process. For example, the Graphics
+ * micro-Controller (GuC) and Display Engine (if present) utilize this Global
+ * address space.
+ *
+ * The Global GTT (GGTT) translates from the Global virtual address to a physical
+ * address that can be accessed by HW. The GGTT is a flat, single-level table.
+ *
+ * Xe implements a simplified version of the GGTT specifically managing only a
+ * certain range of it that goes from the Write Once Protected Content Memory (WOPCM)
+ * Layout to a predefined GUC_GGTT_TOP. This approach avoids complications related to
+ * the GuC (Graphics Microcontroller) hardware limitations. The GuC address space
+ * is limited on both ends of the GGTT, because the GuC shim HW redirects
+ * accesses to those addresses to other HW areas instead of going through the
+ * GGTT. On the bottom end, the GuC can't access offsets below the WOPCM size,
+ * while on the top side the limit is fixed at GUC_GGTT_TOP. To keep things
+ * simple, instead of checking each object to see if they are accessed by GuC or
+ * not, we just exclude those areas from the allocator. Additionally, to simplify
+ * the driver load, we use the maximum WOPCM size in this logic instead of the
+ * programmed one, so we don't need to wait until the actual size to be
+ * programmed is determined (which requires FW fetch) before initializing the
+ * GGTT. These simplifications might waste space in the GGTT (about 20-25 MBs
+ * depending on the platform) but we can live with this. Another benefit of this
+ * is the GuC bootrom can't access anything below the WOPCM max size so anything
+ * the bootrom needs to access (e.g. a RSA key) needs to be placed in the GGTT
+ * above the WOPCM max size. Starting the GGTT allocations above the WOPCM max
+ * give us the correct placement for free.
+ */
+
static u64 xelp_ggtt_pte_encode_bo(struct xe_bo *bo, u64 bo_offset,
u16 pat_index)
{
@@ -128,11 +161,12 @@ static void ggtt_fini_early(struct drm_device *drm, void *arg)
{
struct xe_ggtt *ggtt = arg;
+ destroy_workqueue(ggtt->wq);
mutex_destroy(&ggtt->lock);
drm_mm_takedown(&ggtt->mm);
}
-static void ggtt_fini(struct drm_device *drm, void *arg)
+static void ggtt_fini(void *arg)
{
struct xe_ggtt *ggtt = arg;
@@ -164,12 +198,16 @@ static const struct xe_ggtt_pt_ops xelpg_pt_wa_ops = {
.ggtt_set_pte = xe_ggtt_set_pte_and_flush,
};
-/*
- * Early GGTT initialization, which allows to create new mappings usable by the
- * GuC.
- * Mappings are not usable by the HW engines, as it doesn't have scratch /
+/**
+ * xe_ggtt_init_early - Early GGTT initialization
+ * @ggtt: the &xe_ggtt to be initialized
+ *
+ * It allows to create new mappings usable by the GuC.
+ * Mappings are not usable by the HW engines, as it doesn't have scratch nor
* initial clear done to it yet. That will happen in the regular, non-early
- * GGTT init.
+ * GGTT initialization.
+ *
+ * Return: 0 on success or a negative error code on failure.
*/
int xe_ggtt_init_early(struct xe_ggtt *ggtt)
{
@@ -194,29 +232,6 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
if (IS_DGFX(xe) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
ggtt->flags |= XE_GGTT_FLAGS_64K;
- /*
- * 8B per entry, each points to a 4KB page.
- *
- * The GuC address space is limited on both ends of the GGTT, because
- * the GuC shim HW redirects accesses to those addresses to other HW
- * areas instead of going through the GGTT. On the bottom end, the GuC
- * can't access offsets below the WOPCM size, while on the top side the
- * limit is fixed at GUC_GGTT_TOP. To keep things simple, instead of
- * checking each object to see if they are accessed by GuC or not, we
- * just exclude those areas from the allocator. Additionally, to
- * simplify the driver load, we use the maximum WOPCM size in this logic
- * instead of the programmed one, so we don't need to wait until the
- * actual size to be programmed is determined (which requires FW fetch)
- * before initializing the GGTT. These simplifications might waste space
- * in the GGTT (about 20-25 MBs depending on the platform) but we can
- * live with this.
- *
- * Another benifit of this is the GuC bootrom can't access anything
- * below the WOPCM max size so anything the bootom needs to access (e.g.
- * a RSA key) needs to be placed in the GGTT above the WOPCM max size.
- * Starting the GGTT allocations above the WOPCM max give us the correct
- * placement for free.
- */
if (ggtt->size > GUC_GGTT_TOP)
ggtt->size = GUC_GGTT_TOP;
@@ -228,6 +243,8 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
else
ggtt->pt_ops = &xelp_pt_ops;
+ ggtt->wq = alloc_workqueue("xe-ggtt-wq", 0, 0);
+
drm_mm_init(&ggtt->mm, xe_wopcm_size(xe),
ggtt->size - xe_wopcm_size(xe));
mutex_init(&ggtt->lock);
@@ -262,6 +279,77 @@ static void xe_ggtt_initial_clear(struct xe_ggtt *ggtt)
mutex_unlock(&ggtt->lock);
}
+static void ggtt_node_remove(struct xe_ggtt_node *node)
+{
+ struct xe_ggtt *ggtt = node->ggtt;
+ struct xe_device *xe = tile_to_xe(ggtt->tile);
+ bool bound;
+ int idx;
+
+ bound = drm_dev_enter(&xe->drm, &idx);
+
+ mutex_lock(&ggtt->lock);
+ if (bound)
+ xe_ggtt_clear(ggtt, node->base.start, node->base.size);
+ drm_mm_remove_node(&node->base);
+ node->base.size = 0;
+ mutex_unlock(&ggtt->lock);
+
+ if (!bound)
+ goto free_node;
+
+ if (node->invalidate_on_remove)
+ xe_ggtt_invalidate(ggtt);
+
+ drm_dev_exit(idx);
+
+free_node:
+ xe_ggtt_node_fini(node);
+}
+
+static void ggtt_node_remove_work_func(struct work_struct *work)
+{
+ struct xe_ggtt_node *node = container_of(work, typeof(*node),
+ delayed_removal_work);
+ struct xe_device *xe = tile_to_xe(node->ggtt->tile);
+
+ xe_pm_runtime_get(xe);
+ ggtt_node_remove(node);
+ xe_pm_runtime_put(xe);
+}
+
+/**
+ * xe_ggtt_node_remove - Remove a &xe_ggtt_node from the GGTT
+ * @node: the &xe_ggtt_node to be removed
+ * @invalidate: if node needs invalidation upon removal
+ */
+void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate)
+{
+ struct xe_ggtt *ggtt;
+ struct xe_device *xe;
+
+ if (!node || !node->ggtt)
+ return;
+
+ ggtt = node->ggtt;
+ xe = tile_to_xe(ggtt->tile);
+
+ node->invalidate_on_remove = invalidate;
+
+ if (xe_pm_runtime_get_if_active(xe)) {
+ ggtt_node_remove(node);
+ xe_pm_runtime_put(xe);
+ } else {
+ queue_work(ggtt->wq, &node->delayed_removal_work);
+ }
+}
+
+/**
+ * xe_ggtt_init - Regular non-early GGTT initialization
+ * @ggtt: the &xe_ggtt to be initialized
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
int xe_ggtt_init(struct xe_ggtt *ggtt)
{
struct xe_device *xe = tile_to_xe(ggtt->tile);
@@ -289,7 +377,7 @@ int xe_ggtt_init(struct xe_ggtt *ggtt)
xe_ggtt_initial_clear(ggtt);
- return drmm_add_action_or_reset(&xe->drm, ggtt_fini, ggtt);
+ return devm_add_action_or_reset(xe->drm.dev, ggtt_fini, ggtt);
err:
ggtt->scratch = NULL;
return err;
@@ -314,26 +402,6 @@ static void xe_ggtt_invalidate(struct xe_ggtt *ggtt)
ggtt_invalidate_gt_tlb(ggtt->tile->media_gt);
}
-void xe_ggtt_printk(struct xe_ggtt *ggtt, const char *prefix)
-{
- u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[XE_CACHE_WB];
- u64 addr, scratch_pte;
-
- scratch_pte = ggtt->pt_ops->pte_encode_bo(ggtt->scratch, 0, pat_index);
-
- printk("%sGlobal GTT:", prefix);
- for (addr = 0; addr < ggtt->size; addr += XE_PAGE_SIZE) {
- unsigned int i = addr / XE_PAGE_SIZE;
-
- xe_tile_assert(ggtt->tile, addr <= U32_MAX);
- if (ggtt->gsm[i] == scratch_pte)
- continue;
-
- printk("%s ggtt[0x%08x] = 0x%016llx",
- prefix, (u32)addr, ggtt->gsm[i]);
- }
-}
-
static void xe_ggtt_dump_node(struct xe_ggtt *ggtt,
const struct drm_mm_node *node, const char *description)
{
@@ -347,88 +415,180 @@ static void xe_ggtt_dump_node(struct xe_ggtt *ggtt,
}
/**
- * xe_ggtt_balloon - prevent allocation of specified GGTT addresses
- * @ggtt: the &xe_ggtt where we want to make reservation
+ * xe_ggtt_node_insert_balloon - prevent allocation of specified GGTT addresses
+ * @node: the &xe_ggtt_node to hold reserved GGTT node
* @start: the starting GGTT address of the reserved region
* @end: then end GGTT address of the reserved region
- * @node: the &drm_mm_node to hold reserved GGTT node
*
- * Use xe_ggtt_deballoon() to release a reserved GGTT node.
+ * Use xe_ggtt_node_remove_balloon() to release a reserved GGTT node.
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_ggtt_balloon(struct xe_ggtt *ggtt, u64 start, u64 end, struct drm_mm_node *node)
+int xe_ggtt_node_insert_balloon(struct xe_ggtt_node *node, u64 start, u64 end)
{
+ struct xe_ggtt *ggtt = node->ggtt;
int err;
xe_tile_assert(ggtt->tile, start < end);
xe_tile_assert(ggtt->tile, IS_ALIGNED(start, XE_PAGE_SIZE));
xe_tile_assert(ggtt->tile, IS_ALIGNED(end, XE_PAGE_SIZE));
- xe_tile_assert(ggtt->tile, !drm_mm_node_allocated(node));
+ xe_tile_assert(ggtt->tile, !drm_mm_node_allocated(&node->base));
- node->color = 0;
- node->start = start;
- node->size = end - start;
+ node->base.color = 0;
+ node->base.start = start;
+ node->base.size = end - start;
mutex_lock(&ggtt->lock);
- err = drm_mm_reserve_node(&ggtt->mm, node);
+ err = drm_mm_reserve_node(&ggtt->mm, &node->base);
mutex_unlock(&ggtt->lock);
if (xe_gt_WARN(ggtt->tile->primary_gt, err,
"Failed to balloon GGTT %#llx-%#llx (%pe)\n",
- node->start, node->start + node->size, ERR_PTR(err)))
+ node->base.start, node->base.start + node->base.size, ERR_PTR(err)))
return err;
- xe_ggtt_dump_node(ggtt, node, "balloon");
+ xe_ggtt_dump_node(ggtt, &node->base, "balloon");
return 0;
}
/**
- * xe_ggtt_deballoon - release a reserved GGTT region
- * @ggtt: the &xe_ggtt where reserved node belongs
- * @node: the &drm_mm_node with reserved GGTT region
+ * xe_ggtt_node_remove_balloon - release a reserved GGTT region
+ * @node: the &xe_ggtt_node with reserved GGTT region
*
- * See xe_ggtt_balloon() for details.
+ * See xe_ggtt_node_insert_balloon() for details.
*/
-void xe_ggtt_deballoon(struct xe_ggtt *ggtt, struct drm_mm_node *node)
+void xe_ggtt_node_remove_balloon(struct xe_ggtt_node *node)
{
- if (!drm_mm_node_allocated(node))
+ if (!node || !node->ggtt)
return;
- xe_ggtt_dump_node(ggtt, node, "deballoon");
+ if (!drm_mm_node_allocated(&node->base))
+ goto free_node;
- mutex_lock(&ggtt->lock);
- drm_mm_remove_node(node);
- mutex_unlock(&ggtt->lock);
+ xe_ggtt_dump_node(node->ggtt, &node->base, "remove-balloon");
+
+ mutex_lock(&node->ggtt->lock);
+ drm_mm_remove_node(&node->base);
+ mutex_unlock(&node->ggtt->lock);
+
+free_node:
+ xe_ggtt_node_fini(node);
}
-int xe_ggtt_insert_special_node_locked(struct xe_ggtt *ggtt, struct drm_mm_node *node,
- u32 size, u32 align, u32 mm_flags)
+/**
+ * xe_ggtt_node_insert_locked - Locked version to insert a &xe_ggtt_node into the GGTT
+ * @node: the &xe_ggtt_node to be inserted
+ * @size: size of the node
+ * @align: alignment constrain of the node
+ * @mm_flags: flags to control the node behavior
+ *
+ * It cannot be called without first having called xe_ggtt_init() once.
+ * To be used in cases where ggtt->lock is already taken.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node,
+ u32 size, u32 align, u32 mm_flags)
{
- return drm_mm_insert_node_generic(&ggtt->mm, node, size, align, 0,
+ return drm_mm_insert_node_generic(&node->ggtt->mm, &node->base, size, align, 0,
mm_flags);
}
-int xe_ggtt_insert_special_node(struct xe_ggtt *ggtt, struct drm_mm_node *node,
- u32 size, u32 align)
+/**
+ * xe_ggtt_node_insert - Insert a &xe_ggtt_node into the GGTT
+ * @node: the &xe_ggtt_node to be inserted
+ * @size: size of the node
+ * @align: alignment constrain of the node
+ *
+ * It cannot be called without first having called xe_ggtt_init() once.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_ggtt_node_insert(struct xe_ggtt_node *node, u32 size, u32 align)
{
int ret;
- mutex_lock(&ggtt->lock);
- ret = xe_ggtt_insert_special_node_locked(ggtt, node, size,
- align, DRM_MM_INSERT_HIGH);
- mutex_unlock(&ggtt->lock);
+ if (!node || !node->ggtt)
+ return -ENOENT;
+
+ mutex_lock(&node->ggtt->lock);
+ ret = xe_ggtt_node_insert_locked(node, size, align,
+ DRM_MM_INSERT_HIGH);
+ mutex_unlock(&node->ggtt->lock);
return ret;
}
+/**
+ * xe_ggtt_node_init - Initialize %xe_ggtt_node struct
+ * @ggtt: the &xe_ggtt where the new node will later be inserted/reserved.
+ *
+ * This function will allocated the struct %xe_ggtt_node and return it's pointer.
+ * This struct will then be freed after the node removal upon xe_ggtt_node_remove()
+ * or xe_ggtt_node_remove_balloon().
+ * Having %xe_ggtt_node struct allocated doesn't mean that the node is already allocated
+ * in GGTT. Only the xe_ggtt_node_insert(), xe_ggtt_node_insert_locked(),
+ * xe_ggtt_node_insert_balloon() will ensure the node is inserted or reserved in GGTT.
+ *
+ * Return: A pointer to %xe_ggtt_node struct on success. An ERR_PTR otherwise.
+ **/
+struct xe_ggtt_node *xe_ggtt_node_init(struct xe_ggtt *ggtt)
+{
+ struct xe_ggtt_node *node = kzalloc(sizeof(*node), GFP_NOFS);
+
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_WORK(&node->delayed_removal_work, ggtt_node_remove_work_func);
+ node->ggtt = ggtt;
+
+ return node;
+}
+
+/**
+ * xe_ggtt_node_fini - Forcebly finalize %xe_ggtt_node struct
+ * @node: the &xe_ggtt_node to be freed
+ *
+ * If anything went wrong with either xe_ggtt_node_insert(), xe_ggtt_node_insert_locked(),
+ * or xe_ggtt_node_insert_balloon(); and this @node is not going to be reused, then,
+ * this function needs to be called to free the %xe_ggtt_node struct
+ **/
+void xe_ggtt_node_fini(struct xe_ggtt_node *node)
+{
+ kfree(node);
+}
+
+/**
+ * xe_ggtt_node_allocated - Check if node is allocated in GGTT
+ * @node: the &xe_ggtt_node to be inspected
+ *
+ * Return: True if allocated, False otherwise.
+ */
+bool xe_ggtt_node_allocated(const struct xe_ggtt_node *node)
+{
+ if (!node || !node->ggtt)
+ return false;
+
+ return drm_mm_node_allocated(&node->base);
+}
+
+/**
+ * xe_ggtt_map_bo - Map the BO into GGTT
+ * @ggtt: the &xe_ggtt where node will be mapped
+ * @bo: the &xe_bo to be mapped
+ */
void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
{
u16 cache_mode = bo->flags & XE_BO_FLAG_NEEDS_UC ? XE_CACHE_NONE : XE_CACHE_WB;
u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[cache_mode];
- u64 start = bo->ggtt_node.start;
+ u64 start;
u64 offset, pte;
+ if (XE_WARN_ON(!bo->ggtt_node))
+ return;
+
+ start = bo->ggtt_node->base.start;
+
for (offset = 0; offset < bo->size; offset += XE_PAGE_SIZE) {
pte = ggtt->pt_ops->pte_encode_bo(bo, offset, pat_index);
ggtt->pt_ops->ggtt_set_pte(ggtt, start + offset, pte);
@@ -444,9 +604,9 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K)
alignment = SZ_64K;
- if (XE_WARN_ON(bo->ggtt_node.size)) {
+ if (XE_WARN_ON(bo->ggtt_node)) {
/* Someone's already inserted this BO in the GGTT */
- xe_tile_assert(ggtt->tile, bo->ggtt_node.size == bo->size);
+ xe_tile_assert(ggtt->tile, bo->ggtt_node->base.size == bo->size);
return 0;
}
@@ -455,69 +615,108 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
return err;
xe_pm_runtime_get_noresume(tile_to_xe(ggtt->tile));
+
+ bo->ggtt_node = xe_ggtt_node_init(ggtt);
+ if (IS_ERR(bo->ggtt_node)) {
+ err = PTR_ERR(bo->ggtt_node);
+ goto out;
+ }
+
mutex_lock(&ggtt->lock);
- err = drm_mm_insert_node_in_range(&ggtt->mm, &bo->ggtt_node, bo->size,
+ err = drm_mm_insert_node_in_range(&ggtt->mm, &bo->ggtt_node->base, bo->size,
alignment, 0, start, end, 0);
- if (!err)
+ if (err)
+ xe_ggtt_node_fini(bo->ggtt_node);
+ else
xe_ggtt_map_bo(ggtt, bo);
mutex_unlock(&ggtt->lock);
if (!err && bo->flags & XE_BO_FLAG_GGTT_INVALIDATE)
xe_ggtt_invalidate(ggtt);
+
+out:
xe_pm_runtime_put(tile_to_xe(ggtt->tile));
return err;
}
+/**
+ * xe_ggtt_insert_bo_at - Insert BO at a specific GGTT space
+ * @ggtt: the &xe_ggtt where bo will be inserted
+ * @bo: the &xe_bo to be inserted
+ * @start: address where it will be inserted
+ * @end: end of the range where it will be inserted
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
int xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
u64 start, u64 end)
{
return __xe_ggtt_insert_bo_at(ggtt, bo, start, end);
}
+/**
+ * xe_ggtt_insert_bo - Insert BO into GGTT
+ * @ggtt: the &xe_ggtt where bo will be inserted
+ * @bo: the &xe_bo to be inserted
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
int xe_ggtt_insert_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
{
return __xe_ggtt_insert_bo_at(ggtt, bo, 0, U64_MAX);
}
-void xe_ggtt_remove_node(struct xe_ggtt *ggtt, struct drm_mm_node *node,
- bool invalidate)
+/**
+ * xe_ggtt_remove_bo - Remove a BO from the GGTT
+ * @ggtt: the &xe_ggtt where node will be removed
+ * @bo: the &xe_bo to be removed
+ */
+void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
{
- struct xe_device *xe = tile_to_xe(ggtt->tile);
- bool bound;
- int idx;
-
- bound = drm_dev_enter(&xe->drm, &idx);
- if (bound)
- xe_pm_runtime_get_noresume(xe);
-
- mutex_lock(&ggtt->lock);
- if (bound)
- xe_ggtt_clear(ggtt, node->start, node->size);
- drm_mm_remove_node(node);
- node->size = 0;
- mutex_unlock(&ggtt->lock);
-
- if (!bound)
+ if (XE_WARN_ON(!bo->ggtt_node))
return;
- if (invalidate)
- xe_ggtt_invalidate(ggtt);
+ /* This BO is not currently in the GGTT */
+ xe_tile_assert(ggtt->tile, bo->ggtt_node->base.size == bo->size);
- xe_pm_runtime_put(xe);
- drm_dev_exit(idx);
+ xe_ggtt_node_remove(bo->ggtt_node,
+ bo->flags & XE_BO_FLAG_GGTT_INVALIDATE);
}
-void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
+/**
+ * xe_ggtt_largest_hole - Largest GGTT hole
+ * @ggtt: the &xe_ggtt that will be inspected
+ * @alignment: minimum alignment
+ * @spare: If not NULL: in: desired memory size to be spared / out: Adjusted possible spare
+ *
+ * Return: size of the largest continuous GGTT region
+ */
+u64 xe_ggtt_largest_hole(struct xe_ggtt *ggtt, u64 alignment, u64 *spare)
{
- if (XE_WARN_ON(!bo->ggtt_node.size))
- return;
+ const struct drm_mm *mm = &ggtt->mm;
+ const struct drm_mm_node *entry;
+ u64 hole_min_start = xe_wopcm_size(tile_to_xe(ggtt->tile));
+ u64 hole_start, hole_end, hole_size;
+ u64 max_hole = 0;
- /* This BO is not currently in the GGTT */
- xe_tile_assert(ggtt->tile, bo->ggtt_node.size == bo->size);
+ mutex_lock(&ggtt->lock);
- xe_ggtt_remove_node(ggtt, &bo->ggtt_node,
- bo->flags & XE_BO_FLAG_GGTT_INVALIDATE);
+ drm_mm_for_each_hole(entry, mm, hole_start, hole_end) {
+ hole_start = max(hole_start, hole_min_start);
+ hole_start = ALIGN(hole_start, alignment);
+ hole_end = ALIGN_DOWN(hole_end, alignment);
+ if (hole_start >= hole_end)
+ continue;
+ hole_size = hole_end - hole_start;
+ if (spare)
+ *spare -= min3(*spare, hole_size, max_hole);
+ max_hole = max(max_hole, hole_size);
+ }
+
+ mutex_unlock(&ggtt->lock);
+
+ return max_hole;
}
#ifdef CONFIG_PCI_IOV
@@ -548,22 +747,28 @@ static void xe_ggtt_assign_locked(struct xe_ggtt *ggtt, const struct drm_mm_node
/**
* xe_ggtt_assign - assign a GGTT region to the VF
- * @ggtt: the &xe_ggtt where the node belongs
- * @node: the &drm_mm_node to update
+ * @node: the &xe_ggtt_node to update
* @vfid: the VF identifier
*
* This function is used by the PF driver to assign a GGTT region to the VF.
* In addition to PTE's VFID bits 11:2 also PRESENT bit 0 is set as on some
* platforms VFs can't modify that either.
*/
-void xe_ggtt_assign(struct xe_ggtt *ggtt, const struct drm_mm_node *node, u16 vfid)
+void xe_ggtt_assign(const struct xe_ggtt_node *node, u16 vfid)
{
- mutex_lock(&ggtt->lock);
- xe_ggtt_assign_locked(ggtt, node, vfid);
- mutex_unlock(&ggtt->lock);
+ mutex_lock(&node->ggtt->lock);
+ xe_ggtt_assign_locked(node->ggtt, &node->base, vfid);
+ mutex_unlock(&node->ggtt->lock);
}
#endif
+/**
+ * xe_ggtt_dump - Dump GGTT for debug
+ * @ggtt: the &xe_ggtt to be dumped
+ * @p: the &drm_mm_printer helper handle to be used to dump the information
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
int xe_ggtt_dump(struct xe_ggtt *ggtt, struct drm_printer *p)
{
int err;
@@ -576,3 +781,43 @@ int xe_ggtt_dump(struct xe_ggtt *ggtt, struct drm_printer *p)
mutex_unlock(&ggtt->lock);
return err;
}
+
+/**
+ * xe_ggtt_print_holes - Print holes
+ * @ggtt: the &xe_ggtt to be inspected
+ * @alignment: min alignment
+ * @p: the &drm_printer
+ *
+ * Print GGTT ranges that are available and return total size available.
+ *
+ * Return: Total available size.
+ */
+u64 xe_ggtt_print_holes(struct xe_ggtt *ggtt, u64 alignment, struct drm_printer *p)
+{
+ const struct drm_mm *mm = &ggtt->mm;
+ const struct drm_mm_node *entry;
+ u64 hole_min_start = xe_wopcm_size(tile_to_xe(ggtt->tile));
+ u64 hole_start, hole_end, hole_size;
+ u64 total = 0;
+ char buf[10];
+
+ mutex_lock(&ggtt->lock);
+
+ drm_mm_for_each_hole(entry, mm, hole_start, hole_end) {
+ hole_start = max(hole_start, hole_min_start);
+ hole_start = ALIGN(hole_start, alignment);
+ hole_end = ALIGN_DOWN(hole_end, alignment);
+ if (hole_start >= hole_end)
+ continue;
+ hole_size = hole_end - hole_start;
+ total += hole_size;
+
+ string_get_size(hole_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "range:\t%#llx-%#llx\t(%s)\n",
+ hole_start, hole_end - 1, buf);
+ }
+
+ mutex_unlock(&ggtt->lock);
+
+ return total;
+}
diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h
index 6a96fd54bf60..27e7d67de004 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.h
+++ b/drivers/gpu/drm/xe/xe_ggtt.h
@@ -12,28 +12,30 @@ struct drm_printer;
int xe_ggtt_init_early(struct xe_ggtt *ggtt);
int xe_ggtt_init(struct xe_ggtt *ggtt);
-void xe_ggtt_printk(struct xe_ggtt *ggtt, const char *prefix);
-
-int xe_ggtt_balloon(struct xe_ggtt *ggtt, u64 start, u64 size, struct drm_mm_node *node);
-void xe_ggtt_deballoon(struct xe_ggtt *ggtt, struct drm_mm_node *node);
-
-int xe_ggtt_insert_special_node(struct xe_ggtt *ggtt, struct drm_mm_node *node,
- u32 size, u32 align);
-int xe_ggtt_insert_special_node_locked(struct xe_ggtt *ggtt,
- struct drm_mm_node *node,
- u32 size, u32 align, u32 mm_flags);
-void xe_ggtt_remove_node(struct xe_ggtt *ggtt, struct drm_mm_node *node,
- bool invalidate);
+
+struct xe_ggtt_node *xe_ggtt_node_init(struct xe_ggtt *ggtt);
+void xe_ggtt_node_fini(struct xe_ggtt_node *node);
+int xe_ggtt_node_insert_balloon(struct xe_ggtt_node *node,
+ u64 start, u64 size);
+void xe_ggtt_node_remove_balloon(struct xe_ggtt_node *node);
+
+int xe_ggtt_node_insert(struct xe_ggtt_node *node, u32 size, u32 align);
+int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node,
+ u32 size, u32 align, u32 mm_flags);
+void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate);
+bool xe_ggtt_node_allocated(const struct xe_ggtt_node *node);
void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_bo *bo);
int xe_ggtt_insert_bo(struct xe_ggtt *ggtt, struct xe_bo *bo);
int xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
u64 start, u64 end);
void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo);
+u64 xe_ggtt_largest_hole(struct xe_ggtt *ggtt, u64 alignment, u64 *spare);
int xe_ggtt_dump(struct xe_ggtt *ggtt, struct drm_printer *p);
+u64 xe_ggtt_print_holes(struct xe_ggtt *ggtt, u64 alignment, struct drm_printer *p);
#ifdef CONFIG_PCI_IOV
-void xe_ggtt_assign(struct xe_ggtt *ggtt, const struct drm_mm_node *node, u16 vfid);
+void xe_ggtt_assign(const struct xe_ggtt_node *node, u16 vfid);
#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_ggtt_types.h b/drivers/gpu/drm/xe/xe_ggtt_types.h
index 2245d88d8f39..cb02b7994a9a 100644
--- a/drivers/gpu/drm/xe/xe_ggtt_types.h
+++ b/drivers/gpu/drm/xe/xe_ggtt_types.h
@@ -13,30 +13,70 @@
struct xe_bo;
struct xe_gt;
+/**
+ * struct xe_ggtt - Main GGTT struct
+ *
+ * In general, each tile can contains its own Global Graphics Translation Table
+ * (GGTT) instance.
+ */
struct xe_ggtt {
+ /** @tile: Back pointer to tile where this GGTT belongs */
struct xe_tile *tile;
-
+ /** @size: Total size of this GGTT */
u64 size;
#define XE_GGTT_FLAGS_64K BIT(0)
+ /**
+ * @flags: Flags for this GGTT
+ * Acceptable flags:
+ * - %XE_GGTT_FLAGS_64K - if PTE size is 64K. Otherwise, regular is 4K.
+ */
unsigned int flags;
-
+ /** @scratch: Internal object allocation used as a scratch page */
struct xe_bo *scratch;
-
+ /** @lock: Mutex lock to protect GGTT data */
struct mutex lock;
-
+ /**
+ * @gsm: The iomem pointer to the actual location of the translation
+ * table located in the GSM for easy PTE manipulation
+ */
u64 __iomem *gsm;
-
+ /** @pt_ops: Page Table operations per platform */
const struct xe_ggtt_pt_ops *pt_ops;
-
+ /** @mm: The memory manager used to manage individual GGTT allocations */
struct drm_mm mm;
-
/** @access_count: counts GGTT writes */
unsigned int access_count;
+ /** @wq: Dedicated unordered work queue to process node removals */
+ struct workqueue_struct *wq;
+};
+
+/**
+ * struct xe_ggtt_node - A node in GGTT.
+ *
+ * This struct needs to be initialized (only-once) with xe_ggtt_node_init() before any node
+ * insertion, reservation, or 'ballooning'.
+ * It will, then, be finalized by either xe_ggtt_node_remove() or xe_ggtt_node_deballoon().
+ */
+struct xe_ggtt_node {
+ /** @ggtt: Back pointer to xe_ggtt where this region will be inserted at */
+ struct xe_ggtt *ggtt;
+ /** @base: A drm_mm_node */
+ struct drm_mm_node base;
+ /** @delayed_removal_work: The work struct for the delayed removal */
+ struct work_struct delayed_removal_work;
+ /** @invalidate_on_remove: If it needs invalidation upon removal */
+ bool invalidate_on_remove;
};
+/**
+ * struct xe_ggtt_pt_ops - GGTT Page table operations
+ * Which can vary from platform to platform.
+ */
struct xe_ggtt_pt_ops {
+ /** @pte_encode_bo: Encode PTE address for a given BO */
u64 (*pte_encode_bo)(struct xe_bo *bo, u64 bo_offset, u16 pat_index);
+ /** @ggtt_set_pte: Directly write into GGTT's PTE */
void (*ggtt_set_pte)(struct xe_ggtt *ggtt, u64 addr, u64 pte);
};
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
index e4ad1d6ce1d5..c518d1d16d82 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
@@ -15,11 +15,11 @@ static void xe_sched_process_msg_queue_if_ready(struct xe_gpu_scheduler *sched)
{
struct xe_sched_msg *msg;
- spin_lock(&sched->base.job_list_lock);
+ xe_sched_msg_lock(sched);
msg = list_first_entry_or_null(&sched->msgs, struct xe_sched_msg, link);
if (msg)
xe_sched_process_msg_queue(sched);
- spin_unlock(&sched->base.job_list_lock);
+ xe_sched_msg_unlock(sched);
}
static struct xe_sched_msg *
@@ -27,12 +27,12 @@ xe_sched_get_msg(struct xe_gpu_scheduler *sched)
{
struct xe_sched_msg *msg;
- spin_lock(&sched->base.job_list_lock);
+ xe_sched_msg_lock(sched);
msg = list_first_entry_or_null(&sched->msgs,
struct xe_sched_msg, link);
if (msg)
- list_del(&msg->link);
- spin_unlock(&sched->base.job_list_lock);
+ list_del_init(&msg->link);
+ xe_sched_msg_unlock(sched);
return msg;
}
@@ -93,9 +93,16 @@ void xe_sched_submission_stop(struct xe_gpu_scheduler *sched)
void xe_sched_add_msg(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg)
{
- spin_lock(&sched->base.job_list_lock);
- list_add_tail(&msg->link, &sched->msgs);
- spin_unlock(&sched->base.job_list_lock);
+ xe_sched_msg_lock(sched);
+ xe_sched_add_msg_locked(sched, msg);
+ xe_sched_msg_unlock(sched);
+}
+void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
+ struct xe_sched_msg *msg)
+{
+ lockdep_assert_held(&sched->base.job_list_lock);
+
+ list_add_tail(&msg->link, &sched->msgs);
xe_sched_process_msg_queue(sched);
}
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.h b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
index 10c6bb9c9386..cee9c6809fc0 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.h
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
@@ -24,6 +24,18 @@ void xe_sched_submission_stop(struct xe_gpu_scheduler *sched);
void xe_sched_add_msg(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg);
+void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
+ struct xe_sched_msg *msg);
+
+static inline void xe_sched_msg_lock(struct xe_gpu_scheduler *sched)
+{
+ spin_lock(&sched->base.job_list_lock);
+}
+
+static inline void xe_sched_msg_unlock(struct xe_gpu_scheduler *sched)
+{
+ spin_unlock(&sched->base.job_list_lock);
+}
static inline void xe_sched_stop(struct xe_gpu_scheduler *sched)
{
diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c
index 2a612652bb13..8a137cb83318 100644
--- a/drivers/gpu/drm/xe/xe_gsc.c
+++ b/drivers/gpu/drm/xe/xe_gsc.c
@@ -450,11 +450,6 @@ static void free_resources(void *arg)
xe_exec_queue_put(gsc->q);
gsc->q = NULL;
}
-
- if (gsc->private) {
- xe_bo_unpin_map_no_vm(gsc->private);
- gsc->private = NULL;
- }
}
int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc)
@@ -474,10 +469,9 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc)
if (!hwe)
return -ENODEV;
- bo = xe_bo_create_pin_map(xe, tile, NULL, SZ_4M,
- ttm_bo_type_kernel,
- XE_BO_FLAG_STOLEN |
- XE_BO_FLAG_GGTT);
+ bo = xe_managed_bo_create_pin_map(xe, tile, SZ_4M,
+ XE_BO_FLAG_STOLEN |
+ XE_BO_FLAG_GGTT);
if (IS_ERR(bo))
return PTR_ERR(bo);
diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.c b/drivers/gpu/drm/xe/xe_gsc_proxy.c
index aa812a2bc3ed..2d6ea8c01445 100644
--- a/drivers/gpu/drm/xe/xe_gsc_proxy.c
+++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c
@@ -62,11 +62,6 @@ gsc_to_gt(struct xe_gsc *gsc)
return container_of(gsc, struct xe_gt, uc.gsc);
}
-static inline struct xe_device *kdev_to_xe(struct device *kdev)
-{
- return dev_get_drvdata(kdev);
-}
-
bool xe_gsc_proxy_init_done(struct xe_gsc *gsc)
{
struct xe_gt *gt = gsc_to_gt(gsc);
@@ -345,7 +340,7 @@ void xe_gsc_proxy_irq_handler(struct xe_gsc *gsc, u32 iir)
static int xe_gsc_proxy_component_bind(struct device *xe_kdev,
struct device *mei_kdev, void *data)
{
- struct xe_device *xe = kdev_to_xe(xe_kdev);
+ struct xe_device *xe = kdev_to_xe_device(xe_kdev);
struct xe_gt *gt = xe->tiles[0].media_gt;
struct xe_gsc *gsc = &gt->uc.gsc;
@@ -360,7 +355,7 @@ static int xe_gsc_proxy_component_bind(struct device *xe_kdev,
static void xe_gsc_proxy_component_unbind(struct device *xe_kdev,
struct device *mei_kdev, void *data)
{
- struct xe_device *xe = kdev_to_xe(xe_kdev);
+ struct xe_device *xe = kdev_to_xe_device(xe_kdev);
struct xe_gt *gt = xe->tiles[0].media_gt;
struct xe_gsc *gsc = &gt->uc.gsc;
@@ -376,27 +371,6 @@ static const struct component_ops xe_gsc_proxy_component_ops = {
.unbind = xe_gsc_proxy_component_unbind,
};
-static void proxy_channel_free(struct drm_device *drm, void *arg)
-{
- struct xe_gsc *gsc = arg;
-
- if (!gsc->proxy.bo)
- return;
-
- if (gsc->proxy.to_csme) {
- kfree(gsc->proxy.to_csme);
- gsc->proxy.to_csme = NULL;
- gsc->proxy.from_csme = NULL;
- }
-
- if (gsc->proxy.bo) {
- iosys_map_clear(&gsc->proxy.to_gsc);
- iosys_map_clear(&gsc->proxy.from_gsc);
- xe_bo_unpin_map_no_vm(gsc->proxy.bo);
- gsc->proxy.bo = NULL;
- }
-}
-
static int proxy_channel_alloc(struct xe_gsc *gsc)
{
struct xe_gt *gt = gsc_to_gt(gsc);
@@ -405,18 +379,15 @@ static int proxy_channel_alloc(struct xe_gsc *gsc)
struct xe_bo *bo;
void *csme;
- csme = kzalloc(GSC_PROXY_CHANNEL_SIZE, GFP_KERNEL);
+ csme = drmm_kzalloc(&xe->drm, GSC_PROXY_CHANNEL_SIZE, GFP_KERNEL);
if (!csme)
return -ENOMEM;
- bo = xe_bo_create_pin_map(xe, tile, NULL, GSC_PROXY_CHANNEL_SIZE,
- ttm_bo_type_kernel,
- XE_BO_FLAG_SYSTEM |
- XE_BO_FLAG_GGTT);
- if (IS_ERR(bo)) {
- kfree(csme);
+ bo = xe_managed_bo_create_pin_map(xe, tile, GSC_PROXY_CHANNEL_SIZE,
+ XE_BO_FLAG_SYSTEM |
+ XE_BO_FLAG_GGTT);
+ if (IS_ERR(bo))
return PTR_ERR(bo);
- }
gsc->proxy.bo = bo;
gsc->proxy.to_gsc = IOSYS_MAP_INIT_OFFSET(&bo->vmap, 0);
@@ -424,7 +395,7 @@ static int proxy_channel_alloc(struct xe_gsc *gsc)
gsc->proxy.to_csme = csme;
gsc->proxy.from_csme = csme + GSC_PROXY_BUFFER_SIZE;
- return drmm_add_action_or_reset(&xe->drm, proxy_channel_free, gsc);
+ return 0;
}
/**
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index 282b5dc39908..08a004d698d4 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -112,9 +112,9 @@ static void xe_gt_enable_host_l2_vram(struct xe_gt *gt)
if (!xe_gt_is_media_type(gt)) {
xe_mmio_write32(gt, SCRATCH1LPFC, EN_L3_RW_CCS_CACHE_FLUSH);
- reg = xe_mmio_read32(gt, XE2_GAMREQSTRM_CTRL);
+ reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL);
reg |= CG_DIS_CNTLBUS;
- xe_mmio_write32(gt, XE2_GAMREQSTRM_CTRL, reg);
+ xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg);
}
xe_gt_mcr_multicast_write(gt, XEHPC_L3CLOS_MASK(3), 0x3);
@@ -136,9 +136,9 @@ static void xe_gt_disable_host_l2_vram(struct xe_gt *gt)
if (WARN_ON(err))
return;
- reg = xe_mmio_read32(gt, XE2_GAMREQSTRM_CTRL);
+ reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL);
reg &= ~CG_DIS_CNTLBUS;
- xe_mmio_write32(gt, XE2_GAMREQSTRM_CTRL, reg);
+ xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg);
xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
}
@@ -559,7 +559,6 @@ int xe_gt_init_hwconfig(struct xe_gt *gt)
xe_gt_mcr_init_early(gt);
xe_pat_init(gt);
- xe_gt_enable_host_l2_vram(gt);
err = xe_uc_init(&gt->uc);
if (err)
@@ -571,6 +570,7 @@ int xe_gt_init_hwconfig(struct xe_gt *gt)
xe_gt_topology_init(gt);
xe_gt_mcr_init(gt);
+ xe_gt_enable_host_l2_vram(gt);
out_fw:
xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c
index 5e7fd937917a..8f95d3a5949b 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c
@@ -17,7 +17,9 @@
#include "xe_gt_mcr.h"
#include "xe_gt_sriov_pf_debugfs.h"
#include "xe_gt_sriov_vf_debugfs.h"
+#include "xe_gt_stats.h"
#include "xe_gt_topology.h"
+#include "xe_guc_hwconfig.h"
#include "xe_hw_engine.h"
#include "xe_lrc.h"
#include "xe_macros.h"
@@ -269,6 +271,15 @@ static int vecs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
return 0;
}
+static int hwconfig(struct xe_gt *gt, struct drm_printer *p)
+{
+ xe_pm_runtime_get(gt_to_xe(gt));
+ xe_guc_hwconfig_dump(&gt->uc.guc, p);
+ xe_pm_runtime_put(gt_to_xe(gt));
+
+ return 0;
+}
+
static const struct drm_info_list debugfs_list[] = {
{"hw_engines", .show = xe_gt_debugfs_simple_show, .data = hw_engines},
{"force_reset", .show = xe_gt_debugfs_simple_show, .data = force_reset},
@@ -286,6 +297,8 @@ static const struct drm_info_list debugfs_list[] = {
{"default_lrc_bcs", .show = xe_gt_debugfs_simple_show, .data = bcs_default_lrc},
{"default_lrc_vcs", .show = xe_gt_debugfs_simple_show, .data = vcs_default_lrc},
{"default_lrc_vecs", .show = xe_gt_debugfs_simple_show, .data = vecs_default_lrc},
+ {"stats", .show = xe_gt_debugfs_simple_show, .data = xe_gt_stats_print_info},
+ {"hwconfig", .show = xe_gt_debugfs_simple_show, .data = hwconfig},
};
void xe_gt_debugfs_register(struct xe_gt *gt)
diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c
index 6d948a469126..7d7bd0be6233 100644
--- a/drivers/gpu/drm/xe/xe_gt_mcr.c
+++ b/drivers/gpu/drm/xe/xe_gt_mcr.c
@@ -8,8 +8,10 @@
#include "regs/xe_gt_regs.h"
#include "xe_assert.h"
#include "xe_gt.h"
+#include "xe_gt_printk.h"
#include "xe_gt_topology.h"
#include "xe_gt_types.h"
+#include "xe_guc_hwconfig.h"
#include "xe_mmio.h"
#include "xe_sriov.h"
@@ -297,6 +299,36 @@ static void init_steering_mslice(struct xe_gt *gt)
static unsigned int dss_per_group(struct xe_gt *gt)
{
+ struct xe_guc *guc = &gt->uc.guc;
+ u32 max_slices = 0, max_subslices = 0;
+ int ret;
+
+ /*
+ * Try to query the GuC's hwconfig table for the maximum number of
+ * slices and subslices. These don't reflect the platform's actual
+ * slice/DSS counts, just the physical layout by which we should
+ * determine the steering targets. On older platforms with older GuC
+ * firmware releases it's possible that these attributes may not be
+ * included in the table, so we can always fall back to the old
+ * hardcoded layouts.
+ */
+#define HWCONFIG_ATTR_MAX_SLICES 1
+#define HWCONFIG_ATTR_MAX_SUBSLICES 70
+
+ ret = xe_guc_hwconfig_lookup_u32(guc, HWCONFIG_ATTR_MAX_SLICES,
+ &max_slices);
+ if (ret < 0 || max_slices == 0)
+ goto fallback;
+
+ ret = xe_guc_hwconfig_lookup_u32(guc, HWCONFIG_ATTR_MAX_SUBSLICES,
+ &max_subslices);
+ if (ret < 0 || max_subslices == 0)
+ goto fallback;
+
+ return DIV_ROUND_UP(max_subslices, max_slices);
+
+fallback:
+ xe_gt_dbg(gt, "GuC hwconfig cannot provide dss/slice; using typical fallback values\n");
if (gt_to_xe(gt)->info.platform == XE_PVC)
return 8;
else if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1250)
@@ -314,16 +346,16 @@ static unsigned int dss_per_group(struct xe_gt *gt)
*/
void xe_gt_mcr_get_dss_steering(struct xe_gt *gt, unsigned int dss, u16 *group, u16 *instance)
{
- int dss_per_grp = dss_per_group(gt);
-
xe_gt_assert(gt, dss < XE_MAX_DSS_FUSE_BITS);
- *group = dss / dss_per_grp;
- *instance = dss % dss_per_grp;
+ *group = dss / gt->steering_dss_per_grp;
+ *instance = dss % gt->steering_dss_per_grp;
}
static void init_steering_dss(struct xe_gt *gt)
{
+ gt->steering_dss_per_grp = dss_per_group(gt);
+
xe_gt_mcr_get_dss_steering(gt,
min(xe_dss_mask_group_ffs(gt->fuse_topo.g_dss_mask, 0, 0),
xe_dss_mask_group_ffs(gt->fuse_topo.c_dss_mask, 0, 0)),
diff --git a/drivers/gpu/drm/xe/xe_gt_pagefault.c b/drivers/gpu/drm/xe/xe_gt_pagefault.c
index b2a7fa55bd18..0be4687bfc20 100644
--- a/drivers/gpu/drm/xe/xe_gt_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_gt_pagefault.c
@@ -287,7 +287,7 @@ static bool get_pagefault(struct pf_queue *pf_queue, struct pagefault *pf)
PFD_VIRTUAL_ADDR_LO_SHIFT;
pf_queue->tail = (pf_queue->tail + PF_MSG_LEN_DW) %
- PF_QUEUE_NUM_DW;
+ pf_queue->num_dw;
ret = true;
}
spin_unlock_irq(&pf_queue->lock);
@@ -299,7 +299,8 @@ static bool pf_queue_full(struct pf_queue *pf_queue)
{
lockdep_assert_held(&pf_queue->lock);
- return CIRC_SPACE(pf_queue->head, pf_queue->tail, PF_QUEUE_NUM_DW) <=
+ return CIRC_SPACE(pf_queue->head, pf_queue->tail,
+ pf_queue->num_dw) <=
PF_MSG_LEN_DW;
}
@@ -312,22 +313,23 @@ int xe_guc_pagefault_handler(struct xe_guc *guc, u32 *msg, u32 len)
u32 asid;
bool full;
- /*
- * The below logic doesn't work unless PF_QUEUE_NUM_DW % PF_MSG_LEN_DW == 0
- */
- BUILD_BUG_ON(PF_QUEUE_NUM_DW % PF_MSG_LEN_DW);
-
if (unlikely(len != PF_MSG_LEN_DW))
return -EPROTO;
asid = FIELD_GET(PFD_ASID, msg[1]);
pf_queue = gt->usm.pf_queue + (asid % NUM_PF_QUEUE);
+ /*
+ * The below logic doesn't work unless PF_QUEUE_NUM_DW % PF_MSG_LEN_DW == 0
+ */
+ xe_gt_assert(gt, !(pf_queue->num_dw % PF_MSG_LEN_DW));
+
spin_lock_irqsave(&pf_queue->lock, flags);
full = pf_queue_full(pf_queue);
if (!full) {
memcpy(pf_queue->data + pf_queue->head, msg, len * sizeof(u32));
- pf_queue->head = (pf_queue->head + len) % PF_QUEUE_NUM_DW;
+ pf_queue->head = (pf_queue->head + len) %
+ pf_queue->num_dw;
queue_work(gt->usm.pf_wq, &pf_queue->worker);
} else {
drm_warn(&xe->drm, "PF Queue full, shouldn't be possible");
@@ -386,26 +388,57 @@ static void pagefault_fini(void *arg)
{
struct xe_gt *gt = arg;
struct xe_device *xe = gt_to_xe(gt);
+ int i;
if (!xe->info.has_usm)
return;
destroy_workqueue(gt->usm.acc_wq);
destroy_workqueue(gt->usm.pf_wq);
+
+ for (i = 0; i < NUM_PF_QUEUE; ++i)
+ kfree(gt->usm.pf_queue[i].data);
+}
+
+static int xe_alloc_pf_queue(struct xe_gt *gt, struct pf_queue *pf_queue)
+{
+ xe_dss_mask_t all_dss;
+ int num_dss, num_eus;
+
+ bitmap_or(all_dss, gt->fuse_topo.g_dss_mask, gt->fuse_topo.c_dss_mask,
+ XE_MAX_DSS_FUSE_BITS);
+
+ num_dss = bitmap_weight(all_dss, XE_MAX_DSS_FUSE_BITS);
+ num_eus = bitmap_weight(gt->fuse_topo.eu_mask_per_dss,
+ XE_MAX_EU_FUSE_BITS) * num_dss;
+
+ /* user can issue separate page faults per EU and per CS */
+ pf_queue->num_dw =
+ (num_eus + XE_NUM_HW_ENGINES) * PF_MSG_LEN_DW;
+
+ pf_queue->gt = gt;
+ pf_queue->data = kcalloc(pf_queue->num_dw, sizeof(u32), GFP_KERNEL);
+ if (!pf_queue->data)
+ return -ENOMEM;
+
+ spin_lock_init(&pf_queue->lock);
+ INIT_WORK(&pf_queue->worker, pf_queue_work_func);
+
+ return 0;
}
int xe_gt_pagefault_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
- int i;
+ int i, ret = 0;
if (!xe->info.has_usm)
return 0;
for (i = 0; i < NUM_PF_QUEUE; ++i) {
- gt->usm.pf_queue[i].gt = gt;
- spin_lock_init(&gt->usm.pf_queue[i].lock);
- INIT_WORK(&gt->usm.pf_queue[i].worker, pf_queue_work_func);
+ ret = xe_alloc_pf_queue(gt, &gt->usm.pf_queue[i]);
+ if (ret)
+ return ret;
}
for (i = 0; i < NUM_ACC_QUEUE; ++i) {
gt->usm.acc_queue[i].gt = gt;
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
index 227527785afd..41ed07b153b5 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
@@ -232,14 +232,14 @@ static u32 encode_config_ggtt(u32 *cfg, const struct xe_gt_sriov_config *config)
{
u32 n = 0;
- if (drm_mm_node_allocated(&config->ggtt_region)) {
+ if (xe_ggtt_node_allocated(config->ggtt_region)) {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_GGTT_START);
- cfg[n++] = lower_32_bits(config->ggtt_region.start);
- cfg[n++] = upper_32_bits(config->ggtt_region.start);
+ cfg[n++] = lower_32_bits(config->ggtt_region->base.start);
+ cfg[n++] = upper_32_bits(config->ggtt_region->base.start);
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_GGTT_SIZE);
- cfg[n++] = lower_32_bits(config->ggtt_region.size);
- cfg[n++] = upper_32_bits(config->ggtt_region.size);
+ cfg[n++] = lower_32_bits(config->ggtt_region->base.size);
+ cfg[n++] = upper_32_bits(config->ggtt_region->base.size);
}
return n;
@@ -369,29 +369,28 @@ static int pf_distribute_config_ggtt(struct xe_tile *tile, unsigned int vfid, u6
return err ?: err2;
}
-static void pf_release_ggtt(struct xe_tile *tile, struct drm_mm_node *node)
+static void pf_release_ggtt(struct xe_tile *tile, struct xe_ggtt_node *node)
{
- struct xe_ggtt *ggtt = tile->mem.ggtt;
-
- if (drm_mm_node_allocated(node)) {
+ if (xe_ggtt_node_allocated(node)) {
/*
* explicit GGTT PTE assignment to the PF using xe_ggtt_assign()
* is redundant, as PTE will be implicitly re-assigned to PF by
* the xe_ggtt_clear() called by below xe_ggtt_remove_node().
*/
- xe_ggtt_remove_node(ggtt, node, false);
+ xe_ggtt_node_remove(node, false);
}
}
static void pf_release_vf_config_ggtt(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
- pf_release_ggtt(gt_to_tile(gt), &config->ggtt_region);
+ pf_release_ggtt(gt_to_tile(gt), config->ggtt_region);
+ config->ggtt_region = NULL;
}
static int pf_provision_vf_ggtt(struct xe_gt *gt, unsigned int vfid, u64 size)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
- struct drm_mm_node *node = &config->ggtt_region;
+ struct xe_ggtt_node *node = config->ggtt_region;
struct xe_tile *tile = gt_to_tile(gt);
struct xe_ggtt *ggtt = tile->mem.ggtt;
u64 alignment = pf_get_ggtt_alignment(gt);
@@ -403,40 +402,48 @@ static int pf_provision_vf_ggtt(struct xe_gt *gt, unsigned int vfid, u64 size)
size = round_up(size, alignment);
- if (drm_mm_node_allocated(node)) {
+ if (xe_ggtt_node_allocated(node)) {
err = pf_distribute_config_ggtt(tile, vfid, 0, 0);
if (unlikely(err))
return err;
pf_release_ggtt(tile, node);
}
- xe_gt_assert(gt, !drm_mm_node_allocated(node));
+ xe_gt_assert(gt, !xe_ggtt_node_allocated(node));
if (!size)
return 0;
- err = xe_ggtt_insert_special_node(ggtt, node, size, alignment);
+ node = xe_ggtt_node_init(ggtt);
+ if (IS_ERR(node))
+ return PTR_ERR(node);
+
+ err = xe_ggtt_node_insert(node, size, alignment);
if (unlikely(err))
- return err;
+ goto err;
- xe_ggtt_assign(ggtt, node, vfid);
+ xe_ggtt_assign(node, vfid);
xe_gt_sriov_dbg_verbose(gt, "VF%u assigned GGTT %llx-%llx\n",
- vfid, node->start, node->start + node->size - 1);
+ vfid, node->base.start, node->base.start + node->base.size - 1);
- err = pf_distribute_config_ggtt(gt->tile, vfid, node->start, node->size);
+ err = pf_distribute_config_ggtt(gt->tile, vfid, node->base.start, node->base.size);
if (unlikely(err))
- return err;
+ goto err;
+ config->ggtt_region = node;
return 0;
+err:
+ xe_ggtt_node_fini(node);
+ return err;
}
static u64 pf_get_vf_config_ggtt(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
- struct drm_mm_node *node = &config->ggtt_region;
+ struct xe_ggtt_node *node = config->ggtt_region;
xe_gt_assert(gt, !xe_gt_is_media_type(gt));
- return drm_mm_node_allocated(node) ? node->size : 0;
+ return xe_ggtt_node_allocated(node) ? node->base.size : 0;
}
/**
@@ -587,30 +594,11 @@ int xe_gt_sriov_pf_config_bulk_set_ggtt(struct xe_gt *gt, unsigned int vfid,
static u64 pf_get_max_ggtt(struct xe_gt *gt)
{
struct xe_ggtt *ggtt = gt_to_tile(gt)->mem.ggtt;
- const struct drm_mm *mm = &ggtt->mm;
- const struct drm_mm_node *entry;
u64 alignment = pf_get_ggtt_alignment(gt);
u64 spare = pf_get_spare_ggtt(gt);
- u64 hole_min_start = xe_wopcm_size(gt_to_xe(gt));
- u64 hole_start, hole_end, hole_size;
- u64 max_hole = 0;
+ u64 max_hole;
- mutex_lock(&ggtt->lock);
-
- drm_mm_for_each_hole(entry, mm, hole_start, hole_end) {
- hole_start = max(hole_start, hole_min_start);
- hole_start = ALIGN(hole_start, alignment);
- hole_end = ALIGN_DOWN(hole_end, alignment);
- if (hole_start >= hole_end)
- continue;
- hole_size = hole_end - hole_start;
- xe_gt_sriov_dbg_verbose(gt, "HOLE start %llx size %lluK\n",
- hole_start, hole_size / SZ_1K);
- spare -= min3(spare, hole_size, max_hole);
- max_hole = max(max_hole, hole_size);
- }
-
- mutex_unlock(&ggtt->lock);
+ max_hole = xe_ggtt_largest_hole(ggtt, alignment, &spare);
xe_gt_sriov_dbg_verbose(gt, "HOLE max %lluK reserved %lluK\n",
max_hole / SZ_1K, spare / SZ_1K);
@@ -2025,13 +2013,15 @@ int xe_gt_sriov_pf_config_print_ggtt(struct xe_gt *gt, struct drm_printer *p)
for (n = 1; n <= total_vfs; n++) {
config = &gt->sriov.pf.vfs[n].config;
- if (!drm_mm_node_allocated(&config->ggtt_region))
+ if (!xe_ggtt_node_allocated(config->ggtt_region))
continue;
- string_get_size(config->ggtt_region.size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ string_get_size(config->ggtt_region->base.size, 1, STRING_UNITS_2,
+ buf, sizeof(buf));
drm_printf(p, "VF%u:\t%#0llx-%#llx\t(%s)\n",
- n, config->ggtt_region.start,
- config->ggtt_region.start + config->ggtt_region.size - 1, buf);
+ n, config->ggtt_region->base.start,
+ config->ggtt_region->base.start + config->ggtt_region->base.size - 1,
+ buf);
}
return 0;
@@ -2119,12 +2109,8 @@ int xe_gt_sriov_pf_config_print_dbs(struct xe_gt *gt, struct drm_printer *p)
int xe_gt_sriov_pf_config_print_available_ggtt(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_ggtt *ggtt = gt_to_tile(gt)->mem.ggtt;
- const struct drm_mm *mm = &ggtt->mm;
- const struct drm_mm_node *entry;
u64 alignment = pf_get_ggtt_alignment(gt);
- u64 hole_min_start = xe_wopcm_size(gt_to_xe(gt));
- u64 hole_start, hole_end, hole_size;
- u64 spare, avail, total = 0;
+ u64 spare, avail, total;
char buf[10];
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
@@ -2132,24 +2118,8 @@ int xe_gt_sriov_pf_config_print_available_ggtt(struct xe_gt *gt, struct drm_prin
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
spare = pf_get_spare_ggtt(gt);
+ total = xe_ggtt_print_holes(ggtt, alignment, p);
- mutex_lock(&ggtt->lock);
-
- drm_mm_for_each_hole(entry, mm, hole_start, hole_end) {
- hole_start = max(hole_start, hole_min_start);
- hole_start = ALIGN(hole_start, alignment);
- hole_end = ALIGN_DOWN(hole_end, alignment);
- if (hole_start >= hole_end)
- continue;
- hole_size = hole_end - hole_start;
- total += hole_size;
-
- string_get_size(hole_size, 1, STRING_UNITS_2, buf, sizeof(buf));
- drm_printf(p, "range:\t%#llx-%#llx\t(%s)\n",
- hole_start, hole_end - 1, buf);
- }
-
- mutex_unlock(&ggtt->lock);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
string_get_size(total, 1, STRING_UNITS_2, buf, sizeof(buf));
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h
index 7bc66656fcc7..2d3b73d78f14 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h
@@ -6,8 +6,7 @@
#ifndef _XE_GT_SRIOV_PF_CONFIG_TYPES_H_
#define _XE_GT_SRIOV_PF_CONFIG_TYPES_H_
-#include <drm/drm_mm.h>
-
+#include "xe_ggtt_types.h"
#include "xe_guc_klv_thresholds_set_types.h"
struct xe_bo;
@@ -19,7 +18,7 @@ struct xe_bo;
*/
struct xe_gt_sriov_config {
/** @ggtt_region: GGTT region assigned to the VF. */
- struct drm_mm_node ggtt_region;
+ struct xe_ggtt_node *ggtt_region;
/** @lmem_obj: LMEM allocation for use by the VF. */
struct xe_bo *lmem_obj;
/** @num_ctxs: number of GuC contexts IDs. */
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
index 47222bd9988d..4ebc82e607af 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
@@ -495,6 +495,25 @@ u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt)
return gt->sriov.vf.self_config.lmem_size;
}
+static struct xe_ggtt_node *
+vf_balloon_ggtt_node(struct xe_ggtt *ggtt, u64 start, u64 end)
+{
+ struct xe_ggtt_node *node;
+ int err;
+
+ node = xe_ggtt_node_init(ggtt);
+ if (IS_ERR(node))
+ return node;
+
+ err = xe_ggtt_node_insert_balloon(node, start, end);
+ if (err) {
+ xe_ggtt_node_fini(node);
+ return ERR_PTR(err);
+ }
+
+ return node;
+}
+
static int vf_balloon_ggtt(struct xe_gt *gt)
{
struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
@@ -502,7 +521,6 @@ static int vf_balloon_ggtt(struct xe_gt *gt)
struct xe_ggtt *ggtt = tile->mem.ggtt;
struct xe_device *xe = gt_to_xe(gt);
u64 start, end;
- int err;
xe_gt_assert(gt, IS_SRIOV_VF(xe));
xe_gt_assert(gt, !xe_gt_is_media_type(gt));
@@ -528,35 +546,31 @@ static int vf_balloon_ggtt(struct xe_gt *gt)
start = xe_wopcm_size(xe);
end = config->ggtt_base;
if (end != start) {
- err = xe_ggtt_balloon(ggtt, start, end, &tile->sriov.vf.ggtt_balloon[0]);
- if (err)
- goto failed;
+ tile->sriov.vf.ggtt_balloon[0] = vf_balloon_ggtt_node(ggtt, start, end);
+ if (IS_ERR(tile->sriov.vf.ggtt_balloon[0]))
+ return PTR_ERR(tile->sriov.vf.ggtt_balloon[0]);
}
start = config->ggtt_base + config->ggtt_size;
end = GUC_GGTT_TOP;
if (end != start) {
- err = xe_ggtt_balloon(ggtt, start, end, &tile->sriov.vf.ggtt_balloon[1]);
- if (err)
- goto deballoon;
+ tile->sriov.vf.ggtt_balloon[1] = vf_balloon_ggtt_node(ggtt, start, end);
+ if (IS_ERR(tile->sriov.vf.ggtt_balloon[1])) {
+ xe_ggtt_node_remove_balloon(tile->sriov.vf.ggtt_balloon[0]);
+ return PTR_ERR(tile->sriov.vf.ggtt_balloon[1]);
+ }
}
return 0;
-
-deballoon:
- xe_ggtt_deballoon(ggtt, &tile->sriov.vf.ggtt_balloon[0]);
-failed:
- return err;
}
static void deballoon_ggtt(struct drm_device *drm, void *arg)
{
struct xe_tile *tile = arg;
- struct xe_ggtt *ggtt = tile->mem.ggtt;
xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
- xe_ggtt_deballoon(ggtt, &tile->sriov.vf.ggtt_balloon[1]);
- xe_ggtt_deballoon(ggtt, &tile->sriov.vf.ggtt_balloon[0]);
+ xe_ggtt_node_remove_balloon(tile->sriov.vf.ggtt_balloon[1]);
+ xe_ggtt_node_remove_balloon(tile->sriov.vf.ggtt_balloon[0]);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_gt_stats.c b/drivers/gpu/drm/xe/xe_gt_stats.c
new file mode 100644
index 000000000000..c7364a5aef8f
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_stats.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <linux/atomic.h>
+
+#include <drm/drm_print.h>
+
+#include "xe_gt.h"
+#include "xe_gt_stats.h"
+
+/**
+ * xe_gt_stats_incr - Increments the specified stats counter
+ * @gt: graphics tile
+ * @id: xe_gt_stats_id type id that needs to be incremented
+ * @incr: value to be incremented with
+ *
+ * Increments the specified stats counter.
+ */
+void xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id, int incr)
+{
+ if (id >= __XE_GT_STATS_NUM_IDS)
+ return;
+
+ atomic_add(incr, &gt->stats.counters[id]);
+}
+
+static const char *const stat_description[__XE_GT_STATS_NUM_IDS] = {
+ "tlb_inval_count",
+};
+
+/**
+ * xe_gt_stats_print_info - Print the GT stats
+ * @gt: graphics tile
+ * @p: drm_printer where it will be printed out.
+ *
+ * This prints out all the available GT stats.
+ */
+int xe_gt_stats_print_info(struct xe_gt *gt, struct drm_printer *p)
+{
+ enum xe_gt_stats_id id;
+
+ for (id = 0; id < __XE_GT_STATS_NUM_IDS; ++id)
+ drm_printf(p, "%s: %d\n", stat_description[id],
+ atomic_read(&gt->stats.counters[id]));
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_stats.h b/drivers/gpu/drm/xe/xe_gt_stats.h
new file mode 100644
index 000000000000..91d944f6c4e4
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_stats.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_STATS_H_
+#define _XE_GT_STATS_H_
+
+struct xe_gt;
+struct drm_printer;
+
+enum xe_gt_stats_id {
+ XE_GT_STATS_ID_TLB_INVAL,
+ /* must be the last entry */
+ __XE_GT_STATS_NUM_IDS,
+};
+
+#ifdef CONFIG_DEBUG_FS
+int xe_gt_stats_print_info(struct xe_gt *gt, struct drm_printer *p);
+void xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id, int incr);
+#else
+static inline void
+xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id,
+ int incr)
+{
+}
+
+#endif
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c b/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c
index 87cb76a8718c..cca9cf536f76 100644
--- a/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c
+++ b/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c
@@ -12,6 +12,7 @@
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
+#include "xe_gt_stats.h"
#include "xe_mmio.h"
#include "xe_pm.h"
#include "xe_sriov.h"
@@ -213,6 +214,7 @@ static int send_tlb_invalidation(struct xe_guc *guc,
gt->tlb_invalidation.seqno = 1;
}
mutex_unlock(&guc->ct.lock);
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_TLB_INVAL, 1);
return ret;
}
diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h
index 631928258d71..31946d7fe701 100644
--- a/drivers/gpu/drm/xe/xe_gt_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_types.h
@@ -10,6 +10,7 @@
#include "xe_gt_idle_types.h"
#include "xe_gt_sriov_pf_types.h"
#include "xe_gt_sriov_vf_types.h"
+#include "xe_gt_stats.h"
#include "xe_hw_engine_types.h"
#include "xe_hw_fence_types.h"
#include "xe_oa.h"
@@ -133,6 +134,14 @@ struct xe_gt {
u8 has_indirect_ring_state:1;
} info;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ /** @stats: GT stats */
+ struct {
+ /** @stats.counters: counters for various GT stats */
+ atomic_t counters[__XE_GT_STATS_NUM_IDS];
+ } stats;
+#endif
+
/**
* @mmio: mmio info for GT. All GTs within a tile share the same
* register space, but have their own copy of GSI registers at a
@@ -238,9 +247,14 @@ struct xe_gt {
struct pf_queue {
/** @usm.pf_queue.gt: back pointer to GT */
struct xe_gt *gt;
-#define PF_QUEUE_NUM_DW 128
/** @usm.pf_queue.data: data in the page fault queue */
- u32 data[PF_QUEUE_NUM_DW];
+ u32 *data;
+ /**
+ * @usm.pf_queue.num_dw: number of DWORDS in the page
+ * fault queue. Dynamically calculated based on the number
+ * of compute resources available.
+ */
+ u32 num_dw;
/**
* @usm.pf_queue.tail: tail pointer in DWs for page fault queue,
* moved by worker which processes faults (consumer).
@@ -368,6 +382,12 @@ struct xe_gt {
} steering[NUM_STEERING_TYPES];
/**
+ * @steering_dss_per_grp: number of DSS per steering group (gslice,
+ * cslice, etc.).
+ */
+ unsigned int steering_dss_per_grp;
+
+ /**
* @mcr_lock: protects the MCR_SELECTOR register for the duration
* of a steered operation
*/
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index de0fe9e65746..52df28032a6f 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -350,6 +350,8 @@ int xe_guc_init(struct xe_guc *guc)
if (ret)
goto out;
+ xe_uc_fw_change_status(&guc->fw, XE_UC_FIRMWARE_LOADABLE);
+
ret = devm_add_action_or_reset(xe->drm.dev, guc_fini_hw, guc);
if (ret)
goto out;
@@ -358,8 +360,6 @@ int xe_guc_init(struct xe_guc *guc)
xe_guc_comm_init_early(guc);
- xe_uc_fw_change_status(&guc->fw, XE_UC_FIRMWARE_LOADABLE);
-
return 0;
out:
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index e0bbf98f849d..c3e6b51f7a09 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -11,6 +11,16 @@
#include "xe_hw_engine_types.h"
#include "xe_macros.h"
+/*
+ * GuC version number components are defined to be only 8-bit size,
+ * so converting to a 32bit 8.8.8 integer allows simple (and safe)
+ * numerical comparisons.
+ */
+#define MAKE_GUC_VER(maj, min, pat) (((maj) << 16) | ((min) << 8) | (pat))
+#define MAKE_GUC_VER_STRUCT(ver) MAKE_GUC_VER((ver).major, (ver).minor, (ver).patch)
+#define GUC_SUBMIT_VER(guc) MAKE_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_COMPATIBILITY])
+#define GUC_FIRMWARE_VER(guc) MAKE_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_RELEASE])
+
struct drm_printer;
void xe_guc_comm_init_early(struct xe_guc *guc);
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index 1c60b685dbc6..d1902a8581ca 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -24,6 +24,7 @@
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
+#include "xe_uc_fw.h"
#include "xe_wa.h"
/* Slack of a few additional entries per engine */
@@ -367,6 +368,11 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
0xC40,
&offset, &remain);
+ if (XE_WA(gt, 14022293748) || XE_WA(gt, 22019794406))
+ guc_waklv_enable_simple(ads,
+ GUC_WORKAROUND_KLV_ID_BACK_TO_BACK_RCS_ENGINE_RESET,
+ &offset, &remain);
+
size = guc_ads_waklv_size(ads) - remain;
if (!size)
return;
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index beeeb120d1fc..f24dd5223926 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -105,12 +105,20 @@ ct_to_xe(struct xe_guc_ct *ct)
* enough space to avoid backpressure on the driver. We increase the size
* of the receive buffer (relative to the send) to ensure a G2H response
* CTB has a landing spot.
+ *
+ * In addition to submissions, the G2H buffer needs to be able to hold
+ * enough space for recoverable page fault notifications. The number of
+ * page faults is interrupt driven and can be as much as the number of
+ * compute resources available. However, most of the actual work for these
+ * is in a separate page fault worker thread. Therefore we only need to
+ * make sure the queue has enough space to handle all of the submissions
+ * and responses and an extra buffer for incoming page faults.
*/
#define CTB_DESC_SIZE ALIGN(sizeof(struct guc_ct_buffer_desc), SZ_2K)
#define CTB_H2G_BUFFER_SIZE (SZ_4K)
-#define CTB_G2H_BUFFER_SIZE (4 * CTB_H2G_BUFFER_SIZE)
-#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 4)
+#define CTB_G2H_BUFFER_SIZE (SZ_128K)
+#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 2)
/**
* xe_guc_ct_queue_proc_time_jiffies - Return maximum time to process a full
diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.c b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
index d9b570a154a2..af2c817d552c 100644
--- a/drivers/gpu/drm/xe/xe_guc_hwconfig.c
+++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
@@ -6,6 +6,7 @@
#include "xe_guc_hwconfig.h"
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "abi/guc_actions_abi.h"
#include "xe_bo.h"
@@ -103,3 +104,99 @@ void xe_guc_hwconfig_copy(struct xe_guc *guc, void *dst)
xe_map_memcpy_from(xe, dst, &guc->hwconfig.bo->vmap, 0,
guc->hwconfig.size);
}
+
+void xe_guc_hwconfig_dump(struct xe_guc *guc, struct drm_printer *p)
+{
+ size_t size = xe_guc_hwconfig_size(guc);
+ u32 *hwconfig;
+ u64 num_dw;
+ u32 extra_bytes;
+ int i = 0;
+
+ if (size == 0) {
+ drm_printf(p, "No hwconfig available\n");
+ return;
+ }
+
+ num_dw = div_u64_rem(size, sizeof(u32), &extra_bytes);
+
+ hwconfig = kzalloc(size, GFP_KERNEL);
+ if (!hwconfig) {
+ drm_printf(p, "Error: could not allocate hwconfig memory\n");
+ return;
+ }
+
+ xe_guc_hwconfig_copy(guc, hwconfig);
+
+ /* An entry requires at least three dwords for key, length, value */
+ while (i + 3 <= num_dw) {
+ u32 attribute = hwconfig[i++];
+ u32 len_dw = hwconfig[i++];
+
+ if (i + len_dw > num_dw) {
+ drm_printf(p, "Error: Attribute %u is %u dwords, but only %llu remain\n",
+ attribute, len_dw, num_dw - i);
+ len_dw = num_dw - i;
+ }
+
+ /*
+ * If it's a single dword (as most hwconfig attributes are),
+ * then it's probably a number that makes sense to display
+ * in decimal form. In the rare cases where it's more than
+ * one dword, just print it in hex form and let the user
+ * figure out how to interpret it.
+ */
+ if (len_dw == 1)
+ drm_printf(p, "[%2u] = %u\n", attribute, hwconfig[i]);
+ else
+ drm_printf(p, "[%2u] = { %*ph }\n", attribute,
+ (int)(len_dw * sizeof(u32)), &hwconfig[i]);
+ i += len_dw;
+ }
+
+ if (i < num_dw || extra_bytes)
+ drm_printf(p, "Error: %llu extra bytes at end of hwconfig\n",
+ (num_dw - i) * sizeof(u32) + extra_bytes);
+
+ kfree(hwconfig);
+}
+
+/*
+ * Lookup a specific 32-bit attribute value in the GuC's hwconfig table.
+ */
+int xe_guc_hwconfig_lookup_u32(struct xe_guc *guc, u32 attribute, u32 *val)
+{
+ size_t size = xe_guc_hwconfig_size(guc);
+ u64 num_dw = div_u64(size, sizeof(u32));
+ u32 *hwconfig;
+ bool found = false;
+ int i = 0;
+
+ if (num_dw == 0)
+ return -EINVAL;
+
+ hwconfig = kzalloc(size, GFP_KERNEL);
+ if (!hwconfig)
+ return -ENOMEM;
+
+ xe_guc_hwconfig_copy(guc, hwconfig);
+
+ /* An entry requires at least three dwords for key, length, value */
+ while (i + 3 <= num_dw) {
+ u32 key = hwconfig[i++];
+ u32 len_dw = hwconfig[i++];
+
+ if (key != attribute) {
+ i += len_dw;
+ continue;
+ }
+
+ *val = hwconfig[i];
+ found = true;
+ break;
+ }
+
+ kfree(hwconfig);
+
+ return found ? 0 : -ENOENT;
+}
diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.h b/drivers/gpu/drm/xe/xe_guc_hwconfig.h
index b5794d641900..ab4e5038236e 100644
--- a/drivers/gpu/drm/xe/xe_guc_hwconfig.h
+++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.h
@@ -8,10 +8,13 @@
#include <linux/types.h>
+struct drm_printer;
struct xe_guc;
int xe_guc_hwconfig_init(struct xe_guc *guc);
u32 xe_guc_hwconfig_size(struct xe_guc *guc);
void xe_guc_hwconfig_copy(struct xe_guc *guc, void *dst);
+void xe_guc_hwconfig_dump(struct xe_guc *guc, struct drm_printer *p);
+int xe_guc_hwconfig_lookup_u32(struct xe_guc *guc, u32 attribute, u32 *val);
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
index 32e93a8127d4..def503abeed5 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.c
+++ b/drivers/gpu/drm/xe/xe_guc_pc.c
@@ -1042,7 +1042,7 @@ static void xe_guc_pc_fini_hw(void *arg)
return;
XE_WARN_ON(xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL));
- XE_WARN_ON(xe_guc_pc_gucrc_disable(pc));
+ xe_guc_pc_gucrc_disable(pc);
XE_WARN_ON(xe_guc_pc_stop(pc));
/* Bind requested freq to mert_freq_cap before unload */
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 2adf551500cb..fbbe6a487bbb 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -1374,9 +1374,11 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
struct xe_exec_queue *q = msg->private_data;
if (guc_exec_queue_allowed_to_change_state(q)) {
- q->guc->resume_time = RESUME_PENDING;
clear_exec_queue_suspended(q);
- enable_scheduling(q);
+ if (!exec_queue_enabled(q)) {
+ q->guc->resume_time = RESUME_PENDING;
+ enable_scheduling(q);
+ }
} else {
clear_exec_queue_suspended(q);
}
@@ -1386,6 +1388,8 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
#define SET_SCHED_PROPS 2
#define SUSPEND 3
#define RESUME 4
+#define OPCODE_MASK 0xf
+#define MSG_LOCKED BIT(8)
static void guc_exec_queue_process_msg(struct xe_sched_msg *msg)
{
@@ -1430,7 +1434,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
struct xe_device *xe = guc_to_xe(guc);
struct xe_guc_exec_queue *ge;
long timeout;
- int err;
+ int err, i;
xe_assert(xe, xe_device_uc_enabled(guc_to_xe(guc)));
@@ -1442,6 +1446,9 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
ge->q = q;
init_waitqueue_head(&ge->suspend_wait);
+ for (i = 0; i < MAX_STATIC_MSG_TYPE; ++i)
+ INIT_LIST_HEAD(&ge->static_msgs[i].link);
+
timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT :
msecs_to_jiffies(q->sched_props.job_timeout_ms);
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
@@ -1504,11 +1511,26 @@ static void guc_exec_queue_add_msg(struct xe_exec_queue *q, struct xe_sched_msg
xe_pm_runtime_get_noresume(guc_to_xe(exec_queue_to_guc(q)));
INIT_LIST_HEAD(&msg->link);
- msg->opcode = opcode;
+ msg->opcode = opcode & OPCODE_MASK;
msg->private_data = q;
trace_xe_sched_msg_add(msg);
- xe_sched_add_msg(&q->guc->sched, msg);
+ if (opcode & MSG_LOCKED)
+ xe_sched_add_msg_locked(&q->guc->sched, msg);
+ else
+ xe_sched_add_msg(&q->guc->sched, msg);
+}
+
+static bool guc_exec_queue_try_add_msg(struct xe_exec_queue *q,
+ struct xe_sched_msg *msg,
+ u32 opcode)
+{
+ if (!list_empty(&msg->link))
+ return false;
+
+ guc_exec_queue_add_msg(q, msg, opcode | MSG_LOCKED);
+
+ return true;
}
#define STATIC_MSG_CLEANUP 0
@@ -1582,13 +1604,16 @@ static int guc_exec_queue_set_preempt_timeout(struct xe_exec_queue *q,
static int guc_exec_queue_suspend(struct xe_exec_queue *q)
{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_SUSPEND;
- if (exec_queue_killed_or_banned_or_wedged(q) || q->guc->suspend_pending)
+ if (exec_queue_killed_or_banned_or_wedged(q))
return -EINVAL;
- q->guc->suspend_pending = true;
- guc_exec_queue_add_msg(q, msg, SUSPEND);
+ xe_sched_msg_lock(sched);
+ if (guc_exec_queue_try_add_msg(q, msg, SUSPEND))
+ q->guc->suspend_pending = true;
+ xe_sched_msg_unlock(sched);
return 0;
}
@@ -1603,11 +1628,11 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
* suspend_pending upon kill but to be paranoid but races in which
* suspend_pending is set after kill also check kill here.
*/
- ret = wait_event_timeout(q->guc->suspend_wait,
- !READ_ONCE(q->guc->suspend_pending) ||
- exec_queue_killed(q) ||
- guc_read_stopped(guc),
- HZ * 5);
+ ret = wait_event_interruptible_timeout(q->guc->suspend_wait,
+ !READ_ONCE(q->guc->suspend_pending) ||
+ exec_queue_killed(q) ||
+ guc_read_stopped(guc),
+ HZ * 5);
if (!ret) {
xe_gt_warn(guc_to_gt(guc),
@@ -1617,18 +1642,21 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
return -ETIME;
}
- return 0;
+ return ret < 0 ? ret : 0;
}
static void guc_exec_queue_resume(struct xe_exec_queue *q)
{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_RESUME;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, !q->guc->suspend_pending);
- guc_exec_queue_add_msg(q, msg, RESUME);
+ xe_sched_msg_lock(sched);
+ guc_exec_queue_try_add_msg(q, msg, RESUME);
+ xe_sched_msg_unlock(sched);
}
static bool guc_exec_queue_reset_status(struct xe_exec_queue *q)
diff --git a/drivers/gpu/drm/xe/xe_huc.c b/drivers/gpu/drm/xe/xe_huc.c
index bec4366e5513..f5459f97af23 100644
--- a/drivers/gpu/drm/xe/xe_huc.c
+++ b/drivers/gpu/drm/xe/xe_huc.c
@@ -43,14 +43,6 @@ huc_to_guc(struct xe_huc *huc)
return &container_of(huc, struct xe_uc, huc)->guc;
}
-static void free_gsc_pkt(struct drm_device *drm, void *arg)
-{
- struct xe_huc *huc = arg;
-
- xe_bo_unpin_map_no_vm(huc->gsc_pkt);
- huc->gsc_pkt = NULL;
-}
-
#define PXP43_HUC_AUTH_INOUT_SIZE SZ_4K
static int huc_alloc_gsc_pkt(struct xe_huc *huc)
{
@@ -59,17 +51,16 @@ static int huc_alloc_gsc_pkt(struct xe_huc *huc)
struct xe_bo *bo;
/* we use a single object for both input and output */
- bo = xe_bo_create_pin_map(xe, gt_to_tile(gt), NULL,
- PXP43_HUC_AUTH_INOUT_SIZE * 2,
- ttm_bo_type_kernel,
- XE_BO_FLAG_SYSTEM |
- XE_BO_FLAG_GGTT);
+ bo = xe_managed_bo_create_pin_map(xe, gt_to_tile(gt),
+ PXP43_HUC_AUTH_INOUT_SIZE * 2,
+ XE_BO_FLAG_SYSTEM |
+ XE_BO_FLAG_GGTT);
if (IS_ERR(bo))
return PTR_ERR(bo);
huc->gsc_pkt = bo;
- return drmm_add_action_or_reset(&xe->drm, free_gsc_pkt, huc);
+ return 0;
}
int xe_huc_init(struct xe_huc *huc)
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 07ed9fd28f19..18980238a2ea 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -5,7 +5,10 @@
#include "xe_hw_engine.h"
+#include <linux/nospec.h>
+
#include <drm/drm_managed.h>
+#include <drm/xe_drm.h>
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
@@ -20,6 +23,7 @@
#include "xe_gt_printk.h"
#include "xe_gt_mcr.h"
#include "xe_gt_topology.h"
+#include "xe_hw_engine_group.h"
#include "xe_hw_fence.h"
#include "xe_irq.h"
#include "xe_lrc.h"
@@ -263,7 +267,7 @@ static const struct engine_info engine_infos[] = {
},
};
-static void hw_engine_fini(struct drm_device *drm, void *arg)
+static void hw_engine_fini(void *arg)
{
struct xe_hw_engine *hwe = arg;
@@ -274,8 +278,18 @@ static void hw_engine_fini(struct drm_device *drm, void *arg)
hwe->gt = NULL;
}
-static void hw_engine_mmio_write32(struct xe_hw_engine *hwe, struct xe_reg reg,
- u32 val)
+/**
+ * xe_hw_engine_mmio_write32() - Write engine register
+ * @hwe: engine
+ * @reg: register to write into
+ * @val: desired 32-bit value to write
+ *
+ * This function will write val into an engine specific register.
+ * Forcewake must be held by the caller.
+ *
+ */
+void xe_hw_engine_mmio_write32(struct xe_hw_engine *hwe,
+ struct xe_reg reg, u32 val)
{
xe_gt_assert(hwe->gt, !(reg.addr & hwe->mmio_base));
xe_force_wake_assert_held(gt_to_fw(hwe->gt), hwe->domain);
@@ -285,7 +299,17 @@ static void hw_engine_mmio_write32(struct xe_hw_engine *hwe, struct xe_reg reg,
xe_mmio_write32(hwe->gt, reg, val);
}
-static u32 hw_engine_mmio_read32(struct xe_hw_engine *hwe, struct xe_reg reg)
+/**
+ * xe_hw_engine_mmio_read32() - Read engine register
+ * @hwe: engine
+ * @reg: register to read from
+ *
+ * This function will read from an engine specific register.
+ * Forcewake must be held by the caller.
+ *
+ * Return: value of the 32-bit register.
+ */
+u32 xe_hw_engine_mmio_read32(struct xe_hw_engine *hwe, struct xe_reg reg)
{
xe_gt_assert(hwe->gt, !(reg.addr & hwe->mmio_base));
xe_force_wake_assert_held(gt_to_fw(hwe->gt), hwe->domain);
@@ -304,14 +328,14 @@ void xe_hw_engine_enable_ring(struct xe_hw_engine *hwe)
xe_mmio_write32(hwe->gt, RCU_MODE,
_MASKED_BIT_ENABLE(RCU_MODE_CCS_ENABLE));
- hw_engine_mmio_write32(hwe, RING_HWSTAM(0), ~0x0);
- hw_engine_mmio_write32(hwe, RING_HWS_PGA(0),
- xe_bo_ggtt_addr(hwe->hwsp));
- hw_engine_mmio_write32(hwe, RING_MODE(0),
- _MASKED_BIT_ENABLE(GFX_DISABLE_LEGACY_MODE));
- hw_engine_mmio_write32(hwe, RING_MI_MODE(0),
- _MASKED_BIT_DISABLE(STOP_RING));
- hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
+ xe_hw_engine_mmio_write32(hwe, RING_HWSTAM(0), ~0x0);
+ xe_hw_engine_mmio_write32(hwe, RING_HWS_PGA(0),
+ xe_bo_ggtt_addr(hwe->hwsp));
+ xe_hw_engine_mmio_write32(hwe, RING_MODE(0),
+ _MASKED_BIT_ENABLE(GFX_DISABLE_LEGACY_MODE));
+ xe_hw_engine_mmio_write32(hwe, RING_MI_MODE(0),
+ _MASKED_BIT_DISABLE(STOP_RING));
+ xe_hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
}
static bool xe_hw_engine_match_fixed_cslice_mode(const struct xe_gt *gt,
@@ -425,6 +449,12 @@ hw_engine_setup_default_state(struct xe_hw_engine *hwe)
0xA,
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
},
+ /* Enable Priority Mem Read */
+ { XE_RTP_NAME("Priority_Mem_Read"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0), CS_PRIORITY_MEM_READ,
+ XE_RTP_ACTION_FLAG(ENGINE_BASE)))
+ },
{}
};
@@ -555,7 +585,7 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe,
if (xe->info.has_usm && hwe->class == XE_ENGINE_CLASS_COPY)
gt->usm.reserved_bcs_instance = hwe->instance;
- return drmm_add_action_or_reset(&xe->drm, hw_engine_fini, hwe);
+ return devm_add_action_or_reset(xe->drm.dev, hw_engine_fini, hwe);
err_kernel_lrc:
xe_lrc_put(hwe->kernel_lrc);
@@ -761,6 +791,9 @@ int xe_hw_engines_init(struct xe_gt *gt)
}
hw_engine_setup_logical_mapping(gt);
+ err = xe_hw_engine_setup_groups(gt);
+ if (err)
+ return err;
return 0;
}
@@ -791,7 +824,7 @@ xe_hw_engine_snapshot_instdone_capture(struct xe_hw_engine *hwe,
unsigned int dss;
u16 group, instance;
- snapshot->reg.instdone.ring = hw_engine_mmio_read32(hwe, RING_INSTDONE(0));
+ snapshot->reg.instdone.ring = xe_hw_engine_mmio_read32(hwe, RING_INSTDONE(0));
if (snapshot->hwe->class != XE_ENGINE_CLASS_RENDER)
return;
@@ -887,53 +920,53 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
return snapshot;
snapshot->reg.ring_execlist_status =
- hw_engine_mmio_read32(hwe, RING_EXECLIST_STATUS_LO(0));
- val = hw_engine_mmio_read32(hwe, RING_EXECLIST_STATUS_HI(0));
+ xe_hw_engine_mmio_read32(hwe, RING_EXECLIST_STATUS_LO(0));
+ val = xe_hw_engine_mmio_read32(hwe, RING_EXECLIST_STATUS_HI(0));
snapshot->reg.ring_execlist_status |= val << 32;
snapshot->reg.ring_execlist_sq_contents =
- hw_engine_mmio_read32(hwe, RING_EXECLIST_SQ_CONTENTS_LO(0));
- val = hw_engine_mmio_read32(hwe, RING_EXECLIST_SQ_CONTENTS_HI(0));
+ xe_hw_engine_mmio_read32(hwe, RING_EXECLIST_SQ_CONTENTS_LO(0));
+ val = xe_hw_engine_mmio_read32(hwe, RING_EXECLIST_SQ_CONTENTS_HI(0));
snapshot->reg.ring_execlist_sq_contents |= val << 32;
- snapshot->reg.ring_acthd = hw_engine_mmio_read32(hwe, RING_ACTHD(0));
- val = hw_engine_mmio_read32(hwe, RING_ACTHD_UDW(0));
+ snapshot->reg.ring_acthd = xe_hw_engine_mmio_read32(hwe, RING_ACTHD(0));
+ val = xe_hw_engine_mmio_read32(hwe, RING_ACTHD_UDW(0));
snapshot->reg.ring_acthd |= val << 32;
- snapshot->reg.ring_bbaddr = hw_engine_mmio_read32(hwe, RING_BBADDR(0));
- val = hw_engine_mmio_read32(hwe, RING_BBADDR_UDW(0));
+ snapshot->reg.ring_bbaddr = xe_hw_engine_mmio_read32(hwe, RING_BBADDR(0));
+ val = xe_hw_engine_mmio_read32(hwe, RING_BBADDR_UDW(0));
snapshot->reg.ring_bbaddr |= val << 32;
snapshot->reg.ring_dma_fadd =
- hw_engine_mmio_read32(hwe, RING_DMA_FADD(0));
- val = hw_engine_mmio_read32(hwe, RING_DMA_FADD_UDW(0));
+ xe_hw_engine_mmio_read32(hwe, RING_DMA_FADD(0));
+ val = xe_hw_engine_mmio_read32(hwe, RING_DMA_FADD_UDW(0));
snapshot->reg.ring_dma_fadd |= val << 32;
- snapshot->reg.ring_hwstam = hw_engine_mmio_read32(hwe, RING_HWSTAM(0));
- snapshot->reg.ring_hws_pga = hw_engine_mmio_read32(hwe, RING_HWS_PGA(0));
- snapshot->reg.ring_start = hw_engine_mmio_read32(hwe, RING_START(0));
+ snapshot->reg.ring_hwstam = xe_hw_engine_mmio_read32(hwe, RING_HWSTAM(0));
+ snapshot->reg.ring_hws_pga = xe_hw_engine_mmio_read32(hwe, RING_HWS_PGA(0));
+ snapshot->reg.ring_start = xe_hw_engine_mmio_read32(hwe, RING_START(0));
if (GRAPHICS_VERx100(hwe->gt->tile->xe) >= 2000) {
- val = hw_engine_mmio_read32(hwe, RING_START_UDW(0));
+ val = xe_hw_engine_mmio_read32(hwe, RING_START_UDW(0));
snapshot->reg.ring_start |= val << 32;
}
if (xe_gt_has_indirect_ring_state(hwe->gt)) {
snapshot->reg.indirect_ring_state =
- hw_engine_mmio_read32(hwe, INDIRECT_RING_STATE(0));
+ xe_hw_engine_mmio_read32(hwe, INDIRECT_RING_STATE(0));
}
snapshot->reg.ring_head =
- hw_engine_mmio_read32(hwe, RING_HEAD(0)) & HEAD_ADDR;
+ xe_hw_engine_mmio_read32(hwe, RING_HEAD(0)) & HEAD_ADDR;
snapshot->reg.ring_tail =
- hw_engine_mmio_read32(hwe, RING_TAIL(0)) & TAIL_ADDR;
- snapshot->reg.ring_ctl = hw_engine_mmio_read32(hwe, RING_CTL(0));
+ xe_hw_engine_mmio_read32(hwe, RING_TAIL(0)) & TAIL_ADDR;
+ snapshot->reg.ring_ctl = xe_hw_engine_mmio_read32(hwe, RING_CTL(0));
snapshot->reg.ring_mi_mode =
- hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
- snapshot->reg.ring_mode = hw_engine_mmio_read32(hwe, RING_MODE(0));
- snapshot->reg.ring_imr = hw_engine_mmio_read32(hwe, RING_IMR(0));
- snapshot->reg.ring_esr = hw_engine_mmio_read32(hwe, RING_ESR(0));
- snapshot->reg.ring_emr = hw_engine_mmio_read32(hwe, RING_EMR(0));
- snapshot->reg.ring_eir = hw_engine_mmio_read32(hwe, RING_EIR(0));
- snapshot->reg.ipehr = hw_engine_mmio_read32(hwe, RING_IPEHR(0));
+ xe_hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
+ snapshot->reg.ring_mode = xe_hw_engine_mmio_read32(hwe, RING_MODE(0));
+ snapshot->reg.ring_imr = xe_hw_engine_mmio_read32(hwe, RING_IMR(0));
+ snapshot->reg.ring_esr = xe_hw_engine_mmio_read32(hwe, RING_ESR(0));
+ snapshot->reg.ring_emr = xe_hw_engine_mmio_read32(hwe, RING_EMR(0));
+ snapshot->reg.ring_eir = xe_hw_engine_mmio_read32(hwe, RING_EIR(0));
+ snapshot->reg.ipehr = xe_hw_engine_mmio_read32(hwe, RING_IPEHR(0));
xe_hw_engine_snapshot_instdone_capture(hwe, snapshot);
if (snapshot->hwe->class == XE_ENGINE_CLASS_COMPUTE)
@@ -1135,3 +1168,41 @@ enum xe_force_wake_domains xe_hw_engine_to_fw_domain(struct xe_hw_engine *hwe)
{
return engine_infos[hwe->engine_id].domain;
}
+
+static const enum xe_engine_class user_to_xe_engine_class[] = {
+ [DRM_XE_ENGINE_CLASS_RENDER] = XE_ENGINE_CLASS_RENDER,
+ [DRM_XE_ENGINE_CLASS_COPY] = XE_ENGINE_CLASS_COPY,
+ [DRM_XE_ENGINE_CLASS_VIDEO_DECODE] = XE_ENGINE_CLASS_VIDEO_DECODE,
+ [DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE] = XE_ENGINE_CLASS_VIDEO_ENHANCE,
+ [DRM_XE_ENGINE_CLASS_COMPUTE] = XE_ENGINE_CLASS_COMPUTE,
+};
+
+/**
+ * xe_hw_engine_lookup() - Lookup hardware engine for class:instance
+ * @xe: xe device
+ * @eci: engine class and instance
+ *
+ * This function will find a hardware engine for given engine
+ * class and instance.
+ *
+ * Return: If found xe_hw_engine pointer, NULL otherwise.
+ */
+struct xe_hw_engine *
+xe_hw_engine_lookup(struct xe_device *xe,
+ struct drm_xe_engine_class_instance eci)
+{
+ unsigned int idx;
+
+ if (eci.engine_class >= ARRAY_SIZE(user_to_xe_engine_class))
+ return NULL;
+
+ if (eci.gt_id >= xe->info.gt_count)
+ return NULL;
+
+ idx = array_index_nospec(eci.engine_class,
+ ARRAY_SIZE(user_to_xe_engine_class));
+
+ return xe_gt_hw_engine(xe_device_get_gt(xe, eci.gt_id),
+ user_to_xe_engine_class[idx],
+ eci.engine_instance, true);
+}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.h b/drivers/gpu/drm/xe/xe_hw_engine.h
index 900c8c991430..022819a4a8eb 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine.h
@@ -9,6 +9,8 @@
#include "xe_hw_engine_types.h"
struct drm_printer;
+struct drm_xe_engine_class_instance;
+struct xe_device;
#ifdef CONFIG_DRM_XE_JOB_TIMEOUT_MIN
#define XE_HW_ENGINE_JOB_TIMEOUT_MIN CONFIG_DRM_XE_JOB_TIMEOUT_MIN
@@ -62,6 +64,11 @@ void xe_hw_engine_print(struct xe_hw_engine *hwe, struct drm_printer *p);
void xe_hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe);
bool xe_hw_engine_is_reserved(struct xe_hw_engine *hwe);
+
+struct xe_hw_engine *
+xe_hw_engine_lookup(struct xe_device *xe,
+ struct drm_xe_engine_class_instance eci);
+
static inline bool xe_hw_engine_is_valid(struct xe_hw_engine *hwe)
{
return hwe->name;
@@ -71,4 +78,7 @@ const char *xe_hw_engine_class_to_str(enum xe_engine_class class);
u64 xe_hw_engine_read_timestamp(struct xe_hw_engine *hwe);
enum xe_force_wake_domains xe_hw_engine_to_fw_domain(struct xe_hw_engine *hwe);
+void xe_hw_engine_mmio_write32(struct xe_hw_engine *hwe, struct xe_reg reg, u32 val);
+u32 xe_hw_engine_mmio_read32(struct xe_hw_engine *hwe, struct xe_reg reg);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c
new file mode 100644
index 000000000000..82750520a90a
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <drm/drm_managed.h>
+
+#include "xe_assert.h"
+#include "xe_device.h"
+#include "xe_exec_queue.h"
+#include "xe_gt.h"
+#include "xe_hw_engine_group.h"
+#include "xe_vm.h"
+
+static void
+hw_engine_group_free(struct drm_device *drm, void *arg)
+{
+ struct xe_hw_engine_group *group = arg;
+
+ destroy_workqueue(group->resume_wq);
+ kfree(group);
+}
+
+static void
+hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
+{
+ struct xe_exec_queue *q;
+ struct xe_hw_engine_group *group = container_of(w, struct xe_hw_engine_group, resume_work);
+ int err;
+ enum xe_hw_engine_group_execution_mode previous_mode;
+
+ err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode);
+ if (err)
+ return;
+
+ if (previous_mode == EXEC_MODE_LR)
+ goto put;
+
+ list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
+ if (!xe_vm_in_fault_mode(q->vm))
+ continue;
+
+ q->ops->resume(q);
+ }
+
+put:
+ xe_hw_engine_group_put(group);
+}
+
+static struct xe_hw_engine_group *
+hw_engine_group_alloc(struct xe_device *xe)
+{
+ struct xe_hw_engine_group *group;
+ int err;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return ERR_PTR(-ENOMEM);
+
+ group->resume_wq = alloc_workqueue("xe-resume-lr-jobs-wq", 0, 0);
+ if (!group->resume_wq)
+ return ERR_PTR(-ENOMEM);
+
+ init_rwsem(&group->mode_sem);
+ INIT_WORK(&group->resume_work, hw_engine_group_resume_lr_jobs_func);
+ INIT_LIST_HEAD(&group->exec_queue_list);
+
+ err = drmm_add_action_or_reset(&xe->drm, hw_engine_group_free, group);
+ if (err)
+ return ERR_PTR(err);
+
+ return group;
+}
+
+/**
+ * xe_hw_engine_setup_groups() - Setup the hw engine groups for the gt
+ * @gt: The gt for which groups are setup
+ *
+ * Return: 0 on success, negative error code on error.
+ */
+int xe_hw_engine_setup_groups(struct xe_gt *gt)
+{
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+ struct xe_hw_engine_group *group_rcs_ccs, *group_bcs, *group_vcs_vecs;
+ struct xe_device *xe = gt_to_xe(gt);
+ int err;
+
+ group_rcs_ccs = hw_engine_group_alloc(xe);
+ if (IS_ERR(group_rcs_ccs)) {
+ err = PTR_ERR(group_rcs_ccs);
+ goto err_group_rcs_ccs;
+ }
+
+ group_bcs = hw_engine_group_alloc(xe);
+ if (IS_ERR(group_bcs)) {
+ err = PTR_ERR(group_bcs);
+ goto err_group_bcs;
+ }
+
+ group_vcs_vecs = hw_engine_group_alloc(xe);
+ if (IS_ERR(group_vcs_vecs)) {
+ err = PTR_ERR(group_vcs_vecs);
+ goto err_group_vcs_vecs;
+ }
+
+ for_each_hw_engine(hwe, gt, id) {
+ switch (hwe->class) {
+ case XE_ENGINE_CLASS_COPY:
+ hwe->hw_engine_group = group_bcs;
+ break;
+ case XE_ENGINE_CLASS_RENDER:
+ case XE_ENGINE_CLASS_COMPUTE:
+ hwe->hw_engine_group = group_rcs_ccs;
+ break;
+ case XE_ENGINE_CLASS_VIDEO_DECODE:
+ case XE_ENGINE_CLASS_VIDEO_ENHANCE:
+ hwe->hw_engine_group = group_vcs_vecs;
+ break;
+ case XE_ENGINE_CLASS_OTHER:
+ break;
+ default:
+ drm_warn(&xe->drm, "NOT POSSIBLE");
+ }
+ }
+
+ return 0;
+
+err_group_vcs_vecs:
+ kfree(group_vcs_vecs);
+err_group_bcs:
+ kfree(group_bcs);
+err_group_rcs_ccs:
+ kfree(group_rcs_ccs);
+
+ return err;
+}
+
+/**
+ * xe_hw_engine_group_add_exec_queue() - Add an exec queue to a hw engine group
+ * @group: The hw engine group
+ * @q: The exec_queue
+ *
+ * Return: 0 on success,
+ * -EINTR if the lock could not be acquired
+ */
+int xe_hw_engine_group_add_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q)
+{
+ int err;
+ struct xe_device *xe = gt_to_xe(q->gt);
+
+ xe_assert(xe, group);
+ xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_VM));
+ xe_assert(xe, q->vm);
+
+ if (xe_vm_in_preempt_fence_mode(q->vm))
+ return 0;
+
+ err = down_write_killable(&group->mode_sem);
+ if (err)
+ return err;
+
+ if (xe_vm_in_fault_mode(q->vm) && group->cur_mode == EXEC_MODE_DMA_FENCE) {
+ q->ops->suspend(q);
+ err = q->ops->suspend_wait(q);
+ if (err)
+ goto err_suspend;
+
+ xe_hw_engine_group_resume_faulting_lr_jobs(group);
+ }
+
+ list_add(&q->hw_engine_group_link, &group->exec_queue_list);
+ up_write(&group->mode_sem);
+
+ return 0;
+
+err_suspend:
+ up_write(&group->mode_sem);
+ return err;
+}
+
+/**
+ * xe_hw_engine_group_del_exec_queue() - Delete an exec queue from a hw engine group
+ * @group: The hw engine group
+ * @q: The exec_queue
+ */
+void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q)
+{
+ struct xe_device *xe = gt_to_xe(q->gt);
+
+ xe_assert(xe, group);
+ xe_assert(xe, q->vm);
+
+ down_write(&group->mode_sem);
+
+ if (!list_empty(&q->hw_engine_group_link))
+ list_del(&q->hw_engine_group_link);
+
+ up_write(&group->mode_sem);
+}
+
+/**
+ * xe_hw_engine_group_resume_faulting_lr_jobs() - Asynchronously resume the hw engine group's
+ * faulting LR jobs
+ * @group: The hw engine group
+ */
+void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group)
+{
+ queue_work(group->resume_wq, &group->resume_work);
+}
+
+/**
+ * xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group
+ * @group: The hw engine group
+ *
+ * Return: 0 on success, negative error code on error.
+ */
+static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group)
+{
+ int err;
+ struct xe_exec_queue *q;
+ bool need_resume = false;
+
+ lockdep_assert_held_write(&group->mode_sem);
+
+ list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
+ if (!xe_vm_in_fault_mode(q->vm))
+ continue;
+
+ need_resume = true;
+ q->ops->suspend(q);
+ }
+
+ list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
+ if (!xe_vm_in_fault_mode(q->vm))
+ continue;
+
+ err = q->ops->suspend_wait(q);
+ if (err)
+ goto err_suspend;
+ }
+
+ if (need_resume)
+ xe_hw_engine_group_resume_faulting_lr_jobs(group);
+
+ return 0;
+
+err_suspend:
+ up_write(&group->mode_sem);
+ return err;
+}
+
+/**
+ * xe_hw_engine_group_wait_for_dma_fence_jobs() - Wait for dma fence jobs to complete
+ * @group: The hw engine group
+ *
+ * This function is not meant to be called directly from a user IOCTL as dma_fence_wait()
+ * is not interruptible.
+ *
+ * Return: 0 on success,
+ * -ETIME if waiting for one job failed
+ */
+static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group *group)
+{
+ long timeout;
+ struct xe_exec_queue *q;
+ struct dma_fence *fence;
+
+ lockdep_assert_held_write(&group->mode_sem);
+
+ list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
+ if (xe_vm_in_lr_mode(q->vm))
+ continue;
+
+ fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm);
+ timeout = dma_fence_wait(fence, false);
+ dma_fence_put(fence);
+
+ if (timeout < 0)
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+static int switch_mode(struct xe_hw_engine_group *group)
+{
+ int err = 0;
+ enum xe_hw_engine_group_execution_mode new_mode;
+
+ lockdep_assert_held_write(&group->mode_sem);
+
+ switch (group->cur_mode) {
+ case EXEC_MODE_LR:
+ new_mode = EXEC_MODE_DMA_FENCE;
+ err = xe_hw_engine_group_suspend_faulting_lr_jobs(group);
+ break;
+ case EXEC_MODE_DMA_FENCE:
+ new_mode = EXEC_MODE_LR;
+ err = xe_hw_engine_group_wait_for_dma_fence_jobs(group);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ group->cur_mode = new_mode;
+
+ return 0;
+}
+
+/**
+ * xe_hw_engine_group_get_mode() - Get the group to execute in the new mode
+ * @group: The hw engine group
+ * @new_mode: The new execution mode
+ * @previous_mode: Pointer to the previous mode provided for use by caller
+ *
+ * Return: 0 if successful, -EINTR if locking failed.
+ */
+int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
+ enum xe_hw_engine_group_execution_mode new_mode,
+ enum xe_hw_engine_group_execution_mode *previous_mode)
+__acquires(&group->mode_sem)
+{
+ int err = down_read_interruptible(&group->mode_sem);
+
+ if (err)
+ return err;
+
+ *previous_mode = group->cur_mode;
+
+ if (new_mode != group->cur_mode) {
+ up_read(&group->mode_sem);
+ err = down_write_killable(&group->mode_sem);
+ if (err)
+ return err;
+
+ if (new_mode != group->cur_mode) {
+ err = switch_mode(group);
+ if (err) {
+ up_write(&group->mode_sem);
+ return err;
+ }
+ }
+ downgrade_write(&group->mode_sem);
+ }
+
+ return err;
+}
+
+/**
+ * xe_hw_engine_group_put() - Put the group
+ * @group: The hw engine group
+ */
+void xe_hw_engine_group_put(struct xe_hw_engine_group *group)
+__releases(&group->mode_sem)
+{
+ up_read(&group->mode_sem);
+}
+
+/**
+ * xe_hw_engine_group_find_exec_mode() - Find the execution mode for this exec queue
+ * @q: The exec_queue
+ */
+enum xe_hw_engine_group_execution_mode
+xe_hw_engine_group_find_exec_mode(struct xe_exec_queue *q)
+{
+ if (xe_vm_in_fault_mode(q->vm))
+ return EXEC_MODE_LR;
+ else
+ return EXEC_MODE_DMA_FENCE;
+}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.h b/drivers/gpu/drm/xe/xe_hw_engine_group.h
new file mode 100644
index 000000000000..797ee81acbf2
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_hw_engine_group.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_HW_ENGINE_GROUP_H_
+#define _XE_HW_ENGINE_GROUP_H_
+
+#include "xe_hw_engine_group_types.h"
+
+struct drm_device;
+struct xe_exec_queue;
+struct xe_gt;
+
+int xe_hw_engine_setup_groups(struct xe_gt *gt);
+
+int xe_hw_engine_group_add_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q);
+void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q);
+
+int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
+ enum xe_hw_engine_group_execution_mode new_mode,
+ enum xe_hw_engine_group_execution_mode *previous_mode);
+void xe_hw_engine_group_put(struct xe_hw_engine_group *group);
+
+enum xe_hw_engine_group_execution_mode
+xe_hw_engine_group_find_exec_mode(struct xe_exec_queue *q);
+void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group_types.h b/drivers/gpu/drm/xe/xe_hw_engine_group_types.h
new file mode 100644
index 000000000000..92b6e0712c03
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_hw_engine_group_types.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_HW_ENGINE_GROUP_TYPES_H_
+#define _XE_HW_ENGINE_GROUP_TYPES_H_
+
+#include "xe_force_wake_types.h"
+#include "xe_lrc_types.h"
+#include "xe_reg_sr_types.h"
+
+/**
+ * enum xe_hw_engine_group_execution_mode - possible execution modes of a hw
+ * engine group
+ *
+ * @EXEC_MODE_LR: execution in long-running mode
+ * @EXEC_MODE_DMA_FENCE: execution in dma fence mode
+ */
+enum xe_hw_engine_group_execution_mode {
+ EXEC_MODE_LR,
+ EXEC_MODE_DMA_FENCE,
+};
+
+/**
+ * struct xe_hw_engine_group - Hardware engine group
+ *
+ * hw engines belong to the same group if they share hardware resources in a way
+ * that prevents them from making progress when one is stuck on a page fault.
+ */
+struct xe_hw_engine_group {
+ /**
+ * @exec_queue_list: list of exec queues attached to this
+ * xe_hw_engine_group
+ */
+ struct list_head exec_queue_list;
+ /** @resume_work: worker to resume faulting LR exec queues */
+ struct work_struct resume_work;
+ /** @resume_wq: workqueue to resume faulting LR exec queues */
+ struct workqueue_struct *resume_wq;
+ /**
+ * @mode_sem: used to protect this group's hardware resources and ensure
+ * mutual exclusion between execution only in faulting LR mode and
+ * execution only in DMA_FENCE mode
+ */
+ struct rw_semaphore mode_sem;
+ /** @cur_mode: current execution mode of this hw engine group */
+ enum xe_hw_engine_group_execution_mode cur_mode;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_types.h b/drivers/gpu/drm/xe/xe_hw_engine_types.h
index 70e6434f150d..39f24012d0f4 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_types.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine_types.h
@@ -150,6 +150,8 @@ struct xe_hw_engine {
struct xe_hw_engine_class_intf *eclass;
/** @oa_unit: oa unit for this hw engine */
struct xe_oa_unit *oa_unit;
+ /** @hw_engine_group: the group of hw engines this one belongs to */
+ struct xe_hw_engine_group *hw_engine_group;
};
/**
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index 58121821f081..aec7db39c061 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -5,6 +5,8 @@
#include "xe_lrc.h"
+#include <generated/xe_wa_oob.h>
+
#include <linux/ascii85.h>
#include "instructions/xe_mi_commands.h"
@@ -24,6 +26,7 @@
#include "xe_memirq.h"
#include "xe_sriov.h"
#include "xe_vm.h"
+#include "xe_wa.h"
#define LRC_VALID BIT_ULL(0)
#define LRC_PRIVILEGE BIT_ULL(8)
@@ -1581,19 +1584,31 @@ void xe_lrc_emit_hwe_state_instructions(struct xe_exec_queue *q, struct xe_bb *b
int state_table_size = 0;
/*
- * At the moment we only need to emit non-register state for the RCS
- * engine.
+ * Wa_14019789679
+ *
+ * If the driver doesn't explicitly emit the SVG instructions while
+ * setting up the default LRC, the context switch will write 0's
+ * (noops) into the LRC memory rather than the expected instruction
+ * headers. Application contexts start out as a copy of the default
+ * LRC, and if they also do not emit specific settings for some SVG
+ * state, then on context restore they'll unintentionally inherit
+ * whatever state setting the previous context had programmed into the
+ * hardware (i.e., the lack of a 3DSTATE_* instruction in the LRC will
+ * prevent the hardware from resetting that state back to any specific
+ * value).
+ *
+ * The official workaround only requires emitting 3DSTATE_MESH_CONTROL
+ * since that's a specific state setting that can easily cause GPU
+ * hangs if unintentionally inherited. However to be safe we'll
+ * continue to emit all of the SVG state since it's best not to leak
+ * any of the state between contexts, even if that leakage is harmless.
*/
- if (q->hwe->class != XE_ENGINE_CLASS_RENDER)
- return;
-
- switch (GRAPHICS_VERx100(xe)) {
- case 1255:
- case 1270 ... 2004:
+ if (XE_WA(gt, 14019789679) && q->hwe->class == XE_ENGINE_CLASS_RENDER) {
state_table = xe_hpg_svg_state;
state_table_size = ARRAY_SIZE(xe_hpg_svg_state);
- break;
- default:
+ }
+
+ if (!state_table) {
xe_gt_dbg(gt, "No non-register state to emit on graphics ver %d.%02d\n",
GRAPHICS_VER(xe), GRAPHICS_VERx100(xe) % 100);
return;
@@ -1634,7 +1649,7 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
if (!snapshot)
return NULL;
- if (lrc->bo && lrc->bo->vm)
+ if (lrc->bo->vm)
xe_vm_get(lrc->bo->vm);
snapshot->context_desc = xe_lrc_ggtt_addr(lrc);
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index 6f24aaf58252..cbf54be224c9 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -442,7 +442,7 @@ struct xe_migrate *xe_migrate_init(struct xe_tile *tile)
m->q = xe_exec_queue_create_class(xe, primary_gt, vm,
XE_ENGINE_CLASS_COPY,
EXEC_QUEUE_FLAG_KERNEL |
- EXEC_QUEUE_FLAG_PERMANENT);
+ EXEC_QUEUE_FLAG_PERMANENT, 0);
}
if (IS_ERR(m->q)) {
xe_vm_close_and_put(vm);
@@ -1037,9 +1037,11 @@ static void emit_clear(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs,
* @m: The migration context.
* @bo: The buffer object @dst is currently bound to.
* @dst: The dst TTM resource to be cleared.
+ * @clear_flags: flags to specify which data to clear: CCS, BO, or both.
*
- * Clear the contents of @dst to zero. On flat CCS devices,
- * the CCS metadata is cleared to zero as well on VRAM destinations.
+ * Clear the contents of @dst to zero when XE_MIGRATE_CLEAR_FLAG_BO_DATA is set.
+ * On flat CCS devices, the CCS metadata is cleared to zero with XE_MIGRATE_CLEAR_FLAG_CCS_DATA.
+ * Set XE_MIGRATE_CLEAR_FLAG_FULL to clear bo as well as CCS metadata.
* TODO: Eliminate the @bo argument.
*
* Return: Pointer to a dma_fence representing the last clear batch, or
@@ -1048,18 +1050,27 @@ static void emit_clear(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs,
*/
struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
struct xe_bo *bo,
- struct ttm_resource *dst)
+ struct ttm_resource *dst,
+ u32 clear_flags)
{
bool clear_vram = mem_type_is_vram(dst->mem_type);
+ bool clear_bo_data = XE_MIGRATE_CLEAR_FLAG_BO_DATA & clear_flags;
+ bool clear_ccs = XE_MIGRATE_CLEAR_FLAG_CCS_DATA & clear_flags;
struct xe_gt *gt = m->tile->primary_gt;
struct xe_device *xe = gt_to_xe(gt);
- bool clear_system_ccs = (xe_bo_needs_ccs_pages(bo) && !IS_DGFX(xe)) ? true : false;
+ bool clear_only_system_ccs = false;
struct dma_fence *fence = NULL;
u64 size = bo->size;
struct xe_res_cursor src_it;
struct ttm_resource *src = dst;
int err;
+ if (WARN_ON(!clear_bo_data && !clear_ccs))
+ return NULL;
+
+ if (!clear_bo_data && clear_ccs && !IS_DGFX(xe))
+ clear_only_system_ccs = true;
+
if (!clear_vram)
xe_res_first_sg(xe_bo_sg(bo), 0, bo->size, &src_it);
else
@@ -1085,7 +1096,7 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
batch_size = 2 +
pte_update_size(m, pte_flags, src, &src_it,
&clear_L0, &clear_L0_ofs, &clear_L0_pt,
- clear_system_ccs ? 0 : emit_clear_cmd_len(gt), 0,
+ clear_bo_data ? emit_clear_cmd_len(gt) : 0, 0,
avail_pts);
if (xe_migrate_needs_ccs_emit(xe))
@@ -1107,13 +1118,13 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
if (clear_vram && xe_migrate_allow_identity(clear_L0, &src_it))
xe_res_next(&src_it, clear_L0);
else
- emit_pte(m, bb, clear_L0_pt, clear_vram, clear_system_ccs,
+ emit_pte(m, bb, clear_L0_pt, clear_vram, clear_only_system_ccs,
&src_it, clear_L0, dst);
bb->cs[bb->len++] = MI_BATCH_BUFFER_END;
update_idx = bb->len;
- if (!clear_system_ccs)
+ if (clear_bo_data)
emit_clear(gt, bb, clear_L0_ofs, clear_L0, XE_PAGE_SIZE, clear_vram);
if (xe_migrate_needs_ccs_emit(xe)) {
@@ -1172,7 +1183,7 @@ err_sync:
return ERR_PTR(err);
}
- if (clear_system_ccs)
+ if (clear_ccs)
bo->ccs_cleared = true;
return fence;
diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h
index 453e0ecf5034..0109866e398a 100644
--- a/drivers/gpu/drm/xe/xe_migrate.h
+++ b/drivers/gpu/drm/xe/xe_migrate.h
@@ -6,7 +6,7 @@
#ifndef _XE_MIGRATE_
#define _XE_MIGRATE_
-#include <drm/drm_mm.h>
+#include <linux/types.h>
struct dma_fence;
struct iosys_map;
@@ -102,9 +102,14 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
struct ttm_resource *dst,
bool copy_only_ccs);
+#define XE_MIGRATE_CLEAR_FLAG_BO_DATA BIT(0)
+#define XE_MIGRATE_CLEAR_FLAG_CCS_DATA BIT(1)
+#define XE_MIGRATE_CLEAR_FLAG_FULL (XE_MIGRATE_CLEAR_FLAG_BO_DATA | \
+ XE_MIGRATE_CLEAR_FLAG_CCS_DATA)
struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
struct xe_bo *bo,
- struct ttm_resource *dst);
+ struct ttm_resource *dst,
+ u32 clear_flags);
struct xe_vm *xe_migrate_get_vm(struct xe_migrate *m);
diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c
index f5bdb540e823..3fd462fda625 100644
--- a/drivers/gpu/drm/xe/xe_mmio.c
+++ b/drivers/gpu/drm/xe/xe_mmio.c
@@ -29,9 +29,8 @@ static void tiles_fini(void *arg)
struct xe_tile *tile;
int id;
- for_each_tile(tile, xe, id)
- if (tile != xe_device_get_root_tile(xe))
- tile->mmio.regs = NULL;
+ for_each_remote_tile(tile, xe, id)
+ tile->mmio.regs = NULL;
}
/*
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c
index 499540add465..bfc3deebdaa2 100644
--- a/drivers/gpu/drm/xe/xe_module.c
+++ b/drivers/gpu/drm/xe/xe_module.c
@@ -8,14 +8,17 @@
#include <linux/init.h>
#include <linux/module.h>
+#include <drm/drm_module.h>
+
#include "xe_drv.h"
#include "xe_hw_fence.h"
#include "xe_pci.h"
+#include "xe_pm.h"
#include "xe_observation.h"
#include "xe_sched_job.h"
struct xe_modparam xe_modparam = {
- .enable_display = true,
+ .probe_display = true,
.guc_log_level = 5,
.force_probe = CONFIG_DRM_XE_FORCE_PROBE,
.wedged_mode = 1,
@@ -25,8 +28,8 @@ struct xe_modparam xe_modparam = {
module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444);
MODULE_PARM_DESC(force_execlist, "Force Execlist submission");
-module_param_named(enable_display, xe_modparam.enable_display, bool, 0444);
-MODULE_PARM_DESC(enable_display, "Enable display");
+module_param_named(probe_display, xe_modparam.probe_display, bool, 0444);
+MODULE_PARM_DESC(probe_display, "Probe display HW, otherwise it's left untouched (default: true)");
module_param_named(vram_bar_size, xe_modparam.force_vram_bar_size, uint, 0600);
MODULE_PARM_DESC(vram_bar_size, "Set the vram bar size(in MiB)");
@@ -61,13 +64,28 @@ module_param_named_unsafe(wedged_mode, xe_modparam.wedged_mode, int, 0600);
MODULE_PARM_DESC(wedged_mode,
"Module's default policy for the wedged mode - 0=never, 1=upon-critical-errors[default], 2=upon-any-hang");
+static int xe_check_nomodeset(void)
+{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
+ return 0;
+}
+
struct init_funcs {
int (*init)(void);
void (*exit)(void);
};
+static void xe_dummy_exit(void)
+{
+}
+
static const struct init_funcs init_funcs[] = {
{
+ .init = xe_check_nomodeset,
+ },
+ {
.init = xe_hw_fence_module_init,
.exit = xe_hw_fence_module_exit,
},
@@ -83,17 +101,41 @@ static const struct init_funcs init_funcs[] = {
.init = xe_observation_sysctl_register,
.exit = xe_observation_sysctl_unregister,
},
+ {
+ .init = xe_pm_module_init,
+ .exit = xe_dummy_exit,
+ },
};
+static int __init xe_call_init_func(unsigned int i)
+{
+ if (WARN_ON(i >= ARRAY_SIZE(init_funcs)))
+ return 0;
+ if (!init_funcs[i].init)
+ return 0;
+
+ return init_funcs[i].init();
+}
+
+static void xe_call_exit_func(unsigned int i)
+{
+ if (WARN_ON(i >= ARRAY_SIZE(init_funcs)))
+ return;
+ if (!init_funcs[i].exit)
+ return;
+
+ init_funcs[i].exit();
+}
+
static int __init xe_init(void)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(init_funcs); i++) {
- err = init_funcs[i].init();
+ err = xe_call_init_func(i);
if (err) {
while (i--)
- init_funcs[i].exit();
+ xe_call_exit_func(i);
return err;
}
}
@@ -106,7 +148,7 @@ static void __exit xe_exit(void)
int i;
for (i = ARRAY_SIZE(init_funcs) - 1; i >= 0; i--)
- init_funcs[i].exit();
+ xe_call_exit_func(i);
}
module_init(xe_init);
diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h
index 61a0d28a28c8..161a5e6f717f 100644
--- a/drivers/gpu/drm/xe/xe_module.h
+++ b/drivers/gpu/drm/xe/xe_module.h
@@ -11,7 +11,7 @@
/* Module modprobe variables */
struct xe_modparam {
bool force_execlist;
- bool enable_display;
+ bool probe_display;
u32 force_vram_bar_size;
int guc_log_level;
char *guc_firmware_path;
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index 3ef92eb8fbb1..4d4541e0b24c 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -1244,8 +1244,7 @@ static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
vm_flags_mod(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY,
VM_MAYWRITE | VM_MAYEXEC);
- xe_assert(stream->oa->xe, bo->ttm.ttm->num_pages ==
- (vma->vm_end - vma->vm_start) >> PAGE_SHIFT);
+ xe_assert(stream->oa->xe, bo->ttm.ttm->num_pages == vma_pages(vma));
for (i = 0; i < bo->ttm.ttm->num_pages; i++) {
ret = remap_pfn_range(vma, start, page_to_pfn(bo->ttm.ttm->pages[i]),
PAGE_SIZE, vma->vm_page_prot);
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index b42aa304c4e9..f276194d9c4e 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -338,14 +338,12 @@ static const struct xe_device_desc mtl_desc = {
static const struct xe_device_desc lnl_desc = {
PLATFORM(LUNARLAKE),
.has_display = true,
- .require_force_probe = true,
};
static const struct xe_device_desc bmg_desc = {
DGFX_FEATURES,
PLATFORM(BATTLEMAGE),
.has_display = true,
- .require_force_probe = true,
.has_heci_cscfi = 1,
};
@@ -616,9 +614,9 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.skip_mtcfg = desc->skip_mtcfg;
xe->info.skip_pcode = desc->skip_pcode;
- xe->info.enable_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
- xe_modparam.enable_display &&
- desc->has_display;
+ xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
+ xe_modparam.probe_display &&
+ desc->has_display;
err = xe_tile_init_early(xe_device_get_root_tile(xe), xe, 0);
if (err)
@@ -747,7 +745,7 @@ static void xe_pci_remove(struct pci_dev *pdev)
{
struct xe_device *xe;
- xe = pci_get_drvdata(pdev);
+ xe = pdev_to_xe_device(pdev);
if (!xe) /* driver load aborted, nothing to cleanup */
return;
@@ -829,7 +827,7 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
xe->info.media_name,
xe->info.media_verx100 / 100,
xe->info.media_verx100 % 100,
- str_yes_no(xe->info.enable_display),
+ str_yes_no(xe->info.probe_display),
xe->info.dma_mask_size, xe->info.tile_count,
xe->info.has_heci_gscfi, xe->info.has_heci_cscfi);
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index fcfb49af8c89..2e2accd76fb2 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -70,11 +70,34 @@
*/
#ifdef CONFIG_LOCKDEP
-static struct lockdep_map xe_pm_runtime_lockdep_map = {
- .name = "xe_pm_runtime_lockdep_map"
+static struct lockdep_map xe_pm_runtime_d3cold_map = {
+ .name = "xe_rpm_d3cold_map"
+};
+
+static struct lockdep_map xe_pm_runtime_nod3cold_map = {
+ .name = "xe_rpm_nod3cold_map"
};
#endif
+static bool __maybe_unused xe_rpm_reclaim_safe(const struct xe_device *xe)
+{
+ return !xe->d3cold.capable && !xe->info.has_sriov;
+}
+
+static void xe_rpm_lockmap_acquire(const struct xe_device *xe)
+{
+ lock_map_acquire(xe_rpm_reclaim_safe(xe) ?
+ &xe_pm_runtime_nod3cold_map :
+ &xe_pm_runtime_d3cold_map);
+}
+
+static void xe_rpm_lockmap_release(const struct xe_device *xe)
+{
+ lock_map_release(xe_rpm_reclaim_safe(xe) ?
+ &xe_pm_runtime_nod3cold_map :
+ &xe_pm_runtime_d3cold_map);
+}
+
/**
* xe_pm_suspend - Helper for System suspend, i.e. S0->S3 / S0->S2idle
* @xe: xe device instance
@@ -354,7 +377,7 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
* annotation here and in xe_pm_runtime_get() lockdep will see
* the potential lock inversion and give us a nice splat.
*/
- lock_map_acquire(&xe_pm_runtime_lockdep_map);
+ xe_rpm_lockmap_acquire(xe);
/*
* Applying lock for entire list op as xe_ttm_bo_destroy and xe_bo_move_notify
@@ -366,6 +389,8 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
xe_bo_runtime_pm_release_mmap_offset(bo);
mutex_unlock(&xe->mem_access.vram_userfault.lock);
+ xe_display_pm_runtime_suspend(xe);
+
if (xe->d3cold.allowed) {
xe_display_pm_suspend(xe, true);
@@ -387,7 +412,7 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
out:
if (err)
xe_display_pm_resume(xe, true);
- lock_map_release(&xe_pm_runtime_lockdep_map);
+ xe_rpm_lockmap_release(xe);
xe_pm_write_callback_task(xe, NULL);
return err;
}
@@ -408,7 +433,7 @@ int xe_pm_runtime_resume(struct xe_device *xe)
/* Disable access_ongoing asserts and prevent recursive pm calls */
xe_pm_write_callback_task(xe, current);
- lock_map_acquire(&xe_pm_runtime_lockdep_map);
+ xe_rpm_lockmap_acquire(xe);
if (xe->d3cold.allowed) {
err = xe_pcode_ready(xe, true);
@@ -431,14 +456,16 @@ int xe_pm_runtime_resume(struct xe_device *xe)
for_each_gt(gt, xe, id)
xe_gt_resume(gt);
+ xe_display_pm_runtime_resume(xe);
+
if (xe->d3cold.allowed) {
- xe_display_pm_resume(xe, true);
err = xe_bo_restore_user(xe);
if (err)
goto out;
}
+
out:
- lock_map_release(&xe_pm_runtime_lockdep_map);
+ xe_rpm_lockmap_release(xe);
xe_pm_write_callback_task(xe, NULL);
return err;
}
@@ -452,15 +479,37 @@ out:
* stuff that can happen inside the runtime_resume callback by acquiring
* a dummy lock (it doesn't protect anything and gets compiled out on
* non-debug builds). Lockdep then only needs to see the
- * xe_pm_runtime_lockdep_map -> runtime_resume callback once, and then can
- * hopefully validate all the (callers_locks) -> xe_pm_runtime_lockdep_map.
+ * xe_pm_runtime_xxx_map -> runtime_resume callback once, and then can
+ * hopefully validate all the (callers_locks) -> xe_pm_runtime_xxx_map.
* For example if the (callers_locks) are ever grabbed in the
* runtime_resume callback, lockdep should give us a nice splat.
*/
-static void pm_runtime_lockdep_prime(void)
+static void xe_rpm_might_enter_cb(const struct xe_device *xe)
{
- lock_map_acquire(&xe_pm_runtime_lockdep_map);
- lock_map_release(&xe_pm_runtime_lockdep_map);
+ xe_rpm_lockmap_acquire(xe);
+ xe_rpm_lockmap_release(xe);
+}
+
+/*
+ * Prime the lockdep maps for known locking orders that need to
+ * be supported but that may not always occur on all systems.
+ */
+static void xe_pm_runtime_lockdep_prime(void)
+{
+ struct dma_resv lockdep_resv;
+
+ dma_resv_init(&lockdep_resv);
+ lock_map_acquire(&xe_pm_runtime_d3cold_map);
+ /* D3Cold takes the dma_resv locks to evict bos */
+ dma_resv_lock(&lockdep_resv, NULL);
+ dma_resv_unlock(&lockdep_resv);
+ lock_map_release(&xe_pm_runtime_d3cold_map);
+
+ /* Shrinkers might like to wake up the device under reclaim. */
+ fs_reclaim_acquire(GFP_KERNEL);
+ lock_map_acquire(&xe_pm_runtime_nod3cold_map);
+ lock_map_release(&xe_pm_runtime_nod3cold_map);
+ fs_reclaim_release(GFP_KERNEL);
}
/**
@@ -475,7 +524,7 @@ void xe_pm_runtime_get(struct xe_device *xe)
if (xe_pm_read_callback_task(xe) == current)
return;
- pm_runtime_lockdep_prime();
+ xe_rpm_might_enter_cb(xe);
pm_runtime_resume(xe->drm.dev);
}
@@ -507,7 +556,7 @@ int xe_pm_runtime_get_ioctl(struct xe_device *xe)
if (WARN_ON(xe_pm_read_callback_task(xe) == current))
return -ELOOP;
- pm_runtime_lockdep_prime();
+ xe_rpm_might_enter_cb(xe);
return pm_runtime_get_sync(xe->drm.dev);
}
@@ -575,7 +624,7 @@ bool xe_pm_runtime_resume_and_get(struct xe_device *xe)
return true;
}
- pm_runtime_lockdep_prime();
+ xe_rpm_might_enter_cb(xe);
return pm_runtime_resume_and_get(xe->drm.dev) >= 0;
}
@@ -667,3 +716,14 @@ void xe_pm_d3cold_allowed_toggle(struct xe_device *xe)
drm_dbg(&xe->drm,
"d3cold: allowed=%s\n", str_yes_no(xe->d3cold.allowed));
}
+
+/**
+ * xe_pm_module_init() - Perform xe_pm specific module initialization.
+ *
+ * Return: 0 on success. Currently doesn't fail.
+ */
+int __init xe_pm_module_init(void)
+{
+ xe_pm_runtime_lockdep_prime();
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_pm.h b/drivers/gpu/drm/xe/xe_pm.h
index 104a21ae6dfd..9aef673b1c8a 100644
--- a/drivers/gpu/drm/xe/xe_pm.h
+++ b/drivers/gpu/drm/xe/xe_pm.h
@@ -32,5 +32,6 @@ void xe_pm_assert_unbounded_bridge(struct xe_device *xe);
int xe_pm_set_vram_threshold(struct xe_device *xe, u32 threshold);
void xe_pm_d3cold_allowed_toggle(struct xe_device *xe);
struct task_struct *xe_pm_read_callback_task(struct xe_device *xe);
+int xe_pm_module_init(void);
#endif
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 97a6a0b0b8ba..579ed31b46db 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -1149,10 +1149,12 @@ static int xe_pt_vm_dependencies(struct xe_sched_job *job,
return err;
}
- if (job)
- err = xe_sched_job_last_fence_add_dep(job, vm);
- else
- err = xe_exec_queue_last_fence_test_dep(pt_update_ops->q, vm);
+ if (!(pt_update_ops->q->flags & EXEC_QUEUE_FLAG_KERNEL)) {
+ if (job)
+ err = xe_sched_job_last_fence_add_dep(job, vm);
+ else
+ err = xe_exec_queue_last_fence_test_dep(pt_update_ops->q, vm);
+ }
for (i = 0; job && !err && i < vops->num_syncs; i++)
err = xe_sync_entry_add_deps(&vops->syncs[i], job);
diff --git a/drivers/gpu/drm/xe/xe_res_cursor.h b/drivers/gpu/drm/xe/xe_res_cursor.h
index 655af89b31a9..dca374b6521c 100644
--- a/drivers/gpu/drm/xe/xe_res_cursor.h
+++ b/drivers/gpu/drm/xe/xe_res_cursor.h
@@ -26,7 +26,6 @@
#include <linux/scatterlist.h>
-#include <drm/drm_mm.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_range_manager.h>
#include <drm/ttm/ttm_resource.h>
diff --git a/drivers/gpu/drm/xe/xe_sa.c b/drivers/gpu/drm/xe/xe_sa.c
index f3060979e63f..fe2cb2a96f78 100644
--- a/drivers/gpu/drm/xe/xe_sa.c
+++ b/drivers/gpu/drm/xe/xe_sa.c
@@ -25,10 +25,9 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg)
drm_suballoc_manager_fini(&sa_manager->base);
- if (bo->vmap.is_iomem)
+ if (sa_manager->is_iomem)
kvfree(sa_manager->cpu_ptr);
- xe_bo_unpin_map_no_vm(bo);
sa_manager->bo = NULL;
}
@@ -47,16 +46,17 @@ struct xe_sa_manager *xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32
sa_manager->bo = NULL;
- bo = xe_bo_create_pin_map(xe, tile, NULL, size, ttm_bo_type_kernel,
- XE_BO_FLAG_VRAM_IF_DGFX(tile) |
- XE_BO_FLAG_GGTT |
- XE_BO_FLAG_GGTT_INVALIDATE);
+ bo = xe_managed_bo_create_pin_map(xe, tile, size,
+ XE_BO_FLAG_VRAM_IF_DGFX(tile) |
+ XE_BO_FLAG_GGTT |
+ XE_BO_FLAG_GGTT_INVALIDATE);
if (IS_ERR(bo)) {
drm_err(&xe->drm, "failed to allocate bo for sa manager: %ld\n",
PTR_ERR(bo));
return (struct xe_sa_manager *)bo;
}
sa_manager->bo = bo;
+ sa_manager->is_iomem = bo->vmap.is_iomem;
drm_suballoc_manager_init(&sa_manager->base, managed_size, align);
sa_manager->gpu_addr = xe_bo_ggtt_addr(bo);
@@ -64,7 +64,6 @@ struct xe_sa_manager *xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32
if (bo->vmap.is_iomem) {
sa_manager->cpu_ptr = kvzalloc(managed_size, GFP_KERNEL);
if (!sa_manager->cpu_ptr) {
- xe_bo_unpin_map_no_vm(sa_manager->bo);
sa_manager->bo = NULL;
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/gpu/drm/xe/xe_sa_types.h b/drivers/gpu/drm/xe/xe_sa_types.h
index 2ef896aeca1d..2b070ff1292e 100644
--- a/drivers/gpu/drm/xe/xe_sa_types.h
+++ b/drivers/gpu/drm/xe/xe_sa_types.h
@@ -14,6 +14,7 @@ struct xe_sa_manager {
struct xe_bo *bo;
u64 gpu_addr;
void *cpu_ptr;
+ bool is_iomem;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
index 9628f9deb3c0..55d47450b2c6 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.c
+++ b/drivers/gpu/drm/xe/xe_sched_job.c
@@ -89,8 +89,7 @@ static void xe_sched_job_free_fences(struct xe_sched_job *job)
if (ptrs->lrc_fence)
xe_lrc_free_seqno_fence(ptrs->lrc_fence);
- if (ptrs->chain_fence)
- dma_fence_chain_free(ptrs->chain_fence);
+ dma_fence_chain_free(ptrs->chain_fence);
}
}
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index 3aa6270e5dd7..436faff09bac 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -55,7 +55,7 @@ static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
struct xe_user_fence *ufence;
u64 __user *ptr = u64_to_user_ptr(addr);
- if (!access_ok(ptr, sizeof(ptr)))
+ if (!access_ok(ptr, sizeof(*ptr)))
return ERR_PTR(-EFAULT);
ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
@@ -206,16 +206,9 @@ int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
{
- int err;
-
- if (sync->fence) {
- err = drm_sched_job_add_dependency(&job->drm,
- dma_fence_get(sync->fence));
- if (err) {
- dma_fence_put(sync->fence);
- return err;
- }
- }
+ if (sync->fence)
+ return drm_sched_job_add_dependency(&job->drm,
+ dma_fence_get(sync->fence));
return 0;
}
@@ -256,10 +249,8 @@ void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
{
if (sync->syncobj)
drm_syncobj_put(sync->syncobj);
- if (sync->fence)
- dma_fence_put(sync->fence);
- if (sync->chain_fence)
- dma_fence_chain_free(sync->chain_fence);
+ dma_fence_put(sync->fence);
+ dma_fence_chain_free(sync->chain_fence);
if (sync->ufence)
user_fence_put(sync->ufence);
}
diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
index f46fd2df84de..f7113cf6109d 100644
--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
@@ -5,7 +5,6 @@
*/
#include <drm/drm_managed.h>
-#include <drm/drm_mm.h>
#include <drm/ttm/ttm_device.h>
#include <drm/ttm/ttm_placement.h>
diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c
index 77d4eec0118d..faa1bf42e50e 100644
--- a/drivers/gpu/drm/xe/xe_tuning.c
+++ b/drivers/gpu/drm/xe/xe_tuning.c
@@ -39,12 +39,23 @@ static const struct xe_rtp_entry_sr gt_tunings[] = {
},
{ XE_RTP_NAME("Tuning: Compression Overfetch"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
- XE_RTP_ACTIONS(CLR(CCCHKNREG1, ENCOMPPERFFIX)),
+ XE_RTP_ACTIONS(CLR(CCCHKNREG1, ENCOMPPERFFIX),
+ SET(CCCHKNREG1, L3CMPCTRL))
},
{ XE_RTP_NAME("Tuning: Enable compressible partial write overfetch in L3"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
XE_RTP_ACTIONS(SET(L3SQCREG3, COMPPWOVERFETCHEN))
},
+ { XE_RTP_NAME("Tuning: L2 Overfetch Compressible Only"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_ACTIONS(SET(L3SQCREG2,
+ COMPMEMRD256BOVRFETCHEN))
+ },
+ { XE_RTP_NAME("Tuning: Stateless compression control"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_ACTIONS(FIELD_SET(STATELESS_COMPRESSION_CTRL, UNIFIED_COMPRESSION_FORMAT,
+ REG_FIELD_PREP(UNIFIED_COMPRESSION_FORMAT, 0)))
+ },
{}
};
diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c
index 5b70d23724c4..4bb2a4a80ddc 100644
--- a/drivers/gpu/drm/xe/xe_uc_fw.c
+++ b/drivers/gpu/drm/xe/xe_uc_fw.c
@@ -15,6 +15,7 @@
#include "xe_gsc.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_guc.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_module.h"
@@ -105,15 +106,16 @@ struct fw_blobs_by_type {
};
#define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \
- fw_def(LUNARLAKE, major_ver(xe, guc, lnl, 70, 19, 2)) \
- fw_def(METEORLAKE, major_ver(i915, guc, mtl, 70, 19, 2)) \
- fw_def(DG2, major_ver(i915, guc, dg2, 70, 19, 2)) \
- fw_def(DG1, major_ver(i915, guc, dg1, 70, 19, 2)) \
- fw_def(ALDERLAKE_N, major_ver(i915, guc, tgl, 70, 19, 2)) \
- fw_def(ALDERLAKE_P, major_ver(i915, guc, adlp, 70, 19, 2)) \
- fw_def(ALDERLAKE_S, major_ver(i915, guc, tgl, 70, 19, 2)) \
- fw_def(ROCKETLAKE, major_ver(i915, guc, tgl, 70, 19, 2)) \
- fw_def(TIGERLAKE, major_ver(i915, guc, tgl, 70, 19, 2))
+ fw_def(BATTLEMAGE, major_ver(xe, guc, bmg, 70, 29, 2)) \
+ fw_def(LUNARLAKE, major_ver(xe, guc, lnl, 70, 29, 2)) \
+ fw_def(METEORLAKE, major_ver(i915, guc, mtl, 70, 29, 2)) \
+ fw_def(DG2, major_ver(i915, guc, dg2, 70, 29, 2)) \
+ fw_def(DG1, major_ver(i915, guc, dg1, 70, 29, 2)) \
+ fw_def(ALDERLAKE_N, major_ver(i915, guc, tgl, 70, 29, 2)) \
+ fw_def(ALDERLAKE_P, major_ver(i915, guc, adlp, 70, 29, 2)) \
+ fw_def(ALDERLAKE_S, major_ver(i915, guc, tgl, 70, 29, 2)) \
+ fw_def(ROCKETLAKE, major_ver(i915, guc, tgl, 70, 29, 2)) \
+ fw_def(TIGERLAKE, major_ver(i915, guc, tgl, 70, 29, 2))
#define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver) \
fw_def(BATTLEMAGE, no_ver(xe, huc, bmg)) \
@@ -309,10 +311,10 @@ static int guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css)
xe_gt_assert(gt, uc_fw->type == XE_UC_FW_TYPE_GUC);
- /* We don't support GuC releases older than 70.19 */
- if (release->major < 70 || (release->major == 70 && release->minor < 19)) {
- xe_gt_err(gt, "Unsupported GuC v%u.%u! v70.19 or newer is required\n",
- release->major, release->minor);
+ /* We don't support GuC releases older than 70.29.2 */
+ if (MAKE_GUC_VER_STRUCT(*release) < MAKE_GUC_VER(70, 29, 2)) {
+ xe_gt_err(gt, "Unsupported GuC v%u.%u.%u! v70.29.2 or newer is required\n",
+ release->major, release->minor, release->patch);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 1ff94a9ae7c9..3eb76d874eb2 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -275,6 +275,8 @@ out_up_write:
* xe_vm_remove_compute_exec_queue() - Remove compute exec queue from VM
* @vm: The VM.
* @q: The exec_queue
+ *
+ * Note that this function might be called multiple times on the same queue.
*/
void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
{
@@ -282,8 +284,10 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
return;
down_write(&vm->lock);
- list_del(&q->lr.link);
- --vm->preempt.num_exec_queues;
+ if (!list_empty(&q->lr.link)) {
+ list_del_init(&q->lr.link);
+ --vm->preempt.num_exec_queues;
+ }
if (q->lr.pfence) {
dma_fence_enable_sw_signaling(q->lr.pfence);
dma_fence_put(q->lr.pfence);
@@ -1191,7 +1195,7 @@ static const struct drm_gpuvm_ops gpuvm_ops = {
.vm_free = xe_vm_free,
};
-static u64 pde_encode_pat_index(struct xe_device *xe, u16 pat_index)
+static u64 pde_encode_pat_index(u16 pat_index)
{
u64 pte = 0;
@@ -1204,8 +1208,7 @@ static u64 pde_encode_pat_index(struct xe_device *xe, u16 pat_index)
return pte;
}
-static u64 pte_encode_pat_index(struct xe_device *xe, u16 pat_index,
- u32 pt_level)
+static u64 pte_encode_pat_index(u16 pat_index, u32 pt_level)
{
u64 pte = 0;
@@ -1246,12 +1249,11 @@ static u64 pte_encode_ps(u32 pt_level)
static u64 xelp_pde_encode_bo(struct xe_bo *bo, u64 bo_offset,
const u16 pat_index)
{
- struct xe_device *xe = xe_bo_device(bo);
u64 pde;
pde = xe_bo_addr(bo, bo_offset, XE_PAGE_SIZE);
pde |= XE_PAGE_PRESENT | XE_PAGE_RW;
- pde |= pde_encode_pat_index(xe, pat_index);
+ pde |= pde_encode_pat_index(pat_index);
return pde;
}
@@ -1259,12 +1261,11 @@ static u64 xelp_pde_encode_bo(struct xe_bo *bo, u64 bo_offset,
static u64 xelp_pte_encode_bo(struct xe_bo *bo, u64 bo_offset,
u16 pat_index, u32 pt_level)
{
- struct xe_device *xe = xe_bo_device(bo);
u64 pte;
pte = xe_bo_addr(bo, bo_offset, XE_PAGE_SIZE);
pte |= XE_PAGE_PRESENT | XE_PAGE_RW;
- pte |= pte_encode_pat_index(xe, pat_index, pt_level);
+ pte |= pte_encode_pat_index(pat_index, pt_level);
pte |= pte_encode_ps(pt_level);
if (xe_bo_is_vram(bo) || xe_bo_is_stolen_devmem(bo))
@@ -1276,14 +1277,12 @@ static u64 xelp_pte_encode_bo(struct xe_bo *bo, u64 bo_offset,
static u64 xelp_pte_encode_vma(u64 pte, struct xe_vma *vma,
u16 pat_index, u32 pt_level)
{
- struct xe_device *xe = xe_vma_vm(vma)->xe;
-
pte |= XE_PAGE_PRESENT;
if (likely(!xe_vma_read_only(vma)))
pte |= XE_PAGE_RW;
- pte |= pte_encode_pat_index(xe, pat_index, pt_level);
+ pte |= pte_encode_pat_index(pat_index, pt_level);
pte |= pte_encode_ps(pt_level);
if (unlikely(xe_vma_is_null(vma)))
@@ -1303,7 +1302,7 @@ static u64 xelp_pte_encode_addr(struct xe_device *xe, u64 addr,
pte = addr;
pte |= XE_PAGE_PRESENT | XE_PAGE_RW;
- pte |= pte_encode_pat_index(xe, pat_index, pt_level);
+ pte |= pte_encode_pat_index(pat_index, pt_level);
pte |= pte_encode_ps(pt_level);
if (devmem)
@@ -1483,19 +1482,13 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
/* Kernel migration VM shouldn't have a circular loop.. */
if (!(flags & XE_VM_FLAG_MIGRATION)) {
for_each_tile(tile, xe, id) {
- struct xe_gt *gt = tile->primary_gt;
- struct xe_vm *migrate_vm;
struct xe_exec_queue *q;
u32 create_flags = EXEC_QUEUE_FLAG_VM;
if (!vm->pt_root[id])
continue;
- migrate_vm = xe_migrate_get_vm(tile->migrate);
- q = xe_exec_queue_create_class(xe, gt, migrate_vm,
- XE_ENGINE_CLASS_COPY,
- create_flags);
- xe_vm_put(migrate_vm);
+ q = xe_exec_queue_create_bind(xe, tile, create_flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_close;
@@ -1508,13 +1501,6 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
if (number_tiles > 1)
vm->composite_fence_ctx = dma_fence_context_alloc(1);
- mutex_lock(&xe->usm.lock);
- if (flags & XE_VM_FLAG_FAULT_MODE)
- xe->usm.num_vm_in_fault_mode++;
- else if (!(flags & XE_VM_FLAG_MIGRATION))
- xe->usm.num_vm_in_non_fault_mode++;
- mutex_unlock(&xe->usm.lock);
-
trace_xe_vm_create(vm);
return vm;
@@ -1628,11 +1614,6 @@ void xe_vm_close_and_put(struct xe_vm *vm)
up_write(&vm->lock);
mutex_lock(&xe->usm.lock);
- if (vm->flags & XE_VM_FLAG_FAULT_MODE)
- xe->usm.num_vm_in_fault_mode--;
- else if (!(vm->flags & XE_VM_FLAG_MIGRATION))
- xe->usm.num_vm_in_non_fault_mode--;
-
if (vm->usm.asid) {
void *lookup;
@@ -1770,14 +1751,6 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data,
args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE))
return -EINVAL;
- if (XE_IOCTL_DBG(xe, args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE &&
- xe_device_in_non_fault_mode(xe)))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, !(args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE) &&
- xe_device_in_fault_mode(xe)))
- return -EINVAL;
-
if (XE_IOCTL_DBG(xe, args->extensions))
return -EINVAL;
@@ -3194,9 +3167,10 @@ int xe_vm_invalidate_vma(struct xe_vma *vma)
{
struct xe_device *xe = xe_vma_vm(vma)->xe;
struct xe_tile *tile;
- struct xe_gt_tlb_invalidation_fence fence[XE_MAX_TILES_PER_DEVICE];
- u32 tile_needs_invalidate = 0;
+ struct xe_gt_tlb_invalidation_fence
+ fence[XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE];
u8 id;
+ u32 fence_id = 0;
int ret = 0;
xe_assert(xe, !xe_vma_is_null(vma));
@@ -3224,27 +3198,37 @@ int xe_vm_invalidate_vma(struct xe_vma *vma)
if (xe_pt_zap_ptes(tile, vma)) {
xe_device_wmb(xe);
xe_gt_tlb_invalidation_fence_init(tile->primary_gt,
- &fence[id], true);
+ &fence[fence_id],
+ true);
- /*
- * FIXME: We potentially need to invalidate multiple
- * GTs within the tile
- */
ret = xe_gt_tlb_invalidation_vma(tile->primary_gt,
- &fence[id], vma);
+ &fence[fence_id], vma);
if (ret < 0) {
- xe_gt_tlb_invalidation_fence_fini(&fence[id]);
+ xe_gt_tlb_invalidation_fence_fini(&fence[fence_id]);
goto wait;
}
+ ++fence_id;
+
+ if (!tile->media_gt)
+ continue;
- tile_needs_invalidate |= BIT(id);
+ xe_gt_tlb_invalidation_fence_init(tile->media_gt,
+ &fence[fence_id],
+ true);
+
+ ret = xe_gt_tlb_invalidation_vma(tile->media_gt,
+ &fence[fence_id], vma);
+ if (ret < 0) {
+ xe_gt_tlb_invalidation_fence_fini(&fence[fence_id]);
+ goto wait;
+ }
+ ++fence_id;
}
}
wait:
- for_each_tile(tile, xe, id)
- if (tile_needs_invalidate & BIT(id))
- xe_gt_tlb_invalidation_fence_wait(&fence[id]);
+ for (id = 0; id < fence_id; ++id)
+ xe_gt_tlb_invalidation_fence_wait(&fence[id]);
vma->tile_invalidated = vma->tile_mask;
diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c
index 5b9e5a1857ea..28b7f95b6c2f 100644
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
@@ -557,16 +557,6 @@ static const struct xe_rtp_entry_sr engine_was[] = {
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
},
- /* Xe2_LPM */
-
- { XE_RTP_NAME("16021639441"),
- XE_RTP_RULES(MEDIA_VERSION(2000)),
- XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0),
- GHWSP_CSB_REPORT_DIS |
- PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS,
- XE_RTP_ACTION_FLAG(ENGINE_BASE)))
- },
-
/* Xe2_HPM */
{ XE_RTP_NAME("16021639441"),
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index 540d38603f32..920ca5060146 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -27,7 +27,13 @@
16022287689 GRAPHICS_VERSION(2001)
GRAPHICS_VERSION(2004)
13011645652 GRAPHICS_VERSION(2004)
+14022293748 GRAPHICS_VERSION(2001)
+ GRAPHICS_VERSION(2004)
+22019794406 GRAPHICS_VERSION(2001)
+ GRAPHICS_VERSION(2004)
22019338487 MEDIA_VERSION(2000)
GRAPHICS_VERSION(2001)
22019338487_display PLATFORM(LUNARLAKE)
16023588340 GRAPHICS_VERSION(2001)
+14019789679 GRAPHICS_VERSION(1255)
+ GRAPHICS_VERSION_RANGE(1270, 2004)
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index f006bc931324..b62e4f0e8130 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -404,9 +404,10 @@ static struct iommu_domain *host1x_iommu_attach(struct host1x *host)
if (err < 0)
goto put_group;
- host->domain = iommu_domain_alloc(&platform_bus_type);
- if (!host->domain) {
- err = -ENOMEM;
+ host->domain = iommu_paging_domain_alloc(host->dev);
+ if (IS_ERR(host->domain)) {
+ err = PTR_ERR(host->domain);
+ host->domain = NULL;
goto put_cache;
}
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index 925a118db23f..92031b240a17 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/iommu.h>
#include <linux/iova.h>
+#include <linux/irqreturn.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -81,6 +82,7 @@ struct host1x_intr_ops {
void (*disable_syncpt_intr)(struct host1x *host, unsigned int id);
void (*disable_all_syncpt_intrs)(struct host1x *host);
int (*free_syncpt_irq)(struct host1x *host);
+ irqreturn_t (*isr)(int irq, void *dev_id);
};
struct host1x_sid_entry {
diff --git a/drivers/gpu/host1x/hw/intr_hw.c b/drivers/gpu/host1x/hw/intr_hw.c
index 9880e0c47235..415f8d7e4202 100644
--- a/drivers/gpu/host1x/hw/intr_hw.c
+++ b/drivers/gpu/host1x/hw/intr_hw.c
@@ -6,18 +6,11 @@
* Copyright (c) 2010-2013, NVIDIA Corporation.
*/
-#include <linux/interrupt.h>
-#include <linux/irq.h>
#include <linux/io.h>
#include "../intr.h"
#include "../dev.h"
-struct host1x_intr_irq_data {
- struct host1x *host;
- u32 offset;
-};
-
static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
{
struct host1x_intr_irq_data *irq_data = dev_id;
@@ -54,7 +47,8 @@ static void host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
}
}
-static void intr_hw_init(struct host1x *host, u32 cpm)
+static int
+host1x_intr_init_host_sync(struct host1x *host, u32 cpm)
{
#if HOST1X_HW < 6
/* disable the ip_busy_timeout. this prevents write drops */
@@ -85,32 +79,6 @@ static void intr_hw_init(struct host1x *host, u32 cpm)
host1x_sync_writel(host, irq_index, HOST1X_SYNC_SYNCPT_INTR_DEST(id));
}
#endif
-}
-
-static int
-host1x_intr_init_host_sync(struct host1x *host, u32 cpm)
-{
- int err, i;
- struct host1x_intr_irq_data *irq_data;
-
- irq_data = devm_kcalloc(host->dev, host->num_syncpt_irqs, sizeof(irq_data[0]), GFP_KERNEL);
- if (!irq_data)
- return -ENOMEM;
-
- host1x_hw_intr_disable_all_syncpt_intrs(host);
-
- for (i = 0; i < host->num_syncpt_irqs; i++) {
- irq_data[i].host = host;
- irq_data[i].offset = i;
-
- err = devm_request_irq(host->dev, host->syncpt_irqs[i],
- syncpt_thresh_isr, IRQF_SHARED,
- "host1x_syncpt", &irq_data[i]);
- if (err < 0)
- return err;
- }
-
- intr_hw_init(host, cpm);
return 0;
}
@@ -144,4 +112,5 @@ static const struct host1x_intr_ops host1x_intr_ops = {
.enable_syncpt_intr = host1x_intr_enable_syncpt_intr,
.disable_syncpt_intr = host1x_intr_disable_syncpt_intr,
.disable_all_syncpt_intrs = host1x_intr_disable_all_syncpt_intrs,
+ .isr = syncpt_thresh_isr,
};
diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c
index 995bfa980837..b3285dd10180 100644
--- a/drivers/gpu/host1x/intr.c
+++ b/drivers/gpu/host1x/intr.c
@@ -6,7 +6,7 @@
*/
#include <linux/clk.h>
-
+#include <linux/interrupt.h>
#include "dev.h"
#include "fence.h"
#include "intr.h"
@@ -100,7 +100,9 @@ void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
int host1x_intr_init(struct host1x *host)
{
+ struct host1x_intr_irq_data *irq_data;
unsigned int id;
+ int i, err;
mutex_init(&host->intr_mutex);
@@ -111,6 +113,23 @@ int host1x_intr_init(struct host1x *host)
INIT_LIST_HEAD(&syncpt->fences.list);
}
+ irq_data = devm_kcalloc(host->dev, host->num_syncpt_irqs, sizeof(irq_data[0]), GFP_KERNEL);
+ if (!irq_data)
+ return -ENOMEM;
+
+ host1x_hw_intr_disable_all_syncpt_intrs(host);
+
+ for (i = 0; i < host->num_syncpt_irqs; i++) {
+ irq_data[i].host = host;
+ irq_data[i].offset = i;
+
+ err = devm_request_irq(host->dev, host->syncpt_irqs[i],
+ host->intr_op->isr, IRQF_SHARED,
+ "host1x_syncpt", &irq_data[i]);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
diff --git a/drivers/gpu/host1x/intr.h b/drivers/gpu/host1x/intr.h
index 3b5610b525e5..11cdf13e32fe 100644
--- a/drivers/gpu/host1x/intr.h
+++ b/drivers/gpu/host1x/intr.h
@@ -11,6 +11,11 @@
struct host1x;
struct host1x_syncpt_fence;
+struct host1x_intr_irq_data {
+ struct host1x *host;
+ u32 offset;
+};
+
/* Initialize host1x sync point interrupt */
int host1x_intr_init(struct host1x *host);
diff --git a/include/drm/display/drm_dp_mst_helper.h b/include/drm/display/drm_dp_mst_helper.h
index 02b037d3a93f..f6a1cbb0f600 100644
--- a/include/drm/display/drm_dp_mst_helper.h
+++ b/include/drm/display/drm_dp_mst_helper.h
@@ -244,18 +244,18 @@ struct drm_dp_mst_branch {
bool link_address_sent;
/* global unique identifier to identify branch devices */
- u8 guid[16];
+ guid_t guid;
};
struct drm_dp_nak_reply {
- u8 guid[16];
+ guid_t guid;
u8 reason;
u8 nak_data;
};
struct drm_dp_link_address_ack_reply {
- u8 guid[16];
+ guid_t guid;
u8 nports;
struct drm_dp_link_addr_reply_port {
bool input_port;
@@ -265,7 +265,7 @@ struct drm_dp_link_address_ack_reply {
bool ddps;
bool legacy_device_plug_status;
u8 dpcd_revision;
- u8 peer_guid[16];
+ guid_t peer_guid;
u8 num_sdp_streams;
u8 num_sdp_stream_sinks;
} ports[16];
@@ -348,7 +348,7 @@ struct drm_dp_allocate_payload_ack_reply {
};
struct drm_dp_connection_status_notify {
- u8 guid[16];
+ guid_t guid;
u8 port_number;
bool legacy_device_plug_status;
bool displayport_device_plug_status;
@@ -425,7 +425,7 @@ struct drm_dp_query_payload {
struct drm_dp_resource_status_notify {
u8 port_number;
- u8 guid[16];
+ guid_t guid;
u16 available_pbn;
};
diff --git a/include/drm/drm_accel.h b/include/drm/drm_accel.h
index f4d3784b1dce..8867ce0be94c 100644
--- a/include/drm/drm_accel.h
+++ b/include/drm/drm_accel.h
@@ -51,11 +51,10 @@
#if IS_ENABLED(CONFIG_DRM_ACCEL)
+extern struct xarray accel_minors_xa;
+
void accel_core_exit(void);
int accel_core_init(void);
-void accel_minor_remove(int index);
-int accel_minor_alloc(void);
-void accel_minor_replace(struct drm_minor *minor, int index);
void accel_set_device_instance_params(struct device *kdev, int index);
int accel_open(struct inode *inode, struct file *filp);
void accel_debugfs_init(struct drm_device *dev);
@@ -73,19 +72,6 @@ static inline int __init accel_core_init(void)
return 0;
}
-static inline void accel_minor_remove(int index)
-{
-}
-
-static inline int accel_minor_alloc(void)
-{
- return -EOPNOTSUPP;
-}
-
-static inline void accel_minor_replace(struct drm_minor *minor, int index)
-{
-}
-
static inline void accel_set_device_instance_params(struct device *kdev, int index)
{
}
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 4d7f4c5f2001..31ca88deb10d 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -460,7 +460,7 @@ struct drm_atomic_state {
*
* Used for signaling unbound planes/connectors.
* When a connector or plane is not bound to any CRTC, it's still important
- * to preserve linearity to prevent the atomic states from being freed to early.
+ * to preserve linearity to prevent the atomic states from being freed too early.
*
* This commit (if set) is not bound to any CRTC, but will be completed when
* drm_atomic_helper_commit_hw_done() is called.
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
index ab230d3af138..8c0030c77308 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -45,6 +45,8 @@ struct drm_printer;
struct device;
struct file;
+extern struct xarray drm_minors_xa;
+
/*
* FIXME: Not sure we want to have drm_minor here in the end, but to avoid
* header include loops we need it here for now.
@@ -434,6 +436,9 @@ static inline bool drm_is_accel_client(const struct drm_file *file_priv)
void drm_file_update_pid(struct drm_file *);
+struct drm_minor *drm_minor_acquire(struct xarray *minors_xa, unsigned int minor_id);
+void drm_minor_release(struct drm_minor *minor);
+
int drm_open(struct inode *inode, struct file *filp);
int drm_open_helper(struct file *filp, struct drm_minor *minor);
ssize_t drm_read(struct file *filp, char __user *buffer,
diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
index 5d9dff5149c9..d2676831d765 100644
--- a/include/drm/drm_print.h
+++ b/include/drm/drm_print.h
@@ -221,7 +221,8 @@ drm_vprintf(struct drm_printer *p, const char *fmt, va_list *va)
/**
* struct drm_print_iterator - local struct used with drm_printer_coredump
- * @data: Pointer to the devcoredump output buffer
+ * @data: Pointer to the devcoredump output buffer, can be NULL if using
+ * drm_printer_coredump to determine size of devcoredump
* @start: The offset within the buffer to start writing
* @remain: The number of bytes to write for this iteration
*/
@@ -266,6 +267,57 @@ struct drm_print_iterator {
* coredump_read, ...)
* }
*
+ * The above example has a time complexity of O(N^2), where N is the size of the
+ * devcoredump. This is acceptable for small devcoredumps but scales poorly for
+ * larger ones.
+ *
+ * Another use case for drm_coredump_printer is to capture the devcoredump into
+ * a saved buffer before the dev_coredump() callback. This involves two passes:
+ * one to determine the size of the devcoredump and another to print it to a
+ * buffer. Then, in dev_coredump(), copy from the saved buffer into the
+ * devcoredump read buffer.
+ *
+ * For example::
+ *
+ * char *devcoredump_saved_buffer;
+ *
+ * ssize_t __coredump_print(char *buffer, ssize_t count, ...)
+ * {
+ * struct drm_print_iterator iter;
+ * struct drm_printer p;
+ *
+ * iter.data = buffer;
+ * iter.start = 0;
+ * iter.remain = count;
+ *
+ * p = drm_coredump_printer(&iter);
+ *
+ * drm_printf(p, "foo=%d\n", foo);
+ * ...
+ * return count - iter.remain;
+ * }
+ *
+ * void coredump_print(...)
+ * {
+ * ssize_t count;
+ *
+ * count = __coredump_print(NULL, INT_MAX, ...);
+ * devcoredump_saved_buffer = kvmalloc(count, GFP_KERNEL);
+ * __coredump_print(devcoredump_saved_buffer, count, ...);
+ * }
+ *
+ * void coredump_read(char *buffer, loff_t offset, size_t count,
+ * void *data, size_t datalen)
+ * {
+ * ...
+ * memcpy(buffer, devcoredump_saved_buffer + offset, count);
+ * ...
+ * }
+ *
+ * The above example has a time complexity of O(N*2), where N is the size of the
+ * devcoredump. This scales better than the previous example for larger
+ * devcoredumps.
+ *
* RETURNS:
* The &drm_printer object
*/
diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h
index 73fcb899a01d..46f09cf68458 100644
--- a/include/drm/drm_rect.h
+++ b/include/drm/drm_rect.h
@@ -238,6 +238,21 @@ static inline void drm_rect_fp_to_int(struct drm_rect *dst,
drm_rect_height(src) >> 16);
}
+/**
+ * drm_rect_overlap - Check if two rectangles overlap
+ * @a: first rectangle
+ * @b: second rectangle
+ *
+ * RETURNS:
+ * %true if the rectangles overlap, %false otherwise.
+ */
+static inline bool drm_rect_overlap(const struct drm_rect *a,
+ const struct drm_rect *b)
+{
+ return (a->x2 > b->x1 && b->x2 > a->x1 &&
+ a->y2 > b->y1 && b->y2 > a->y1);
+}
+
bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip);
bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst,
const struct drm_rect *clip);
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index d1a732d56259..7294dde240fb 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -222,7 +222,7 @@ struct ttm_lru_walk {
struct ttm_operation_ctx *ctx;
/** @ticket: The struct ww_acquire_ctx if any. */
struct ww_acquire_ctx *ticket;
- /** @tryock_only: Only use trylock for locking. */
+ /** @trylock_only: Only use trylock for locking. */
bool trylock_only;
};