Pull drm updates from Dave Airlie:
 "Highlights:
   - xe: add initial CRI platform support
   - amdgpu: initial HDMI 2.1 FRL support
   - rust: add some new type concepts for device lifetimes
   - scheduler: moves to a fair algorithm and lots of cleanups

  But it's mostly the usual mountain of changes across the board.

  core:
   - add docbook for DRM_IOCTL_SYNCOBJ_EVENTFD
   - change signature of drm_connector_attach_hdr_output_metadata_property
   - dedup counter and timestamp retrieval in vblank code
   - parse AMD VSDB v3 in CTA extension blocks
   - add P230, Y7, XYYY2101010, T430, XVUY210101010 formats
   - don't call drop master on file close if not master
   - use drm_printf_indent in atomic / bridge
   - fix 32b format descriptions
   - docs: fix toctree
   - hdmi: add common TMDS character rates
   - fix drm_syncobj_find_fence leak

  rust:
   - introduce Higher-Ranked lifetime types
   - replace drvdata with scoped registration data
   - add GPUVM immediate mode abstraction for rust GPU drivers
   - introduce DeviceContext type state for drm::Device

  bridge:
   - clarify drm_bridge_get/put
   - create drm_get_bridge_by_endpoint and use it
   - analogix_dp: add panel probing
   - ite-it6211 - use drm audio hdmi helpers

  buddy:
   - add lockdep annotations

  dp:
   - add PR and VRR updates
   - mst: fix buffer overflows
   - add Adaptive Sync SDP decoding support
   - fix OOB reads in dp-mst

  ttm:
   - bump fpfn/lpfn to 64-bit

  scheduler:
   - change default to fair scheduler
   - map runqueue 1:1 with scheduler

  dma-buf:
   - port selftests to kunit
   - convert dma-buf system/heap allocators to module
   - add separate DMABUF_HEAPS_SYSTEM_CC_SHARED Kconfig

  udmabuf:
   - revert hugetlb support
   - fix error with CONFIG_DMA_API_DEBUG

  dma-fence:
   - fix tracepoints lifetime
   - remove unused signal on any support

  ras:
   - add clear error counter netlink command to drm ras

  gpusvm:
   - reject VMAs with VM_IO or VM_PFNMAP when creating SVM ranges
   - use IOVA allocations

  pagemap:
   - use IOVA allocations

  panels:
   - update to use ref counts
   - add support for CSW PNB601LS1-2, LGD LP116WHA-SPB1
   - add support for waveshare panels
   - CMN N116BCN-EA1, CMN N140HCA-EEK, IVO M140NWFQ R5,
   - IVO, R140NWFW R0, BOE NT140*, BOE NV133FHM-N4F,
   - AUO B140*, AUO B133HAN06.6 and AUO B116XTN02.3 eDP panels
   - Surface Pro 12 Panel

  xe:
   - add CRI PCI-IDs
   - debugfs add multi-lrc info
   - engine init cleanup
   - PF fair scheduling auto provisioning
   - system controller support for CRI/Xe3p
   - PXP state machine fixes
   - Reset/wedge/unload corner case fixes
   - Wedge path memory allocation fixes
   - PAT type cleanups
   - Reject unsafe PAT for CPU cached memory
   - OA improvements for CRI device memory
   - kernel doc syntax in xe headers
   - xe_drm.h documentation fixes
   - include guard cleanups
   - VF CCS memory pool
   - i915/xe step unification
   - Xe3p GT tuning fixes
   - forcewake cleanup in GT and GuC
   - admin-only PF mode
   - enable hwmon energy attributes for CRI
   - enable GT_MI_USER_INTERRUPT
   - refactor emit functions
   - oa workarounds
   - multi_queue: allow QUEUE_TIMESTAMP register
   - convert stolen memory to ttm range manager
   - use xe2 style blitter as a feature flag
   - make drm_driver const
   - add/use IRQ page to HW engine definition
   - fix oops when display disabled

  i915:
   - enable PIPEDMC_ERROR interrupt
   - more common display code refactoring
   - restructure DP/HDMI sink format handling
   - eliminate FB usage from lowlevel pinning code
   - panel replay bw optimization
   - integrate sharpness filter into the scaler
   - new fb_pin abstraction for xe/i915 fb transparent handling
   - skip inactive MST connectors on HDCP
   - start switching to display specific registers
   - use polling when irq unavailable
   - Adaptive-sync SDP prep

  amdgpu:
   - use drm_display_info for AMD VSDB data
   - Initial HDMI 2.1 FRL support
   - Initial DCN 4.2.1 support
   - GART fixes for non-4k pages
   - GC 11.5.6/SDMA 6.4.0/and other new IPs
   - GFX9/DCE6/Hawaii/SDMA4/GART/Userq fixes
   - Finish support for using multiple SDMA queues for TTM operations
   - SWSMU updates
   - GC 12.1 updates
   - SMU 15.0.8 updates
   - DCN 4.2 updates
   - DC type conversion fixes
   - Enable DC power module
   - Replay/PSR updates
   - SMU 13.x updates
   - Compute queue quantum MQD updates
   - ASPM fix
   - Align VKMS with common implementation
   - DC analog support fixes
   - UVD 3 fixes
   - TCC harvesting fixes for SI
   - GC 11 APU module reload fix
   - NBIO 6.3.2 support
   - IH 7.1 updates
   - DC cursor fixes
   - VCN/JPEG user fence fixes
   - DC support for connectors without DDC
   - Prefer ROM BAR for default VGA device
   - DC bandwidth fixes
   - Add PTL support for profiler
   - Introduce dc_plane_cm and migrate surface update color path
   - Add FRL registers for HDMI 2.1
   - Restructure VM state machine
   - Auxless ALPM support
   - GEM_OP locking/warning fixes
   - switch to system_dfl_wq

  amdkfd:
   - GPUVM TLB flush fix
   - Hotplug fix
   - Boundary check fixes
   - SVM fixes
   - CRIU fixes
   - add profiler API
   - MES 12.1 updates

  msm:
   - core:
     - fix shrinker documentation
     - IFPC enabled for gen8
     - PERFCNTR_CONFIG ioctl support
   - GPU:
     - reworked UBWC handling
     - a810 support
   - MDSS:
     - add support for Milos platform
     - reworked UBWC handling
   - DisplayPort:
     - reworked HPD handling as prep for MST
   - DPU:
     - Milos platform support
     - reworked UBWC handling
   - DSI:
     - Milos platform support

  nova:
   - Hopper/Blackwell enablement (GH100/GB100/GB202)
     - FSP support
     - 32-bit firmware support
     - HAL functions
   - refactor GSP boot/unload
   - GA100 support
   - VBIOS hardening/refactoring
   - Adopt higher order lifetime types

  tyr:
   - define register blocks
   - add shmem backed GEM objects
   - adopt higher order lifetime types
   - move clock cleanup into Drop

  radeon:
   - Hawaii SMU fixes
   - CS parser fix
   - use struct drm_edid instead of edid

  amdxdna:
   - export per-client BO memory via fdinfo
   - AIE4 device support
   - support medium/lower power modes
   - expandable device heap support
   - revert read-only user-pointer BO mappings

  ivpu:
   - support frequency limiting

  panthor:
   - enable GEM shrinker support
   - add eviction and reclaim info to fdinfo

  v3d:
   - enable runtime PM

  mgag200:
   - support XRGB1555 + C8

  ast:
   - support XRGB1555 + C8
   - use constants for lots of registers
   - fix register handling

  imagination:
   - fence handling refactoring

  nouveau:
   - fix sched double call
   - expose VBIOS on GSP-RM systems
   - add GA100 support

  virtio:
   - add VIRTIO_GPU_F_BLOB_ALIGNMENT flag
   - add deferred mapping support

  gud:
   - add RCade Display Adapter

  hibmc:
   - fix no connectors usage

  mediatek:
   - hdmi: convert error handling
   - simplify mtk_crtc allocation

  exynos:
   - move fbdev emulation to drm client buffers
   - use drm format helpers for geometry/size
   - adopt core DMA tracking
   - fix framebuffer offset handling

  renesas:
   - add RZ/T2H SOC support

  versilicon:
   - add cursor plane support

  tegra:
   - use drm client for framebuffer"

* tag 'drm-next-2026-06-17' of https://gitlab.freedesktop.org/drm/kernel: (1731 commits)
  dma-buf: move system_cc_shared heap under separate Kconfig
  accel/amdxdna: Clear sva pointer after unbind
  agp/amd64: Fix broken error propagation in agp_amd64_probe()
  accel/amdxdna: Require carveout when PASID and force_iova are disabled
  drm/amdkfd: always resume_all after suspend_all
  drm/amdgpu/gfx: move fault and EOP IRQ get/put to hw_init/hw_fini
  drm/amd/display: Consult MCCS FreeSync cap only if requested & supported
  drm/amd/pm: Use strscpy in profile mode parsing
  drm/amdkfd: Fix infinite loop parsing CRAT with zero subtype length
  drm/amdkfd: fix sysfs topology prop length on buffer truncation
  drm/amdgpu: drop retry loop in amdgpu_hmm_range_get_pages
  drm/amd/pm: bound OD parameter parsing to stack array size
  drm/amd/pm: Stop pp_od_clk_voltage emit at PAGE_SIZE
  drm/amdkfd: Unwind debug trap enable on copy_to_user failure
  drm/amdgpu: validate the mes firmware version for gfx12.1
  drm/amdgpu: validate the mes firmware version for gfx12
  drm/amdgpu: compare MES firmware version ucode for gfx11
  drm/amdkfd: Add bounds check for AMDKFD_IOC_WAIT_EVENTS
  drm/amdgpu: restart the CS if some parts of the VM are still invalidated
  drm/amd/display: use unsigned types for local pipe and REG_GET counters
  ...
This commit is contained in:
Linus Torvalds
2026-06-17 10:21:00 +01:00
1976 changed files with 245130 additions and 30005 deletions

View File

@@ -0,0 +1,30 @@
What: /sys/bus/pci/drivers/intel_vpu/.../sched_mode
Date: October 2024
KernelVersion: 6.12
Contact: dri-devel@lists.freedesktop.org
Description: Current NPU scheduling mode. Returns one of the following strings:
- "HW" - Hardware Scheduler mode
- "OS" - Operating System Scheduler mode
Read-only.
Deprecated since the "OS" scheduling mode is not usable
and will be removed from future versions of the driver.
Will be removed in 2027
What: /sys/bus/pci/drivers/intel_vpu/.../npu_max_frequency_mhz
Date: April 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq.
Shows maximum frequency in MHz of the NPU's data processing unit.
Read-only.
Will be removed in 2027
What: /sys/bus/pci/drivers/intel_vpu/.../npu_current_frequency_mhz
Date: April 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq.
Shows current frequency in MHz of the NPU's data processing unit.
The value is read only when the device is active; otherwise it returns 0.
Read-only.
Will be removed in 2027

View File

@@ -0,0 +1,65 @@
What: /sys/bus/pci/drivers/intel_vpu/.../npu_busy_time_us
Date: May 2024
KernelVersion: 6.11
Contact: dri-devel@lists.freedesktop.org
Description: Time in microseconds that the device spent executing jobs. The time is
counted when and only when there are jobs submitted to firmware. This time
can be used to measure the utilization of NPU, either by calculating the
difference between two timepoints or monitoring utilization percentage by
reading periodically. Recommended read period is 1 second to avoid impact
on job submission performance. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../npu_memory_utilization
Date: Jan 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Current NPU memory utilization in bytes. Reports the total size of all
resident buffer objects allocated for NPU use. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_min_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Minimum frequency in MHz supported by the NPU hardware. This is a
hardware capability and cannot be changed. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_efficient_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Most efficient operating frequency in MHz for the NPU. This represents
the frequency at which the NPU operates most efficiently in terms of power
and performance. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Maximum frequency in MHz supported by the NPU hardware. This is a
hardware capability and cannot be changed. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Current operating frequency in MHz of the NPU. The value is valid only
when the device is active; returns 0 when idle. The actual frequency may
be lower than the requested range due to power or thermal constraints.
Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_min_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Configured minimum operating frequency in MHz (50XX devices and newer).
Values written are clamped to hardware limits (hw_min_freq to hw_max_freq).
If set_min_freq exceeds set_max_freq, the driver clamps set_min_freq to
set_max_freq when selecting the operating frequency. Read-write.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_max_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Configured maximum operating frequency in MHz (50XX devices and newer).
Values written are clamped to hardware limits (hw_min_freq to hw_max_freq).
Read-write.

View File

@@ -270,6 +270,31 @@ MERT can report various kinds of telemetry information like the following:
* Deep Sleep counter
* etc.
.. _amdxdna-usage-stats:
Amdxdna DRM client usage stats implementation
=============================================
The amdxdna driver implements the DRM client usage stats specification as
documented in :ref:`drm-client-usage-stats`.
Example of the output showing the implemented key value pairs:
::
pos: 0
flags: 0100002
mnt_id: 29
ino: 939
drm-driver: amdxdna_accel_driver
drm-client-id: 3219
drm-pdev: 0000:c5:00.1
amdxdna_accel_driver-heap-alloc: 60 KiB
amdxdna_accel_driver-internal-alloc: 67588 KiB
amdxdna_accel_driver-external-alloc: 0
drm-total-memory: 67632 KiB
drm-shared-memory: 0
References
==========

View File

@@ -36,18 +36,56 @@ properties:
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description:
Primary MIPI DSI port-1 for MIPI input or
LVDS port-1 for LVDS input or DPI input.
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
description: array of physical DSI data lane indexes.
minItems: 1
items:
- const: 1
- const: 2
- const: 3
- const: 4
required:
- data-lanes
port@1:
$ref: /schemas/graph.yaml#/properties/port
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description:
Additional MIPI port-2 for MIPI input or LVDS port-2
for LVDS input. Used in combination with primary
port-1 to drive higher resolution displays
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
description: array of physical DSI data lane indexes.
minItems: 1
items:
- const: 1
- const: 2
- const: 3
- const: 4
required:
- data-lanes
port@2:
$ref: /schemas/graph.yaml#/properties/port
description:
@@ -99,6 +137,7 @@ examples:
reg = <0>;
endpoint {
data-lanes = <1 2 3 4>;
remote-endpoint = <&dsi0_out>;
};
};

View File

@@ -34,6 +34,7 @@ properties:
- items:
- enum:
- doestek,dtc34lm85am # For the Doestek DTC34LM85AM Flat Panel Display (FPD) Transmitter
- idt,v103 # For the Triple 10-BIT LVDS Transmitter
- onnn,fin3385 # OnSemi FIN3385
- ti,ds90c185 # For the TI DS90C185 FPD-Link Serializer
- ti,ds90c187 # For the TI DS90C187 FPD-Link Serializer

View File

@@ -30,6 +30,7 @@ properties:
- algoltek,ag6311
- asl-tek,cs5263
- dumb-vga-dac
- mstar,tsumu88adt3-lf-1
- parade,ps185hdm
- radxa,ra620
- realtek,rtd2171

View File

@@ -10,11 +10,14 @@ maintainers:
- Joseph Guo <qijian.guo@nxp.com>
description:
Waveshare bridge board is part of Waveshare panel which converts DSI to DPI.
Waveshare bridge board is part of Waveshare panel which converts DSI to DPI
or LVDS.
properties:
compatible:
const: waveshare,dsi2dpi
enum:
- waveshare,dsi2dpi
- waveshare,dsi2lvds
reg:
maxItems: 1
@@ -53,7 +56,7 @@ properties:
port@1:
$ref: /schemas/graph.yaml#/properties/port
description:
Video port for MIPI DPI output panel.
Video port for MIPI DPI or LVDS output to the panel.
required:
- port@0

View File

@@ -16,6 +16,7 @@ properties:
- enum:
- qcom,apq8064-dsi-ctrl
- qcom,kaanapali-dsi-ctrl
- qcom,milos-dsi-ctrl
- qcom,msm8226-dsi-ctrl
- qcom,msm8916-dsi-ctrl
- qcom,msm8953-dsi-ctrl
@@ -339,6 +340,7 @@ allOf:
compatible:
contains:
enum:
- qcom,milos-dsi-ctrl
- qcom,msm8998-dsi-ctrl
- qcom,sa8775p-dsi-ctrl
- qcom,sar2130p-dsi-ctrl

View File

@@ -300,6 +300,36 @@ allOf:
required:
- qcom,qmp
- if:
properties:
compatible:
contains:
const: qcom,adreno-gmu-810.0
then:
properties:
reg:
items:
- description: Core GMU registers
reg-names:
items:
- const: gmu
clocks:
items:
- description: GPU AHB clock
- description: GMU clock
- description: GPU CX clock
- description: GPU AXI clock
- description: GPU MEMNOC clock
- description: GMU HUB clock
clock-names:
items:
- const: ahb
- const: gmu
- const: cxo
- const: axi
- const: memnoc
- const: hub
- if:
properties:
compatible:

View File

@@ -411,6 +411,23 @@ allOf:
- clocks
- clock-names
- if:
properties:
compatible:
contains:
enum:
- qcom,adreno-44010000
- qcom,adreno-44070001
then:
properties:
reg:
minItems: 2
maxItems: 2
reg-names:
minItems: 2
maxItems: 2
- if:
properties:
compatible:
@@ -434,6 +451,8 @@ allOf:
- qcom,adreno-43050a01
- qcom,adreno-43050c01
- qcom,adreno-43051401
- qcom,adreno-44010000
- qcom,adreno-44070001
then: # Starting with A6xx, the clocks are usually defined in the GMU node
properties:

View File

@@ -0,0 +1,286 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,milos-mdss.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Milos Display MDSS
maintainers:
- Luca Weiss <luca.weiss@fairphone.com>
description:
Milos MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
DPU display controller, DSI and DP interfaces etc.
$ref: /schemas/display/msm/mdss-common.yaml#
properties:
compatible:
const: qcom,milos-mdss
clocks:
items:
- description: Display AHB
- description: Display hf AXI
- description: Display core
iommus:
maxItems: 1
interconnects:
items:
- description: Interconnect path from mdp0 port to the data bus
- description: Interconnect path from CPU to the reg bus
interconnect-names:
items:
- const: mdp0-mem
- const: cpu-cfg
patternProperties:
"^display-controller@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
const: qcom,milos-dpu
"^displayport-controller@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
const: qcom,milos-dp
"^dsi@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
contains:
const: qcom,milos-dsi-ctrl
"^phy@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
const: qcom,milos-dsi-phy-4nm
required:
- compatible
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,dsi-phy-28nm.h>
#include <dt-bindings/clock/qcom,milos-dispcc.h>
#include <dt-bindings/clock/qcom,milos-gcc.h>
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/interconnect/qcom,icc.h>
#include <dt-bindings/interconnect/qcom,milos-rpmh.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
display-subsystem@ae00000 {
compatible = "qcom,milos-mdss";
reg = <0x0ae00000 0x1000>;
reg-names = "mdss";
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
<&gcc GCC_DISP_HF_AXI_CLK>,
<&dispcc DISP_CC_MDSS_MDP_CLK>;
resets = <&dispcc DISP_CC_MDSS_CORE_BCR>;
interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
<&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
&cnoc_main SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
interconnect-names = "mdp0-mem",
"cpu-cfg";
power-domains = <&dispcc DISP_CC_MDSS_CORE_GDSC>;
iommus = <&apps_smmu 0x1c00 0x2>;
interrupt-controller;
#interrupt-cells = <1>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
display-controller@ae01000 {
compatible = "qcom,milos-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x3000>;
reg-names = "mdp",
"vbif";
interrupts-extended = <&mdss 0>;
clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
<&dispcc DISP_CC_MDSS_AHB_CLK>,
<&dispcc DISP_CC_MDSS_MDP_LUT_CLK>,
<&dispcc DISP_CC_MDSS_MDP_CLK>,
<&dispcc DISP_CC_MDSS_VSYNC_CLK>;
clock-names = "nrt_bus",
"iface",
"lut",
"core",
"vsync";
assigned-clocks = <&dispcc DISP_CC_MDSS_VSYNC_CLK>;
assigned-clock-rates = <19200000>;
operating-points-v2 = <&mdp_opp_table>;
power-domains = <&rpmhpd RPMHPD_CX>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dpu_intf1_out: endpoint {
remote-endpoint = <&mdss_dsi0_in>;
};
};
};
mdp_opp_table: opp-table {
compatible = "operating-points-v2";
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-342000000 {
opp-hz = /bits/ 64 <342000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-402000000 {
opp-hz = /bits/ 64 <402000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-535000000 {
opp-hz = /bits/ 64 <535000000>;
required-opps = <&rpmhpd_opp_nom>;
};
opp-600000000 {
opp-hz = /bits/ 64 <600000000>;
required-opps = <&rpmhpd_opp_nom_l1>;
};
opp-630000000 {
opp-hz = /bits/ 64 <630000000>;
required-opps = <&rpmhpd_opp_turbo>;
};
};
};
dsi@ae94000 {
compatible = "qcom,milos-dsi-ctrl", "qcom,mdss-dsi-ctrl";
reg = <0x0ae94000 0x1000>;
reg-names = "dsi_ctrl";
interrupts-extended = <&mdss 4>;
clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK>,
<&dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>,
<&dispcc DISP_CC_MDSS_PCLK0_CLK>,
<&dispcc DISP_CC_MDSS_ESC0_CLK>,
<&dispcc DISP_CC_MDSS_AHB_CLK>,
<&gcc GCC_DISP_HF_AXI_CLK>;
clock-names = "byte",
"byte_intf",
"pixel",
"core",
"iface",
"bus";
assigned-clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
<&dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
assigned-clock-parents = <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>,
<&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>;
operating-points-v2 = <&mdss_dsi_opp_table>;
power-domains = <&rpmhpd RPMHPD_CX>;
phys = <&mdss_dsi0_phy>;
phy-names = "dsi";
#address-cells = <1>;
#size-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mdss_dsi0_in: endpoint {
remote-endpoint = <&dpu_intf1_out>;
};
};
port@1 {
reg = <1>;
mdss_dsi0_out: endpoint {
};
};
};
mdss_dsi_opp_table: opp-table {
compatible = "operating-points-v2";
opp-187500000 {
opp-hz = /bits/ 64 <187500000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-300000000 {
opp-hz = /bits/ 64 <300000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-358000000 {
opp-hz = /bits/ 64 <358000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
};
};
mdss_dsi0_phy: phy@ae95000 {
compatible = "qcom,milos-dsi-phy-4nm";
reg = <0x0ae95000 0x200>,
<0x0ae95200 0x300>,
<0x0ae95500 0x400>;
reg-names = "dsi_phy",
"dsi_phy_lane",
"dsi_pll";
clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
<&rpmhcc RPMH_CXO_CLK>;
clock-names = "iface",
"ref";
#clock-cells = <1>;
#phy-cells = <0>;
};
};
...

View File

@@ -18,6 +18,7 @@ properties:
- qcom,eliza-dpu
- qcom,glymur-dpu
- qcom,kaanapali-dpu
- qcom,milos-dpu
- qcom,sa8775p-dpu
- qcom,sm8650-dpu
- qcom,sm8750-dpu

View File

@@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/chipwealth,ch13726a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Chip Wealth Technology CH13726A AMOLED driver
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
Chip Wealth Technology CH13726A is a single-chip solution
for AMOLED connected using a MIPI-DSI video interface.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- const: ayntec,thor-panel-bottom
- const: chipwealth,ch13726a
reg:
maxItems: 1
description: DSI virtual channel
vdd-supply: true
vddio-supply: true
vdd1v2-supply: true
avdd-supply: true
port: true
reset-gpios: true
rotation: true
required:
- compatible
- reg
- vdd-supply
- vddio-supply
- vdd1v2-supply
- avdd-supply
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "ayntec,thor-panel-bottom", "chipwealth,ch13726a";
reg = <0>;
vdd1v2-supply = <&vreg_l11b_1p2>;
vddio-supply = <&vdd_disp_1v8>;
vdd-supply = <&vreg_l13b_3p0>;
avdd-supply = <&vdd_disp2_2v8>;
reset-gpios = <&tlmm 133 GPIO_ACTIVE_HIGH>;
};
};
...

View File

@@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/focaltech,ota7290b.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Focaltech OTA7290B DSI panels
maintainers:
- Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
const: waveshare,8.8-dsi-touch-a
reg:
maxItems: 1
vdd-supply:
description: supply regulator for VDD, usually 3.3V
vdda-supply:
description: supply regulator for VDDA, 7-10V
vcc-supply:
description: supply regulator for VCCIO, usually 1.5V
reset-gpios: true
backlight: true
rotation: true
port: true
required:
- compatible
- reg
- vdd-supply
- vcc-supply
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "waveshare,8.8-dsi-touch-a";
reg = <0>;
vdd-supply = <&vdd>;
vcc-supply = <&vccio>;
reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
backlight = <&backlight>;
port {
endpoint {
remote-endpoint = <&mipi_out_panel>;
};
};
};
};
...

View File

@@ -30,6 +30,8 @@ properties:
- starry,2082109qfh040022-50e
# STARRY himax83102-j02 10.51" WUXGA TFT LCD panel
- starry,himax83102-j02
# Waveshare 12.3-DSI-TOUCH-A panel
- waveshare,12.3-dsi-touch-a
- const: himax,hx83102
reg:

View File

@@ -23,6 +23,8 @@ properties:
- hannstar,hsd060bhw4
- microchip,ac40t08a-mipi-panel
- powkiddy,x55-panel
- waveshare,5.0-dsi-touch-a
- waveshare,5.5-dsi-touch-a
- const: himax,hx8394
- items:
- enum:

View File

@@ -24,6 +24,7 @@ properties:
- raspberrypi,dsi-7inch
- startek,kd050hdfia020
- tdo,tl050hdv35
- waveshare,7.0-dsi-touch-a
- wanchanglong,w552946aaa
- wanchanglong,w552946aba
- const: ilitek,ili9881c
@@ -34,6 +35,7 @@ properties:
backlight: true
port: true
power-supply: true
iovcc-supply: true
reset-gpios: true
rotation: true

View File

@@ -24,6 +24,12 @@ properties:
- radxa,display-10hd-ad001
- radxa,display-8hd-ad002
- taiguanck,xti05101-01a
- waveshare,3.4-dsi-touch-c
- waveshare,4.0-dsi-touch-c
- waveshare,8.0-dsi-touch-a
- waveshare,9.0-dsi-touch-b
- waveshare,10.1-dsi-touch-a
- waveshare,10.1-dsi-touch-b
- const: jadard,jd9365da-h3
reg:

View File

@@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/novatek,nt35532.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Novatek NT35532-based DSI display panels
maintainers:
- Cristian Cozzolino <cristian_ci@protonmail.com>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- flipkart,rimob-panel-nt35532-cs
- const: novatek,nt35532
reg:
maxItems: 1
backlight: true
reset-gpios: true
avdd-supply:
description: positive boost supply regulator
avee-supply:
description: negative boost supply regulator
vci-supply:
description: regulator that supplies the analog voltage
vddam-supply:
description: power supply for MIPI interface
vddi-supply:
description: regulator that supplies the I/O voltage
port: true
required:
- compatible
- reg
- reset-gpios
- vddi-supply
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "flipkart,rimob-panel-nt35532-cs", "novatek,nt35532";
reg = <0>;
backlight = <&pmi8950_wled>;
reset-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>;
avdd-supply = <&lab>;
avee-supply = <&ibb>;
vci-supply = <&pm8953_l17>;
vddi-supply = <&pm8953_l6>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
...

View File

@@ -58,6 +58,10 @@ properties:
- hydis,hv070wx2-1e0
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
- jenson,bl-jt60050-01a
# Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel
- riverdi,rvt101hvlnwc00
# Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel
- riverdi,rvt70hslnwca0
# Samsung LTN070NL01 7.0" WSVGA (1024x600) TFT LCD LVDS panel
- samsung,ltn070nl01
# Samsung LTN101AL03 10.1" WXGA (800x1280) TFT LCD LVDS panel

View File

@@ -63,6 +63,8 @@ properties:
- samsung,s6e3fa7-ams559nk06
# Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel
- tdo,tl070wsh30
# Team Source Display Technology 7" TST070WSBE-196C 1024x600 TFT LCD panel
- team-source-display,tst070wsbe-196c
reg:
maxItems: 1

View File

@@ -31,6 +31,8 @@ properties:
# Ampire AM-1280800N3TZQW-T00H 10.1" WQVGA TFT LCD panel
- ampire,am-1280800n3tzqw-t00h
# Ampire AM-1280800W8TZQW-T00H 10.1" WXGA TFT LCD panel
- ampire,am-1280800w8tzqw-t00h
# Ampire AM-480272H3TMQW-T01H 4.3" WQVGA TFT LCD panel
- ampire,am-480272h3tmqw-t01h
# Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel
@@ -97,6 +99,8 @@ properties:
- dataimage,fg1001l0dsswmg01
# DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface.
- dataimage,scf0700c48ggu18
# Displaytech DT050BTFT-PTS 5.0" 800x480 TFT LCD Panel
- displaytech,dt050btft-pts
# DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel
- dlc,dlc1010gig
# Emerging Display Technology Corp. 3.5" QVGA TFT LCD panel
@@ -232,6 +236,8 @@ properties:
- nec,nl12880bc20-05
# NEC LCD Technologies,Ltd. WQVGA TFT LCD panel
- nec,nl4827hc19-05b
# NEC LCD Technologies,Ltd. VGA TFT LCD panel
- nec,nl6448bc33-70c
# Netron-DY E231732 7.0" WSVGA TFT LCD panel
- netron-dy,e231732
# Newhaven Display International 480 x 272 TFT LCD panel
@@ -304,6 +310,8 @@ properties:
- shelly,sca07010-bfn-lnn
# Starry KR070PE2T 7" WVGA TFT LCD panel
- starry,kr070pe2t
# Startek KD070HDFLD092 7" WSVGA TFT LCD panel
- startek,kd070hdfld092
# Startek KD070WVFPA043-C069A 7" TFT LCD panel
- startek,kd070wvfpa
# Team Source Display Technology TST043015CMHX 4.3" WQVGA TFT LCD panel
@@ -341,10 +349,38 @@ properties:
- vivax,tpc9150-panel
# VXT 800x480 color TFT LCD panel
- vxt,vl050-8048nt-c01
# Waveshare 10.1" WXGA (1280x800) LCD panel
- waveshare,10.1inch-c-panel
# Waveshare 11.9" (320x1480) LCD panel
- waveshare,11.9inch-panel
# Waveshare 13.3" FHD (1920x1080) LCD panel
- waveshare,13.3inch-panel
# Waveshare 2.8" VGA (480x640) LCD panel
- waveshare,2.8inch-panel
# Waveshare 3.4" (800x800) LCD panel
- waveshare,3.4inch-c-panel
# Waveshare 4.0" WVGA (480x800) LCD panel
- waveshare,4.0inch-panel
# Waveshare 4.0" (720x720) LCD panel
- waveshare,4.0inch-c-panel
# Waveshare 5.0" WSVGA (1024x600) LCD panel
- waveshare,5.0inch-c-panel
# Waveshare 5.0" HD 720p (720x1280) LCD panel
- waveshare,5.0inch-d-panel
# Waveshare 6.25" (720x1560) LCD panel
- waveshare,6.25inch-panel
# Waveshare 7.0" WSVGA (1024x600) LCD panel
- waveshare,7.0inch-c-panel
# Waveshare 7.0" WXGA (1280x800) LCD panel
- waveshare,7.0inch-e-panel
# Waveshare 7.0" HD 720p (720x1280) LCD panel
- waveshare,7.0inch-h-panel
# Waveshare 7.9" (400x1280) LCD panel
- waveshare,7.9inch-panel
# Waveshare 8.0" WXGA (1280x800) LCD panel
- waveshare,8.0inch-c-panel
# Waveshare 8.8" (480x1920) LCD panel
- waveshare,8.8inch-panel
# Winstar Display Corporation 3.5" QVGA (320x240) TFT LCD panel
- winstar,wf35ltiacd
# Yes Optoelectronics YTC700TLAG-05-201C 7" TFT LCD panel

View File

@@ -21,6 +21,7 @@ properties:
- renesas,r9a07g043u-du # RZ/G2UL
- renesas,r9a07g044-du # RZ/G2{L,LC}
- renesas,r9a09g057-du # RZ/V2H(P)
- renesas,r9a09g077-du # RZ/T2H
- items:
- enum:
- renesas,r9a07g054-du # RZ/V2L
@@ -28,6 +29,9 @@ properties:
- items:
- const: renesas,r9a09g056-du # RZ/V2N
- const: renesas,r9a09g057-du # RZ/V2H(P) fallback
- items:
- const: renesas,r9a09g087-du # RZ/N2H
- const: renesas,r9a09g077-du # RZ/T2H fallback
reg:
maxItems: 1
@@ -83,7 +87,6 @@ required:
- interrupts
- clocks
- clock-names
- resets
- power-domains
- ports
- renesas,vsps
@@ -95,13 +98,16 @@ allOf:
properties:
compatible:
contains:
const: renesas,r9a07g043u-du
enum:
- renesas,r9a07g043u-du
- renesas,r9a09g077-du
then:
properties:
ports:
properties:
port@0:
description: DPI
port@1: false
required:
- port@0
@@ -137,6 +143,17 @@ allOf:
required:
- port@0
- if:
properties:
compatible:
contains:
const: renesas,r9a09g077-du
then:
properties:
resets: false
else:
required:
- resets
examples:
# RZ/G2L DU

View File

@@ -26,6 +26,7 @@ properties:
- realtek,rtd1619-mali
- renesas,r9a07g044-mali
- renesas,r9a07g054-mali
- renesas,r9a08g046-mali
- renesas,r9a09g047-mali
- renesas,r9a09g056-mali
- renesas,r9a09g057-mali
@@ -150,6 +151,7 @@ allOf:
enum:
- renesas,r9a07g044-mali
- renesas,r9a07g054-mali
- renesas,r9a08g046-mali
- renesas,r9a09g047-mali
- renesas,r9a09g056-mali
- renesas,r9a09g057-mali

View File

@@ -20,6 +20,7 @@ properties:
- qcom,dsi-phy-7nm
- qcom,dsi-phy-7nm-8150
- qcom,kaanapali-dsi-phy-3nm
- qcom,milos-dsi-phy-4nm
- qcom,sa8775p-dsi-phy-5nm
- qcom,sar2130p-dsi-phy-5nm
- qcom,sc7280-dsi-phy-7nm

View File

@@ -437,6 +437,8 @@ patternProperties:
description: Diodes, Inc.
"^dioo,.*":
description: Dioo Microcircuit Co., Ltd
"^displaytech,.*":
description: Displaytech Ltd.
"^djn,.*":
description: Shenzhen DJN Optronics Technology Co., Ltd
"^dlc,.*":

View File

@@ -233,8 +233,15 @@ we have a dedicated glossary for Display Core at
TC
Texture Cache
TCC
Texture Cache per Channel - L2 cache attached to the memory channels.
May be used when shader cores are accessing memory.
Despite "Texture" in the name, this is used by any kind of memory access.
TCCs may be mapped to TCPs, depending on the architecture.
TCP (AMDGPU)
Texture Cache per Pipe. Even though the name "Texture" is part of this
Texture Cache per Pipe - L1 cache attached to each CU.
Even though the name "Texture" is part of this
acronym, the TCP represents the path to memory shaders; i.e., it is not
related to texture. The name is a leftover from older designs where shader
stages had different cache designs; it refers to the L1 cache in older

View File

@@ -23,3 +23,4 @@ Next (GCN), Radeon DNA (RDNA), and Compute DNA (CDNA) architectures.
debugfs
process-isolation
amdgpu-glossary
ptl

View File

@@ -0,0 +1,94 @@
=======================================
Peak Tops Limiter (PTL) sysfs Interface
=======================================
Overview
--------
The Peak Tops Limiter (PTL) sysfs interface enables users to control and
configure the PTL feature for each GPU individually. All PTL-related
sysfs files are located under `/sys/class/drm/cardX/device/ptl/`, where
`X` is the GPU index. Through these files, users can enable or disable
PTL, set preferred data formats, and query supported formats for each GPU.
PTL sysfs files
----------------
The following files are available under `/sys/class/drm/cardX/device/ptl/`:
- `ptl_enable`
- `ptl_format`
- `ptl_supported_formats`
PTL Enable/Disable
------------------
File: `ptl_enable`
Type: Read/Write (rw)
Read: Returns the current PTL status as a string: `enabled` if PTL
is active, or `disabled` if inactive.
Write:
- Write `1` or `enabled` to enable PTL
- Write `0` or `disabled` to disable PTL
Examples::
# Query PTL status
cat /sys/class/drm/card1/device/ptl/ptl_enable
# Output: enabled
# Enable PTL
sudo bash -c "echo 1 > /sys/class/drm/card1/device/ptl/ptl_enable"
# Disable PTL
sudo bash -c "echo 0 > /sys/class/drm/card1/device/ptl/ptl_enable"
PTL Format (Preferred Data Formats)
-----------------------------------
File: `ptl_format`
Type: Read/Write (rw)
Read: Returns the two preferred formats, e.g. `I8,F32`.
Write: Accepts two formats separated by a comma, e.g. `I8,F32`.
- Both formats must be supported and different.
- If an invalid format is provided (not supported, or both formats are the
same), the driver will return "write error: Invalid argument".
Examples::
# Query PTL formats
cat /sys/class/drm/card1/device/ptl/ptl_format
# Output: I8,F32
# Set PTL formats
sudo bash -c "echo I8,F32 > /sys/class/drm/card1/device/ptl/ptl_format"
Supported Formats
-----------------
File: `ptl_supported_formats`
Type: Read-only (r)
Read: Returns a comma-separated list of supported formats, e.g.
`I8,F16,BF16,F32,F64`.
Example::
# Check supported formats
cat /sys/class/drm/card1/device/ptl/ptl_supported_formats
# Output: I8,F16,BF16,F32,F64
Behavioral Notes
----------------
- PTL formats can only be set when PTL is enabled.
- If PTL is disabled, `ptl_format` returns `N/A`.
- Only two formats can be set at a time, and they must be from the supported set and different..
- All commands support per-GPU targeting.
- Root permission is required to enable/disable PTL or change formats.
- If the hardware does not support PTL, the PTL sysfs directory will not
be created.
Implementation
--------------
The PTL sysfs nodes are implemented in `drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c`.

View File

@@ -2,6 +2,8 @@
DRM Driver uAPI
===============
.. contents::
drm/i915 uAPI
=============

View File

@@ -8,6 +8,7 @@ GPU Driver Documentation
amdgpu/index
i915
imagination/index
intel-display/index
mcde
meson
nouveau

View File

@@ -18,6 +18,8 @@ event handling, memory management, output management, framebuffer
management, command submission & fencing, suspend/resume support, and
DMA services.
.. contents::
Driver Initialization
=====================

View File

@@ -33,6 +33,8 @@ There are a few areas these helpers can grouped into:
pipeline: Planes, handling rectangles for visibility checking and scissoring,
flip queues and assorted bits.
.. contents::
Modeset Helper Reference for Common Vtables
===========================================

View File

@@ -1,3 +1,6 @@
.. _drm-kms:
=========================
Kernel Mode Setting (KMS)
=========================
@@ -15,6 +18,8 @@ be setup by initializing the following fields.
- struct drm_mode_config_funcs \*funcs;
Mode setting functions.
.. contents::
Overview
========
@@ -206,11 +211,11 @@ Atomic Mode Setting
style=dashed
label="Free-standing state"
"drm_atomic_state" -> "duplicated drm_plane_state A"
"drm_atomic_state" -> "duplicated drm_plane_state B"
"drm_atomic_state" -> "duplicated drm_crtc_state"
"drm_atomic_state" -> "duplicated drm_connector_state"
"drm_atomic_state" -> "duplicated driver private state"
"drm_atomic_commit" -> "duplicated drm_plane_state A"
"drm_atomic_commit" -> "duplicated drm_plane_state B"
"drm_atomic_commit" -> "duplicated drm_crtc_state"
"drm_atomic_commit" -> "duplicated drm_connector_state"
"drm_atomic_commit" -> "duplicated driver private state"
}
subgraph cluster_current {
@@ -230,7 +235,7 @@ Atomic Mode Setting
"driver private object" -> "driver private state"
}
"drm_atomic_state" -> "drm_device" [label="atomic_commit"]
"drm_atomic_commit" -> "drm_device" [label="atomic_commit"]
"duplicated drm_plane_state A" -> "drm_device"[style=invis]
}
@@ -265,7 +270,7 @@ Taken all together there's two consequences for the atomic design:
drm_private_state<drm_private_state>`.
- An atomic update is assembled and validated as an entirely free-standing pile
of structures within the :c:type:`drm_atomic_state <drm_atomic_state>`
of structures within the :c:type:`drm_atomic_commit <drm_atomic_commit>`
container. Driver private state structures are also tracked in the same
structure; see the next chapter. Only when a state is committed is it applied
to the driver and modeset objects. This way rolling back an update boils down

View File

@@ -25,6 +25,8 @@ share it. GEM has simpler initialization and execution requirements than
TTM, but has no video RAM management capabilities and is thus limited to
UMA devices.
.. contents::
The Translation Table Manager (TTM)
===================================

View File

@@ -24,6 +24,8 @@ Key Goals:
nodes for different IP blocks, sub-blocks, or other logical subdivisions
as applicable.
.. contents::
Nodes
=====
@@ -52,6 +54,8 @@ User space tools can:
as a parameter.
* Query specific error counter values with the ``get-error-counter`` command, using both
``node-id`` and ``error-id`` as parameters.
* Clear specific error counters with the ``clear-error-counter`` command, using both
``node-id`` and ``error-id`` as parameters.
YAML-based Interface
--------------------
@@ -101,3 +105,9 @@ Example: Query an error counter for a given node
sudo ynl --family drm_ras --do get-error-counter --json '{"node-id":0, "error-id":1}'
{'error-id': 1, 'error-name': 'error_name1', 'error-value': 0}
Example: Clear an error counter for a given node
.. code-block:: bash
sudo ynl --family drm_ras --do clear-error-counter --json '{"node-id":0, "error-id":1}'
None

View File

@@ -16,6 +16,8 @@ management, and output management.
Cover generic ioctls and sysfs layout here. We only need high-level
info, since man pages should cover the rest.
.. contents::
libdrm Device Lookup
====================
@@ -118,6 +120,10 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs
for the same thing co-existing. If we add a few more complete mistakes into the
mix every year it would be entirely unmanageable.
The DRM subsystem has however no concern with independent closed-source
userspace implementations. To officialize that position, the DRM uAPI headers
are covered by the MIT license.
.. _drm_render_node:
Render nodes
@@ -761,4 +767,4 @@ Stable uAPI events
From ``drivers/gpu/drm/scheduler/gpu_scheduler_trace.h``
.. kernel-doc:: drivers/gpu/drm/scheduler/gpu_scheduler_trace.h
:doc: uAPI trace events
:doc: uAPI trace events

View File

@@ -16,6 +16,8 @@ output is split between common and driver specific parts. Having said that,
wherever possible effort should still be made to standardise as much as
possible.
.. contents::
File format specification
=========================
@@ -215,3 +217,4 @@ Driver specific implementations
* :ref:`panfrost-usage-stats`
* :ref:`panthor-usage-stats`
* :ref:`xe-usage-stats`
* :ref:`amdxdna-usage-stats`

View File

@@ -1,3 +1,6 @@
.. _drm/i915:
===========================
drm/i915 Intel GFX Driver
===========================
@@ -7,6 +10,9 @@ models) integrated GFX chipsets with both Intel display and rendering
blocks. This excludes a set of SoC platforms with an SGX rendering unit,
those have basic support through the gma500 drm driver.
The display, or :ref:`drm-kms`, support for drm/i915 is provided by
:ref:`drm/intel-display`, and shared with :ref:`drm/xe <drm/xe>`.
Core Driver Infrastructure
==========================
@@ -64,200 +70,6 @@ Workarounds
.. kernel-doc:: drivers/gpu/drm/i915/gt/intel_workarounds.c
:doc: Hardware workarounds
Display Hardware Handling
=========================
This section covers everything related to the display hardware including
the mode setting infrastructure, plane, sprite and cursor handling and
display, output probing and related topics.
Mode Setting Infrastructure
---------------------------
The i915 driver is thus far the only DRM driver which doesn't use the
common DRM helper code to implement mode setting sequences. Thus it has
its own tailor-made infrastructure for executing a display configuration
change.
Frontbuffer Tracking
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.c
:doc: frontbuffer tracking
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.h
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.c
:internal:
Display FIFO Underrun Reporting
-------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fifo_underrun.c
:doc: fifo underrun handling
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fifo_underrun.c
:internal:
Plane Configuration
-------------------
This section covers plane configuration and composition with the primary
plane, sprites, cursors and overlays. This includes the infrastructure
to do atomic vsync'ed updates of all this state and also tightly coupled
topics like watermark setup and computation, framebuffer compression and
panel self refresh.
Atomic Plane Helpers
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_plane.c
:doc: atomic plane helpers
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_plane.c
:internal:
Asynchronous Page Flip
----------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_display.c
:doc: asynchronous flip implementation
Output Probing
--------------
This section covers output probing and related infrastructure like the
hotplug interrupt storm detection and mitigation code. Note that the
i915 driver still uses most of the common DRM helper code for output
probing, so those sections fully apply.
Hotplug
-------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_hotplug.c
:doc: Hotplug
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_hotplug.c
:internal:
High Definition Audio
---------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_audio.c
:doc: High Definition Audio over HDMI and Display Port
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_audio.c
:internal:
.. kernel-doc:: include/drm/intel/i915_component.h
:internal:
Intel HDMI LPE Audio Support
----------------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_lpe_audio.c
:doc: LPE Audio integration for HDMI or DP playback
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_lpe_audio.c
:internal:
Panel Self Refresh PSR (PSR/SRD)
--------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_psr.c
:doc: Panel Self Refresh (PSR/SRD)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_psr.c
:internal:
Frame Buffer Compression (FBC)
------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fbc.c
:doc: Frame Buffer Compression (FBC)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fbc.c
:internal:
Display Refresh Rate Switching (DRRS)
-------------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_drrs.c
:doc: Display Refresh Rate Switching (DRRS)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_drrs.c
:internal:
DPIO
----
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpio_phy.c
:doc: DPIO
DMC Firmware Support
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc.c
:doc: DMC Firmware Support
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc.c
:internal:
DMC Flip Queue
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_flipq.c
:doc: DMC Flip Queue
DMC wakelock support
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc_wl.c
:doc: DMC wakelock support
Video BIOS Table (VBT)
----------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_bios.c
:doc: Video BIOS Table (VBT)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_bios.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_vbt_defs.h
:internal:
Display clocks
--------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_cdclk.c
:doc: CDCLK / RAWCLK
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_cdclk.c
:internal:
Display PLLs
------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpll_mgr.c
:doc: Display PLLs
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpll_mgr.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpll_mgr.h
:internal:
Display State Buffer
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dsb.c
:doc: DSB
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dsb.c
:internal:
GT Programming
==============
@@ -568,7 +380,7 @@ The HuC FW layout is the same as the GuC one, see `GuC Firmware Layout`_
DMC
---
See `DMC Firmware Support`_
See :ref:`drm/intel-display/dmc`.
Tracing
=======

View File

@@ -3,6 +3,7 @@ GPU Driver Developer's Guide
============================
.. toctree::
:maxdepth: 2
introduction
drm-internals

View File

@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Asynchronous Page Flip
======================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_display.c
:doc: asynchronous flip implementation

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Atomic Modeset Support
======================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_atomic.c
:doc: atomic modeset support
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_atomic.c
:internal:

View File

@@ -0,0 +1,23 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
High Definition Audio
=====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_audio.c
:doc: High Definition Audio over HDMI and Display Port
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_audio.c
:internal:
.. kernel-doc:: include/drm/intel/i915_component.h
:internal:
Intel HDMI LPE Audio Support
============================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_lpe_audio.c
:doc: LPE Audio integration for HDMI or DP playback
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_lpe_audio.c
:internal:

View File

@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Content Adaptive Sharpness Filter (CASF)
========================================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_casf.c
:doc: Content Adaptive Sharpness Filter (CASF)

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Display clocks
==============
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_cdclk.c
:doc: CDCLK / RAWCLK
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_cdclk.c
:internal:

View File

@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Common Primary Timing Generator (CMTG)
======================================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_cmtg.c
:doc: Common Primary Timing Generator (CMTG)

View File

@@ -0,0 +1,26 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
.. _drm/intel-display/dmc:
DMC Firmware Support
====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc.c
:doc: DMC Firmware Support
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc.c
:internal:
DMC Flip Queue
==============
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_flipq.c
:doc: DMC Flip Queue
DMC wakelock support
====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc_wl.c
:doc: DMC wakelock support

View File

@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
DPIO
====
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpio_phy.c
:doc: DPIO

View File

@@ -0,0 +1,14 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Display PLLs
============
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpll_mgr.c
:doc: Display PLLs
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpll_mgr.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dpll_mgr.h
:internal:

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Display Refresh Rate Switching (DRRS)
=====================================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_drrs.c
:doc: Display Refresh Rate Switching (DRRS)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_drrs.c
:internal:

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Display State Buffer
====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dsb.c
:doc: DSB
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dsb.c
:internal:

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Frame Buffer Compression (FBC)
==============================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fbc.c
:doc: Frame Buffer Compression (FBC)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fbc.c
:internal:

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Display FIFO Underrun Reporting
===============================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fifo_underrun.c
:doc: fifo underrun handling
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_fifo_underrun.c
:internal:

View File

@@ -0,0 +1,14 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Frontbuffer Tracking
====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.c
:doc: frontbuffer tracking
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.h
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.c
:internal:

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Hotplug
=======
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_hotplug.c
:doc: Hotplug
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_hotplug.c
:internal:

View File

@@ -0,0 +1,44 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
.. _drm/intel-display:
====================
Intel Display Driver
====================
The Intel display driver provides the display, or :ref:`drm-kms`, support for
both the :ref:`drm/xe <drm/xe>` and :ref:`drm/i915 <drm/i915>` Intel GPU
drivers.
The source code currently resides under ``drivers/gpu/drm/i915/display`` due to
historical reasons, and it's compiled separately into both drm/xe and drm/i915
kernel modules.
The drm/xe and drm/i915 drivers are the "core" or "parent" drivers for display,
as they initialize and own the drm device, and pass that on to the display
driver. The display driver isn't an independent driver in that sense.
.. toctree::
:maxdepth: 1
:caption: Detailed display topics
async-flip
atomic
audio
casf
cdclk
cmtg
dmc
dpio
dpll
drrs
dsb
fbc
fifo-underrun
frontbuffer
hotplug
plane
psr
snps-phy
vbt

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Atomic Plane Helpers
====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_plane.c
:doc: atomic plane helpers
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_plane.c
:internal:

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Panel Self Refresh PSR (PSR/SRD)
================================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_psr.c
:doc: Panel Self Refresh (PSR/SRD)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_psr.c
:internal:

View File

@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Synopsis PHY support
====================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_snps_phy.c
:doc: Synopsis PHY support

View File

@@ -0,0 +1,14 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
Video BIOS Table (VBT)
======================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_bios.c
:doc: Video BIOS Table (VBT)
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_bios.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_vbt_defs.h
:internal:

View File

@@ -16,6 +16,8 @@ found in current kernels.
[Insert diagram of typical DRM stack here]
.. contents::
Style Guidelines
================

View File

@@ -367,7 +367,7 @@ So, one KMS-Obj represents a sub-pipeline of komeda resources.
So, for komeda, we treat KMS crtc/plane/connector as users of pipeline and
component, and at any one time a pipeline/component only can be used by one
user. And pipeline/component will be treated as private object of DRM-KMS; the
state will be managed by drm_atomic_state as well.
state will be managed by drm_atomic_commit as well.
How to map plane to Layer(input) pipeline
-----------------------------------------
@@ -416,8 +416,8 @@ Add :c:type:`drm_private_obj` to :c:type:`komeda_component`, :c:type:`komeda_pip
...
}
Tracking component_state/pipeline_state by drm_atomic_state
-----------------------------------------------------------
Tracking component_state/pipeline_state by drm_atomic_commit
------------------------------------------------------------
Add :c:type:`drm_private_state` and user to :c:type:`komeda_component_state`,
:c:type:`komeda_pipeline_state`
@@ -454,7 +454,7 @@ similar, usually including the following steps:
put the data flow into next stage.
Setup 2: check user_state with component features and capabilities to see
if requirements can be met; if not, return fail.
Setup 3: get component_state from drm_atomic_state, and try set to set
Setup 3: get component_state from drm_atomic_commit, and try set to set
user to component; fail if component has been assigned to another
user already.
Setup 3: configure the component_state, like set its input component,

View File

@@ -46,12 +46,71 @@ region is only accessible to heavy-secure ucode.
are of type 0xE0 and can be identified as such. This could be subject to change
in future generations.
IFR Header
----------
On Kepler and later GPUs, the ROM begins with an Init-from-ROM (IFR) header
rather than a standard PCI ROM signature (0xAA55). The driver must parse the
IFR header to find where the PCI ROM images actually start.
Init-from-ROM (IFR) is a special GPU feature used for power management
on some Nvidia GPUs. It references data in the VBIOS for its operation,
but for drivers the important piece is a header that precedes the
VBIOS PCI Expansion ROM.
Most such GPUs do not need to parse the IFR header in order to find the
VBIOS, but the Nvidia GA100 is the exception. GA100 lacks a display engine,
so the PRAMIN method (which reads the VBIOS from VRAM via display hardware)
is unavailable, forcing the driver to read the ROM directly via PROM.
On other similar GPUs, either PRAMIN succeeds before PROM is tried, or the
IFR hardware has already applied the ROM offset so that PROM reads
transparently skip the IFR header.
The driver should first check for the standard 0xAA55 signature at offset 0.
If found, there is no IFR header and the PCI ROM images start at
offset 0. If not found, check for the IFR signature and parse the header to
determine the PCI ROM image offset.
Fixed Header Format
~~~~~~~~~~~~~~~~~~~
The IFR header begins with four 32-bit words at fixed offsets::
Offset Name Fields
------ ------- ------
0x00 FIXED0 bits 31:0 - Signature (must be 0x4947564E, ASCII "NVGI")
0x04 FIXED1 bit 31 - Reserved
bits 30:16 - FIXED_DATA_SIZE Fixed data size (offset to extended section)
bits 15:8 - VERSIONSW Software version
bits 7:0 - Reserved
0x08 FIXED2 bit 31 - Reserved
bits 30:20 - Reserved (zero)
bits 19:0 - TOTAL_DATA_SIZE Total data size
Finding the PCI ROM Image Offset
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The method to find this offset depends on `VERSIONSW`.
- **Version 1 and 2**: Read `FIXED_DATA_SIZE` from `FIXED1` to get the extended
section offset. The PCI ROM image is the 32-bit value at `FIXED_DATA_SIZE + 4`.
- **Version 3**: Read `TOTAL_DATA_SIZE` from `FIXED2`. The 32-bit value at that
offset is a flash status offset. Add 4096 to get the ROM directory offset,
`ROM_DIRECTORY_OFFSET`. The ROM directory must have signature 0x44524652
(ASCII "RFRD"). The PCI ROM image offset is the 32-bit value at
`ROM_DIRECTORY_OFFSET + 8`.
The PCI ROM image offset must be 4-byte aligned. All offsets are relative to the
start of ROM (BAR0 + 0x300000).
VBIOS ROM Layout
----------------
The VBIOS layout is roughly a series of concatenated images laid out as follows::
The VBIOS (PCI Expansion ROM) is a series of concatenated images laid out as
follows. On GPUs with an IFR header, this layout begins at the image offset
determined by parsing the IFR header. On older GPUs, it begins at offset 0::
+----------------------------------------------------------------------------+
| VBIOS (Starting at ROM_OFFSET: 0x300000) |
| VBIOS (Starting at ROM_OFFSET: 0x300000 + IFR image offset) |
+----------------------------------------------------------------------------+
| +-----------------------------------------------+ |
| | PciAt Image (Type 0x00) | |
@@ -173,7 +232,7 @@ Falcon data in the VBIOS which contains the PMU lookup table. This lookup table
used to find the required Falcon ucode based on an application ID.
The location of the PMU lookup table is found by scanning the BIT (`BIOS Information Table`_)
tokens for a token with the id `BIT_TOKEN_ID_FALCON_DATA` (0x70) which indicates the
tokens for a token with the Falcon data token id (0x70) which indicates the
offset of the same from the start of the VBIOS image. Unfortunately, the offset
does not account for the EFI image located between the PciAt and FwSec images.
The `vbios.rs` code compensates for this with appropriate arithmetic.

View File

@@ -18,23 +18,9 @@ host such documentation:
.. toctree::
gpusvm.rst
.. toctree::
i915_gem_lmem.rst
.. toctree::
i915_scheduler.rst
.. toctree::
i915_small_bar.rst
.. toctree::
i915_vm_bind.rst
.. toctree::
color_pipeline.rst
gpusvm
i915_gem_lmem
i915_scheduler
i915_small_bar
i915_vm_bind
color_pipeline

View File

@@ -152,29 +152,6 @@ Contact: Simona Vetter, respective driver maintainers
Level: Advanced
Rename drm_atomic_state
-----------------------
The KMS framework uses two slightly different definitions for the ``state``
concept. For a given object (plane, CRTC, encoder, etc., so
``drm_$OBJECT_state``), the state is the entire state of that object. However,
at the device level, ``drm_atomic_state`` refers to a state update for a
limited number of objects.
The state isn't the entire device state, but only the full state of some
objects in that device. This is confusing to newcomers, and
``drm_atomic_state`` should be renamed to something clearer like
``drm_atomic_commit``.
In addition to renaming the structure itself, it would also imply renaming some
related functions (``drm_atomic_state_alloc``, ``drm_atomic_state_get``,
``drm_atomic_state_put``, ``drm_atomic_state_init``,
``__drm_atomic_state_free``, etc.).
Contact: Maxime Ripard <mripard@kernel.org>
Level: Advanced
Fallout from atomic KMS
-----------------------

View File

@@ -1,5 +1,7 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
.. _drm/xe:
=======================
drm/xe Intel GFX Driver
=======================
@@ -8,6 +10,9 @@ The drm/xe driver supports some future GFX cards with rendering, display,
compute and media. Support for currently available platforms like TGL, ADL,
DG2, etc is provided to prototype the driver.
The display, or :ref:`drm-kms`, support for drm/xe is provided by
:ref:`drm/intel-display`, and shared with :ref:`drm/i915 <drm/i915>`.
.. toctree::
:titlesonly:
@@ -29,3 +34,4 @@ DG2, etc is provided to prototype the driver.
xe_device
xe-drm-usage-stats.rst
xe_configfs
xe_gt_stats

View File

@@ -7,10 +7,10 @@ Firmware
Firmware Layout
===============
.. kernel-doc:: drivers/gpu/drm/xe/xe_uc_fw_abi.h
.. kernel-doc:: drivers/gpu/drm/xe/abi/uc_fw_abi.h
:doc: CSS-based Firmware Layout
.. kernel-doc:: drivers/gpu/drm/xe/xe_uc_fw_abi.h
.. kernel-doc:: drivers/gpu/drm/xe/abi/uc_fw_abi.h
:doc: GSC-based Firmware Layout
Write Once Protected Content Memory (WOPCM) Layout

View File

@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
================
Xe GT Statistics
================
.. kernel-doc:: drivers/gpu/drm/xe/xe_gt_stats.c
:doc: Xe GT Statistics
.. kernel-doc:: drivers/gpu/drm/xe/xe_gt_stats_types.h
:internal:

View File

@@ -99,7 +99,7 @@ operations:
flags: [admin-perm]
do:
request:
attributes:
attributes: &id-attrs
- node-id
- error-id
reply:
@@ -113,3 +113,14 @@ operations:
- node-id
reply:
attributes: *errorinfo
-
name: clear-error-counter
doc: >-
Clear error counter for a given node.
The request includes the error-id and node-id of the
counter to be cleared.
attribute-set: error-counter-attrs
flags: [admin-perm]
do:
request:
attributes: *id-attrs

View File

@@ -7852,11 +7852,13 @@ F: drivers/soc/ti/smartreflex.c
F: include/linux/power/smartreflex.h
DRM ACCEL DRIVERS FOR INTEL VPU
M: Maciej Falkowski <maciej.falkowski@linux.intel.com>
M: Karol Wachowski <karol.wachowski@linux.intel.com>
M: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/ABI/obsolete/sysfs-driver-ivpu
F: Documentation/ABI/testing/sysfs-driver-ivpu
F: drivers/accel/ivpu/
F: include/uapi/drm/ivpu_accel.h
@@ -8165,6 +8167,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/novatek,nt35510.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt35510.c
DRM DRIVER FOR NOVATEK NT35532 PANELS
M: Cristian Cozzolino <cristian_ci@protonmail.com>
S: Maintained
F: Documentation/devicetree/bindings/display/panel/novatek,nt35532.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt35532.c
DRM DRIVER FOR NOVATEK NT35560 PANELS
M: Linus Walleij <linusw@kernel.org>
S: Maintained
@@ -8573,6 +8581,7 @@ M: Robert Foss <rfoss@kernel.org>
R: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
R: Jonas Karlman <jonas@kwiboo.se>
R: Jernej Skrabec <jernej.skrabec@gmail.com>
R: Luca Ceresoli <luca.ceresoli@bootlin.com>
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/bridge/
@@ -8896,6 +8905,8 @@ S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/drm_gpuvm.c
F: include/drm/drm_gpuvm.h
F: rust/helpers/drm_gpuvm.c
F: rust/kernel/drm/gpuvm/
DRM LOG
M: Jocelyn Falempe <jfalempe@redhat.com>
@@ -15032,7 +15043,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
F: drivers/hid/hid-lg-g15.c
LONTIUM LT8912B MIPI TO HDMI BRIDGE
M: Adrien Grassein <adrien.grassein@gmail.com>
M: Francesco Dolcini <francesco@dolcini.it>
S: Maintained
F: Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml
F: drivers/gpu/drm/bridge/lontium-lt8912b.c

View File

@@ -1414,6 +1414,9 @@ CONFIG_RZ_DMAC=y
CONFIG_TI_K3_UDMA=y
CONFIG_TI_K3_UDMA_GLUE_LAYER=y
CONFIG_STM32_DMA3=m
CONFIG_DMABUF_HEAPS=y
CONFIG_DMABUF_HEAPS_SYSTEM=m
CONFIG_DMABUF_HEAPS_CMA=m
CONFIG_VFIO=y
CONFIG_VFIO_PCI=y
CONFIG_VIRTIO_PCI=y

View File

@@ -1,14 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
amdxdna-y := \
aie.o \
aie_psp.o \
aie_smu.o \
aie2_ctx.o \
aie2_error.o \
aie2_message.o \
aie2_pci.o \
aie2_pm.o \
aie2_psp.o \
aie2_smu.o \
aie2_solver.o \
aie4_ctx.o \
aie4_message.o \
aie4_pci.o \
amdxdna_cbuf.o \
amdxdna_ctx.o \
amdxdna_gem.o \
amdxdna_iommu.o \
@@ -19,7 +24,11 @@ amdxdna-y := \
amdxdna_sysfs.o \
amdxdna_ubuf.o \
npu1_regs.o \
npu3_regs.o \
npu4_regs.o \
npu5_regs.o \
npu6_regs.o
amdxdna-$(CONFIG_PCI_IOV) += aie4_sriov.o
amdxdna-$(CONFIG_DEBUG_FS) += amdxdna_debugfs.o
obj-$(CONFIG_DRM_ACCEL_AMDXDNA) = amdxdna.o

167
drivers/accel/amdxdna/aie.c Normal file
View File

@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <linux/errno.h>
#include "aie.h"
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_pci_drv.h"
void aie_dump_mgmt_chann_debug(struct aie_device *aie)
{
struct amdxdna_dev *xdna = aie->xdna;
XDNA_DBG(xdna, "i2x tail 0x%x", aie->mgmt_i2x.mb_tail_ptr_reg);
XDNA_DBG(xdna, "i2x head 0x%x", aie->mgmt_i2x.mb_head_ptr_reg);
XDNA_DBG(xdna, "i2x ringbuf 0x%x", aie->mgmt_i2x.rb_start_addr);
XDNA_DBG(xdna, "i2x rsize 0x%x", aie->mgmt_i2x.rb_size);
XDNA_DBG(xdna, "x2i tail 0x%x", aie->mgmt_x2i.mb_tail_ptr_reg);
XDNA_DBG(xdna, "x2i head 0x%x", aie->mgmt_x2i.mb_head_ptr_reg);
XDNA_DBG(xdna, "x2i ringbuf 0x%x", aie->mgmt_x2i.rb_start_addr);
XDNA_DBG(xdna, "x2i rsize 0x%x", aie->mgmt_x2i.rb_size);
XDNA_DBG(xdna, "x2i chann index 0x%x", aie->mgmt_chan_idx);
XDNA_DBG(xdna, "mailbox protocol major 0x%x", aie->mgmt_prot_major);
XDNA_DBG(xdna, "mailbox protocol minor 0x%x", aie->mgmt_prot_minor);
}
void aie_destroy_chann(struct aie_device *aie, struct mailbox_channel **chann)
{
struct amdxdna_dev *xdna = aie->xdna;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
if (!*chann)
return;
xdna_mailbox_stop_channel(*chann);
xdna_mailbox_free_channel(*chann);
*chann = NULL;
}
int aie_send_mgmt_msg_wait(struct aie_device *aie, struct xdna_mailbox_msg *msg)
{
struct amdxdna_dev *xdna = aie->xdna;
struct xdna_notify *hdl = msg->handle;
int ret;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
if (!aie->mgmt_chann)
return -ENODEV;
ret = xdna_send_msg_wait(xdna, aie->mgmt_chann, msg);
if (ret == -ETIME)
aie_destroy_chann(aie, &aie->mgmt_chann);
if (!ret && *hdl->status) {
XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
msg->opcode, *hdl->data);
ret = -EINVAL;
}
return ret;
}
int aie_check_protocol(struct aie_device *aie, u32 fw_major, u32 fw_minor)
{
const struct amdxdna_fw_feature_tbl *feature;
bool found = false;
for (feature = aie->xdna->dev_info->fw_feature_tbl;
feature->major; feature++) {
if (feature->major != fw_major)
continue;
if (fw_minor < feature->min_minor)
continue;
if (feature->max_minor > 0 && fw_minor > feature->max_minor)
continue;
aie->feature_mask |= feature->features;
/* firmware version matches one of the driver support entry */
found = true;
}
return found ? 0 : -EOPNOTSUPP;
}
static void amdxdna_update_vbnv(struct amdxdna_dev *xdna,
const struct amdxdna_rev_vbnv *tbl,
u32 rev)
{
int i;
for (i = 0; tbl[i].vbnv; i++) {
if (tbl[i].revision == rev) {
xdna->vbnv = tbl[i].vbnv;
break;
}
}
}
void amdxdna_vbnv_init(struct amdxdna_dev *xdna)
{
const struct amdxdna_dev_info *info = xdna->dev_info;
u32 rev;
xdna->vbnv = info->default_vbnv;
if (!info->ops->get_dev_revision || !info->rev_vbnv_tbl)
return;
if (info->ops->get_dev_revision(xdna, &rev))
return;
amdxdna_update_vbnv(xdna, info->rev_vbnv_tbl, rev);
}
int amdxdna_get_metadata(struct aie_device *aie,
struct amdxdna_client *client,
struct amdxdna_drm_get_info *args)
{
int ret = 0;
u32 buf_sz;
buf_sz = min(args->buffer_size, sizeof(aie->metadata));
if (copy_to_user(u64_to_user_ptr(args->buffer), &aie->metadata, buf_sz))
ret = -EFAULT;
return ret;
}
void *amdxdna_alloc_msg_buffer(struct amdxdna_dev *xdna, u32 *size,
dma_addr_t *dma_addr)
{
void *vaddr;
int order;
*size = max_t(u32, *size, SZ_8K);
order = get_order(*size);
if (order > MAX_PAGE_ORDER)
return ERR_PTR(-EINVAL);
*size = PAGE_SIZE << order;
if (amdxdna_iova_on(xdna))
return amdxdna_iommu_alloc(xdna, *size, dma_addr);
vaddr = dma_alloc_noncoherent(xdna->ddev.dev, *size, dma_addr,
DMA_FROM_DEVICE, GFP_KERNEL);
if (!vaddr)
return ERR_PTR(-ENOMEM);
return vaddr;
}
void amdxdna_free_msg_buffer(struct amdxdna_dev *xdna, size_t size,
void *cpu_addr, dma_addr_t dma_addr)
{
if (amdxdna_iova_on(xdna)) {
amdxdna_iommu_free(xdna, size, cpu_addr, dma_addr);
return;
}
dma_free_noncoherent(xdna->ddev.dev, size, cpu_addr, dma_addr, DMA_FROM_DEVICE);
}

120
drivers/accel/amdxdna/aie.h Normal file
View File

@@ -0,0 +1,120 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AIE_H_
#define _AIE_H_
#include <drm/amdxdna_accel.h>
#include "amdxdna_pci_drv.h"
#include "amdxdna_mailbox.h"
#define AIE_INTERVAL 20000 /* us */
#define AIE_TIMEOUT 1000000 /* us */
struct psp_device;
struct smu_device;
struct aie_device {
struct amdxdna_dev *xdna;
struct mailbox_channel *mgmt_chann;
struct xdna_mailbox_chann_res mgmt_x2i;
struct xdna_mailbox_chann_res mgmt_i2x;
u32 mgmt_chan_idx;
u32 mgmt_prot_major;
u32 mgmt_prot_minor;
unsigned long feature_mask;
struct psp_device *psp_hdl;
struct smu_device *smu_hdl;
struct amdxdna_drm_query_aie_metadata metadata;
};
#define DECLARE_AIE_MSG(name, op) \
DECLARE_XDNA_MSG_COMMON(name, op, -1)
#define AIE_FEATURE_ON(aie, feature) test_bit(feature, &(aie)->feature_mask)
#define PSP_REG_BAR(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].bar_idx)
#define PSP_REG_OFF(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].offset)
#define SMU_REG_BAR(ndev, idx) ((ndev)->priv->smu_regs_off[(idx)].bar_idx)
#define SMU_REG_OFF(ndev, idx) ((ndev)->priv->smu_regs_off[(idx)].offset)
#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
[reg_name] = {bar##_BAR_INDEX, (reg_addr) - bar##_BAR_BASE}
enum smu_reg_idx {
SMU_CMD_REG = 0,
SMU_ARG_REG,
SMU_INTR_REG,
SMU_RESP_REG,
SMU_OUT_REG,
SMU_MAX_REGS /* Keep this at the end */
};
enum psp_reg_idx {
PSP_CMD_REG = 0,
PSP_ARG0_REG,
PSP_ARG1_REG,
PSP_ARG2_REG,
PSP_NUM_IN_REGS, /* number of input registers */
PSP_INTR_REG = PSP_NUM_IN_REGS,
PSP_STATUS_REG,
PSP_RESP_REG,
PSP_PWAITMODE_REG,
PSP_MAX_REGS /* Keep this at the end */
};
struct aie_bar_off_pair {
int bar_idx;
u32 offset;
};
struct smu_config {
void __iomem *smu_regs[SMU_MAX_REGS];
};
struct psp_config {
const void *fw_buf;
u32 fw_size;
const void *certfw_buf;
u32 certfw_size;
void __iomem *psp_regs[PSP_MAX_REGS];
u32 arg2_mask;
u32 notify_val;
};
/* Device revision to VBNV string mapping table entry */
struct amdxdna_rev_vbnv {
u32 revision;
const char *vbnv;
};
/* aie.c */
void aie_dump_mgmt_chann_debug(struct aie_device *aie);
void aie_destroy_chann(struct aie_device *aie, struct mailbox_channel **chann);
int aie_send_mgmt_msg_wait(struct aie_device *aie, struct xdna_mailbox_msg *msg);
int aie_check_protocol(struct aie_device *aie, u32 fw_major, u32 fw_minor);
void amdxdna_vbnv_init(struct amdxdna_dev *xdna);
int amdxdna_get_metadata(struct aie_device *aie, struct amdxdna_client *client,
struct amdxdna_drm_get_info *args);
void *amdxdna_alloc_msg_buffer(struct amdxdna_dev *xdna, u32 *size,
dma_addr_t *dma_addr);
void amdxdna_free_msg_buffer(struct amdxdna_dev *xdna, size_t size,
void *cpu_addr, dma_addr_t dma_addr);
/* aie_psp.c */
struct psp_device *aiem_psp_create(struct drm_device *ddev, struct psp_config *conf);
int aie_psp_start(struct psp_device *psp);
void aie_psp_stop(struct psp_device *psp);
int aie_psp_waitmode_poll(struct psp_device *psp);
/* aie_smu.c */
struct smu_device *aiem_smu_create(struct drm_device *ddev, struct smu_config *conf);
int aie_smu_init(struct smu_device *smu);
void aie_smu_fini(struct smu_device *smu);
int aie_smu_set_clocks(struct smu_device *smu, u32 *npuclk, u32 *hclk);
int aie_smu_set_dpm(struct smu_device *smu, u32 dpm_level);
#endif /* _AIE_H_ */

View File

@@ -27,7 +27,9 @@ static bool force_cmdlist = true;
module_param(force_cmdlist, bool, 0600);
MODULE_PARM_DESC(force_cmdlist, "Force use command list (Default true)");
#define HWCTX_MAX_TIMEOUT 60000 /* milliseconds */
uint tdr_timeout_ms = 2000;
module_param(tdr_timeout_ms, int, 0400);
MODULE_PARM_DESC(tdr_timeout_ms, "TDR (Timeout Detection and Recovery) timeout in milliseconds (0 = disable)");
struct aie2_ctx_health {
struct amdxdna_ctx_health header;
@@ -39,11 +41,30 @@ struct aie2_ctx_health {
u32 fatal_error_app_module;
};
static inline void aie2_tdr_signal(struct amdxdna_dev *xdna)
{
WRITE_ONCE(xdna->dev_handle->tdr_status, AIE2_TDR_SIGNALED);
}
static bool aie2_tdr_detect(struct amdxdna_dev *xdna)
{
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
if (READ_ONCE(ndev->tdr_status) == AIE2_TDR_WAIT) {
XDNA_ERR(xdna, "TDR timeout detected");
return true;
}
WRITE_ONCE(ndev->tdr_status, AIE2_TDR_WAIT);
return false;
}
static void aie2_job_release(struct kref *ref)
{
struct amdxdna_sched_job *job;
job = container_of(ref, struct amdxdna_sched_job, refcnt);
amdxdna_sched_job_cleanup(job);
atomic64_inc(&job->hwctx->job_free_cnt);
wake_up(&job->hwctx->priv->job_free_wq);
@@ -70,6 +91,7 @@ static void aie2_hwctx_stop(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwct
static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx)
{
struct amdxdna_gem_obj *heap = hwctx->priv->heap;
unsigned long heap_id;
int ret;
ret = aie2_create_context(xdna->dev_handle, hwctx);
@@ -86,6 +108,17 @@ static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hw
goto out;
}
xa_for_each_range(&hwctx->client->dev_heap_xa, heap_id, heap, 1,
hwctx->last_attached_heap) {
ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
amdxdna_obj_dma_addr(heap),
heap->mem.size);
if (ret) {
XDNA_ERR(xdna, "Add heap %ld failed ret %d", heap_id, ret);
goto out;
}
}
ret = aie2_config_cu(hwctx, NULL);
if (ret) {
XDNA_ERR(xdna, "Config cu failed, ret %d", ret);
@@ -175,8 +208,10 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
{
struct dma_fence *fence = job->fence;
trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
trace_xdna_job(&job->base, job->hwctx->name, "signaling fence",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
aie2_tdr_signal(job->hwctx->client->xdna);
job->hwctx->priv->completed++;
dma_fence_signal(fence);
@@ -270,17 +305,13 @@ aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size)
struct amdxdna_sched_job *job = handle;
int ret = 0;
if (unlikely(!data))
goto out;
if (unlikely(size != sizeof(u32))) {
if (unlikely(!data || size != sizeof(u32))) {
job->drv_cmd->result = U32_MAX;
ret = -EINVAL;
goto out;
} else {
job->drv_cmd->result = readl(data);
}
job->drv_cmd->result = readl(data);
out:
aie2_sched_notify(job);
return ret;
}
@@ -345,6 +376,9 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
struct dma_fence *fence;
int ret;
trace_xdna_job(sched_job, hwctx->name, "job run",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
if (!hwctx->priv->mbox_chann)
return NULL;
@@ -360,6 +394,7 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
break;
case ATTACH_DEBUG_BO:
case DETACH_DEBUG_BO:
ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
break;
default:
@@ -384,8 +419,11 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
aie2_job_put(job);
mmput(job->mm);
fence = ERR_PTR(ret);
} else {
aie2_tdr_signal(hwctx->client->xdna);
}
trace_xdna_job(sched_job, hwctx->name, "sent to device", job->seq);
trace_xdna_job(sched_job, hwctx->name, "sent to device",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
return fence;
}
@@ -395,7 +433,9 @@ static void aie2_sched_job_free(struct drm_sched_job *sched_job)
struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job);
struct amdxdna_hwctx *hwctx = job->hwctx;
trace_xdna_job(sched_job, hwctx->name, "job free", job->seq);
/* job->drv_cmd could be freed, so use DEFAULT_IO */
trace_xdna_job(sched_job, hwctx->name, "job free",
job->seq, DEFAULT_IO);
if (!job->job_done)
up(&hwctx->priv->job_sem);
@@ -413,10 +453,12 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
int ret;
xdna = hwctx->client->xdna;
trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq);
job->job_timeout = true;
mutex_lock(&xdna->dev_lock);
guard(mutex)(&xdna->dev_lock);
if (!aie2_tdr_detect(xdna))
return DRM_GPU_SCHED_STAT_NO_HANG;
report = kzalloc_obj(*report);
if (!report)
goto reset_hwctx;
@@ -428,10 +470,10 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
job->aie2_job_health = report;
reset_hwctx:
job->job_timeout = true;
aie2_hwctx_stop(xdna, hwctx, sched_job);
aie2_hwctx_restart(xdna, hwctx);
mutex_unlock(&xdna->dev_lock);
return DRM_GPU_SCHED_STAT_RESET;
}
@@ -456,12 +498,12 @@ static int aie2_hwctx_col_list(struct amdxdna_hwctx *hwctx)
}
ndev = xdna->dev_handle;
if (unlikely(!ndev->metadata.core.row_count)) {
if (unlikely(!ndev->aie.metadata.core.row_count)) {
XDNA_WARN(xdna, "Core tile row count is zero");
return -EINVAL;
}
hwctx->num_col = hwctx->num_tiles / ndev->metadata.core.row_count;
hwctx->num_col = hwctx->num_tiles / ndev->aie.metadata.core.row_count;
if (!hwctx->num_col || hwctx->num_col > ndev->total_col) {
XDNA_ERR(xdna, "Invalid num_col %d", hwctx->num_col);
return -EINVAL;
@@ -513,22 +555,24 @@ static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct alloc_requests *xrs_req;
u32 temporal_only_col = 0;
int ret;
if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) {
hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col;
hwctx->num_col = xdna->dev_handle->total_col;
return aie2_create_context(xdna->dev_handle, hwctx);
}
xrs_req = kzalloc_obj(*xrs_req);
if (!xrs_req)
return -ENOMEM;
xrs_req->cdo.start_cols = hwctx->col_list;
xrs_req->cdo.cols_len = hwctx->col_list_len;
xrs_req->cdo.ncols = hwctx->num_col;
xrs_req->cdo.qos_cap.opc = hwctx->max_opc;
if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) {
xrs_req->cdo.start_cols = &temporal_only_col;
xrs_req->cdo.cols_len = 1;
xrs_req->cdo.ncols = xdna->dev_handle->total_col;
} else {
xrs_req->cdo.start_cols = hwctx->col_list;
xrs_req->cdo.cols_len = hwctx->col_list_len;
xrs_req->cdo.ncols = hwctx->num_col;
}
/* Use platform opc */
xrs_req->cdo.qos_cap.opc = xdna->dev_handle->priv->col_opc * hwctx->num_col;
xrs_req->rqos.gops = hwctx->qos.gops;
xrs_req->rqos.fps = hwctx->qos.fps;
@@ -552,15 +596,9 @@ static void aie2_release_resource(struct amdxdna_hwctx *hwctx)
struct amdxdna_dev *xdna = hwctx->client->xdna;
int ret;
if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) {
ret = aie2_destroy_context(xdna->dev_handle, hwctx);
if (ret && ret != -ENODEV)
XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret);
} else {
ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
if (ret)
XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
}
ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
if (ret)
XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
}
static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx)
@@ -605,9 +643,8 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
struct amdxdna_dev *xdna = client->xdna;
const struct drm_sched_init_args args = {
.ops = &sched_ops,
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = HWCTX_MAX_CMDS,
.timeout = msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
.timeout = msecs_to_jiffies(tdr_timeout_ms),
.name = "amdxdna_js",
.dev = xdna->ddev.dev,
};
@@ -622,7 +659,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
hwctx->priv = priv;
mutex_lock(&client->mm_lock);
heap = client->dev_heap;
heap = xa_load(&client->dev_heap_xa, 0);
if (!heap) {
XDNA_ERR(xdna, "The client dev heap object not exist");
mutex_unlock(&client->mm_lock);
@@ -704,6 +741,12 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
goto release_resource;
}
ret = amdxdna_update_heap(client, hwctx);
if (ret) {
XDNA_ERR(xdna, "Update heap failed, ret %d", ret);
goto release_resource;
}
ret = aie2_ctx_syncobj_create(hwctx);
if (ret) {
XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret);
@@ -893,6 +936,7 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
aie2_cmd_wait(hwctx, seq);
if (cmd.result) {
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
ret = -EINVAL;
goto put_obj;
}
@@ -1136,3 +1180,28 @@ void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo,
else if (ret == -ERESTARTSYS)
XDNA_DBG(xdna, "Wait for bo interrupted by signal");
}
int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx,
struct amdxdna_gem_obj *heap)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
u64 addr;
int ret;
ret = amdxdna_pm_resume_get_locked(xdna);
if (ret)
return ret;
addr = amdxdna_obj_dma_addr(heap);
ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
addr, heap->mem.size);
if (ret) {
XDNA_ERR(xdna, "Add heap failed hwctx %s 0x%lx ret %d",
hwctx->name, heap->mem.size, ret);
}
amdxdna_pm_suspend_put(xdna);
return ret;
}

View File

@@ -11,6 +11,7 @@
#include <linux/kthread.h>
#include <linux/kernel.h>
#include "aie.h"
#include "aie2_msg_priv.h"
#include "aie2_pci.h"
#include "amdxdna_error.h"
@@ -249,12 +250,12 @@ static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u3
enum aie_error_category cat;
cat = aie_get_error_category(err->row, err->event_id, err->mod_type);
XDNA_ERR(ndev->xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d",
XDNA_ERR(ndev->aie.xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d",
err->row, err->col, err->mod_type,
err->event_id, cat);
if (err->col >= 32) {
XDNA_WARN(ndev->xdna, "Invalid column number");
XDNA_WARN(ndev->aie.xdna, "Invalid column number");
break;
}
@@ -294,7 +295,7 @@ static void aie2_error_worker(struct work_struct *err_work)
e = container_of(err_work, struct async_event, work);
xdna = e->ndev->xdna;
xdna = e->ndev->aie.xdna;
if (e->resp.status == MAX_AIE2_STATUS_CODE)
return;
@@ -329,7 +330,7 @@ static void aie2_error_worker(struct work_struct *err_work)
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->xdna;
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct async_events *events;
events = ndev->async_events;
@@ -338,13 +339,13 @@ void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
destroy_workqueue(events->wq);
mutex_lock(&xdna->dev_lock);
aie2_free_msg_buffer(ndev, events->size, events->buf, events->addr);
amdxdna_free_msg_buffer(xdna, events->size, events->buf, events->addr);
kfree(events);
}
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->xdna;
struct amdxdna_dev *xdna = ndev->aie.xdna;
u32 total_col = ndev->total_col;
u32 total_size = ASYNC_BUF_SIZE * total_col;
struct async_events *events;
@@ -354,7 +355,7 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
if (!events)
return -ENOMEM;
events->buf = aie2_alloc_msg_buffer(ndev, &total_size, &events->addr);
events->buf = amdxdna_alloc_msg_buffer(xdna, &total_size, &events->addr);
if (IS_ERR(events->buf)) {
ret = PTR_ERR(events->buf);
goto free_events;
@@ -394,7 +395,7 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
free_wq:
destroy_workqueue(events->wq);
free_buf:
aie2_free_msg_buffer(ndev, events->size, events->buf, events->addr);
amdxdna_free_msg_buffer(xdna, events->size, events->buf, events->addr);
free_events:
kfree(events);
return ret;
@@ -402,12 +403,15 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args)
{
struct amdxdna_dev *xdna = ndev->xdna;
struct amdxdna_dev *xdna = ndev->aie.xdna;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
if (!args->num_element)
return -EINVAL;
args->num_element = 1;
args->element_size = sizeof(ndev->last_async_err);
args->element_size = min(args->element_size, sizeof(ndev->last_async_err));
if (copy_to_user(u64_to_user_ptr(args->buffer),
&ndev->last_async_err, args->element_size))
return -EFAULT;

View File

@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/xarray.h>
#include "aie.h"
#include "aie2_msg_priv.h"
#include "aie2_pci.h"
#include "amdxdna_ctx.h"
@@ -24,103 +25,40 @@
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
#define DECLARE_AIE2_MSG(name, op) \
DECLARE_XDNA_MSG_COMMON(name, op, MAX_AIE2_STATUS_CODE)
#define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops)
static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
struct xdna_mailbox_msg *msg)
{
struct amdxdna_dev *xdna = ndev->xdna;
struct xdna_notify *hdl = msg->handle;
int ret;
if (!ndev->mgmt_chann)
return -ENODEV;
ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg);
if (ret == -ETIME)
aie2_destroy_mgmt_chann(ndev);
if (!ret && *hdl->status != AIE2_STATUS_SUCCESS) {
XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
msg->opcode, *hdl->data);
ret = -EINVAL;
}
return ret;
}
void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size,
dma_addr_t *dma_addr)
{
struct amdxdna_dev *xdna = ndev->xdna;
void *vaddr;
int order;
*size = max(*size, SZ_8K);
order = get_order(*size);
if (order > MAX_PAGE_ORDER)
return ERR_PTR(-EINVAL);
*size = PAGE_SIZE << order;
if (amdxdna_iova_on(xdna))
return amdxdna_iommu_alloc(xdna, *size, dma_addr);
vaddr = dma_alloc_noncoherent(xdna->ddev.dev, *size, dma_addr,
DMA_FROM_DEVICE, GFP_KERNEL);
if (!vaddr)
return ERR_PTR(-ENOMEM);
return vaddr;
}
void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size,
void *cpu_addr, dma_addr_t dma_addr)
{
struct amdxdna_dev *xdna = ndev->xdna;
if (amdxdna_iova_on(xdna)) {
amdxdna_iommu_free(xdna, size, cpu_addr, dma_addr);
return;
}
dma_free_noncoherent(xdna->ddev.dev, size, cpu_addr, dma_addr, DMA_FROM_DEVICE);
}
int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE2_MSG(suspend, MSG_OP_SUSPEND);
DECLARE_AIE_MSG(suspend, MSG_OP_SUSPEND);
int ret;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(ndev->xdna, "Failed to suspend fw, ret %d", ret);
XDNA_ERR(ndev->aie.xdna, "Failed to suspend fw, ret %d", ret);
return ret;
}
return aie2_psp_waitmode_poll(ndev->psp_hdl);
return aie_psp_waitmode_poll(ndev->aie.psp_hdl);
}
int aie2_resume_fw(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE2_MSG(suspend, MSG_OP_RESUME);
DECLARE_AIE_MSG(suspend, MSG_OP_RESUME);
return aie2_send_mgmt_msg_wait(ndev, &msg);
return aie_send_mgmt_msg_wait(&ndev->aie, &msg);
}
int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value)
{
DECLARE_AIE2_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG);
DECLARE_AIE_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG);
int ret;
req.type = type;
req.value = value;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(ndev->xdna, "Failed to set runtime config, ret %d", ret);
XDNA_ERR(ndev->aie.xdna, "Failed to set runtime config, ret %d", ret);
return ret;
}
@@ -129,13 +67,13 @@ int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value)
int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value)
{
DECLARE_AIE2_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG);
DECLARE_AIE_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG);
int ret;
req.type = type;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(ndev->xdna, "Failed to get runtime config, ret %d", ret);
XDNA_ERR(ndev->aie.xdna, "Failed to get runtime config, ret %d", ret);
return ret;
}
@@ -145,20 +83,21 @@ int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value)
int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid)
{
DECLARE_AIE2_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID);
DECLARE_AIE_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID);
req.pasid = pasid;
return aie2_send_mgmt_msg_wait(ndev, &msg);
return aie_send_mgmt_msg_wait(&ndev->aie, &msg);
}
int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version)
int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev,
struct amdxdna_drm_query_aie_version *version)
{
DECLARE_AIE2_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION);
struct amdxdna_dev *xdna = ndev->xdna;
DECLARE_AIE_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -171,16 +110,17 @@ int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *ver
return 0;
}
int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata)
int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev,
struct amdxdna_drm_query_aie_metadata *metadata)
{
DECLARE_AIE2_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO);
DECLARE_AIE_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO);
int ret;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
metadata->size = resp.info.size;
metadata->col_size = resp.info.size;
metadata->cols = resp.info.cols;
metadata->rows = resp.info.rows;
@@ -211,10 +151,10 @@ int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *m
int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
struct amdxdna_fw_ver *fw_ver)
{
DECLARE_AIE2_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION);
DECLARE_AIE_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION);
int ret;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -228,12 +168,12 @@ int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id)
{
DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
struct amdxdna_dev *xdna = ndev->xdna;
DECLARE_AIE_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
req.context_id = id;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret && ret != -ENODEV)
XDNA_WARN(xdna, "Destroy context failed, ret %d", ret);
else if (ret == -ENODEV)
@@ -245,7 +185,7 @@ static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id)
static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev,
struct amdxdna_hwctx *hwctx)
{
if (!AIE2_FEATURE_ON(ndev, AIE2_PREEMPT))
if (!AIE_FEATURE_ON(&ndev->aie, AIE2_PREEMPT))
return PRIORITY_HIGH;
switch (hwctx->qos.priority) {
@@ -264,8 +204,8 @@ static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev,
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
{
DECLARE_AIE2_MSG(create_ctx, MSG_OP_CREATE_CONTEXT);
struct amdxdna_dev *xdna = ndev->xdna;
DECLARE_AIE_MSG(create_ctx, MSG_OP_CREATE_CONTEXT);
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct xdna_mailbox_chann_res x2i;
struct xdna_mailbox_chann_res i2x;
struct cq_pair *cq_pair;
@@ -280,7 +220,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
req.pasid = amdxdna_pasid_on(hwctx->client) ? hwctx->client->pasid : 0;
req.context_priority = aie2_get_context_priority(ndev, hwctx);
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -344,7 +284,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
{
struct amdxdna_dev *xdna = ndev->xdna;
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
if (!hwctx->priv->mbox_chann)
@@ -361,25 +301,59 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc
return ret;
}
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
static int aie2_send_host_buf_msgs(struct amdxdna_dev_hdl *ndev, u32 context_id,
u64 addr, u64 size, u32 initial_opcode)
{
DECLARE_AIE2_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER);
struct amdxdna_dev *xdna = ndev->xdna;
DECLARE_AIE_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER);
struct amdxdna_dev *xdna = ndev->aie.xdna;
size_t chunk_size;
int ret;
req.context_id = context_id;
req.buf_addr = addr;
req.buf_size = size;
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
if (ret)
return ret;
chunk_size = xdna->dev_info->dev_mem_size;
if (!size || !IS_ALIGNED(size, chunk_size)) {
XDNA_ERR(xdna, "Invalid size 0x%llx for chunk 0x%lx",
size, chunk_size);
return -EINVAL;
}
XDNA_DBG(xdna, "fw ctx %d map host buf addr 0x%llx size 0x%llx",
context_id, addr, size);
msg.opcode = initial_opcode;
do {
req.context_id = context_id;
req.buf_addr = addr;
req.buf_size = chunk_size;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "fw ctx %d addr 0x%llx size 0x%lx",
context_id, addr, chunk_size);
return ret;
}
XDNA_DBG(xdna, "fw ctx %d host buf op 0x%x addr 0x%llx size 0x%lx",
context_id, msg.opcode, addr, chunk_size);
addr += chunk_size;
size -= chunk_size;
msg.opcode = MSG_OP_ADD_HOST_BUFFER;
} while (size);
return 0;
}
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
{
return aie2_send_host_buf_msgs(ndev, context_id, addr, size,
MSG_OP_MAP_HOST_BUFFER);
}
int aie2_add_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
{
if (!AIE_FEATURE_ON(&ndev->aie, AIE2_ADD_HOST_BUFFER))
return -EOPNOTSUPP;
return aie2_send_host_buf_msgs(ndev, context_id, addr, size,
MSG_OP_ADD_HOST_BUFFER);
}
static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg)
{
u32 *bitmap = arg;
@@ -392,15 +366,16 @@ static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg)
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
u32 size, u32 *cols_filled)
{
DECLARE_AIE2_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS);
struct amdxdna_dev *xdna = ndev->xdna;
u32 buf_sz = size, aie_bitmap = 0;
DECLARE_AIE_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS);
struct amdxdna_dev *xdna = ndev->aie.xdna;
u32 buf_sz, aie_bitmap = 0;
struct amdxdna_client *client;
dma_addr_t dma_addr;
u8 *buff_addr;
int ret;
buff_addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr);
buf_sz = ndev->aie.metadata.cols * ndev->aie.metadata.col_size;
buff_addr = amdxdna_alloc_msg_buffer(xdna, &buf_sz, &dma_addr);
if (IS_ERR(buff_addr))
return PTR_ERR(buff_addr);
@@ -414,8 +389,8 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
req.num_cols = hweight32(aie_bitmap);
req.aie_bitmap = aie_bitmap;
drm_clflush_virt_range(buff_addr, size); /* device can access */
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
drm_clflush_virt_range(buff_addr, req.dump_buff_size); /* device can access */
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "Error during NPU query, status %d", ret);
goto fail;
@@ -423,13 +398,14 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
XDNA_DBG(xdna, "Query NPU status completed");
if (size < resp.size) {
if (buf_sz < resp.size) {
ret = -EINVAL;
XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size);
goto fail;
}
if (copy_to_user(buf, buff_addr, resp.size)) {
size = min(size, resp.size);
if (copy_to_user(buf, buff_addr, size)) {
ret = -EFAULT;
XDNA_ERR(xdna, "Failed to copy NPU status to user space");
goto fail;
@@ -438,7 +414,7 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
*cols_filled = aie_bitmap;
fail:
aie2_free_msg_buffer(ndev, buf_sz, buff_addr, dma_addr);
amdxdna_free_msg_buffer(xdna, buf_sz, buff_addr, dma_addr);
return ret;
}
@@ -446,17 +422,18 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
char __user *buf, u32 size,
struct amdxdna_drm_query_telemetry_header *header)
{
DECLARE_AIE2_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
struct amdxdna_dev *xdna = ndev->xdna;
DECLARE_AIE_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
struct amdxdna_dev *xdna = ndev->aie.xdna;
dma_addr_t dma_addr;
u32 buf_sz = size;
u32 buf_sz;
u8 *addr;
int ret;
if (header->type >= MAX_TELEMETRY_TYPE)
return -EINVAL;
addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr);
buf_sz = min(size, SZ_4M);
addr = amdxdna_alloc_msg_buffer(xdna, &buf_sz, &dma_addr);
if (IS_ERR(addr))
return PTR_ERR(addr);
@@ -464,20 +441,21 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
req.buf_size = buf_sz;
req.type = header->type;
drm_clflush_virt_range(addr, size); /* device can access */
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
drm_clflush_virt_range(addr, req.buf_size); /* device can access */
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "Query telemetry failed, status %d", ret);
goto free_buf;
}
if (size < resp.size) {
if (buf_sz < resp.size) {
ret = -EINVAL;
XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size);
goto free_buf;
}
if (copy_to_user(buf, addr, resp.size)) {
size = min(size, resp.size);
if (copy_to_user(buf, addr, size)) {
ret = -EFAULT;
XDNA_ERR(xdna, "Failed to copy telemetry to user space");
goto free_buf;
@@ -487,7 +465,7 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
header->minor = resp.minor;
free_buf:
aie2_free_msg_buffer(ndev, buf_sz, addr, dma_addr);
amdxdna_free_msg_buffer(xdna, buf_sz, addr, dma_addr);
return ret;
}
@@ -506,8 +484,8 @@ int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr,
req.buf_addr = addr;
req.buf_size = size;
XDNA_DBG(ndev->xdna, "Register addr 0x%llx size 0x%x", addr, size);
return xdna_mailbox_send_msg(ndev->mgmt_chann, &msg, TX_TIMEOUT);
XDNA_DBG(ndev->aie.xdna, "Register addr 0x%llx size 0x%x", addr, size);
return xdna_mailbox_send_msg(ndev->aie.mgmt_chann, &msg, TX_TIMEOUT);
}
int aie2_config_cu(struct amdxdna_hwctx *hwctx,
@@ -866,7 +844,6 @@ static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo,
int ret;
u32 op;
op = amdxdna_cmd_get_op(cmd_abo);
switch (op) {
case ERT_START_CU:
@@ -915,12 +892,12 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size);
break;
case ERT_START_NPU_PREEMPT:
if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT))
return -EOPNOTSUPP;
ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size);
break;
case ERT_START_NPU_PREEMPT_ELF:
if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT))
return -EOPNOTSUPP;
ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size);
break;
@@ -935,26 +912,12 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
void aie2_msg_init(struct amdxdna_dev_hdl *ndev)
{
if (AIE2_FEATURE_ON(ndev, AIE2_NPU_COMMAND))
if (AIE_FEATURE_ON(&ndev->aie, AIE2_NPU_COMMAND))
ndev->exec_msg_ops = &npu_exec_message_ops;
else
ndev->exec_msg_ops = &legacy_exec_message_ops;
}
void aie2_destroy_mgmt_chann(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->xdna;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
if (!ndev->mgmt_chann)
return;
xdna_mailbox_stop_channel(ndev->mgmt_chann);
xdna_mailbox_free_channel(ndev->mgmt_chann);
ndev->mgmt_chann = NULL;
}
static inline struct amdxdna_gem_obj *
aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
{
@@ -1199,20 +1162,20 @@ int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *
int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
struct app_health_report *report)
{
DECLARE_AIE2_MSG(get_app_health, MSG_OP_GET_APP_HEALTH);
struct amdxdna_dev *xdna = ndev->xdna;
DECLARE_AIE_MSG(get_app_health, MSG_OP_GET_APP_HEALTH);
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct app_health_report *buf;
dma_addr_t dma_addr;
u32 buf_size;
int ret;
if (!AIE2_FEATURE_ON(ndev, AIE2_APP_HEALTH)) {
if (!AIE_FEATURE_ON(&ndev->aie, AIE2_APP_HEALTH)) {
XDNA_DBG(xdna, "App health feature not supported");
return -EOPNOTSUPP;
}
buf_size = sizeof(*report);
buf = aie2_alloc_msg_buffer(ndev, &buf_size, &dma_addr);
buf = amdxdna_alloc_msg_buffer(xdna, &buf_size, &dma_addr);
if (IS_ERR(buf)) {
XDNA_ERR(xdna, "Failed to allocate buffer for app health");
return PTR_ERR(buf);
@@ -1222,8 +1185,8 @@ int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
req.context_id = context_id;
req.buf_size = buf_size;
drm_clflush_virt_range(buf, sizeof(*report));
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
drm_clflush_virt_range(buf, req.buf_size);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "Get app health failed, ret %d status 0x%x", ret, resp.status);
goto free_buf;
@@ -1233,6 +1196,75 @@ int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
memcpy(report, buf, sizeof(*report));
free_buf:
aie2_free_msg_buffer(ndev, buf_size, buf, dma_addr);
amdxdna_free_msg_buffer(xdna, buf_size, buf, dma_addr);
return ret;
}
static int
aie2_runtime_update_ctx_prop(struct amdxdna_dev_hdl *ndev,
struct amdxdna_hwctx *ctx, u32 type, u32 value)
{
DECLARE_AIE_MSG(update_property, MSG_OP_UPDATE_PROPERTY);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
if (!AIE_FEATURE_ON(&ndev->aie, AIE2_UPDATE_PROPERTY))
return -EOPNOTSUPP;
if (ctx)
req.context_id = ctx->fw_ctx_id;
else
req.context_id = AIE2_UPDATE_PROPERTY_ALL_CTX;
req.time_quota_us = value;
req.type = type;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "%s update property failed, type %d ret %d",
ctx ? ctx->name : "ctx.all", type, ret);
return ret;
}
return 0;
}
int aie2_update_prop_time_quota(struct amdxdna_dev_hdl *ndev, u32 us)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
ret = aie2_runtime_update_ctx_prop(ndev, NULL, UPDATE_PROPERTY_TIME_QUOTA, us);
if (ret == -EOPNOTSUPP) {
XDNA_DBG(xdna, "update time quota not support, skipped");
ret = 0;
} else if (!ret) {
XDNA_DBG(xdna, "Ctx exec time quantum updated to %u us", us);
}
return ret;
}
int aie2_get_dev_revision(struct amdxdna_dev_hdl *ndev, enum aie2_dev_revision *rev)
{
DECLARE_AIE_MSG(get_dev_revision, MSG_OP_GET_DEV_REVISION);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
if (!AIE_FEATURE_ON(&ndev->aie, AIE2_GET_DEV_REVISION))
return -EOPNOTSUPP;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
*rev = resp.rev;
if (*rev < AIE2_DEV_REVISION_STXA || *rev >= AIE2_DEV_REVISION_UNKN) {
XDNA_ERR(xdna, "Unknown device revision: %d (raw fuse: 0x%x)",
*rev, resp.raw_fuse_data);
return -EINVAL;
}
XDNA_DBG(xdna, "Device revision: %d (raw fuse: 0x%x)", *rev, resp.raw_fuse_data);
return 0;
}

View File

@@ -31,7 +31,10 @@ enum aie2_msg_opcode {
MSG_OP_SET_RUNTIME_CONFIG = 0x10A,
MSG_OP_GET_RUNTIME_CONFIG = 0x10B,
MSG_OP_REGISTER_ASYNC_EVENT_MSG = 0x10C,
MSG_OP_UPDATE_PROPERTY = 0x113,
MSG_OP_GET_APP_HEALTH = 0x114,
MSG_OP_ADD_HOST_BUFFER = 0x115,
MSG_OP_GET_DEV_REVISION = 0x117,
MSG_OP_MAX_DRV_OPCODE,
MSG_OP_GET_PROTOCOL_VERSION = 0x301,
MSG_OP_MAX_OPCODE
@@ -460,7 +463,7 @@ struct fatal_error_info {
__u32 exception_pc; /* Program Counter at the time of the exception */
__u32 app_module; /* Error module name */
__u32 task_index; /* Index of the task in which the error occurred */
__u32 reserved[128];
__u32 reserved[127];
};
struct app_health_report {
@@ -503,4 +506,41 @@ struct get_app_health_resp {
__u32 required_buffer_size;
__u32 reserved[7];
} __packed;
struct update_property_req {
#define UPDATE_PROPERTY_TIME_QUOTA 0
__u32 type;
#define AIE2_UPDATE_PROPERTY_ALL_CTX 0xFF
__u8 context_id;
__u8 reserved[7];
__u32 time_quota_us;
__u32 reserved1;
} __packed;
struct update_property_resp {
enum aie2_msg_status status;
} __packed;
enum aie2_dev_revision {
AIE2_DEV_REVISION_STXA = 1,
AIE2_DEV_REVISION_STXB,
AIE2_DEV_REVISION_KRK1,
AIE2_DEV_REVISION_KRK2,
AIE2_DEV_REVISION_HALO,
AIE2_DEV_REVISION_GPT1,
AIE2_DEV_REVISION_GPT2,
AIE2_DEV_REVISION_GPT3,
AIE2_DEV_REVISION_UNKN,
};
struct get_dev_revision_req {
__u32 place_holder;
} __packed;
struct get_dev_revision_resp {
enum aie2_msg_status status;
enum aie2_dev_revision rev;
__u32 raw_fuse_data;
} __packed;
#endif /* _AIE2_MSG_PRIV_H_ */

View File

@@ -33,6 +33,8 @@ static int aie2_max_col = XRS_MAX_COL;
module_param(aie2_max_col, uint, 0600);
MODULE_PARM_DESC(aie2_max_col, "Maximum column could be used");
#define DEFAULT_TIME_QUANTUM 30000 /* microseconds */
static char *npu_fw[] = {
"npu_7.sbin",
"npu.sbin"
@@ -60,45 +62,6 @@ struct mgmt_mbox_chann_info {
__u32 rsvd[4];
};
static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor)
{
const struct aie2_fw_feature_tbl *feature;
bool found = false;
for (feature = ndev->priv->fw_feature_tbl; feature->major; feature++) {
if (feature->major != fw_major)
continue;
if (fw_minor < feature->min_minor)
continue;
if (feature->max_minor > 0 && fw_minor > feature->max_minor)
continue;
ndev->feature_mask |= feature->features;
/* firmware version matches one of the driver support entry */
found = true;
}
return found ? 0 : -EOPNOTSUPP;
}
static void aie2_dump_chann_info_debug(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->xdna;
XDNA_DBG(xdna, "i2x tail 0x%x", ndev->mgmt_i2x.mb_tail_ptr_reg);
XDNA_DBG(xdna, "i2x head 0x%x", ndev->mgmt_i2x.mb_head_ptr_reg);
XDNA_DBG(xdna, "i2x ringbuf 0x%x", ndev->mgmt_i2x.rb_start_addr);
XDNA_DBG(xdna, "i2x rsize 0x%x", ndev->mgmt_i2x.rb_size);
XDNA_DBG(xdna, "x2i tail 0x%x", ndev->mgmt_x2i.mb_tail_ptr_reg);
XDNA_DBG(xdna, "x2i head 0x%x", ndev->mgmt_x2i.mb_head_ptr_reg);
XDNA_DBG(xdna, "x2i ringbuf 0x%x", ndev->mgmt_x2i.rb_start_addr);
XDNA_DBG(xdna, "x2i rsize 0x%x", ndev->mgmt_x2i.rb_size);
XDNA_DBG(xdna, "x2i chann index 0x%x", ndev->mgmt_chan_idx);
XDNA_DBG(xdna, "mailbox protocol major 0x%x", ndev->mgmt_prot_major);
XDNA_DBG(xdna, "mailbox protocol minor 0x%x", ndev->mgmt_prot_minor);
}
static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
{
struct mgmt_mbox_chann_info info_regs;
@@ -118,7 +81,7 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
* is alive.
*/
ret = readx_poll_timeout(readl, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF),
addr, addr, AIE2_INTERVAL, AIE2_TIMEOUT);
addr, addr, AIE_INTERVAL, AIE_TIMEOUT);
if (ret || !addr)
return -ETIME;
@@ -128,13 +91,13 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
reg[i] = readl(ndev->sram_base + off + i * sizeof(u32));
if (info_regs.magic != MGMT_MBOX_MAGIC) {
XDNA_ERR(ndev->xdna, "Invalid mbox magic 0x%x", info_regs.magic);
XDNA_ERR(ndev->aie.xdna, "Invalid mbox magic 0x%x", info_regs.magic);
ret = -EINVAL;
goto done;
}
i2x = &ndev->mgmt_i2x;
x2i = &ndev->mgmt_x2i;
i2x = &ndev->aie.mgmt_i2x;
x2i = &ndev->aie.mgmt_x2i;
i2x->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_head);
i2x->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_tail);
@@ -146,14 +109,15 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
x2i->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.x2i_buf);
x2i->rb_size = info_regs.x2i_buf_sz;
ndev->mgmt_chan_idx = info_regs.msi_id;
ndev->mgmt_prot_major = info_regs.prot_major;
ndev->mgmt_prot_minor = info_regs.prot_minor;
ndev->aie.mgmt_chan_idx = info_regs.msi_id;
ndev->aie.mgmt_prot_major = info_regs.prot_major;
ndev->aie.mgmt_prot_minor = info_regs.prot_minor;
ret = aie2_check_protocol(ndev, ndev->mgmt_prot_major, ndev->mgmt_prot_minor);
ret = aie_check_protocol(&ndev->aie, ndev->aie.mgmt_prot_major,
ndev->aie.mgmt_prot_minor);
done:
aie2_dump_chann_info_debug(ndev);
aie_dump_mgmt_chann_debug(&ndev->aie);
/* Must clear address at FW_ALIVE_OFF */
writel(0, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF));
@@ -173,13 +137,14 @@ int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
continue;
if (cfg->feature_mask &&
bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX))
bitmap_subset(&cfg->feature_mask, &ndev->aie.feature_mask,
AIE2_FEATURE_MAX))
continue;
value = val ? *val : cfg->value;
ret = aie2_set_runtime_cfg(ndev, cfg->type, value);
if (ret) {
XDNA_ERR(ndev->xdna, "Set type %d value %d failed",
XDNA_ERR(ndev->aie.xdna, "Set type %d value %d failed",
cfg->type, value);
return ret;
}
@@ -194,13 +159,13 @@ static int aie2_xdna_reset(struct amdxdna_dev_hdl *ndev)
ret = aie2_suspend_fw(ndev);
if (ret) {
XDNA_ERR(ndev->xdna, "Suspend firmware failed");
XDNA_ERR(ndev->aie.xdna, "Suspend firmware failed");
return ret;
}
ret = aie2_resume_fw(ndev);
if (ret) {
XDNA_ERR(ndev->xdna, "Resume firmware failed");
XDNA_ERR(ndev->aie.xdna, "Resume firmware failed");
return ret;
}
@@ -213,19 +178,25 @@ static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev)
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_INIT, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Runtime config failed");
XDNA_ERR(ndev->aie.xdna, "Runtime config failed");
return ret;
}
ret = aie2_assign_mgmt_pasid(ndev, 0);
if (ret) {
XDNA_ERR(ndev->xdna, "Can not assign PASID");
XDNA_ERR(ndev->aie.xdna, "Can not assign PASID");
return ret;
}
ret = aie2_update_prop_time_quota(ndev, DEFAULT_TIME_QUANTUM);
if (ret) {
XDNA_ERR(ndev->aie.xdna, "Failed to update execution time quantum");
return ret;
}
ret = aie2_xdna_reset(ndev);
if (ret) {
XDNA_ERR(ndev->xdna, "Reset firmware failed");
XDNA_ERR(ndev->aie.xdna, "Reset firmware failed");
return ret;
}
@@ -236,25 +207,25 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
{
int ret;
ret = aie2_query_firmware_version(ndev, &ndev->xdna->fw_ver);
ret = aie2_query_firmware_version(ndev, &ndev->aie.xdna->fw_ver);
if (ret) {
XDNA_ERR(ndev->xdna, "query firmware version failed");
XDNA_ERR(ndev->aie.xdna, "query firmware version failed");
return ret;
}
ret = aie2_query_aie_version(ndev, &ndev->version);
if (ret) {
XDNA_ERR(ndev->xdna, "Query AIE version failed");
XDNA_ERR(ndev->aie.xdna, "Query AIE version failed");
return ret;
}
ret = aie2_query_aie_metadata(ndev, &ndev->metadata);
ret = aie2_query_aie_metadata(ndev, &ndev->aie.metadata);
if (ret) {
XDNA_ERR(ndev->xdna, "Query AIE metadata failed");
XDNA_ERR(ndev->aie.xdna, "Query AIE metadata failed");
return ret;
}
ndev->total_col = min(aie2_max_col, ndev->metadata.cols);
ndev->total_col = min(aie2_max_col, ndev->aie.metadata.cols);
return 0;
}
@@ -262,8 +233,8 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
static void aie2_mgmt_fw_fini(struct amdxdna_dev_hdl *ndev)
{
if (aie2_suspend_fw(ndev))
XDNA_ERR(ndev->xdna, "Suspend_fw failed");
XDNA_DBG(ndev->xdna, "Firmware suspended");
XDNA_ERR(ndev->aie.xdna, "Suspend_fw failed");
XDNA_DBG(ndev->aie.xdna, "Firmware suspended");
}
static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action)
@@ -275,6 +246,7 @@ static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action)
xdna = hwctx->client->xdna;
hwctx->start_col = action->part.start_col;
hwctx->num_unused_col = action->part.ncols - hwctx->num_col;
hwctx->num_col = action->part.ncols;
ret = aie2_create_context(xdna->dev_handle, hwctx);
if (ret)
@@ -319,6 +291,12 @@ static struct xrs_action_ops aie2_xrs_actions = {
.set_dft_dpm_level = aie2_xrs_set_dft_dpm_level,
};
static void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
{
ndev->priv->hw_ops->set_dpm(ndev, 0);
aie_smu_fini(ndev->aie.smu_hdl);
}
static void aie2_hw_stop(struct amdxdna_dev *xdna)
{
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
@@ -331,10 +309,10 @@ static void aie2_hw_stop(struct amdxdna_dev *xdna)
aie2_runtime_cfg(ndev, AIE2_RT_CFG_CLK_GATING, NULL);
aie2_mgmt_fw_fini(ndev);
aie2_destroy_mgmt_chann(ndev);
aie_destroy_chann(&ndev->aie, &ndev->aie.mgmt_chann);
drmm_kfree(&xdna->ddev, ndev->mbox);
ndev->mbox = NULL;
aie2_psp_stop(ndev->psp_hdl);
aie_psp_stop(ndev->aie.psp_hdl);
aie2_smu_fini(ndev);
aie2_error_async_events_free(ndev);
pci_disable_device(pdev);
@@ -374,20 +352,20 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
goto disable_dev;
}
ndev->mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox);
if (!ndev->mgmt_chann) {
ndev->aie.mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox);
if (!ndev->aie.mgmt_chann) {
XDNA_ERR(xdna, "failed to alloc channel");
ret = -ENODEV;
goto disable_dev;
}
ret = aie2_smu_init(ndev);
ret = aie_smu_init(ndev->aie.smu_hdl);
if (ret) {
XDNA_ERR(xdna, "failed to init smu, ret %d", ret);
goto free_channel;
}
ret = aie2_psp_start(ndev->psp_hdl);
ret = aie_psp_start(ndev->aie.psp_hdl);
if (ret) {
XDNA_ERR(xdna, "failed to start psp, ret %d", ret);
goto fini_smu;
@@ -399,17 +377,17 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
goto stop_psp;
}
mgmt_mb_irq = pci_irq_vector(pdev, ndev->mgmt_chan_idx);
mgmt_mb_irq = pci_irq_vector(pdev, ndev->aie.mgmt_chan_idx);
if (mgmt_mb_irq < 0) {
ret = mgmt_mb_irq;
XDNA_ERR(xdna, "failed to alloc irq vector, ret %d", ret);
goto stop_psp;
}
xdna_mailbox_intr_reg = ndev->mgmt_i2x.mb_head_ptr_reg + 4;
ret = xdna_mailbox_start_channel(ndev->mgmt_chann,
&ndev->mgmt_x2i,
&ndev->mgmt_i2x,
xdna_mailbox_intr_reg = ndev->aie.mgmt_i2x.mb_head_ptr_reg + 4;
ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann,
&ndev->aie.mgmt_x2i,
&ndev->aie.mgmt_i2x,
xdna_mailbox_intr_reg,
mgmt_mb_irq);
if (ret) {
@@ -448,14 +426,14 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
stop_fw:
aie2_suspend_fw(ndev);
xdna_mailbox_stop_channel(ndev->mgmt_chann);
xdna_mailbox_stop_channel(ndev->aie.mgmt_chann);
stop_psp:
aie2_psp_stop(ndev->psp_hdl);
aie_psp_stop(ndev->aie.psp_hdl);
fini_smu:
aie2_smu_fini(ndev);
free_channel:
xdna_mailbox_free_channel(ndev->mgmt_chann);
ndev->mgmt_chann = NULL;
xdna_mailbox_free_channel(ndev->aie.mgmt_chann);
ndev->aie.mgmt_chann = NULL;
disable_dev:
pci_disable_device(pdev);
@@ -500,7 +478,8 @@ static int aie2_init(struct amdxdna_dev *xdna)
void __iomem *tbl[PCI_NUM_RESOURCES] = {0};
struct init_config xrs_cfg = { 0 };
struct amdxdna_dev_hdl *ndev;
struct psp_config psp_conf;
struct psp_config psp_conf = { 0 };
struct smu_config smu_conf;
const struct firmware *fw;
unsigned long bars = 0;
char *fw_full_path;
@@ -521,7 +500,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
return -ENOMEM;
ndev->priv = xdna->dev_info->dev_priv;
ndev->xdna = xdna;
ndev->aie.xdna = xdna;
for (i = 0; i < ARRAY_SIZE(npu_fw); i++) {
fw_full_path = kasprintf(GFP_KERNEL, "%s%s", ndev->priv->fw_path, npu_fw[i]);
@@ -550,9 +529,10 @@ static int aie2_init(struct amdxdna_dev *xdna)
for (i = 0; i < PSP_MAX_REGS; i++)
set_bit(PSP_REG_BAR(ndev, i), &bars);
for (i = 0; i < SMU_MAX_REGS; i++)
set_bit(SMU_REG_BAR(ndev, i), &bars);
set_bit(xdna->dev_info->sram_bar, &bars);
set_bit(xdna->dev_info->smu_bar, &bars);
set_bit(xdna->dev_info->mbox_bar, &bars);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
@@ -567,7 +547,6 @@ static int aie2_init(struct amdxdna_dev *xdna)
}
ndev->sram_base = tbl[xdna->dev_info->sram_bar];
ndev->smu_base = tbl[xdna->dev_info->smu_bar];
ndev->mbox_base = tbl[xdna->dev_info->mbox_bar];
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
@@ -591,14 +570,25 @@ static int aie2_init(struct amdxdna_dev *xdna)
psp_conf.fw_size = fw->size;
psp_conf.fw_buf = fw->data;
psp_conf.arg2_mask = GENMASK(23, 0);
psp_conf.notify_val = 1;
for (i = 0; i < PSP_MAX_REGS; i++)
psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(ndev, i)] + PSP_REG_OFF(ndev, i);
ndev->psp_hdl = aie2m_psp_create(&xdna->ddev, &psp_conf);
if (!ndev->psp_hdl) {
ndev->aie.psp_hdl = aiem_psp_create(&xdna->ddev, &psp_conf);
if (!ndev->aie.psp_hdl) {
XDNA_ERR(xdna, "failed to create psp");
ret = -ENOMEM;
goto release_fw;
}
for (i = 0; i < SMU_MAX_REGS; i++)
smu_conf.smu_regs[i] = tbl[SMU_REG_BAR(ndev, i)] + SMU_REG_OFF(ndev, i);
ndev->aie.smu_hdl = aiem_smu_create(&xdna->ddev, &smu_conf);
if (!ndev->aie.smu_hdl) {
XDNA_ERR(xdna, "failed to create smu");
ret = -ENOMEM;
goto release_fw;
}
xdna->dev_handle = ndev;
ret = aie2_hw_start(xdna);
@@ -610,7 +600,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1;
for (i = 0; i < xrs_cfg.clk_list.num_levels; i++)
xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk;
xrs_cfg.sys_eff_factor = 1;
xrs_cfg.sys_eff_factor = 2;
xrs_cfg.ddev = &xdna->ddev;
xrs_cfg.actions = &aie2_xrs_actions;
xrs_cfg.total_col = ndev->total_col;
@@ -624,6 +614,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
release_firmware(fw);
aie2_msg_init(ndev);
amdxdna_vbnv_init(xdna);
amdxdna_pm_init(xdna);
return 0;
@@ -644,23 +635,19 @@ static void aie2_fini(struct amdxdna_dev *xdna)
static int aie2_get_aie_status(struct amdxdna_client *client,
struct amdxdna_drm_get_info *args)
{
struct amdxdna_drm_query_aie_status status;
struct amdxdna_drm_query_aie_status status = {};
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
u32 buf_sz;
int ret;
ndev = xdna->dev_handle;
if (copy_from_user(&status, u64_to_user_ptr(args->buffer), sizeof(status))) {
buf_sz = min(args->buffer_size, sizeof(status));
if (copy_from_user(&status, u64_to_user_ptr(args->buffer), buf_sz)) {
XDNA_ERR(xdna, "Failed to copy AIE request into kernel");
return -EFAULT;
}
if (ndev->metadata.cols * ndev->metadata.size < status.buffer_size) {
XDNA_ERR(xdna, "Invalid buffer size. Given Size: %u. Need Size: %u.",
status.buffer_size, ndev->metadata.cols * ndev->metadata.size);
return -EINVAL;
}
ret = aie2_query_status(ndev, u64_to_user_ptr(status.buffer),
status.buffer_size, &status.cols_filled);
if (ret) {
@@ -668,7 +655,7 @@ static int aie2_get_aie_status(struct amdxdna_client *client,
return ret;
}
if (copy_to_user(u64_to_user_ptr(args->buffer), &status, sizeof(status))) {
if (copy_to_user(u64_to_user_ptr(args->buffer), &status, buf_sz)) {
XDNA_ERR(xdna, "Failed to copy AIE request info to user space");
return -EFAULT;
}
@@ -676,63 +663,20 @@ static int aie2_get_aie_status(struct amdxdna_client *client,
return 0;
}
static int aie2_get_aie_metadata(struct amdxdna_client *client,
struct amdxdna_drm_get_info *args)
{
struct amdxdna_drm_query_aie_metadata *meta;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
int ret = 0;
ndev = xdna->dev_handle;
meta = kzalloc_obj(*meta);
if (!meta)
return -ENOMEM;
meta->col_size = ndev->metadata.size;
meta->cols = ndev->metadata.cols;
meta->rows = ndev->metadata.rows;
meta->version.major = ndev->metadata.version.major;
meta->version.minor = ndev->metadata.version.minor;
meta->core.row_count = ndev->metadata.core.row_count;
meta->core.row_start = ndev->metadata.core.row_start;
meta->core.dma_channel_count = ndev->metadata.core.dma_channel_count;
meta->core.lock_count = ndev->metadata.core.lock_count;
meta->core.event_reg_count = ndev->metadata.core.event_reg_count;
meta->mem.row_count = ndev->metadata.mem.row_count;
meta->mem.row_start = ndev->metadata.mem.row_start;
meta->mem.dma_channel_count = ndev->metadata.mem.dma_channel_count;
meta->mem.lock_count = ndev->metadata.mem.lock_count;
meta->mem.event_reg_count = ndev->metadata.mem.event_reg_count;
meta->shim.row_count = ndev->metadata.shim.row_count;
meta->shim.row_start = ndev->metadata.shim.row_start;
meta->shim.dma_channel_count = ndev->metadata.shim.dma_channel_count;
meta->shim.lock_count = ndev->metadata.shim.lock_count;
meta->shim.event_reg_count = ndev->metadata.shim.event_reg_count;
if (copy_to_user(u64_to_user_ptr(args->buffer), meta, sizeof(*meta)))
ret = -EFAULT;
kfree(meta);
return ret;
}
static int aie2_get_aie_version(struct amdxdna_client *client,
struct amdxdna_drm_get_info *args)
{
struct amdxdna_drm_query_aie_version version;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
u32 buf_sz;
ndev = xdna->dev_handle;
version.major = ndev->version.major;
version.minor = ndev->version.minor;
if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version)))
buf_sz = min(args->buffer_size, sizeof(version));
if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz))
return -EFAULT;
return 0;
@@ -743,13 +687,15 @@ static int aie2_get_firmware_version(struct amdxdna_client *client,
{
struct amdxdna_drm_query_firmware_version version;
struct amdxdna_dev *xdna = client->xdna;
u32 buf_sz;
version.major = xdna->fw_ver.major;
version.minor = xdna->fw_ver.minor;
version.patch = xdna->fw_ver.sub;
version.build = xdna->fw_ver.build;
if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version)))
buf_sz = min(args->buffer_size, sizeof(version));
if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz))
return -EFAULT;
return 0;
@@ -761,11 +707,13 @@ static int aie2_get_power_mode(struct amdxdna_client *client,
struct amdxdna_drm_get_power_mode mode = {};
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
u32 buf_sz;
ndev = xdna->dev_handle;
mode.power_mode = ndev->pw_mode;
if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, sizeof(mode)))
buf_sz = min(args->buffer_size, sizeof(mode));
if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, buf_sz))
return -EFAULT;
return 0;
@@ -778,19 +726,22 @@ static int aie2_get_clock_metadata(struct amdxdna_client *client,
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
int ret = 0;
u32 buf_sz;
ndev = xdna->dev_handle;
clock = kzalloc_obj(*clock);
if (!clock)
return -ENOMEM;
aie2_update_counters(ndev);
snprintf(clock->mp_npu_clock.name, sizeof(clock->mp_npu_clock.name),
"MP-NPU Clock");
clock->mp_npu_clock.freq_mhz = ndev->npuclk_freq;
snprintf(clock->h_clock.name, sizeof(clock->h_clock.name), "H Clock");
clock->h_clock.freq_mhz = ndev->hclk_freq;
if (copy_to_user(u64_to_user_ptr(args->buffer), clock, sizeof(*clock)))
buf_sz = min(args->buffer_size, sizeof(*clock));
if (copy_to_user(u64_to_user_ptr(args->buffer), clock, buf_sz))
ret = -EFAULT;
kfree(clock);
@@ -816,12 +767,14 @@ static int aie2_get_sensors(struct amdxdna_client *client,
scnprintf(sensor.label, sizeof(sensor.label), "Total Power");
scnprintf(sensor.units, sizeof(sensor.units), "mW");
if (args->buffer_size < sizeof(sensor))
goto out;
if (copy_to_user(u64_to_user_ptr(args->buffer), &sensor, sizeof(sensor)))
return -EFAULT;
args->buffer_size -= sizeof(sensor);
sensors_count++;
if (args->buffer_size <= sensors_count * sizeof(sensor))
goto out;
for (i = 0; i < min_t(u32, ndev->total_col, 8); i++) {
memset(&sensor, 0, sizeof(sensor));
@@ -831,13 +784,15 @@ static int aie2_get_sensors(struct amdxdna_client *client,
scnprintf(sensor.label, sizeof(sensor.label), "Column %d Utilization", i);
scnprintf(sensor.units, sizeof(sensor.units), "%%");
if (args->buffer_size < sizeof(sensor))
goto out;
if (copy_to_user(u64_to_user_ptr(args->buffer) + sensors_count * sizeof(sensor),
&sensor, sizeof(sensor)))
return -EFAULT;
args->buffer_size -= sizeof(sensor);
sensors_count++;
if (args->buffer_size <= sensors_count * sizeof(sensor))
goto out;
}
out:
@@ -933,18 +888,21 @@ static int aie2_query_resource_info(struct amdxdna_client *client,
const struct amdxdna_dev_priv *priv;
struct amdxdna_dev_hdl *ndev;
struct amdxdna_dev *xdna;
u32 buf_sz;
xdna = client->xdna;
ndev = xdna->dev_handle;
priv = ndev->priv;
aie2_update_counters(ndev);
res_info.npu_clk_max = priv->dpm_clk_tbl[ndev->max_dpm_level].hclk;
res_info.npu_tops_max = ndev->max_tops;
res_info.npu_task_max = priv->hwctx_limit;
res_info.npu_tops_curr = ndev->curr_tops;
res_info.npu_task_curr = ndev->hwctx_num;
if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, sizeof(res_info)))
buf_sz = min(args->buffer_size, sizeof(res_info));
if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, buf_sz))
return -EFAULT;
return 0;
@@ -980,12 +938,7 @@ static int aie2_get_telemetry(struct amdxdna_client *client,
XDNA_ERR(xdna, "Invalid buffer size");
return -EINVAL;
}
telemetry_data_sz = args->buffer_size - header_sz;
if (telemetry_data_sz > SZ_4M) {
XDNA_ERR(xdna, "Buffer size is too big, %d", telemetry_data_sz);
return -EINVAL;
}
header = kzalloc(header_sz, GFP_KERNEL);
if (!header)
@@ -1026,6 +979,7 @@ static int aie2_get_preempt_state(struct amdxdna_client *client,
struct amdxdna_drm_attribute_state state = {};
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
u32 buf_sz;
ndev = xdna->dev_handle;
if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE)
@@ -1033,7 +987,8 @@ static int aie2_get_preempt_state(struct amdxdna_client *client,
else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE)
state.state = ndev->frame_boundary_preempt;
if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state)))
buf_sz = min(args->buffer_size, sizeof(state));
if (copy_to_user(u64_to_user_ptr(args->buffer), &state, buf_sz))
return -EFAULT;
return 0;
@@ -1042,6 +997,7 @@ static int aie2_get_preempt_state(struct amdxdna_client *client,
static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args)
{
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
int ret, idx;
if (!drm_dev_enter(&xdna->ddev, &idx))
@@ -1056,7 +1012,7 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i
ret = aie2_get_aie_status(client, args);
break;
case DRM_AMDXDNA_QUERY_AIE_METADATA:
ret = aie2_get_aie_metadata(client, args);
ret = amdxdna_get_metadata(&ndev->aie, client, args);
break;
case DRM_AMDXDNA_QUERY_AIE_VERSION:
ret = aie2_get_aie_version(client, args);
@@ -1260,6 +1216,21 @@ static int aie2_set_state(struct amdxdna_client *client,
return ret;
}
static int aie2_get_dev_rev(struct amdxdna_dev *xdna, u32 *rev)
{
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
enum aie2_dev_revision aie2_rev;
int ret;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
ret = aie2_get_dev_revision(ndev, &aie2_rev);
if (!ret)
*rev = (u32)aie2_rev;
return ret;
}
const struct amdxdna_dev_ops aie2_ops = {
.init = aie2_init,
.fini = aie2_fini,
@@ -1274,4 +1245,6 @@ const struct amdxdna_dev_ops aie2_ops = {
.cmd_submit = aie2_cmd_submit,
.hmm_invalidate = aie2_hmm_invalidate,
.get_array = aie2_get_array,
.get_dev_revision = aie2_get_dev_rev,
.hwctx_heap_expand = aie2_hwctx_heap_expand,
};

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
* Copyright (C) 2023-2026, Advanced Micro Devices, Inc.
*/
#ifndef _AIE2_PCI_H_
@@ -10,30 +10,22 @@
#include <linux/limits.h>
#include <linux/semaphore.h>
#include "aie.h"
#include "aie2_msg_priv.h"
#include "amdxdna_mailbox.h"
#define AIE2_INTERVAL 20000 /* us */
#define AIE2_TIMEOUT 1000000 /* us */
/* Firmware determines device memory base address and size */
#define AIE2_DEVM_BASE 0x4000000
#define AIE2_DEVM_SIZE SZ_64M
#define AIE2_DEVM_BASE 0x4000000
#define AIE2_DEVM_SIZE SZ_64M
#define AIE2_DEVM_MAX_SIZE SZ_512M
#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->xdna->ddev.dev))
#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->aie.xdna->ddev.dev))
#define AIE2_SRAM_OFF(ndev, addr) ((addr) - (ndev)->priv->sram_dev_addr)
#define AIE2_MBOX_OFF(ndev, addr) ((addr) - (ndev)->priv->mbox_dev_addr)
#define PSP_REG_BAR(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].bar_idx)
#define PSP_REG_OFF(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].offset)
#define SRAM_REG_OFF(ndev, idx) ((ndev)->priv->sram_offs[(idx)].offset)
#define SMU_REG(ndev, idx) \
({ \
typeof(ndev) _ndev = ndev; \
((_ndev)->smu_base + (_ndev)->priv->smu_regs_off[(idx)].offset); \
})
#define SRAM_GET_ADDR(ndev, idx) \
({ \
typeof(ndev) _ndev = ndev; \
@@ -45,7 +37,7 @@
({ \
typeof(ndev) _ndev = (ndev); \
((_ndev)->priv->mbox_size) ? (_ndev)->priv->mbox_size : \
pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->xdna->dev_info->mbox_bar); \
pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->aie.xdna->dev_info->mbox_bar); \
})
#if IS_ENABLED(CONFIG_AMD_PMF)
@@ -75,68 +67,17 @@
})
#endif
enum aie2_smu_reg_idx {
SMU_CMD_REG = 0,
SMU_ARG_REG,
SMU_INTR_REG,
SMU_RESP_REG,
SMU_OUT_REG,
SMU_MAX_REGS /* Keep this at the end */
};
enum aie2_sram_reg_idx {
MBOX_CHANN_OFF = 0,
FW_ALIVE_OFF,
SRAM_MAX_INDEX /* Keep this at the end */
};
enum psp_reg_idx {
PSP_CMD_REG = 0,
PSP_ARG0_REG,
PSP_ARG1_REG,
PSP_ARG2_REG,
PSP_NUM_IN_REGS, /* number of input registers */
PSP_INTR_REG = PSP_NUM_IN_REGS,
PSP_STATUS_REG,
PSP_RESP_REG,
PSP_PWAITMODE_REG,
PSP_MAX_REGS /* Keep this at the end */
};
struct amdxdna_client;
struct amdxdna_fw_ver;
struct amdxdna_hwctx;
struct amdxdna_sched_job;
struct psp_config {
const void *fw_buf;
u32 fw_size;
void __iomem *psp_regs[PSP_MAX_REGS];
};
struct aie_version {
u16 major;
u16 minor;
};
struct aie_tile_metadata {
u16 row_count;
u16 row_start;
u16 dma_channel_count;
u16 lock_count;
u16 event_reg_count;
};
struct aie_metadata {
u32 size;
u16 cols;
u16 rows;
struct aie_version version;
struct aie_tile_metadata core;
struct aie_tile_metadata mem;
struct aie_tile_metadata shim;
};
enum rt_config_category {
AIE2_RT_CFG_INIT,
AIE2_RT_CFG_CLK_GATING,
@@ -202,24 +143,19 @@ struct aie2_exec_msg_ops {
u32 (*get_chain_msg_op)(u32 cmd_op);
};
enum aie2_tdr_status {
AIE2_TDR_WAIT,
AIE2_TDR_SIGNALED,
};
struct amdxdna_dev_hdl {
struct amdxdna_dev *xdna;
struct aie_device aie;
const struct amdxdna_dev_priv *priv;
void __iomem *sram_base;
void __iomem *smu_base;
void __iomem *mbox_base;
struct psp_device *psp_hdl;
struct xdna_mailbox_chann_res mgmt_x2i;
struct xdna_mailbox_chann_res mgmt_i2x;
u32 mgmt_chan_idx;
u32 mgmt_prot_major;
u32 mgmt_prot_minor;
u32 total_col;
struct aie_version version;
struct aie_metadata metadata;
unsigned long feature_mask;
struct amdxdna_drm_query_aie_version version;
struct aie2_exec_msg_ops *exec_msg_ops;
/* power management and clock*/
@@ -237,63 +173,58 @@ struct amdxdna_dev_hdl {
/* Mailbox and the management channel */
struct mailbox *mbox;
struct mailbox_channel *mgmt_chann;
struct async_events *async_events;
enum aie2_dev_status dev_status;
u32 hwctx_num;
struct amdxdna_async_error last_async_err;
};
#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
[reg_name] = {bar##_BAR_INDEX, (reg_addr) - bar##_BAR_BASE}
struct aie2_bar_off_pair {
int bar_idx;
u32 offset;
enum aie2_tdr_status tdr_status;
};
struct aie2_hw_ops {
int (*set_dpm)(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
int (*update_counters)(struct amdxdna_dev_hdl *ndev);
};
#define aie2_update_counters(ndev) \
({ \
typeof(ndev) _ndev = ndev; \
if (_ndev->priv->hw_ops->update_counters) \
_ndev->priv->hw_ops->update_counters(_ndev); \
})
enum aie2_fw_feature {
AIE2_NPU_COMMAND,
AIE2_PREEMPT,
AIE2_TEMPORAL_ONLY,
AIE2_APP_HEALTH,
AIE2_ADD_HOST_BUFFER,
AIE2_UPDATE_PROPERTY,
AIE2_GET_DEV_REVISION,
AIE2_FEATURE_MAX
};
struct aie2_fw_feature_tbl {
u64 features;
u32 major;
u32 max_minor;
u32 min_minor;
};
#define AIE2_ALL_FEATURES GENMASK_ULL(AIE2_FEATURE_MAX - 1, AIE2_NPU_COMMAND)
#define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask)
struct amdxdna_dev_priv {
const char *fw_path;
const struct rt_config *rt_config;
const struct dpm_clk_freq *dpm_clk_tbl;
const struct aie2_fw_feature_tbl *fw_feature_tbl;
#define COL_ALIGN_NONE 0
#define COL_ALIGN_NATURE 1
u32 col_align;
u32 col_opc;
u32 mbox_dev_addr;
/* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */
u32 mbox_size;
u32 hwctx_limit;
u32 sram_dev_addr;
struct aie2_bar_off_pair sram_offs[SRAM_MAX_INDEX];
struct aie2_bar_off_pair psp_regs_off[PSP_MAX_REGS];
struct aie2_bar_off_pair smu_regs_off[SMU_MAX_REGS];
struct aie2_hw_ops hw_ops;
struct aie_bar_off_pair sram_offs[SRAM_MAX_INDEX];
struct aie_bar_off_pair psp_regs_off[PSP_MAX_REGS];
struct aie_bar_off_pair smu_regs_off[SMU_MAX_REGS];
const struct aie2_hw_ops *hw_ops;
};
extern const struct amdxdna_dev_ops aie2_ops;
@@ -306,25 +237,15 @@ extern const struct dpm_clk_freq npu1_dpm_clk_table[];
extern const struct dpm_clk_freq npu4_dpm_clk_table[];
extern const struct rt_config npu1_default_rt_cfg[];
extern const struct rt_config npu4_default_rt_cfg[];
extern const struct aie2_fw_feature_tbl npu4_fw_feature_table[];
/* aie2_smu.c */
int aie2_smu_init(struct amdxdna_dev_hdl *ndev);
void aie2_smu_fini(struct amdxdna_dev_hdl *ndev);
int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
extern const struct amdxdna_fw_feature_tbl npu4_fw_feature_table[];
extern const struct amdxdna_rev_vbnv npu4_rev_vbnv_tbl[];
extern const struct aie2_hw_ops npu4_hw_ops;
/* aie2_pm.c */
int aie2_pm_init(struct amdxdna_dev_hdl *ndev);
int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target);
int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
/* aie2_psp.c */
struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf);
int aie2_psp_start(struct psp_device *psp);
void aie2_psp_stop(struct psp_device *psp);
int aie2_psp_waitmode_poll(struct psp_device *psp);
/* aie2_error.c */
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev);
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev);
@@ -340,15 +261,19 @@ int aie2_resume_fw(struct amdxdna_dev_hdl *ndev);
int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value);
int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value);
int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid);
int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version);
int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata);
int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev,
struct amdxdna_drm_query_aie_version *version);
int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev,
struct amdxdna_drm_query_aie_metadata *metadata);
int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
struct amdxdna_fw_ver *fw_ver);
int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
struct app_health_report *report);
int aie2_get_dev_revision(struct amdxdna_dev_hdl *ndev, enum aie2_dev_revision *rev);
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
int aie2_add_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled);
int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
char __user *buf, u32 size,
@@ -369,10 +294,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, void __iomem *, size_t));
void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size,
dma_addr_t *dma_addr);
void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size,
void *cpu_addr, dma_addr_t dma_addr);
int aie2_update_prop_time_quota(struct amdxdna_dev_hdl *ndev, u32 us);
/* aie2_hwctx.c */
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
@@ -383,5 +305,6 @@ void aie2_hwctx_suspend(struct amdxdna_client *client);
int aie2_hwctx_resume(struct amdxdna_client *client);
int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, struct amdxdna_gem_obj *heap);
#endif /* _AIE2_PCI_H_ */

View File

@@ -31,14 +31,14 @@ int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
int ret;
ret = amdxdna_pm_resume_get_locked(ndev->xdna);
ret = amdxdna_pm_resume_get_locked(ndev->aie.xdna);
if (ret)
return ret;
ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level);
ret = ndev->priv->hw_ops->set_dpm(ndev, dpm_level);
if (!ret)
ndev->dpm_level = dpm_level;
amdxdna_pm_suspend_put(ndev->xdna);
amdxdna_pm_suspend_put(ndev->aie.xdna);
return ret;
}
@@ -49,7 +49,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
if (ndev->dev_status != AIE2_DEV_UNINIT) {
/* Resume device */
ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->dpm_level);
ret = ndev->priv->hw_ops->set_dpm(ndev, ndev->dpm_level);
if (ret)
return ret;
@@ -64,7 +64,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
ndev->max_dpm_level++;
ndev->max_dpm_level--;
ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->max_dpm_level);
ret = ndev->priv->hw_ops->set_dpm(ndev, ndev->max_dpm_level);
if (ret)
return ret;
ndev->dpm_level = ndev->max_dpm_level;
@@ -74,14 +74,14 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
return ret;
ndev->pw_mode = POWER_MODE_DEFAULT;
ndev->dft_dpm_level = ndev->max_dpm_level;
ndev->dft_dpm_level = 0;
return 0;
}
int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target)
{
struct amdxdna_dev *xdna = ndev->xdna;
struct amdxdna_dev *xdna = ndev->aie.xdna;
u32 clk_gating, dpm_level;
int ret;
@@ -108,6 +108,14 @@ int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = ndev->dft_dpm_level;
break;
case POWER_MODE_LOW:
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = 0;
break;
case POWER_MODE_MEDIUM:
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = ndev->max_dpm_level / 2;
break;
default:
return -EOPNOTSUPP;
}

View File

@@ -1,161 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
*/
#include <drm/drm_device.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include "aie2_pci.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_pci_drv.h"
#define PSP_STATUS_READY BIT(31)
/* PSP commands */
#define PSP_VALIDATE 1
#define PSP_START 2
#define PSP_RELEASE_TMR 3
/* PSP special arguments */
#define PSP_START_COPY_FW 1
/* PSP response error code */
#define PSP_ERROR_CANCEL 0xFFFF0002
#define PSP_ERROR_BAD_STATE 0xFFFF0007
#define PSP_FW_ALIGN 0x10000
#define PSP_POLL_INTERVAL 20000 /* us */
#define PSP_POLL_TIMEOUT 1000000 /* us */
#define PSP_REG(p, reg) ((p)->psp_regs[reg])
struct psp_device {
struct drm_device *ddev;
struct psp_config conf;
u32 fw_buf_sz;
u64 fw_paddr;
void *fw_buffer;
void __iomem *psp_regs[PSP_MAX_REGS];
};
static int psp_exec(struct psp_device *psp, u32 *reg_vals)
{
u32 resp_code;
int ret, i;
u32 ready;
/* Write command and argument registers */
for (i = 0; i < PSP_NUM_IN_REGS; i++)
writel(reg_vals[i], PSP_REG(psp, i));
/* clear and set PSP INTR register to kick off */
writel(0, PSP_REG(psp, PSP_INTR_REG));
writel(1, PSP_REG(psp, PSP_INTR_REG));
/* PSP should be busy. Wait for ready, so we know task is done. */
ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
FIELD_GET(PSP_STATUS_READY, ready),
PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
if (ret) {
drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
return ret;
}
resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
if (resp_code) {
drm_err(psp->ddev, "fw return error 0x%x", resp_code);
return -EIO;
}
return 0;
}
int aie2_psp_waitmode_poll(struct psp_device *psp)
{
struct amdxdna_dev *xdna = to_xdna_dev(psp->ddev);
u32 mode_reg;
int ret;
ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_PWAITMODE_REG), mode_reg,
(mode_reg & 0x1) == 1,
PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
if (ret)
XDNA_ERR(xdna, "fw waitmode reg error, ret %d", ret);
return ret;
}
void aie2_psp_stop(struct psp_device *psp)
{
u32 reg_vals[PSP_NUM_IN_REGS] = { PSP_RELEASE_TMR, };
int ret;
ret = psp_exec(psp, reg_vals);
if (ret)
drm_err(psp->ddev, "release tmr failed, ret %d", ret);
}
int aie2_psp_start(struct psp_device *psp)
{
u32 reg_vals[PSP_NUM_IN_REGS];
int ret;
reg_vals[0] = PSP_VALIDATE;
reg_vals[1] = lower_32_bits(psp->fw_paddr);
reg_vals[2] = upper_32_bits(psp->fw_paddr);
reg_vals[3] = psp->fw_buf_sz;
ret = psp_exec(psp, reg_vals);
if (ret) {
drm_err(psp->ddev, "failed to validate fw, ret %d", ret);
return ret;
}
memset(reg_vals, 0, sizeof(reg_vals));
reg_vals[0] = PSP_START;
reg_vals[1] = PSP_START_COPY_FW;
ret = psp_exec(psp, reg_vals);
if (ret) {
drm_err(psp->ddev, "failed to start fw, ret %d", ret);
return ret;
}
return 0;
}
struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf)
{
struct psp_device *psp;
u64 offset;
psp = drmm_kzalloc(ddev, sizeof(*psp), GFP_KERNEL);
if (!psp)
return NULL;
psp->ddev = ddev;
memcpy(psp->psp_regs, conf->psp_regs, sizeof(psp->psp_regs));
psp->fw_buf_sz = ALIGN(conf->fw_size, PSP_FW_ALIGN);
psp->fw_buffer = drmm_kmalloc(ddev, psp->fw_buf_sz + PSP_FW_ALIGN, GFP_KERNEL);
if (!psp->fw_buffer) {
drm_err(ddev, "no memory for fw buffer");
return NULL;
}
/*
* AMD Platform Security Processor(PSP) requires host physical
* address to load NPU firmware.
*/
psp->fw_paddr = virt_to_phys(psp->fw_buffer);
offset = ALIGN(psp->fw_paddr, PSP_FW_ALIGN) - psp->fw_paddr;
psp->fw_paddr += offset;
memcpy(psp->fw_buffer + offset, conf->fw_buf, conf->fw_size);
return psp;
}

View File

@@ -1,156 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
*/
#include <drm/drm_device.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <linux/iopoll.h>
#include "aie2_pci.h"
#include "amdxdna_pci_drv.h"
#define SMU_RESULT_OK 1
/* SMU commands */
#define AIE2_SMU_POWER_ON 0x3
#define AIE2_SMU_POWER_OFF 0x4
#define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5
#define AIE2_SMU_SET_HCLK_FREQ 0x6
#define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7
#define AIE2_SMU_SET_HARD_DPMLEVEL 0x8
#define NPU4_DPM_TOPS(ndev, dpm_level) \
({ \
typeof(ndev) _ndev = ndev; \
(4096 * (_ndev)->total_col * \
(_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
})
static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
u32 reg_arg, u32 *out)
{
u32 resp;
int ret;
writel(0, SMU_REG(ndev, SMU_RESP_REG));
writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG));
writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG));
/* Clear and set SMU_INTR_REG to kick off */
writel(0, SMU_REG(ndev, SMU_INTR_REG));
writel(1, SMU_REG(ndev, SMU_INTR_REG));
ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp,
resp, AIE2_INTERVAL, AIE2_TIMEOUT);
if (ret) {
XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd);
return ret;
}
if (out)
*out = readl(SMU_REG(ndev, SMU_OUT_REG));
if (resp != SMU_RESULT_OK) {
XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp);
return -EINVAL;
}
return 0;
}
int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
u32 freq;
int ret;
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
if (ret) {
XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
return ret;
}
ndev->npuclk_freq = freq;
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq);
if (ret) {
XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
return ret;
}
ndev->hclk_freq = freq;
ndev->max_tops = 2 * ndev->total_col;
ndev->curr_tops = ndev->max_tops * freq / 1028;
XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
ndev->npuclk_freq, ndev->hclk_freq);
return 0;
}
int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
int ret;
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
dpm_level, ret);
return ret;
}
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
dpm_level, ret);
return ret;
}
ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
ndev->npuclk_freq, ndev->hclk_freq);
return 0;
}
int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
{
int ret;
/*
* Failing to set power off indicates an unrecoverable hardware or
* firmware error.
*/
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
return ret;
}
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
return ret;
}
return 0;
}
void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
{
int ret;
ndev->priv->hw_ops.set_dpm(ndev, 0);
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
if (ret)
XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret);
}

View File

@@ -52,7 +52,7 @@ static u32 calculate_gops(struct aie_qos *rqos)
u32 service_rate = 0;
if (rqos->latency)
service_rate = (1000 / rqos->latency);
service_rate = max_t(u32, 1000 / rqos->latency, 1);
if (rqos->fps > service_rate)
return rqos->fps * rqos->gops;
@@ -348,6 +348,7 @@ int xrs_release_resource(void *hdl, u64 rid)
{
struct solver_state *xrs = hdl;
struct solver_node *node;
u32 level = 0;
node = rg_search_node(&xrs->rgp, rid);
if (!node) {
@@ -358,6 +359,13 @@ int xrs_release_resource(void *hdl, u64 rid)
xrs->cfg.actions->unload(node->cb_arg);
remove_solver_node(&xrs->rgp, node);
/* set the dpm level which fits all the sessions */
list_for_each_entry(node, &xrs->rgp.node_list, list) {
if (node->dpm_level > level)
level = node->dpm_level;
}
xrs->cfg.actions->set_dft_dpm_level(xrs->cfg.ddev, level);
return 0;
}

View File

@@ -0,0 +1,333 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/amdxdna_accel.h>
#include <drm/drm_device.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <linux/types.h>
#include "aie.h"
#include "aie4_host_queue.h"
#include "aie4_msg_priv.h"
#include "aie4_pci.h"
#include "amdxdna_ctx.h"
#include "amdxdna_gem.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
static irqreturn_t cert_comp_isr(int irq, void *p)
{
struct cert_comp *cert_comp = p;
wake_up_all(&cert_comp->waitq);
return IRQ_HANDLED;
}
static struct cert_comp *aie4_lookup_cert_comp(struct amdxdna_dev_hdl *ndev, u32 msix_idx)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
struct cert_comp *cert_comp;
int ret;
guard(mutex)(&ndev->cert_comp_lock);
cert_comp = xa_load(&ndev->cert_comp_xa, msix_idx);
if (cert_comp) {
kref_get(&cert_comp->kref);
return cert_comp;
}
cert_comp = kzalloc_obj(*cert_comp);
if (!cert_comp)
return NULL;
cert_comp->ndev = ndev;
cert_comp->msix_idx = msix_idx;
init_waitqueue_head(&cert_comp->waitq);
kref_init(&cert_comp->kref);
ret = pci_irq_vector(pdev, cert_comp->msix_idx);
if (ret < 0) {
XDNA_ERR(xdna, "MSI-X idx %u is invalid, ret:%d", msix_idx, ret);
goto free_cert_comp;
}
cert_comp->irq = ret;
ret = request_irq(cert_comp->irq, cert_comp_isr, 0, "xdna_hsa", cert_comp);
if (ret) {
XDNA_ERR(xdna, "request irq %d failed %d", cert_comp->irq, ret);
goto free_cert_comp;
}
ret = xa_err(xa_store(&ndev->cert_comp_xa, msix_idx, cert_comp, GFP_KERNEL));
if (ret) {
XDNA_ERR(xdna, "store cert_comp for msix idx %d failed %d", msix_idx, ret);
goto free_irq;
}
return cert_comp;
free_irq:
free_irq(cert_comp->irq, cert_comp);
free_cert_comp:
kfree(cert_comp);
return NULL;
}
static void cert_comp_release(struct kref *kref)
{
struct cert_comp *cert_comp = container_of(kref, struct cert_comp, kref);
struct amdxdna_dev_hdl *ndev = cert_comp->ndev;
drm_WARN_ON(&ndev->aie.xdna->ddev, !mutex_is_locked(&ndev->cert_comp_lock));
xa_erase(&ndev->cert_comp_xa, cert_comp->msix_idx);
free_irq(cert_comp->irq, cert_comp);
kfree(cert_comp);
}
static void aie4_put_cert_comp(struct cert_comp *cert_comp)
{
struct amdxdna_dev_hdl *ndev;
ndev = cert_comp->ndev;
guard(mutex)(&ndev->cert_comp_lock);
kref_put(&cert_comp->kref, cert_comp_release);
}
static int aie4_msg_destroy_context(struct amdxdna_dev_hdl *ndev, u32 hw_context_id)
{
DECLARE_AIE_MSG(aie4_msg_destroy_hw_context, AIE4_MSG_OP_DESTROY_HW_CONTEXT);
req.hw_context_id = hw_context_id;
return aie_send_mgmt_msg_wait(&ndev->aie, &msg);
}
static int aie4_hwctx_create(struct amdxdna_hwctx *hwctx)
{
DECLARE_AIE_MSG(aie4_msg_create_hw_context, AIE4_MSG_OP_CREATE_HW_CONTEXT);
struct amdxdna_client *client = hwctx->client;
struct amdxdna_hwctx_priv *priv = hwctx->priv;
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
int ret;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
if (!ndev->partition_id || !hwctx->num_tiles) {
XDNA_ERR(xdna, "invalid request partition_id %d, num_tiles %d",
ndev->partition_id, hwctx->num_tiles);
return -EINVAL;
}
req.partition_id = ndev->partition_id;
req.request_num_tiles = hwctx->num_tiles;
req.pasid = FIELD_PREP(AIE4_MSG_PASID, client->pasid) |
FIELD_PREP(AIE4_MSG_PASID_VLD, 1);
req.priority_band = hwctx->qos.priority;
req.hsa_addr_high = upper_32_bits(amdxdna_gem_dev_addr(priv->umq_bo));
req.hsa_addr_low = lower_32_bits(amdxdna_gem_dev_addr(priv->umq_bo));
XDNA_DBG(xdna, "pasid 0x%x, num_tiles %d, hsa[0x%x 0x%x]",
req.pasid, req.request_num_tiles, req.hsa_addr_high, req.hsa_addr_low);
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "create ctx failed: %d", ret);
return ret;
}
XDNA_DBG(xdna, "resp msix: %d, ctx id: %d, doorbell: %d",
resp.job_complete_msix_idx,
resp.hw_context_id,
resp.doorbell_offset);
/* setup interrupt completion per msix index */
priv->cert_comp = aie4_lookup_cert_comp(ndev, resp.job_complete_msix_idx);
if (!priv->cert_comp) {
aie4_msg_destroy_context(ndev, resp.hw_context_id);
return -EINVAL;
}
priv->hw_ctx_id = resp.hw_context_id;
hwctx->doorbell_offset = resp.doorbell_offset;
return 0;
}
static void aie4_hwctx_destroy(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_hwctx_priv *priv = hwctx->priv;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
aie4_msg_destroy_context(ndev, priv->hw_ctx_id);
aie4_put_cert_comp(priv->cert_comp);
}
static void aie4_hwctx_umq_fini(struct amdxdna_hwctx *hwctx)
{
if (hwctx->priv && hwctx->priv->umq_bo)
amdxdna_gem_put_obj(hwctx->priv->umq_bo);
}
static int aie4_hwctx_umq_init(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_hwctx_priv *priv = hwctx->priv;
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct amdxdna_gem_obj *umq_bo;
struct host_queue_header *qhdr;
int ret;
umq_bo = amdxdna_gem_get_obj(hwctx->client, hwctx->umq_bo_hdl, AMDXDNA_BO_SHARE);
if (!umq_bo) {
XDNA_ERR(xdna, "cannot find umq_bo handle %d", hwctx->umq_bo_hdl);
return -ENOENT;
}
if (umq_bo->mem.size < sizeof(*qhdr)) {
XDNA_ERR(xdna, "umq_bo size is too small");
ret = -EINVAL;
goto put_umq_bo;
}
/* get kva address for host queue read index and write index */
qhdr = amdxdna_gem_vmap(umq_bo);
if (!qhdr) {
ret = -ENOMEM;
goto put_umq_bo;
}
priv->umq_bo = umq_bo;
priv->umq_read_index = &qhdr->read_index;
priv->umq_write_index = &qhdr->write_index;
return 0;
put_umq_bo:
amdxdna_gem_put_obj(umq_bo);
return ret;
}
int aie4_hwctx_init(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_hwctx_priv *priv;
int ret;
priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
hwctx->priv = priv;
ret = aie4_hwctx_umq_init(hwctx);
if (ret)
goto free_priv;
ret = aie4_hwctx_create(hwctx);
if (ret)
goto umq_fini;
XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name);
return 0;
umq_fini:
aie4_hwctx_umq_fini(hwctx);
free_priv:
kfree(priv);
hwctx->priv = NULL;
return ret;
}
void aie4_hwctx_fini(struct amdxdna_hwctx *hwctx)
{
aie4_hwctx_destroy(hwctx);
aie4_hwctx_umq_fini(hwctx);
kfree(hwctx->priv);
}
static inline bool valid_queue_index(u64 read, u64 write, u32 capacity)
{
return (write >= read) && ((write - read) <= capacity);
}
static u64 get_read_index(struct amdxdna_hwctx *hwctx)
{
u64 wi = READ_ONCE(*hwctx->priv->umq_write_index);
u64 ri = READ_ONCE(*hwctx->priv->umq_read_index);
struct amdxdna_dev *xdna = hwctx->client->xdna;
/*
* CERT cannot update read index as uint64 atomically. Driver may read
* half-updated read index when it has bits in high 32bit. In case read
* index is not valid, wait for some time and retry once. It should
* allow CERT to complete the read index update.
*/
if (!valid_queue_index(ri, wi, CTX_MAX_CMDS)) {
XDNA_WARN(xdna, "Invalid index, ri %llu, wi %llu", ri, wi);
usleep_range(100, 200);
ri = READ_ONCE(*hwctx->priv->umq_read_index);
if (!valid_queue_index(ri, wi, CTX_MAX_CMDS)) {
XDNA_ERR(xdna, "Invalid index after retry, ri %llu, wi %llu", ri, wi);
ri = 0;
}
}
return ri;
}
static inline bool check_cmd_done(struct amdxdna_hwctx *hwctx, u64 seq)
{
u64 read_idx = get_read_index(hwctx);
return read_idx > seq;
}
int aie4_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout)
{
unsigned long wait_jifs = MAX_SCHEDULE_TIMEOUT;
struct amdxdna_hwctx_priv *priv = hwctx->priv;
struct cert_comp *cert_comp = priv->cert_comp;
long ret;
if (timeout)
wait_jifs = msecs_to_jiffies(timeout);
ret = wait_event_interruptible_timeout(cert_comp->waitq,
(check_cmd_done(hwctx, seq)),
wait_jifs);
if (!ret)
ret = -ETIME;
return ret <= 0 ? ret : 0;
}
int aie4_hwctx_valid_doorbell(struct amdxdna_client *client, u32 vm_pgoff)
{
struct amdxdna_hwctx *hwctx;
unsigned long hwctx_id;
int idx;
idx = srcu_read_lock(&client->hwctx_srcu);
amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
if (vm_pgoff == (hwctx->doorbell_offset >> PAGE_SHIFT)) {
srcu_read_unlock(&client->hwctx_srcu, idx);
return 1;
}
}
srcu_read_unlock(&client->hwctx_srcu, idx);
return 0;
}

View File

@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AIE4_HOST_QUEUE_H_
#define _AIE4_HOST_QUEUE_H_
#include <linux/types.h>
#define CTX_MAX_CMDS 32
struct host_queue_header {
__u64 read_index;
struct {
__u16 major;
__u16 minor;
} version;
__u32 capacity; /* Queue capacity, must be power of two. */
__u64 write_index;
__u64 data_address; /* The xdna dev addr for payload. */
};
#endif /* _AIE4_HOST_QUEUE_H_ */

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/amdxdna_accel.h>
#include <drm/drm_print.h>
#include <linux/mutex.h>
#include "aie.h"
#include "aie4_msg_priv.h"
#include "aie4_pci.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
int aie4_suspend_fw(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE_MSG(aie4_msg_suspend, AIE4_MSG_OP_SUSPEND);
int ret;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
XDNA_ERR(ndev->aie.xdna, "Failed to suspend fw, ret %d", ret);
return ret;
}
int aie4_query_aie_metadata(struct amdxdna_dev_hdl *ndev,
struct amdxdna_drm_query_aie_metadata *metadata)
{
DECLARE_AIE_MSG(aie4_msg_aie4_tile_info, AIE4_MSG_OP_AIE_TILE_INFO);
int ret;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
metadata->col_size = resp.info.size;
metadata->cols = resp.info.cols;
metadata->rows = resp.info.rows;
metadata->version.major = resp.info.major;
metadata->version.minor = resp.info.minor;
metadata->core.row_count = resp.info.core_rows;
metadata->core.row_start = resp.info.core_row_start;
metadata->core.dma_channel_count = resp.info.core_dma_channels;
metadata->core.lock_count = resp.info.core_locks;
metadata->core.event_reg_count = resp.info.core_events;
metadata->mem.row_count = resp.info.mem_rows;
metadata->mem.row_start = resp.info.mem_row_start;
metadata->mem.dma_channel_count = resp.info.mem_dma_channels;
metadata->mem.lock_count = resp.info.mem_locks;
metadata->mem.event_reg_count = resp.info.mem_events;
metadata->shim.row_count = resp.info.shim_rows;
metadata->shim.row_start = resp.info.shim_row_start;
metadata->shim.dma_channel_count = resp.info.shim_dma_channels;
metadata->shim.lock_count = resp.info.shim_locks;
metadata->shim.event_reg_count = resp.info.shim_events;
return 0;
}
int aie4_attach_work_buffer(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE_MSG(aie4_msg_attach_work_buffer, AIE4_MSG_OP_ATTACH_WORK_BUFFER);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
req.buff_addr = ndev->work_buf_addr;
req.buff_size = AIE4_WORK_BUFFER_MIN_SIZE;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
XDNA_ERR(xdna, "Failed to attach work buffer, ret %d", ret);
else
XDNA_DBG(xdna, "Attached work buffer");
return ret;
}

View File

@@ -0,0 +1,147 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AIE4_MSG_PRIV_H_
#define _AIE4_MSG_PRIV_H_
#include <linux/sizes.h>
#include <linux/types.h>
enum aie4_msg_opcode {
AIE4_MSG_OP_SUSPEND = 0x10003,
AIE4_MSG_OP_ATTACH_WORK_BUFFER = 0x1000D,
AIE4_MSG_OP_CREATE_VFS = 0x20001,
AIE4_MSG_OP_DESTROY_VFS = 0x20002,
AIE4_MSG_OP_CREATE_PARTITION = 0x30001,
AIE4_MSG_OP_DESTROY_PARTITION = 0x30002,
AIE4_MSG_OP_CREATE_HW_CONTEXT = 0x30003,
AIE4_MSG_OP_DESTROY_HW_CONTEXT = 0x30004,
AIE4_MSG_OP_AIE_TILE_INFO = 0x30006,
};
enum aie4_msg_status {
AIE4_MSG_STATUS_SUCCESS = 0x0,
AIE4_MSG_STATUS_ERROR = 0x1,
AIE4_MSG_STATUS_NOTSUPP = 0x2,
MAX_AIE4_MSG_STATUS_CODE = 0x4,
};
struct aie4_msg_suspend_req {
__u32 rsvd;
} __packed;
struct aie4_msg_suspend_resp {
enum aie4_msg_status status;
} __packed;
struct aie4_msg_create_vfs_req {
__u32 vf_cnt;
} __packed;
struct aie4_msg_create_vfs_resp {
enum aie4_msg_status status;
} __packed;
struct aie4_msg_destroy_vfs_req {
__u32 rsvd;
} __packed;
struct aie4_msg_destroy_vfs_resp {
enum aie4_msg_status status;
} __packed;
struct aie4_msg_create_partition_req {
__u32 partition_col_start;
__u32 partition_col_count;
} __packed;
struct aie4_msg_create_partition_resp {
enum aie4_msg_status status;
__u32 partition_id;
} __packed;
struct aie4_msg_destroy_partition_req {
__u32 partition_id;
} __packed;
struct aie4_msg_destroy_partition_resp {
enum aie4_msg_status status;
} __packed;
struct aie4_msg_create_hw_context_req {
__u32 partition_id;
__u32 request_num_tiles;
__u32 hsa_addr_high;
__u32 hsa_addr_low;
#define AIE4_MSG_PASID GENMASK(19, 0)
#define AIE4_MSG_PASID_VLD GENMASK(31, 31)
__u32 pasid;
__u32 priority_band;
} __packed;
struct aie4_msg_create_hw_context_resp {
enum aie4_msg_status status;
__u32 hw_context_id;
__u32 doorbell_offset;
__u32 job_complete_msix_idx;
} __packed;
struct aie4_msg_destroy_hw_context_req {
__u32 hw_context_id;
__u32 resvd1;
} __packed;
struct aie4_msg_destroy_hw_context_resp {
enum aie4_msg_status status;
} __packed;
struct aie4_tile_info {
__u32 size;
__u16 major;
__u16 minor;
__u16 cols;
__u16 rows;
__u16 core_rows;
__u16 mem_rows;
__u16 shim_rows;
__u16 core_row_start;
__u16 mem_row_start;
__u16 shim_row_start;
__u16 core_dma_channels;
__u16 mem_dma_channels;
__u16 shim_dma_channels;
__u16 core_locks;
__u16 mem_locks;
__u16 shim_locks;
__u16 core_events;
__u16 mem_events;
__u16 shim_events;
__u16 resvd;
} __packed;
struct aie4_msg_aie4_tile_info_req {
__u32 resvd;
} __packed;
struct aie4_msg_aie4_tile_info_resp {
enum aie4_msg_status status;
struct aie4_tile_info info;
} __packed;
#define AIE4_WORK_BUFFER_MIN_SIZE SZ_4M
struct aie4_msg_attach_work_buffer_req {
__u64 buff_addr;
__u32 reserved;
__u32 buff_size;
} __packed;
struct aie4_msg_attach_work_buffer_resp {
enum aie4_msg_status status;
} __packed;
#endif /* _AIE4_MSG_PRIV_H_ */

View File

@@ -0,0 +1,667 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/amdxdna_accel.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <linux/firmware.h>
#include <linux/sizes.h>
#include "aie.h"
#include "aie4_msg_priv.h"
#include "aie4_pci.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
#define NO_IOHUB 0
#define PSP_NOTIFY_INTR 0xD007BE11
#define AIE4_TOTAL_COLUMN 3
/*
* The management mailbox channel is allocated by firmware.
* The related register and ring buffer information is on SRAM BAR.
* This struct is the register layout.
*/
struct mailbox_info {
__u32 valid;
__u32 protocol_major;
__u32 protocol_minor;
__u32 x2i_tail_offset;
__u32 x2i_head_offset;
__u32 x2i_buffer_addr;
__u32 x2i_buffer_size;
__u32 i2x_tail_offset;
__u32 i2x_head_offset;
__u32 i2x_buffer_addr;
__u32 i2x_buffer_size;
__u32 i2x_msi_idx;
__u32 reserved[4];
};
static int aie4_fw_is_alive(struct amdxdna_dev *xdna)
{
const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
u32 __iomem *src;
u32 fw_is_valid;
int ret;
src = ndev->rbuf_base + npriv->mbox_info_off;
ret = readx_poll_timeout(readl, src + offsetof(struct mailbox_info, valid),
fw_is_valid, (fw_is_valid == 0x1),
AIE_INTERVAL, AIE_TIMEOUT);
if (ret)
XDNA_ERR(xdna, "fw_is_valid=%d after %d ms",
fw_is_valid, DIV_ROUND_CLOSEST(AIE_TIMEOUT, 1000000));
return ret;
}
static void aie4_read_mbox_info(struct amdxdna_dev *xdna,
struct mailbox_info *mbox_info)
{
const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
u32 *dst = (u32 *)mbox_info;
u32 __iomem *src;
int i;
src = ndev->rbuf_base + npriv->mbox_info_off;
for (i = 0; i < sizeof(*mbox_info) / sizeof(u32); i++)
dst[i] = readl(&src[i]);
}
static int aie4_mailbox_info(struct amdxdna_dev *xdna,
struct mailbox_info *mbox_info)
{
int ret;
ret = aie4_fw_is_alive(xdna);
if (ret)
return ret;
aie4_read_mbox_info(xdna, mbox_info);
ret = aie_check_protocol(&xdna->dev_handle->aie,
mbox_info->protocol_major,
mbox_info->protocol_minor);
if (ret)
XDNA_ERR(xdna, "mailbox major.minor %d.%d is not supported",
mbox_info->protocol_major, mbox_info->protocol_minor);
return ret;
}
static void aie4_mailbox_fini(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
aie_destroy_chann(&ndev->aie, &ndev->aie.mgmt_chann);
drmm_kfree(&xdna->ddev, ndev->mbox);
ndev->mbox = NULL;
}
static int aie4_irq_init(struct amdxdna_dev *xdna)
{
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
int ret, nvec;
nvec = pci_msix_vec_count(pdev);
XDNA_DBG(xdna, "irq vectors:%d", nvec);
if (nvec <= 0) {
XDNA_ERR(xdna, "does not get number of interrupt vector");
return -EINVAL;
}
ret = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
if (ret < 0) {
XDNA_ERR(xdna, "failed to alloc irq vector, ret: %d", ret);
return ret;
}
return 0;
}
static int aie4_mailbox_start(struct amdxdna_dev *xdna,
struct mailbox_info *mbi)
{
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
struct xdna_mailbox_chann_res *i2x;
struct xdna_mailbox_chann_res *x2i;
int mgmt_mb_irq;
int ret;
struct xdna_mailbox_res mbox_res = {
.ringbuf_base = ndev->rbuf_base,
.ringbuf_size = pci_resource_len(pdev, npriv->mbox_rbuf_bar),
.mbox_base = ndev->mbox_base,
.mbox_size = pci_resource_len(pdev, npriv->mbox_bar),
.name = "xdna_aie4_mailbox",
};
i2x = &ndev->aie.mgmt_i2x;
x2i = &ndev->aie.mgmt_x2i;
x2i->mb_head_ptr_reg = mbi->x2i_head_offset;
x2i->mb_tail_ptr_reg = mbi->x2i_tail_offset;
x2i->rb_start_addr = mbi->x2i_buffer_addr;
x2i->rb_size = mbi->x2i_buffer_size;
i2x->rb_start_addr = mbi->i2x_buffer_addr;
i2x->rb_size = mbi->i2x_buffer_size;
i2x->mb_head_ptr_reg = mbi->i2x_head_offset;
i2x->mb_tail_ptr_reg = mbi->i2x_tail_offset;
ndev->aie.mgmt_chan_idx = mbi->i2x_msi_idx;
aie_dump_mgmt_chann_debug(&ndev->aie);
ndev->mbox = xdnam_mailbox_create(&xdna->ddev, &mbox_res);
if (!ndev->mbox) {
XDNA_ERR(xdna, "failed to create mailbox device");
return -ENODEV;
}
ndev->aie.mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox);
if (!ndev->aie.mgmt_chann) {
XDNA_ERR(xdna, "failed to alloc mailbox channel");
return -ENODEV;
}
mgmt_mb_irq = pci_irq_vector(pdev, ndev->aie.mgmt_chan_idx);
if (mgmt_mb_irq < 0) {
XDNA_ERR(xdna, "failed to alloc irq vector, return %d", mgmt_mb_irq);
ret = mgmt_mb_irq;
goto free_channel;
}
ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann,
&ndev->aie.mgmt_x2i,
&ndev->aie.mgmt_i2x,
NO_IOHUB,
mgmt_mb_irq);
if (ret) {
XDNA_ERR(xdna, "failed to start management mailbox channel");
ret = -EINVAL;
goto free_channel;
}
XDNA_DBG(xdna, "Mailbox management channel created");
return 0;
free_channel:
xdna_mailbox_free_channel(ndev->aie.mgmt_chann);
ndev->aie.mgmt_chann = NULL;
return ret;
}
static int aie4_mailbox_init(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct mailbox_info mbox_info;
int ret;
ret = aie4_mailbox_info(xdna, &mbox_info);
if (ret)
return ret;
return aie4_mailbox_start(xdna, &mbox_info);
}
static void aie4_fw_stop(struct amdxdna_dev_hdl *ndev)
{
aie_psp_stop(ndev->aie.psp_hdl);
aie_smu_fini(ndev->aie.smu_hdl);
}
static int aie4_fw_start(struct amdxdna_dev_hdl *ndev)
{
int ret;
ret = aie_smu_init(ndev->aie.smu_hdl);
if (ret) {
XDNA_ERR(ndev->aie.xdna, "failed to init smu, ret %d", ret);
return ret;
}
ret = aie_psp_start(ndev->aie.psp_hdl);
if (ret) {
XDNA_ERR(ndev->aie.xdna, "failed to start psp, ret %d", ret);
aie_smu_fini(ndev->aie.smu_hdl);
}
return ret;
}
static int aie4_partition_init(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE_MSG(aie4_msg_create_partition, AIE4_MSG_OP_CREATE_PARTITION);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
req.partition_col_start = 0;
req.partition_col_count = AIE4_TOTAL_COLUMN;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "partition init failed: %d", ret);
return ret;
}
ndev->partition_id = resp.partition_id;
return 0;
}
static void aie4_partition_fini(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE_MSG(aie4_msg_destroy_partition, AIE4_MSG_OP_DESTROY_PARTITION);
struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
req.partition_id = ndev->partition_id;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
XDNA_ERR(xdna, "partition fini failed: %d", ret);
}
static int aie4_query(struct amdxdna_dev_hdl *ndev)
{
return aie4_query_aie_metadata(ndev, &ndev->aie.metadata);
}
static int aie4_pf_hw_start(struct amdxdna_dev_hdl *ndev)
{
int ret;
ret = aie4_fw_start(ndev);
if (ret)
return ret;
ret = aie4_mailbox_init(ndev);
if (ret)
goto stop_fw;
ret = aie4_attach_work_buffer(ndev);
if (ret)
goto mbox_fini;
return 0;
mbox_fini:
aie4_mailbox_fini(ndev);
stop_fw:
aie4_fw_stop(ndev);
return ret;
}
static void aie4_pf_hw_stop(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
aie4_suspend_fw(ndev);
aie4_mailbox_fini(ndev);
aie4_fw_stop(ndev);
}
static int aie4_vf_hw_start(struct amdxdna_dev_hdl *ndev)
{
int ret;
ret = aie4_mailbox_init(ndev);
if (ret)
return ret;
ret = aie4_query(ndev);
if (ret)
goto mailbox_fini;
ret = aie4_partition_init(ndev);
if (ret)
goto mailbox_fini;
return 0;
mailbox_fini:
aie4_mailbox_fini(ndev);
return ret;
}
static void aie4_vf_hw_stop(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
aie4_partition_fini(ndev);
aie4_mailbox_fini(ndev);
}
static int aie4_request_firmware(struct amdxdna_dev_hdl *ndev,
const struct firmware **npufw,
const struct firmware **certfw)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
char fw_name[128];
int ret;
ret = snprintf(fw_name, sizeof(fw_name), "amdnpu/%04x_%02x/%s",
pdev->device, pdev->revision, ndev->priv->npufw_path);
if (ret >= sizeof(fw_name)) {
XDNA_ERR(xdna, "npu firmware path is truncated");
return -EINVAL;
}
ret = request_firmware(npufw, fw_name, &pdev->dev);
if (ret) {
XDNA_ERR(xdna, "failed to request_firmware %s, ret %d", fw_name, ret);
return ret;
}
ret = snprintf(fw_name, sizeof(fw_name), "amdnpu/%04x_%02x/%s",
pdev->device, pdev->revision, ndev->priv->certfw_path);
if (ret >= sizeof(fw_name)) {
XDNA_ERR(xdna, "cert firmware path is truncated");
ret = -EINVAL;
goto release_npufw;
}
ret = request_firmware(certfw, fw_name, &pdev->dev);
if (ret) {
XDNA_ERR(xdna, "failed to request_firmware %s, ret %d", fw_name, ret);
goto release_npufw;
}
return 0;
release_npufw:
release_firmware(*npufw);
return ret;
}
static void aie4_release_firmware(struct amdxdna_dev_hdl *ndev,
const struct firmware *npufw,
const struct firmware *certfw)
{
release_firmware(certfw);
release_firmware(npufw);
}
static int aie4_prepare_firmware(struct amdxdna_dev_hdl *ndev,
const struct firmware *npufw,
const struct firmware *certfw,
void __iomem *tbl[PCI_NUM_RESOURCES])
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct psp_config psp_conf;
struct smu_config smu_conf;
int i;
psp_conf.fw_size = npufw->size;
psp_conf.fw_buf = npufw->data;
psp_conf.certfw_size = certfw->size;
psp_conf.certfw_buf = certfw->data;
psp_conf.arg2_mask = ~0;
psp_conf.notify_val = PSP_NOTIFY_INTR;
for (i = 0; i < PSP_MAX_REGS; i++)
psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(ndev, i)] + PSP_REG_OFF(ndev, i);
ndev->aie.psp_hdl = aiem_psp_create(&xdna->ddev, &psp_conf);
if (!ndev->aie.psp_hdl) {
XDNA_ERR(xdna, "failed to create psp");
return -ENOMEM;
}
for (i = 0; i < SMU_MAX_REGS; i++)
smu_conf.smu_regs[i] = tbl[SMU_REG_BAR(ndev, i)] + SMU_REG_OFF(ndev, i);
ndev->aie.smu_hdl = aiem_smu_create(&xdna->ddev, &smu_conf);
if (!ndev->aie.smu_hdl) {
XDNA_ERR(xdna, "failed to create smu");
return -ENOMEM;
}
return 0;
}
static int aie4_load_fw(struct amdxdna_dev_hdl *ndev,
void __iomem *tbl[PCI_NUM_RESOURCES])
{
const struct firmware *npufw, *certfw;
int ret;
if (!ndev->priv->npufw_path && !ndev->priv->certfw_path)
return 0;
ret = aie4_request_firmware(ndev, &npufw, &certfw);
if (ret)
return ret;
ret = aie4_prepare_firmware(ndev, npufw, certfw, tbl);
aie4_release_firmware(ndev, npufw, certfw);
return ret;
}
static int aie4m_pcidev_init(struct amdxdna_dev *xdna)
{
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
struct amdxdna_dev_hdl *ndev;
void __iomem *tbl[PCI_NUM_RESOURCES] = {0};
unsigned long bars = 0;
int ret, i;
ndev = drmm_kzalloc(&xdna->ddev, sizeof(*ndev), GFP_KERNEL);
if (!ndev)
return -ENOMEM;
ndev->priv = xdna->dev_info->dev_priv;
ndev->aie.xdna = xdna;
xdna->dev_handle = ndev;
xa_init_flags(&ndev->cert_comp_xa, XA_FLAGS_ALLOC);
mutex_init(&ndev->cert_comp_lock);
/* Enable managed PCI device */
ret = pcim_enable_device(pdev);
if (ret) {
XDNA_ERR(xdna, "pcim enable device failed, ret %d", ret);
return ret;
}
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
XDNA_ERR(xdna, "failed to set DMA mask to 64:%d", ret);
return ret;
}
for (i = 0; i < PSP_MAX_REGS; i++)
set_bit(PSP_REG_BAR(ndev, i), &bars);
for (i = 0; i < SMU_MAX_REGS; i++)
set_bit(SMU_REG_BAR(ndev, i), &bars);
set_bit(xdna->dev_info->mbox_bar, &bars);
set_bit(xdna->dev_info->sram_bar, &bars);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
if (!test_bit(i, &bars))
continue;
tbl[i] = pcim_iomap(pdev, i, 0);
if (!tbl[i]) {
XDNA_ERR(xdna, "map bar %d failed", i);
return -ENOMEM;
}
}
ndev->mbox_base = tbl[xdna->dev_info->mbox_bar];
ndev->rbuf_base = tbl[xdna->dev_info->sram_bar];
pci_set_master(pdev);
ret = aie4_load_fw(ndev, tbl);
if (ret)
return ret;
ret = aie4_irq_init(xdna);
if (ret)
return ret;
amdxdna_vbnv_init(xdna);
XDNA_DBG(xdna, "init finished");
return 0;
}
static int aie4_doorbell_mmap(struct amdxdna_client *client, struct vm_area_struct *vma)
{
struct amdxdna_dev *xdna = client->xdna;
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
phys_addr_t res_start;
unsigned long pfn;
int ret;
if (!aie4_hwctx_valid_doorbell(client, vma->vm_pgoff)) {
XDNA_ERR(xdna, "Invalid doorbell page offset 0x%lx", vma->vm_pgoff);
return -EINVAL;
}
if (vma_pages(vma) != 1) {
XDNA_ERR(xdna, "can only map one page, got %ld", vma_pages(vma));
return -EINVAL;
}
res_start = pci_resource_start(pdev, xdna->dev_info->doorbell_bar) + npriv->doorbell_off;
pfn = PHYS_PFN(res_start) + vma->vm_pgoff;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vm_flags_set(vma, VM_IO | VM_DONTEXPAND | VM_DONTDUMP);
ret = io_remap_pfn_range(vma, vma->vm_start,
pfn,
PAGE_SIZE,
vma->vm_page_prot);
XDNA_DBG(xdna, "doorbell ret %d", ret);
return ret;
}
static int aie4_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args)
{
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
int ret;
switch (args->param) {
case DRM_AMDXDNA_QUERY_AIE_METADATA:
ret = amdxdna_get_metadata(&ndev->aie, client, args);
break;
default:
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
ret = -EOPNOTSUPP;
}
XDNA_DBG(xdna, "Got param %d", args->param);
return ret;
}
static int aie4_alloc_work_buffer(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
u32 buf_size = AIE4_WORK_BUFFER_MIN_SIZE;
ndev->work_buf = amdxdna_alloc_msg_buffer(xdna, &buf_size,
&ndev->work_buf_addr);
if (IS_ERR(ndev->work_buf)) {
int ret = PTR_ERR(ndev->work_buf);
XDNA_ERR(xdna, "Failed to alloc work buffer, size 0x%x",
AIE4_WORK_BUFFER_MIN_SIZE);
ndev->work_buf = NULL;
return ret;
}
ndev->work_buf_size = buf_size;
XDNA_DBG(xdna, "Work buffer allocated: size 0x%x", buf_size);
return 0;
}
static void aie4_free_work_buffer(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
if (!ndev->work_buf)
return;
amdxdna_free_msg_buffer(xdna, ndev->work_buf_size, ndev->work_buf,
ndev->work_buf_addr);
ndev->work_buf = NULL;
}
static int aie4_pf_init(struct amdxdna_dev *xdna)
{
int ret;
ret = aie4m_pcidev_init(xdna);
if (ret)
return ret;
ret = aie4_alloc_work_buffer(xdna->dev_handle);
if (ret)
return ret;
ret = aie4_pf_hw_start(xdna->dev_handle);
if (ret)
goto free_work_buf;
return 0;
free_work_buf:
aie4_free_work_buffer(xdna->dev_handle);
return ret;
}
static int aie4_vf_init(struct amdxdna_dev *xdna)
{
int ret;
ret = aie4m_pcidev_init(xdna);
if (ret)
return ret;
return aie4_vf_hw_start(xdna->dev_handle);
}
static void aie4_pf_fini(struct amdxdna_dev *xdna)
{
aie4_sriov_stop(xdna->dev_handle);
aie4_pf_hw_stop(xdna->dev_handle);
aie4_free_work_buffer(xdna->dev_handle);
}
static void aie4_vf_fini(struct amdxdna_dev *xdna)
{
aie4_vf_hw_stop(xdna->dev_handle);
}
const struct amdxdna_dev_ops aie4_pf_ops = {
.init = aie4_pf_init,
.fini = aie4_pf_fini,
.sriov_configure = aie4_sriov_configure,
};
const struct amdxdna_dev_ops aie4_vf_ops = {
.init = aie4_vf_init,
.fini = aie4_vf_fini,
.hwctx_init = aie4_hwctx_init,
.hwctx_fini = aie4_hwctx_fini,
.mmap = aie4_doorbell_mmap,
.cmd_wait = aie4_cmd_wait,
.get_aie_info = aie4_get_info,
};

View File

@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AIE4_PCI_H_
#define _AIE4_PCI_H_
#include <linux/device.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include "aie.h"
#include "amdxdna_mailbox.h"
struct cert_comp {
struct amdxdna_dev_hdl *ndev;
u32 msix_idx;
int irq;
struct kref kref;
wait_queue_head_t waitq;
};
struct amdxdna_hwctx_priv {
struct amdxdna_gem_obj *umq_bo;
u64 *umq_read_index;
u64 *umq_write_index;
struct cert_comp *cert_comp;
u32 hw_ctx_id;
};
struct amdxdna_dev_priv {
const char *npufw_path;
const char *certfw_path;
u32 mbox_bar;
u32 mbox_rbuf_bar;
u64 mbox_info_off;
u32 doorbell_off;
struct aie_bar_off_pair psp_regs_off[PSP_MAX_REGS];
struct aie_bar_off_pair smu_regs_off[SMU_MAX_REGS];
};
struct amdxdna_dev_hdl {
struct aie_device aie;
const struct amdxdna_dev_priv *priv;
void __iomem *mbox_base;
void __iomem *rbuf_base;
struct mailbox *mbox;
u32 partition_id;
struct xarray cert_comp_xa; /* device level indexed by msix id */
struct mutex cert_comp_lock; /* protects cert_comp operations*/
void *work_buf;
dma_addr_t work_buf_addr;
u32 work_buf_size;
};
/* aie4_message.c */
int aie4_query_aie_metadata(struct amdxdna_dev_hdl *ndev,
struct amdxdna_drm_query_aie_metadata *metadata);
int aie4_suspend_fw(struct amdxdna_dev_hdl *ndev);
int aie4_attach_work_buffer(struct amdxdna_dev_hdl *ndev);
/* aie4_ctx.c */
int aie4_hwctx_init(struct amdxdna_hwctx *hwctx);
void aie4_hwctx_fini(struct amdxdna_hwctx *hwctx);
int aie4_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout);
int aie4_hwctx_valid_doorbell(struct amdxdna_client *client, u32 vm_pgoff);
/* aie4_sriov.c */
#if IS_ENABLED(CONFIG_PCI_IOV)
int aie4_sriov_configure(struct amdxdna_dev *xdna, int num_vfs);
int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev);
#else
#define aie4_sriov_configure NULL
static inline int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev)
{
return 0;
}
#endif
extern const struct amdxdna_dev_ops aie4_pf_ops;
extern const struct amdxdna_dev_ops aie4_vf_ops;
#endif /* _AIE4_PCI_H_ */

View File

@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/amdxdna_accel.h>
#include <drm/drm_print.h>
#include <linux/pci.h>
#include "aie.h"
#include "aie4_msg_priv.h"
#include "aie4_pci.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
static int aie4_destroy_vfs(struct amdxdna_dev_hdl *ndev)
{
DECLARE_AIE_MSG(aie4_msg_destroy_vfs, AIE4_MSG_OP_DESTROY_VFS);
int ret;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
XDNA_ERR(ndev->aie.xdna, "destroy vfs op failed: %d", ret);
return ret;
}
static int aie4_create_vfs(struct amdxdna_dev_hdl *ndev, int num_vfs)
{
DECLARE_AIE_MSG(aie4_msg_create_vfs, AIE4_MSG_OP_CREATE_VFS);
int ret;
req.vf_cnt = num_vfs;
ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
XDNA_ERR(ndev->aie.xdna, "create vfs op failed: %d", ret);
return ret;
}
int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
int ret;
if (!pci_num_vf(pdev))
return 0;
ret = pci_vfs_assigned(pdev);
if (ret) {
XDNA_ERR(xdna, "VFs are still assigned to VMs");
return -EPERM;
}
pci_disable_sriov(pdev);
return aie4_destroy_vfs(ndev);
}
static int aie4_sriov_start(struct amdxdna_dev_hdl *ndev, int num_vfs)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
int ret;
ret = aie4_create_vfs(ndev, num_vfs);
if (ret)
return ret;
ret = pci_enable_sriov(pdev, num_vfs);
if (ret) {
XDNA_ERR(xdna, "configure VFs failed, ret: %d", ret);
aie4_destroy_vfs(ndev);
return ret;
}
return num_vfs;
}
int aie4_sriov_configure(struct amdxdna_dev *xdna, int num_vfs)
{
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
return (num_vfs) ? aie4_sriov_start(ndev, num_vfs) : aie4_sriov_stop(ndev);
}

View File

@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/drm_device.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include <linux/slab.h>
#include "aie.h"
#define PSP_STATUS_READY BIT(31)
/* PSP commands */
#define PSP_VALIDATE 1
#define PSP_START 2
#define PSP_RELEASE_TMR 3
#define PSP_VALIDATE_CERT 4
/* PSP special arguments */
#define PSP_START_COPY_FW 1
/* PSP response error code */
#define PSP_ERROR_CANCEL 0xFFFF0002
#define PSP_ERROR_BAD_STATE 0xFFFF0007
#define PSP_FW_ALIGN 0x10000
#define PSP_CFW_ALIGN 0x8000
#define PSP_POLL_INTERVAL 20000 /* us */
#define PSP_POLL_TIMEOUT 1000000 /* us */
#define PSP_REG(p, reg) ((p)->conf.psp_regs[reg])
#define PSP_SET_CMD(psp, reg_vals, cmd, arg0, arg1, arg2) \
({ \
u32 *_regs = reg_vals; \
u32 _cmd = cmd; \
_regs[0] = _cmd; \
_regs[1] = arg0; \
_regs[2] = arg1; \
_regs[3] = ((arg2) | ((_cmd) << 24)) & (psp)->conf.arg2_mask; \
})
struct psp_device {
struct drm_device *ddev;
struct psp_config conf;
u32 fw_buf_sz;
u64 fw_paddr;
void *fw_buffer;
u32 certfw_buf_sz;
u64 certfw_paddr;
void *certfw_buffer;
};
static int psp_exec(struct psp_device *psp, u32 *reg_vals)
{
u32 resp_code;
int ret, i;
u32 ready;
/* Check for PSP ready before any write */
ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
FIELD_GET(PSP_STATUS_READY, ready),
PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
if (ret) {
drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
return ret;
}
/* Write command and argument registers */
for (i = 0; i < PSP_NUM_IN_REGS; i++)
writel(reg_vals[i], PSP_REG(psp, i));
/* clear and set PSP INTR register to kick off */
writel(0, PSP_REG(psp, PSP_INTR_REG));
writel(psp->conf.notify_val, PSP_REG(psp, PSP_INTR_REG));
/* PSP should be busy. Wait for ready, so we know task is done. */
ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
FIELD_GET(PSP_STATUS_READY, ready),
PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
if (ret) {
drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
return ret;
}
resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
if (resp_code) {
drm_err(psp->ddev, "fw return error 0x%x", resp_code);
return -EIO;
}
return 0;
}
int aie_psp_waitmode_poll(struct psp_device *psp)
{
struct amdxdna_dev *xdna = to_xdna_dev(psp->ddev);
u32 mode_reg;
int ret;
ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_PWAITMODE_REG), mode_reg,
(mode_reg & 0x1) == 1,
PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
if (ret)
XDNA_ERR(xdna, "fw waitmode reg error, ret %d", ret);
return ret;
}
void aie_psp_stop(struct psp_device *psp)
{
u32 reg_vals[PSP_NUM_IN_REGS];
int ret;
PSP_SET_CMD(psp, reg_vals, PSP_RELEASE_TMR, 0, 0, 0);
ret = psp_exec(psp, reg_vals);
if (ret)
drm_err(psp->ddev, "release tmr failed, ret %d", ret);
}
static int psp_validate_fw(struct psp_device *psp, u8 cmd, u64 paddr, u32 buf_sz)
{
u32 reg_vals[PSP_NUM_IN_REGS];
int ret;
PSP_SET_CMD(psp, reg_vals, cmd, lower_32_bits(paddr),
upper_32_bits(paddr), buf_sz);
ret = psp_exec(psp, reg_vals);
if (ret)
drm_err(psp->ddev, "failed to validate fw, ret %d", ret);
return ret;
}
static int psp_start(struct psp_device *psp)
{
u32 reg_vals[PSP_NUM_IN_REGS];
int ret;
PSP_SET_CMD(psp, reg_vals, PSP_START, PSP_START_COPY_FW, 0, 0);
ret = psp_exec(psp, reg_vals);
if (ret)
drm_err(psp->ddev, "failed to start fw, ret %d", ret);
return ret;
}
int aie_psp_start(struct psp_device *psp)
{
int ret;
ret = psp_validate_fw(psp, PSP_VALIDATE,
psp->fw_paddr, psp->fw_buf_sz);
if (ret)
return ret;
if (!psp->certfw_buf_sz)
goto psp_start;
ret = psp_validate_fw(psp, PSP_VALIDATE_CERT,
psp->certfw_paddr, psp->certfw_buf_sz);
if (ret)
return ret;
psp_start:
return psp_start(psp);
}
/*
* PSP requires host physical address to load firmware.
* Allocate a buffer, obtain its physical address, align, and copy data in.
*/
static void *psp_alloc_fw_buf(struct psp_device *psp, const void *fw_data,
u32 fw_size, u32 align, u32 *buf_sz,
u64 *paddr)
{
u32 alloc_sz;
void *buffer;
u64 offset;
*buf_sz = ALIGN(fw_size, align);
alloc_sz = *buf_sz + align;
buffer = drmm_kmalloc(psp->ddev, alloc_sz, GFP_KERNEL);
if (!buffer)
return NULL;
*paddr = virt_to_phys(buffer);
offset = ALIGN(*paddr, align) - *paddr;
*paddr += offset;
memcpy(buffer + offset, fw_data, fw_size);
return buffer;
}
struct psp_device *aiem_psp_create(struct drm_device *ddev, struct psp_config *conf)
{
struct psp_device *psp;
psp = drmm_kzalloc(ddev, sizeof(*psp), GFP_KERNEL);
if (!psp)
return NULL;
psp->ddev = ddev;
psp->fw_buffer = psp_alloc_fw_buf(psp, conf->fw_buf, conf->fw_size,
PSP_FW_ALIGN, &psp->fw_buf_sz,
&psp->fw_paddr);
if (!psp->fw_buffer)
return NULL;
if (!conf->certfw_size) {
drm_dbg(ddev, "no cert fw");
goto done;
}
/* CERT firmware */
psp->certfw_buffer = psp_alloc_fw_buf(psp, conf->certfw_buf,
conf->certfw_size, PSP_CFW_ALIGN,
&psp->certfw_buf_sz,
&psp->certfw_paddr);
if (!psp->certfw_buffer) {
drm_err(ddev, "no memory for cert fw buffer");
return NULL;
}
done:
memcpy(&psp->conf, conf, sizeof(psp->conf));
return psp;
}

View File

@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include "drm/amdxdna_accel.h"
#include <drm/drm_device.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <linux/iopoll.h>
#include "aie.h"
#define SMU_RESULT_OK 1
/* SMU commands */
#define AIE_SMU_POWER_ON 0x3
#define AIE_SMU_POWER_OFF 0x4
#define AIE_SMU_SET_MPNPUCLK_FREQ 0x5
#define AIE_SMU_SET_HCLK_FREQ 0x6
#define AIE_SMU_SET_SOFT_DPMLEVEL 0x7
#define AIE_SMU_SET_HARD_DPMLEVEL 0x8
#define SMU_REG(s, reg) ((s)->smu_regs[reg])
struct smu_device {
struct drm_device *ddev;
struct smu_config conf;
void __iomem *smu_regs[SMU_MAX_REGS];
};
static int aie_smu_exec(struct smu_device *smu, u32 reg_cmd, u32 reg_arg, u32 *out)
{
u32 resp;
int ret;
writel(0, SMU_REG(smu, SMU_RESP_REG));
writel(reg_arg, SMU_REG(smu, SMU_ARG_REG));
writel(reg_cmd, SMU_REG(smu, SMU_CMD_REG));
/* Clear and set SMU_INTR_REG to kick off */
writel(0, SMU_REG(smu, SMU_INTR_REG));
writel(1, SMU_REG(smu, SMU_INTR_REG));
ret = readx_poll_timeout(readl, SMU_REG(smu, SMU_RESP_REG), resp,
resp, AIE_INTERVAL, AIE_TIMEOUT);
if (ret) {
drm_err(smu->ddev, "smu cmd %d timed out", reg_cmd);
return ret;
}
if (out)
*out = readl(SMU_REG(smu, SMU_OUT_REG));
if (resp != SMU_RESULT_OK) {
drm_err(smu->ddev, "smu cmd %d failed, 0x%x", reg_cmd, resp);
return -EINVAL;
}
return 0;
}
int aie_smu_init(struct smu_device *smu)
{
int ret;
/*
* Failing to set power off indicates an unrecoverable hardware or
* firmware error.
*/
ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL);
if (ret) {
drm_err(smu->ddev, "Access power failed, ret %d", ret);
return ret;
}
ret = aie_smu_exec(smu, AIE_SMU_POWER_ON, 0, NULL);
if (ret) {
drm_err(smu->ddev, "Power on failed, ret %d", ret);
return ret;
}
return 0;
}
void aie_smu_fini(struct smu_device *smu)
{
int ret;
ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL);
if (ret)
drm_err(smu->ddev, "Power off failed, ret %d", ret);
}
int aie_smu_set_clocks(struct smu_device *smu, u32 *npuclk, u32 *hclk)
{
int ret;
if (npuclk) {
ret = aie_smu_exec(smu, AIE_SMU_SET_MPNPUCLK_FREQ, *npuclk, npuclk);
if (ret) {
drm_err(smu->ddev, "Set mpnpu clock to %d failed, ret %d", *npuclk, ret);
return ret;
}
}
if (hclk) {
ret = aie_smu_exec(smu, AIE_SMU_SET_HCLK_FREQ, *hclk, hclk);
if (ret) {
drm_err(smu->ddev, "Set hclock to %d failed, ret %d",
*hclk, ret);
return ret;
}
}
return 0;
}
int aie_smu_set_dpm(struct smu_device *smu, u32 dpm_level)
{
int ret;
ret = aie_smu_exec(smu, AIE_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
if (ret) {
drm_err(smu->ddev, "Set hard dpm level %d failed, ret %d",
dpm_level, ret);
return ret;
}
ret = aie_smu_exec(smu, AIE_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
if (ret) {
drm_err(smu->ddev, "Set soft dpm level %d failed, ret %d",
dpm_level, ret);
return ret;
}
return 0;
}
struct smu_device *aiem_smu_create(struct drm_device *ddev, struct smu_config *conf)
{
struct smu_device *smu;
smu = drmm_kzalloc(ddev, sizeof(*smu), GFP_KERNEL);
if (!smu)
return NULL;
smu->ddev = ddev;
memcpy(smu->smu_regs, conf->smu_regs, sizeof(smu->smu_regs));
return smu;
}

View File

@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/drm_mm.h>
#include <drm/drm_prime.h>
#include "amdxdna_cbuf.h"
#include "amdxdna_pci_drv.h"
/*
* Carveout memory is a chunk of memory which is physically contiguous and
* is reserved during early boot time. There is only one chunk of such memory
* per device. Once available, all BOs accessible from device should be
* allocated from this memory. This is a platform debug/bringup feature.
*/
struct amdxdna_carveout {
u64 addr;
u64 size;
struct drm_mm mm;
struct mutex lock; /* protect mm */
};
bool amdxdna_use_carveout(struct amdxdna_dev *xdna)
{
return !!xdna->carveout;
}
void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size)
{
if (amdxdna_use_carveout(xdna)) {
*addr = xdna->carveout->addr;
*size = xdna->carveout->size;
} else {
*addr = 0;
*size = 0;
}
}
int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size)
{
struct amdxdna_carveout *carveout;
/* Only allow carveout memory to be set up once. */
if (amdxdna_use_carveout(xdna)) {
XDNA_ERR(xdna, "Carveout memory has already been set up.");
return -EBUSY;
}
carveout = kzalloc_obj(*carveout);
if (!carveout)
return -ENOMEM;
carveout->addr = carveout_addr;
carveout->size = carveout_size;
mutex_init(&carveout->lock);
drm_mm_init(&carveout->mm, carveout->addr, carveout->size);
xdna->carveout = carveout;
XDNA_INFO(xdna, "Use carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr);
return 0;
}
void amdxdna_carveout_fini(struct amdxdna_dev *xdna)
{
struct amdxdna_carveout *carveout = xdna->carveout;
if (!amdxdna_use_carveout(xdna))
return;
XDNA_INFO(xdna, "Cleanup carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr);
drm_mm_takedown(&carveout->mm);
mutex_destroy(&carveout->lock);
kfree(carveout);
xdna->carveout = NULL;
}
struct amdxdna_cbuf_priv {
struct amdxdna_dev *xdna;
struct drm_mm_node node;
};
static struct sg_table *amdxdna_cbuf_map(struct dma_buf_attachment *attach,
enum dma_data_direction direction)
{
struct amdxdna_cbuf_priv *cbuf = attach->dmabuf->priv;
struct device *dev = attach->dev;
struct scatterlist *sgl, *sg;
int ret, n_entries, i;
struct sg_table *sgt;
dma_addr_t dma_addr;
size_t dma_size;
size_t max_seg;
sgt = kzalloc_obj(*sgt);
if (!sgt)
return ERR_PTR(-ENOMEM);
max_seg = min_t(size_t, UINT_MAX, dma_max_mapping_size(dev));
n_entries = (cbuf->node.size + max_seg - 1) / max_seg;
sgl = kzalloc_objs(*sg, n_entries);
if (!sgl) {
ret = -ENOMEM;
goto free_sgt;
}
sg_init_table(sgl, n_entries);
sgt->orig_nents = n_entries;
sgt->nents = n_entries;
sgt->sgl = sgl;
dma_size = cbuf->node.size;
dma_addr = dma_map_resource(dev, cbuf->node.start, dma_size,
direction, DMA_ATTR_SKIP_CPU_SYNC);
ret = dma_mapping_error(dev, dma_addr);
if (ret) {
pr_err("Failed to dma_map_resource carveout dma buf, ret %d\n", ret);
goto free_sgl;
}
for_each_sgtable_dma_sg(sgt, sg, i) {
size_t len = min_t(size_t, max_seg, dma_size);
sg_dma_address(sg) = dma_addr;
sg_dma_len(sg) = len;
dma_addr += len;
dma_size -= len;
}
return sgt;
free_sgl:
kfree(sgl);
free_sgt:
kfree(sgt);
return ERR_PTR(ret);
}
static void amdxdna_cbuf_unmap(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction direction)
{
dma_unmap_resource(attach->dev, sg_dma_address(sgt->sgl),
drm_prime_get_contiguous_size(sgt), direction,
DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sgt);
kfree(sgt);
}
static void amdxdna_cbuf_release(struct dma_buf *dbuf)
{
struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
struct amdxdna_carveout *carveout;
carveout = cbuf->xdna->carveout;
mutex_lock(&carveout->lock);
drm_mm_remove_node(&cbuf->node);
mutex_unlock(&carveout->lock);
kfree(cbuf);
}
static vm_fault_t amdxdna_cbuf_vm_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct amdxdna_cbuf_priv *cbuf;
unsigned long pfn;
pgoff_t pgoff;
cbuf = vma->vm_private_data;
pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
pfn = (cbuf->node.start >> PAGE_SHIFT) + pgoff;
return vmf_insert_pfn(vma, vmf->address, pfn);
}
static const struct vm_operations_struct amdxdna_cbuf_vm_ops = {
.fault = amdxdna_cbuf_vm_fault,
};
static int amdxdna_cbuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
{
struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
vma->vm_ops = &amdxdna_cbuf_vm_ops;
vma->vm_private_data = cbuf;
vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
return 0;
}
static int amdxdna_cbuf_vmap(struct dma_buf *dbuf, struct iosys_map *map)
{
struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
void *kva;
kva = memremap(cbuf->node.start, cbuf->node.size, MEMREMAP_WB);
if (!kva) {
pr_err("Failed to vmap carveout dma buf\n");
return -ENOMEM;
}
iosys_map_set_vaddr(map, kva);
return 0;
}
static void amdxdna_cbuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map)
{
memunmap(map->vaddr);
}
static const struct dma_buf_ops amdxdna_cbuf_dmabuf_ops = {
.map_dma_buf = amdxdna_cbuf_map,
.unmap_dma_buf = amdxdna_cbuf_unmap,
.release = amdxdna_cbuf_release,
.mmap = amdxdna_cbuf_mmap,
.vmap = amdxdna_cbuf_vmap,
.vunmap = amdxdna_cbuf_vunmap,
};
static int amdxdna_cbuf_clear(struct dma_buf *dbuf)
{
struct iosys_map vmap = IOSYS_MAP_INIT_VADDR(NULL);
dma_buf_vmap(dbuf, &vmap);
if (!vmap.vaddr)
return -EFAULT;
memset(vmap.vaddr, 0, dbuf->size);
dma_buf_vunmap(dbuf, &vmap);
return 0;
}
struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment)
{
struct amdxdna_dev *xdna = to_xdna_dev(dev);
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct amdxdna_carveout *carveout;
struct amdxdna_cbuf_priv *cbuf;
struct dma_buf *dbuf;
int ret;
cbuf = kzalloc_obj(*cbuf);
if (!cbuf)
return ERR_PTR(-ENOMEM);
cbuf->xdna = xdna;
carveout = xdna->carveout;
mutex_lock(&carveout->lock);
ret = drm_mm_insert_node_generic(&carveout->mm, &cbuf->node, size,
alignment, 0, DRM_MM_INSERT_BEST);
mutex_unlock(&carveout->lock);
if (ret)
goto free_cbuf;
exp_info.size = size;
exp_info.ops = &amdxdna_cbuf_dmabuf_ops;
exp_info.priv = cbuf;
exp_info.flags = O_RDWR;
dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) {
ret = PTR_ERR(dbuf);
goto remove_node;
}
ret = amdxdna_cbuf_clear(dbuf);
if (ret) {
dma_buf_put(dbuf);
goto out;
}
return dbuf;
remove_node:
drm_mm_remove_node(&cbuf->node);
free_cbuf:
kfree(cbuf);
out:
return ERR_PTR(ret);
}

View File

@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AMDXDNA_CBUF_H_
#define _AMDXDNA_CBUF_H_
#include "amdxdna_pci_drv.h"
#include <drm/drm_device.h>
#include <linux/dma-buf.h>
bool amdxdna_use_carveout(struct amdxdna_dev *xdna);
int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size);
void amdxdna_carveout_fini(struct amdxdna_dev *xdna);
void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size);
struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment);
#endif

View File

@@ -61,16 +61,35 @@ static struct dma_fence *amdxdna_fence_create(struct amdxdna_hwctx *hwctx)
return &fence->base;
}
static void amdxdna_hwctx_release_expanded_heap(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
mutex_lock(&client->mm_lock);
if (hwctx->last_attached_heap) {
xa_for_each_range(&client->dev_heap_xa, heap_id, heap, 1,
hwctx->last_attached_heap) {
amdxdna_gem_unpin(heap);
drm_gem_object_put(to_gobj(heap));
}
}
mutex_unlock(&client->mm_lock);
}
static void amdxdna_hwctx_destroy_rcu(struct amdxdna_hwctx *hwctx,
struct srcu_struct *ss)
{
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
synchronize_srcu(ss);
/* At this point, user is not able to submit new commands */
xdna->dev_info->ops->hwctx_fini(hwctx);
amdxdna_hwctx_release_expanded_heap(hwctx);
kfree(hwctx->name);
kfree(hwctx);
}
@@ -207,6 +226,9 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
if (args->ext || args->ext_flags)
return -EINVAL;
if (!xdna->dev_info->ops->hwctx_init)
return -EOPNOTSUPP;
hwctx = kzalloc_obj(*hwctx);
if (!hwctx)
return -ENOMEM;
@@ -220,6 +242,8 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
hwctx->client = client;
hwctx->fw_ctx_id = -1;
hwctx->num_tiles = args->num_tiles;
hwctx->umq_bo_hdl = args->umq_bo;
hwctx->doorbell_offset = AMDXDNA_INVALID_DOORBELL_OFFSET;
hwctx->mem_size = args->mem_size;
hwctx->max_opc = args->max_opc;
@@ -233,7 +257,7 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
ret = xdna->dev_info->ops->hwctx_init(hwctx);
if (ret) {
XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
goto dev_exit;
goto release_expanded_heap;
}
hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->fw_ctx_id);
@@ -252,6 +276,7 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
args->handle = hwctx->id;
args->syncobj_handle = hwctx->syncobj_hdl;
args->umq_doorbell = hwctx->doorbell_offset;
atomic64_set(&hwctx->job_submit_cnt, 0);
atomic64_set(&hwctx->job_free_cnt, 0);
@@ -263,7 +288,8 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
kfree(hwctx->name);
fini_hwctx:
xdna->dev_info->ops->hwctx_fini(hwctx);
dev_exit:
release_expanded_heap:
amdxdna_hwctx_release_expanded_heap(hwctx);
drm_dev_exit(idx);
free_hwctx:
kfree(hwctx);
@@ -401,6 +427,62 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
return ret;
}
static int amdxdna_hwctx_expand_heap(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_gem_obj *heap;
unsigned long heap_id, nid;
int ret = 0;
nid = hwctx->last_attached_heap + 1;
if (nid == client->dev_heap_nid)
goto out;
xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
nid, client->dev_heap_nid) {
drm_gem_object_get(to_gobj(heap));
ret = amdxdna_gem_pin(heap);
if (ret) {
drm_gem_object_put(to_gobj(heap));
break;
}
mutex_unlock(&client->mm_lock);
ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap);
mutex_lock(&client->mm_lock);
if (ret) {
amdxdna_gem_unpin(heap);
drm_gem_object_put(to_gobj(heap));
break;
}
hwctx->last_attached_heap = heap_id;
}
out:
return ret;
}
int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwctx)
{
unsigned long hwctx_id;
int ret;
guard(mutex)(&client->mm_lock);
if (hwctx)
return amdxdna_hwctx_expand_heap(hwctx);
amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
ret = amdxdna_hwctx_expand_heap(hwctx);
if (ret)
return ret;
}
return 0;
}
static void
amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
{
@@ -514,7 +596,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
goto unlock_srcu;
}
job->hwctx = hwctx;
job->mm = current->mm;
@@ -612,6 +693,8 @@ int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_
if (args->ext || args->ext_flags)
return -EINVAL;
trace_amdxdna_debug_point(current->comm, args->type, "job received");
switch (args->type) {
case AMDXDNA_CMD_SUBMIT_EXEC_BUF:
return amdxdna_drm_submit_execbuf(client, args);
@@ -620,3 +703,37 @@ int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_
XDNA_ERR(client->xdna, "Invalid command type %d", args->type);
return -EINVAL;
}
int amdxdna_drm_wait_cmd_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
struct amdxdna_client *client = filp->driver_priv;
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_drm_wait_cmd *args = data;
struct amdxdna_hwctx *hwctx;
int ret, idx;
XDNA_DBG(xdna, "PID %d ctx %d timeout set %d ms for cmd %llu",
client->pid, args->hwctx, args->timeout, args->seq);
if (!xdna->dev_info->ops->cmd_wait)
return -EOPNOTSUPP;
idx = srcu_read_lock(&client->hwctx_srcu);
hwctx = xa_load(&client->hwctx_xa, args->hwctx);
if (!hwctx) {
XDNA_DBG(xdna, "PID %d failed to get ctx %d", client->pid, args->hwctx);
ret = -EINVAL;
goto unlock_ctx_srcu;
}
ret = xdna->dev_info->ops->cmd_wait(hwctx, args->seq, args->timeout);
XDNA_DBG(xdna, "PID %d ctx %d cmd %lld wait finished, ret %d",
client->pid, args->hwctx, args->seq, ret);
trace_amdxdna_debug_point(current->comm, args->seq, "job returned to user");
unlock_ctx_srcu:
srcu_read_unlock(&client->hwctx_srcu, idx);
return ret;
}

View File

@@ -14,6 +14,7 @@ struct amdxdna_hwctx_priv;
enum ert_cmd_opcode {
ERT_START_CU = 0,
ERT_START_DPU = 18,
ERT_CMD_CHAIN = 19,
ERT_START_NPU = 20,
ERT_START_NPU_PREEMPT = 21,
@@ -105,7 +106,10 @@ struct amdxdna_hwctx {
u32 *col_list;
u32 start_col;
u32 num_col;
u32 umq_bo_hdl;
u32 doorbell_offset;
u32 num_unused_col;
u32 last_attached_heap;
struct amdxdna_qos_info qos;
struct amdxdna_hwctx_param_config_cu *cus;
@@ -119,6 +123,7 @@ struct amdxdna_hwctx {
container_of(j, struct amdxdna_sched_job, base)
enum amdxdna_job_opcode {
DEFAULT_IO,
SYNC_DEBUG_BO,
ATTACH_DEBUG_BO,
DETACH_DEBUG_BO,
@@ -201,18 +206,17 @@ void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwctx);
int amdxdna_cmd_submit(struct amdxdna_client *client,
struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
u32 *arg_bo_hdls, u32 arg_bo_cnt,
u32 hwctx_hdl, u64 *seq);
int amdxdna_cmd_wait(struct amdxdna_client *client, u32 hwctx_hdl,
u64 seq, u32 timeout);
int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
int amdxdna_drm_wait_cmd_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
#endif /* _AMDXDNA_CTX_H_ */

View File

@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include "amdxdna_cbuf.h"
#include "amdxdna_debugfs.h"
#include <drm/drm_file.h>
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#define _DBGFS_FOPS(_open, _release, _write) \
{ \
.owner = THIS_MODULE, \
.open = _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = _release, \
.write = _write, \
}
#define AMDXDNA_DBGFS_FOPS(_name, _show, _write) \
static int amdxdna_dbgfs_##_name##_open(struct inode *inode, struct file *file) \
{ \
return single_open(file, _show, inode->i_private); \
} \
static int amdxdna_dbgfs_##_name##_release(struct inode *inode, struct file *file) \
{ \
return single_release(inode, file); \
} \
static const struct file_operations amdxdna_fops_##_name = \
_DBGFS_FOPS(amdxdna_dbgfs_##_name##_open, amdxdna_dbgfs_##_name##_release, _write)
#define AMDXDNA_DBGFS_FILE(_name, _mode) { #_name, &amdxdna_fops_##_name, _mode }
#define file_to_xdna(file) (((struct seq_file *)(file)->private_data)->private)
static ssize_t amdxdna_carveout_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct amdxdna_dev *xdna = file_to_xdna(file);
char kbuf[128];
u64 size, addr;
char *sep;
int ret;
if (count == 0 || count >= sizeof(kbuf))
return -EINVAL;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
kbuf[count] = '\0';
strim(kbuf);
XDNA_DBG(xdna, "Trying to set carveout to %s", kbuf);
sep = strchr(kbuf, '@');
if (!sep)
return -EINVAL;
*sep = '\0';
sep++;
ret = kstrtou64(kbuf, 0, &size);
if (ret)
return ret;
ret = kstrtou64(sep, 0, &addr);
if (ret)
return ret;
/* Sanity check the addr and size. */
if (!size)
return -EINVAL;
if (!IS_ALIGNED(addr, PAGE_SIZE) || !IS_ALIGNED(size, PAGE_SIZE))
return -EINVAL;
guard(mutex)(&xdna->dev_lock);
ret = amdxdna_carveout_init(xdna, addr, size);
if (ret)
return ret;
return count;
}
static int amdxdna_carveout_show(struct seq_file *m, void *unused)
{
struct amdxdna_dev *xdna = m->private;
u64 addr, size;
guard(mutex)(&xdna->dev_lock);
amdxdna_get_carveout_conf(xdna, &addr, &size);
seq_printf(m, "0x%llx@0x%llx\n", size, addr);
return 0;
}
/*
* Input/output format: <carveout_size>@<carveout_address>
*/
AMDXDNA_DBGFS_FOPS(carveout, amdxdna_carveout_show, amdxdna_carveout_write);
static const struct {
const char *name;
const struct file_operations *fops;
umode_t mode;
} amdxdna_dbgfs_files[] = {
AMDXDNA_DBGFS_FILE(carveout, 0600),
};
void amdxdna_debugfs_init(struct amdxdna_dev *xdna)
{
struct drm_minor *minor = xdna->ddev.accel;
int i;
/*
* It should be okay that debugfs fails to init.
* We rely on DRM framework to finish debugfs.
*/
for (i = 0; i < ARRAY_SIZE(amdxdna_dbgfs_files); i++) {
debugfs_create_file(amdxdna_dbgfs_files[i].name,
amdxdna_dbgfs_files[i].mode,
minor->debugfs_root,
xdna,
amdxdna_dbgfs_files[i].fops);
}
}

View File

@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AMDXDNA_DEBUGFS_H_
#define _AMDXDNA_DEBUGFS_H_
#include "amdxdna_pci_drv.h"
#if defined(CONFIG_DEBUG_FS)
void amdxdna_debugfs_init(struct amdxdna_dev *xdna);
#else
static inline void amdxdna_debugfs_init(struct amdxdna_dev *xdna)
{
}
#endif /* CONFIG_DEBUG_FS */
#endif

View File

@@ -16,6 +16,7 @@
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include "amdxdna_cbuf.h"
#include "amdxdna_ctx.h"
#include "amdxdna_gem.h"
#include "amdxdna_pci_drv.h"
@@ -23,6 +24,77 @@
MODULE_IMPORT_NS("DMA_BUF");
/*
* The dev BO could be across multiple heap BO chunks. The heap chunks should
* be mapped to userspace and the userspace virtual address should be
* contiguous.
*/
static int
amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo)
{
struct amdxdna_client *client = dev_bo->client;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_gem_obj *heap;
u64 heap_addr, exp_heap_uva;
u32 heap_id;
if (xa_empty(&client->dev_heap_xa)) {
XDNA_DBG(xdna, "Empty heap xa");
return -EAGAIN;
}
for (heap_id = 0; heap_id < client->dev_heap_nid; heap_id++) {
heap = xa_load(&client->dev_heap_xa, heap_id);
if (!heap) {
XDNA_ERR(xdna, "Failed to load heap %d", heap_id);
return -EINVAL;
}
heap_addr = amdxdna_gem_dev_addr(heap);
if (heap_addr > dev_bo->mm_node.start)
break;
}
heap_id--;
heap = xa_load(&client->dev_heap_xa, heap_id);
exp_heap_uva = amdxdna_gem_uva(heap);
heap_addr = amdxdna_gem_dev_addr(heap);
dev_bo->heap_start_id = heap_id;
dev_bo->mem.uva = dev_bo->mm_node.start - heap_addr + exp_heap_uva;
for (; heap_id < client->dev_heap_nid; heap_id++) {
heap = xa_load(&client->dev_heap_xa, heap_id);
if (!heap) {
XDNA_ERR(xdna, "Failed to load heap %d", heap_id);
return -EINVAL;
}
heap_addr = amdxdna_gem_uva(heap);
if (heap_addr == AMDXDNA_INVALID_ADDR) {
XDNA_ERR(xdna, "Heap %d is not mapped", heap_id);
return -EAGAIN;
}
if (heap_addr != exp_heap_uva) {
XDNA_ERR(xdna, "Heap %d uva is not contiguous", heap_id);
return -EINVAL;
}
if (heap->dev_addr + heap->mem.size >=
dev_bo->mm_node.start + dev_bo->mem.size)
break;
exp_heap_uva += heap->mem.size;
}
if (heap_id == client->dev_heap_nid) {
XDNA_DBG(xdna, "Can not find heap end");
return -EAGAIN;
}
dev_bo->heap_end_id = heap_id;
return 0;
}
static int
amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
{
@@ -30,32 +102,22 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_mem *mem = &abo->mem;
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
u32 align;
int ret;
mutex_lock(&client->mm_lock);
heap = client->dev_heap;
if (!heap) {
ret = -EINVAL;
goto unlock_out;
}
if (amdxdna_gem_uva(heap) == AMDXDNA_INVALID_ADDR) {
XDNA_ERR(xdna, "Invalid dev heap userptr");
ret = -EINVAL;
goto unlock_out;
}
if (mem->size == 0 || mem->size > heap->mem.size) {
XDNA_ERR(xdna, "Invalid dev bo size 0x%lx, limit 0x%lx",
mem->size, heap->mem.size);
if (!mem->size || mem->size > xdna->dev_info->dev_heap_max_size) {
XDNA_ERR(xdna, "Invalid dev bo size 0x%lx, max heap 0x%lx",
mem->size, xdna->dev_info->dev_heap_max_size);
ret = -EINVAL;
goto unlock_out;
}
align = 1 << max(PAGE_SHIFT, xdna->dev_info->dev_mem_buf_shift);
ret = drm_mm_insert_node_generic(&heap->mm, &abo->mm_node,
ret = drm_mm_insert_node_generic(&client->dev_heap_mm,
&abo->mm_node,
mem->size, align,
0, DRM_MM_INSERT_BEST);
if (ret) {
@@ -63,9 +125,16 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
goto unlock_out;
}
client->heap_usage += mem->size;
ret = amdxdna_init_dev_bo(abo);
if (ret) {
drm_mm_remove_node(&abo->mm_node);
goto unlock_out;
}
drm_gem_object_get(to_gobj(heap));
client->heap_usage += mem->size;
xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
abo->heap_start_id, abo->heap_end_id)
drm_gem_object_get(to_gobj(heap));
unlock_out:
mutex_unlock(&client->mm_lock);
@@ -78,13 +147,16 @@ amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo)
{
struct amdxdna_client *client = abo->client;
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
mutex_lock(&client->mm_lock);
drm_mm_remove_node(&abo->mm_node);
client->heap_usage -= abo->mem.size;
heap = client->dev_heap;
drm_gem_object_put(to_gobj(heap));
xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
abo->heap_start_id, abo->heap_end_id)
drm_gem_object_put(to_gobj(heap));
mutex_unlock(&client->mm_lock);
}
@@ -160,31 +232,13 @@ static void amdxdna_gem_vunmap(struct amdxdna_gem_obj *abo)
}
}
/*
* Obtain the user virtual address for accessing the BO.
* It can be used for device to access the BO when PASID is enabled.
*/
u64 amdxdna_gem_uva(struct amdxdna_gem_obj *abo)
{
if (abo->type == AMDXDNA_BO_DEV) {
struct amdxdna_gem_obj *heap = abo->client->dev_heap;
u64 off = amdxdna_dev_bo_offset(abo);
if (amdxdna_gem_uva(heap) != AMDXDNA_INVALID_ADDR)
return amdxdna_gem_uva(heap) + off;
return AMDXDNA_INVALID_ADDR;
}
return abo->mem.uva;
}
/*
* Obtain the address for device to access the BO.
*/
u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo)
{
if (abo->type == AMDXDNA_BO_DEV_HEAP)
return abo->client->xdna->dev_info->dev_mem_base;
return abo->dev_addr;
if (abo->type == AMDXDNA_BO_DEV)
return abo->mm_node.start;
return amdxdna_obj_dma_addr(abo);
@@ -211,7 +265,8 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni,
mmu_interval_set_seq(&mapp->notifier, cur_seq);
up_write(&xdna->notifier_lock);
xdna->dev_info->ops->hmm_invalidate(abo, cur_seq);
if (xdna->dev_info->ops->hmm_invalidate)
xdna->dev_info->ops->hmm_invalidate(abo, cur_seq);
if (range->event == MMU_NOTIFY_UNMAP) {
down_write(&xdna->notifier_lock);
@@ -294,8 +349,11 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo,
u32 nr_pages;
int ret;
if (!xdna->dev_info->ops->hmm_invalidate)
if (!amdxdna_pasid_on(abo->client)) {
/* Need to set uva for heap uva validation */
abo->mem.uva = addr;
return 0;
}
mapp = kzalloc_obj(*mapp);
if (!mapp)
@@ -519,10 +577,6 @@ static void amdxdna_imported_obj_free(struct amdxdna_gem_obj *abo)
static inline bool
amdxdna_gem_skip_bo_usage(struct amdxdna_gem_obj *abo)
{
/* Do not count imported BOs since the buffer is not allocated by us. */
if (is_import_bo(abo))
return true;
/* Already counted as part of HEAP BO */
if (abo->type == AMDXDNA_BO_DEV)
return true;
@@ -571,12 +625,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
if (abo->pinned)
amdxdna_gem_unpin(abo);
if (abo->type == AMDXDNA_BO_DEV_HEAP)
drm_mm_takedown(&abo->mm);
if (amdxdna_iova_on(xdna))
amdxdna_iommu_unmap_bo(xdna, abo);
amdxdna_dma_unmap_bo(xdna, abo);
amdxdna_gem_vunmap(abo);
mutex_destroy(&abo->lock);
@@ -594,18 +643,20 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi
guard(mutex)(&abo->lock);
abo->open_ref++;
if (abo->open_ref > 1)
return 0;
if (abo->open_ref == 1) {
/* Attached to the client when first opened by it. */
abo->client = filp->driver_priv;
amdxdna_gem_add_bo_usage(abo);
}
if (amdxdna_iova_on(xdna)) {
ret = amdxdna_iommu_map_bo(xdna, abo);
/* Attached to the client when first opened by it. */
abo->client = filp->driver_priv;
/* No need to set up dma addr mapping in PASID mode. */
if (!amdxdna_pasid_on(abo->client)) {
ret = amdxdna_dma_map_bo(xdna, abo);
if (ret)
return ret;
}
amdxdna_gem_add_bo_usage(abo);
return 0;
}
@@ -623,14 +674,59 @@ static void amdxdna_gem_obj_close(struct drm_gem_object *gobj, struct drm_file *
}
}
static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
int ret;
iosys_map_clear(map);
dma_resv_assert_held(obj->resv);
if (is_import_bo(abo))
ret = dma_buf_vmap(abo->dma_buf, map);
else
ret = drm_gem_shmem_object_vmap(obj, map);
if (ret)
return ret;
if (!map->vaddr)
return -ENOMEM;
return 0;
}
static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
dma_resv_assert_held(obj->resv);
if (is_import_bo(abo))
dma_buf_vunmap(abo->dma_buf, map);
else
drm_gem_shmem_object_vunmap(obj, map);
}
static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
void *base = amdxdna_gem_vmap(abo->client->dev_heap);
u64 offset = amdxdna_dev_bo_offset(abo);
struct amdxdna_gem_obj *heap;
void *base;
u64 offset;
/* vmap dev bo which is across more than 1 heap is not allowed */
if (abo->heap_start_id != abo->heap_end_id)
return -ENOMEM;
heap = xa_load(&abo->client->dev_heap_xa, abo->heap_start_id);
if (!heap)
return -ENOMEM;
base = amdxdna_gem_vmap(heap);
if (!base)
return -ENOMEM;
offset = amdxdna_gem_dev_addr(abo) - amdxdna_gem_dev_addr(heap);
iosys_map_set_vaddr(map, base + offset);
return 0;
}
@@ -648,8 +744,8 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
.vmap = drm_gem_shmem_object_vmap,
.vunmap = drm_gem_shmem_object_vunmap,
.vmap = amdxdna_gem_obj_vmap,
.vunmap = amdxdna_gem_obj_vunmap,
.mmap = amdxdna_gem_obj_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
.export = amdxdna_gem_prime_export,
@@ -721,6 +817,36 @@ amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create
return abo;
}
static struct amdxdna_gem_obj *
amdxdna_gem_create_cbuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args)
{
struct amdxdna_dev *xdna = to_xdna_dev(dev);
size_t size = PAGE_ALIGN(args->size);
struct drm_gem_object *gobj;
struct amdxdna_gem_obj *ret;
struct dma_buf *dma_buf;
u64 align;
if (!size) {
XDNA_ERR(xdna, "Invalid BO size 0x%llx", args->size);
return ERR_PTR(-EINVAL);
}
align = (args->type == AMDXDNA_BO_DEV_HEAP) ? xdna->dev_info->dev_mem_size : 0;
dma_buf = amdxdna_get_cbuf(dev, size, align);
if (IS_ERR(dma_buf))
return ERR_CAST(dma_buf);
gobj = amdxdna_gem_prime_import(dev, dma_buf);
if (IS_ERR(gobj))
ret = ERR_CAST(gobj);
else
ret = to_xdna_obj(gobj);
dma_buf_put(dma_buf);
return ret;
}
struct drm_gem_object *
amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)
{
@@ -776,6 +902,8 @@ amdxdna_drm_create_share_bo(struct drm_device *dev,
if (args->vaddr)
abo = amdxdna_gem_create_ubuf_object(dev, args);
else if (amdxdna_use_carveout(to_xdna_dev(dev)))
abo = amdxdna_gem_create_cbuf_object(dev, args);
else
abo = amdxdna_gem_create_shmem_object(dev, args);
if (IS_ERR(abo))
@@ -817,15 +945,32 @@ amdxdna_drm_create_dev_heap_bo(struct drm_device *dev,
/* Set up heap for this client. */
mutex_lock(&client->mm_lock);
if (client->dev_heap) {
XDNA_DBG(client->xdna, "dev heap is already created");
ret = -EBUSY;
if (!xdna->dev_info->ops->hwctx_heap_expand &&
client->dev_heap_nid > 0) {
XDNA_ERR(xdna, "Heap expansion is not supported");
ret = -EOPNOTSUPP;
goto mm_unlock;
}
client->dev_heap = abo;
drm_gem_object_get(to_gobj(abo));
drm_mm_init(&abo->mm, xdna->dev_info->dev_mem_base, abo->mem.size);
if (client->total_heap_size + abo->mem.size >
xdna->dev_info->dev_heap_max_size) {
XDNA_ERR(xdna, "Heap size 0x%lx + 0x%lx exceeds max 0x%lx",
client->total_heap_size, abo->mem.size,
xdna->dev_info->dev_heap_max_size);
ret = -ENOSPC;
goto mm_unlock;
}
ret = xa_insert(&client->dev_heap_xa, client->dev_heap_nid, abo, GFP_KERNEL);
if (ret) {
XDNA_ERR(xdna, "Add heap failed %d", ret);
goto mm_unlock;
}
abo->dev_addr = xdna->dev_info->dev_mem_base + client->total_heap_size;
client->total_heap_size += abo->mem.size;
client->dev_heap_nid++;
drm_gem_object_get(to_gobj(abo));
mutex_unlock(&client->mm_lock);
@@ -868,10 +1013,10 @@ amdxdna_drm_create_dev_bo(struct drm_device *dev,
ret = amdxdna_gem_heap_alloc(abo);
if (ret) {
XDNA_ERR(xdna, "Failed to alloc dev bo memory, ret %d", ret);
amdxdna_gem_destroy_obj(abo);
return ERR_PTR(ret);
}
drm_gem_private_object_init(dev, gobj, aligned_sz);
return abo;
@@ -879,6 +1024,7 @@ amdxdna_drm_create_dev_bo(struct drm_device *dev,
int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
struct amdxdna_client *client = filp->driver_priv;
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_drm_create_bo *args = data;
struct amdxdna_gem_obj *abo;
@@ -891,7 +1037,6 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
args->type, args->vaddr, args->size, args->flags);
switch (args->type) {
case AMDXDNA_BO_CMD:
fallthrough;
case AMDXDNA_BO_SHARE:
abo = amdxdna_drm_create_share_bo(dev, args, filp);
break;
@@ -900,6 +1045,13 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
break;
case AMDXDNA_BO_DEV:
abo = amdxdna_drm_create_dev_bo(dev, args, filp);
if (!IS_ERR(abo)) {
mutex_lock(&xdna->dev_lock);
ret = amdxdna_update_heap(client, NULL);
mutex_unlock(&xdna->dev_lock);
if (ret)
goto put_obj;
}
break;
default:
return -EINVAL;
@@ -923,14 +1075,11 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
return ret;
}
int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
static int amdxdna_bo_pin(struct amdxdna_gem_obj *abo)
{
struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
int ret;
if (abo->type == AMDXDNA_BO_DEV)
abo = abo->client->dev_heap;
if (is_import_bo(abo))
return 0;
@@ -940,6 +1089,45 @@ int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
return ret;
}
static void amdxdna_bo_unpin(struct amdxdna_gem_obj *abo)
{
struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
if (is_import_bo(abo))
return;
drm_gem_shmem_unpin(&abo->base);
XDNA_DBG(xdna, "BO type %d", abo->type);
}
int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
{
struct amdxdna_client *client = abo->client;
struct amdxdna_gem_obj *heap;
unsigned long heap_id, last = ULONG_MAX;
int ret = 0;
if (abo->type != AMDXDNA_BO_DEV)
return amdxdna_bo_pin(abo);
xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
abo->heap_start_id, abo->heap_end_id) {
ret = amdxdna_bo_pin(heap);
if (ret)
break;
last = heap_id;
}
if (ret && last <= abo->heap_end_id) {
xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
abo->heap_start_id, last)
amdxdna_bo_unpin(heap);
}
return ret;
}
int amdxdna_gem_pin(struct amdxdna_gem_obj *abo)
{
int ret;
@@ -953,14 +1141,18 @@ int amdxdna_gem_pin(struct amdxdna_gem_obj *abo)
void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo)
{
if (abo->type == AMDXDNA_BO_DEV)
abo = abo->client->dev_heap;
if (is_import_bo(abo))
return;
mutex_lock(&abo->lock);
drm_gem_shmem_unpin(&abo->base);
if (abo->type == AMDXDNA_BO_DEV) {
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
xa_for_each_range(&abo->client->dev_heap_xa, heap_id, heap,
abo->heap_start_id, abo->heap_end_id)
amdxdna_bo_unpin(heap);
} else {
amdxdna_bo_unpin(abo);
}
mutex_unlock(&abo->lock);
}
@@ -1017,6 +1209,29 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm
return ret;
}
static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)
{
u64 end;
if (offset >= abo->mem.size)
return -EINVAL;
if (check_add_overflow(offset, size, &end))
return -EINVAL;
size = min(abo->mem.size, end) - offset;
if (is_import_bo(abo))
drm_clflush_sg(abo->base.sgt);
else if (amdxdna_gem_vmap(abo))
drm_clflush_virt_range(amdxdna_gem_vmap(abo) + offset, size);
else if (abo->base.pages)
drm_clflush_pages(abo->base.pages, abo->mem.size >> PAGE_SHIFT);
else
return -EINVAL;
return 0;
}
/*
* The sync bo ioctl is to make sure the CPU cache is in sync with memory.
* This is required because NPU is not cache coherent device. CPU cache
@@ -1027,11 +1242,12 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm
int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
void *data, struct drm_file *filp)
{
struct amdxdna_client *client = filp->driver_priv;
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_drm_sync_bo *args = data;
struct amdxdna_gem_obj *abo;
struct drm_gem_object *gobj;
int ret;
int ret = 0;
gobj = drm_gem_object_lookup(filp, args->handle);
if (!gobj) {
@@ -1040,23 +1256,46 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
}
abo = to_xdna_obj(gobj);
ret = amdxdna_gem_pin(abo);
if (ret) {
XDNA_ERR(xdna, "Pin BO %d failed, ret %d", args->handle, ret);
goto put_obj;
if (abo->type == AMDXDNA_BO_DEV) {
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
u64 bo_start = amdxdna_gem_dev_addr(abo);
u64 flush_start = bo_start + args->offset;
u64 flush_end = flush_start + args->size;
xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
abo->heap_start_id, abo->heap_end_id) {
u64 heap_start = amdxdna_gem_dev_addr(heap);
u64 heap_end = heap_start + heap->mem.size;
u64 start = max(flush_start, heap_start);
u64 end = min(flush_end, heap_end);
if (start >= end)
continue;
ret = amdxdna_flush_bo(heap, start - heap_start, end - start);
if (ret) {
XDNA_ERR(xdna, "Failed to flush heap %ld ret %d",
heap_id, ret);
goto put_obj;
}
}
} else {
ret = amdxdna_gem_pin(abo);
if (ret) {
XDNA_ERR(xdna, "Pin BO %d failed, ret %d", args->handle, ret);
goto put_obj;
}
ret = amdxdna_flush_bo(abo, args->offset, args->size);
amdxdna_gem_unpin(abo);
if (ret) {
drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
goto put_obj;
}
}
if (is_import_bo(abo))
drm_clflush_sg(abo->base.sgt);
else if (amdxdna_gem_vmap(abo))
drm_clflush_virt_range(amdxdna_gem_vmap(abo) + args->offset, args->size);
else if (abo->base.pages)
drm_clflush_pages(abo->base.pages, gobj->size >> PAGE_SHIFT);
else
drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
amdxdna_gem_unpin(abo);
XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
args->handle, args->offset, args->size);

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