mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:59:29 +02:00
Merge tag 'drm-next-2026-08-20' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie:
"Highlights:
- dmemcg eviction support is good for low VRAM things like Steam
Machine
- AMD adds gfx6-8 modifier support for older GPUs that enables a
bunch of wayland stuff
- i915/xe has some new hw support but also a lot of display
refactoring
Everything:
perf:
- export perf_allow_ APIs for xe
udmabuf:
- remove default size limit of 64MB
rust:
- i/o rework (signed tag from driver-core tree)
- add registration guard and registration data
- fix unbounded lifetimes in ioctl handler args
- fix a drm_dev_register race
- gem_shmem: add DmaResvGuard helper
- gpuvm: require send/sync for driver data
- implement send/sync for GpuVaAlloc and GpuVmBo
- add SmContext lifetime
- rename dma_handle to dma_address
- change pci_sriov_get_totalvfs return to unsigned int
core:
- create drm_of_get_panel_orientation
- send per-connector hotplug events
- add thunderbolt UBHR tunneling support
connector:
- add color format property
dmem:
- introduce a peak file
- accept one region per limit
- add dmemcg support for eviction
gpusvm:
- reorg code to give drivers more flexibility
atomic:
- add create_state callback and helper
- add documentation on atomic commit lifetime
buddy:
- add per-order free
- add used block scoreboard
- fix UAF
- test buffer clearance on resume
- add phys_addr->block helper
gem:
- drop DRIVER_GEM_GPUVA flag
ttm:
- be more aggressive allocating below protection limit
sched:
- add test suite for concurrent job submissions
hdmi:
- hook the color format property in helpers
mipi-dsi:
- add MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT
bridge:
- add atomic create callbacks
- drop atomic reset
- display-connector: don't autoenable HPD IRQ
- trigger initial HPD for DP
- ti-sn65dsi83: remove NO_HFP and NO_HBP mode flags
- analogix_dp: switch to DP link training helpers
dp:
- add support for DSC max delta BPP
edid:
- parse panel type from DisplayID 2.x Display Parameters
sysfb:
- improve panel, stride, framebuffer size validation
panel:
- implement ref counting for struct drm_panel
- himax-hx83121a: add backlight regulator support
- novatek-nt36672a: Inline panel init sequences
- visionox-vtdr6130: enable DSC
- novatek-nt37801: Use mipi_dsi_*_multi() functions
- samsung-s6d16d0: Fix prepare error handling
- support Novatek NT36536 plus DT bindings
- sofef00: fix backlight updates
- osd101t2587: use mipi_dsi_*_multi interface
- panel-edp: adjust timing for AUO displays
- panel-lvds: support Opto Logic SCX1001511GGC49
- panel-simple: support Kyocera tcg070wvlq
- panel-edp: quirks
- AUO B116XAT04.3, CMN N116BCP-EA2, CSW MNB601LS1-8
- BOE NV116WH2-M30, BOE NT116WHM-N21, BOE NV116FH1-M31
- BOE NV116FH1-M30, NV140FHM-N5B, TM156VDXP25
- BOE NE160QDM-NY1, MB116AS01
- new:
- Samsung ATNA40HQ08-0, Anbernic TD4310
- Chipone ICNA35XX, Ilitek ILI9488
- Ilitek ILI7807S, Renesas R63419
- MNE001BS6-2, MNF601BS4-1, Sharp LQ120P1JX51
virtio:
- add support for save/restore virtio_gpu_objects
- abort vq wait on device removal
amdgpu:
- add color format DRM property
- initial compute pipe reset support
- add GFX 6-8 modifier support
- initial DCN 6.0.0 support
- dmemcg eviction support
- improved boundary checking for bios parsing
- RAS updates and rework
- VCN secure submission fixes
- 8K panel fix
- Display KUNIT tests
- parse panel type from DisplayID
- Align IP discovery to pci device lifetime
- SOC15 register macro cleanups
- UVD memory placement fixes
- GFX9 mode2 reset fixes
- drop unnecessary BUG/BUG_ON
- GFX8 soft reset rework
- enable soft reset on GFX8
- PSP/SMU 15.0.9 update
- VI ASPM fix
- userq fixes
- amdgpu_vm_get_task_info_pasid lifetime fix
- DC CACP support
- change system_unbound_wq with system_dfl_wq
- Loosen VFCT bios parsing to deal with pci=realloc
- SI/SMU7 AC/DC switch fix
- VM fence handling fix
- GEM close optimisation
- Apple Studio Display fixes
- DC FRL fixes
amdkfd:
- initial compute pipe reset support
- allow applications to opt out of sigbus on fatal errors
- improve CRIU boundary checks
- MQD handling rework
- move TBA/TMA from system to device memory
- avoid topology-lock in kfd_mmap
- SVM eviction fixes
radeon:
- fix unset CONFIG_ACPI build
i915:
- Novalake (NVL display version 35) timing generator enabling
- NVL DC3CO enabling
- enable UBHR link rates on thunderbolt tunnels
- Reduce Xe3+ PM demand peak bandwidth
- enable pipe DMC error interrupts for display 30+
- add kunit tests for DP link config selection
- refactor and document DP link recovery
- i915/xe driver display probe/remove/suspend/resume/shutdown cleanup
and unification
- i915/xe display runtime PM unified
- Break i915 and xe panic dependency on struct intel_framebuffer
- Streamline Pre/Post-CSC LUT loops
- drop TGL DC3DO support
- CDCLK santization
- fix HDMI scrambling enable
- fix phys bo pread/pwrite with offset
- add missing nospec on parallel submit slot
- fix some NULL derefs
xe:
- drop force_execlist module param
- gate observation streams with perf_allow_cpu
- skip FORCE_WC and vm_bound check for external dma-bufs
- dmemcg eviction support
- remove unused NVL-S GuC
- TLB invalidation improvements
- NVL-S updated PCI-IDs and w/a
- madvise: optimise invalidation path
- fix infinite gt-reset loop in timeout recovery
- update TTM device benefical_order
- wait on external BO kernel fences in exec ioctl
- add/use more KLV helpers
- sriov: disable display in admin only PF mode
- add RAS GPU health indicator
- optimise TTM populate for DONTNEED BO
- drop force_probe for NVL-s
- add debugfs for pcode info
amdxdna:
- disable device buffer export
nova:
- build nova-core/nova-drm from drivers/gpu
- export nova-core rust symbols (workaround)
- GSP boot process consolidation
- Boot GSP with vGPU enabled
- TLV firmware image format support
- Hopper/Blackwell fixes and cleanups
- I/O projection adoption
tyr:
- firmware loading and MCU boot
- add generic slot manager + MMU
- GPU VM support ARM64 LPAE page tables
- add kernel buffer object for internal allocations
- add parser for Mali CSF
- add MCU booting
nouveau:
- race fixes
- check instmem iomapping at first use
- add dmemcg support
- expose NVDEC channels
- add scanline position/head state support for GSP
qxl:
- convert simple encoder to regular
ethosu:
- add perf counter support
etnaviv:
- force flush on power register ops
msm:
- support DSC configuration with slice_per_pkt > 1
mxsfb:
- fix disable sequence
panthor:
- support sparse mappings
rockchip:
- switch away from simple helpers
- support YUV background color
- fix layer config timeout
- add edp support for rk3576
- add batch command submission function
rocket:
- error handling and NULL ptr deref fixes
sun4i:
- switch away from simple helpers
imagination:
- mark BXM-4-64 MC1 as support
host1x:
- support tegra264
tegra:
- add DSI for tegra 20/30
v3d:
- reduce PM runtime autosuspend delay
- scheduler fixes and refactoring
- deprecate v3d 3.3 and 4.1
- validate CPU job query boundaries
hibmc:
- improve plane format handling
- switch to gem shmem
mediatek:
- cec: correct compat for mt7623-8167?
exynos:
- remove simple dependency
- add error handling to encoder paths
- take i2c adapter module reference"
* tag 'drm-next-2026-08-20' of https://gitlab.freedesktop.org/drm/kernel: (2074 commits)
drm/xe/mcr: Take vcs1/vecs1 into account for first media slice
drm/xe: Fix a bug in pc_adjust_freq_bounds()
drm/xe: Fix xe_device_probe() failure
drm/xe/drm_ras: Move has_drm_ras check to drm_ras layer
drm/xe/ras: Fix boot-time ras error processing
drm/amd/display: make DC_RUN_WITH_PREEMPTION_ENABLED misuse a build error
drm/amd/pm: silence uninitialized variable warnings
drm/amdgpu: skip BOs being torn down during GTT recovery
drm/amdgpu: Reject UVD message with invalid number of h265 refs
drm/amdgpu: keep PRT mappings off the vm_bo state lists
drm/amdgpu: fix nbif 6.3.1 l1 low power not functional
drm/amd/display: fix BT.2020 YCbCr output CSC matrices for DCE
drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix
drm/amdgpu: Implement insert_end for VCE 3
drm/amdgpu: Fix UVD min buffer sizes
drm/amdgpu: Fix UVD decode image min size calculation
drm/amdgpu: Fix UVD dpb min size calculation for H264
drm/amdgpu: Reject UVD message with dimensions above 4096
drm/amdgpu: check ASPM on the dGPU host link
drm/radeon: fix autosuspend cleanup during teardown
...
This commit is contained in:
+1
-1
@@ -167,7 +167,7 @@ ForEachMacros:
|
||||
- 'drm_connector_for_each_possible_encoder'
|
||||
- 'drm_exec_for_each_locked_object'
|
||||
- 'drm_exec_for_each_locked_object_reverse'
|
||||
- 'drm_for_each_bridge_in_chain_scoped'
|
||||
- 'drm_for_each_bridge_in_chain'
|
||||
- 'drm_for_each_connector_iter'
|
||||
- 'drm_for_each_crtc'
|
||||
- 'drm_for_each_crtc_reverse'
|
||||
|
||||
@@ -579,6 +579,7 @@ Martyna Szapar-Mudlaw <martyna.szapar-mudlaw@linux.intel.com> <martyna.szapar-mu
|
||||
Mathieu Othacehe <othacehe@gnu.org> <m.othacehe@gmail.com>
|
||||
Mat Martineau <martineau@kernel.org> <mathew.j.martineau@linux.intel.com>
|
||||
Mat Martineau <martineau@kernel.org> <mathewm@codeaurora.org>
|
||||
Matt Coster <opensource@mtcoster.net> <matt.coster@imgtec.com>
|
||||
Matthew Wilcox <willy@infradead.org> <matthew.r.wilcox@intel.com>
|
||||
Matthew Wilcox <willy@infradead.org> <matthew@wil.cx>
|
||||
Matthew Wilcox <willy@infradead.org> <mawilcox@linuxonhyperv.com>
|
||||
@@ -645,7 +646,8 @@ Nadav Amit <nadav.amit@gmail.com> <namit@cs.technion.ac.il>
|
||||
Nadia Yvette Chambers <nyc@holomorphy.com> William Lee Irwin III <wli@holomorphy.com>
|
||||
Naoya Horiguchi <nao.horiguchi@gmail.com> <n-horiguchi@ah.jp.nec.com>
|
||||
Naoya Horiguchi <nao.horiguchi@gmail.com> <naoya.horiguchi@nec.com>
|
||||
Natalie Vock <natalie.vock@gmx.de> <friedrich.vock@gmx.de>
|
||||
Natalie Vock <nat@pixelcluster.dev> <friedrich.vock@gmx.de>
|
||||
Natalie Vock <nat@pixelcluster.dev> <natalie.vock@gmx.de>
|
||||
Nathan Chancellor <nathan@kernel.org> <natechancellor@gmail.com>
|
||||
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.ibm.com>
|
||||
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.vnet.ibm.com>
|
||||
|
||||
@@ -251,6 +251,13 @@ Description: RO. Fan 2 speed in RPM.
|
||||
|
||||
Only supported for particular Intel Xe graphics platforms.
|
||||
|
||||
On DG2 the driver always shows two fan channels, because the
|
||||
FSC_READ_NUM_FANS command does not work on some cards. OEMs
|
||||
decide how the fans map to tach channels, so two fans can share
|
||||
one tach line. When that happens, the second channel
|
||||
reads 0 RPM even though the fan is spinning. This is normal, not
|
||||
a bug.
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan3_input
|
||||
Date: March 2025
|
||||
KernelVersion: 6.16
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
What: /sys/bus/pci/drivers/xe/.../gpu_health
|
||||
Date: July 2026
|
||||
KernelVersion: 7.3
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
This file exposes the current gpu health state and allows the gpu
|
||||
health state to be updated.
|
||||
|
||||
This sysfs file is present only on Intel Xe platforms that support
|
||||
the gpu health indicator interface for RAS. Reading the current
|
||||
health state is available to all users, while updating the health
|
||||
state is restricted to administrative users only.
|
||||
|
||||
Read returns a single line containing one of the valid values for
|
||||
the current gpu health state. Writing one of the valid values
|
||||
updates the current gpu health state.
|
||||
|
||||
The valid values for the gpu health state are:
|
||||
|
||||
ok
|
||||
The gpu is healthy and operating within normal
|
||||
parameters.
|
||||
|
||||
warning
|
||||
The gpu is experiencing minor issues but remains
|
||||
operational.
|
||||
|
||||
critical
|
||||
The gpu is in a critical state and may not be
|
||||
operational.
|
||||
@@ -2918,6 +2918,12 @@ DMEM Interface Files
|
||||
The semantics are the same as for the memory cgroup controller, and are
|
||||
calculated in the same way.
|
||||
|
||||
dmem.peak
|
||||
A read-only nested-keyed file that exists on non-root cgroups.
|
||||
|
||||
The max device memory usage recorded for the cgroup and its
|
||||
descendants since the creation of the cgroup for each region.
|
||||
|
||||
dmem.capacity
|
||||
A read-only file that describes maximum region capacity.
|
||||
It only exists on the root cgroup. Not all memory can be
|
||||
|
||||
@@ -28,6 +28,7 @@ properties:
|
||||
- const: renesas,r9a09g057-mipi-dsi
|
||||
|
||||
- enum:
|
||||
- renesas,r9a08g046-mipi-dsi # RZ/G3L
|
||||
- renesas,r9a09g057-mipi-dsi # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/renesas,r9a08g046-lvds.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/G3L LVDS Encoder
|
||||
|
||||
maintainers:
|
||||
- Biju Das <biju.das.jz@bp.renesas.com>
|
||||
- Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
|
||||
|
||||
description: |
|
||||
This binding describes the LVDS encoder embedded in the Renesas RZ/G3L
|
||||
SoC. The encoder can operate in LVDS Single-link mode with 4 lanes
|
||||
(Data) + 1 lane (Clock).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: renesas,r9a08g046-lvds
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Peripheral clock
|
||||
- description: PHY clock
|
||||
- description: Dot clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: phyclk
|
||||
- const: dotclk
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: LVDS_RESET_N
|
||||
- description: MIPI_DSI_PRESET_N
|
||||
- description: MIPI_DSI_CMN_RSTB
|
||||
- description: MIPI_DSI_ARESET_N
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: lvdrst
|
||||
- const: prst
|
||||
- const: rst
|
||||
- const: arst
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Input channel, directly connected to the Display Unit.
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: |
|
||||
Output channel, directly connected to the LVDS panel or bridge.
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- power-domains
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/renesas,r9a08g046-cpg.h>
|
||||
|
||||
lvds@108a0000 {
|
||||
compatible = "renesas,r9a08g046-lvds";
|
||||
reg = <0x108a0000 0x10000>;
|
||||
clocks = <&cpg CPG_MOD R9A08G046_MIPI_DSI_PCLK>,
|
||||
<&cpg CPG_MOD R9A08G046_LVDS_PLLCLK>,
|
||||
<&cpg CPG_MOD R9A08G046_LVDS_CLK_DOT0>;
|
||||
clock-names = "pclk", "phyclk", "dotclk";
|
||||
resets = <&cpg R9A08G046_LVDS_RESET_N>,
|
||||
<&cpg R9A08G046_MIPI_DSI_PRESET_N>,
|
||||
<&cpg R9A08G046_MIPI_DSI_CMN_RSTB>,
|
||||
<&cpg R9A08G046_MIPI_DSI_ARESET_N>;
|
||||
reset-names = "lvdrst", "prst", "rst", "arst";
|
||||
power-domains = <&cpg>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
lvds0_in: endpoint {
|
||||
remote-endpoint = <&du_out_lvds0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
lvds0_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -15,10 +15,13 @@ description: |
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt7623-cec
|
||||
- mediatek,mt8167-cec
|
||||
- mediatek,mt8173-cec
|
||||
oneOf:
|
||||
- const: mediatek,mt8173-cec
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt7623-cec
|
||||
- mediatek,mt8167-cec
|
||||
- const: mediatek,mt8173-cec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/anbernic,td4310.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Anbernic TD4310 Based Panels
|
||||
|
||||
maintainers:
|
||||
- Chris Morgan <macromorgan@hotmail.com>
|
||||
|
||||
description:
|
||||
Anbernic TD4310 Based Panels, such as the RG-Vita-Pro panel
|
||||
(a 1080x1920 5.5 inch panel).
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- anbernic,panel-vita-pro
|
||||
- const: anbernic,td4310
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Panel power supply
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- port
|
||||
- reg
|
||||
- reset-gpios
|
||||
- vdd-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "anbernic,panel-vita-pro", "anbernic,td4310";
|
||||
reg = <0>;
|
||||
backlight = <&backlight>;
|
||||
enable-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
rotation = <270>;
|
||||
vdd-supply = <&vdd_lcd>;
|
||||
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&dsi_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -0,0 +1,79 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/chipone,icna3512.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Chipone ICNA3512 and ICNA3520 display drivers
|
||||
|
||||
maintainers:
|
||||
- Neil Armstrong <neil.armstrong@linaro.org>
|
||||
|
||||
description:
|
||||
The Chipone ICNA3512 and ICNA3520 are DDICs connected
|
||||
using a MIPI-DSI video interface.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- ayaneo,pocketds-panel-top
|
||||
- ayntec,odin2portal-panel
|
||||
- const: chipone,icna3512
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- ayntec,odin3-panel
|
||||
- ayntec,thor-panel-top
|
||||
- const: chipone,icna3520
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
vci-supply: true
|
||||
disp-supply: true
|
||||
blvdd-supply: true
|
||||
|
||||
port: true
|
||||
reset-gpios: true
|
||||
rotation: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
- vci-supply
|
||||
- disp-supply
|
||||
- blvdd-supply
|
||||
- reset-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
panel@0 {
|
||||
compatible = "ayntec,odin2portal-panel", "chipone,icna3512";
|
||||
reg = <0>;
|
||||
vdd-supply = <&vreg_l11b_1p2>;
|
||||
vddio-supply = <&vreg_l12b_1p8>;
|
||||
vci-supply = <&vreg_l13b_3p0>;
|
||||
disp-supply = <&vdd_disp_2v8>;
|
||||
blvdd-supply = <&vdd_bl_5v0>;
|
||||
reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -40,6 +40,9 @@ properties:
|
||||
vddi-supply:
|
||||
description: power supply for IC
|
||||
|
||||
bl-supply:
|
||||
description: power supply for backlight, in case it's managed via DSC
|
||||
|
||||
backlight: true
|
||||
ports: true
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/ilitek,ili7807s.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ilitek ILI7807S-based DSI panels
|
||||
|
||||
maintainers:
|
||||
- Arpit Saini <arpit.saini@oss.qualcomm.com>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- dlc,dlc0697
|
||||
- const: ilitek,ili7807s
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel
|
||||
|
||||
vddi-supply:
|
||||
description: I/O voltage supply (1.8V)
|
||||
|
||||
avdd-supply:
|
||||
description: Positive LCD bias supply (AVDD), typically +5.5V
|
||||
(range 4.5V to 6.3V)
|
||||
|
||||
avee-supply:
|
||||
description: Negative LCD bias supply (AVEE), typically -5.5V
|
||||
(range -6.3V to -4.5V)
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reset-gpios
|
||||
- vddi-supply
|
||||
- avdd-supply
|
||||
- avee-supply
|
||||
- port
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "dlc,dlc0697", "ilitek,ili7807s";
|
||||
reg = <0>;
|
||||
|
||||
reset-gpios = <&tlmm 3 GPIO_ACTIVE_LOW>;
|
||||
vddi-supply = <&pm4125_l15>;
|
||||
avdd-supply = <&avdd>;
|
||||
avee-supply = <&avee>;
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/ilitek,ili9488.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ilitek ILI9488 based MIPI-DSI panels
|
||||
|
||||
maintainers:
|
||||
- Igor Reznichenko <igor@reznichenko.net>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- focuslcds,e35gh-i-mw800cb
|
||||
- const: ilitek,ili9488
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vci-supply: true
|
||||
iovcc-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vci-supply
|
||||
- iovcc-supply
|
||||
- reset-gpios
|
||||
- backlight
|
||||
- port
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "focuslcds,e35gh-i-mw800cb", "ilitek,ili9488";
|
||||
reg = <0>;
|
||||
vci-supply = <®_vci_panel>;
|
||||
iovcc-supply = <®_iovcc_panel>;
|
||||
reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>;
|
||||
backlight = <&pwm_bl>;
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&dsi_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -19,6 +19,7 @@ properties:
|
||||
- anbernic,rg-ds-display-bottom
|
||||
- anbernic,rg-ds-display-top
|
||||
- chongzhou,cz101b4001
|
||||
- dcltek,dt300250
|
||||
- kingdisplay,kd101ne3-40ti
|
||||
- melfas,lmfbx101117480
|
||||
- radxa,display-10hd-ad001
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/novatek,nt36536.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Novatek NT36536 based DSI display Panels
|
||||
|
||||
maintainers:
|
||||
- Pengyu Luo <mitltlatltl@gmail.com>
|
||||
|
||||
description:
|
||||
The Novatek NT36536 is a generic DSI Panel IC used to drive dsi
|
||||
panels. Support video mode panels from China Star Optoelectronics
|
||||
Technology (CSOT), such as PP8807HB1-1 which is a dual-link 10-bit
|
||||
panel.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common-dual.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- csot,pp8807hb1-1
|
||||
- const: novatek,nt36536
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
vddio-supply:
|
||||
description: I/O source voltage rail
|
||||
|
||||
vsp-supply:
|
||||
description: Positive source voltage rail
|
||||
|
||||
vsn-supply:
|
||||
description: Negative source voltage rail
|
||||
|
||||
backlight: true
|
||||
ports: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vddio-supply
|
||||
- reset-gpios
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "csot,pp8807hb1-1", "novatek,nt36536";
|
||||
reg = <0>;
|
||||
|
||||
vddio-supply = <&vreg_iovdd_1p8>;
|
||||
reset-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
panel_in_0: endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1{
|
||||
reg = <1>;
|
||||
panel_in_1: endpoint {
|
||||
remote-endpoint = <&dsi1_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -58,6 +58,8 @@ properties:
|
||||
- hydis,hv070wx2-1e0
|
||||
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
|
||||
- jenson,bl-jt60050-01a
|
||||
# Opto Logic SCX1001511GGC49 10.1" WXGA (1280x800) TFT LCD LVDS panel
|
||||
- optologic,scx1001511ggc49
|
||||
# Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel
|
||||
- riverdi,rvt101hvlnwc00
|
||||
# Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel
|
||||
|
||||
@@ -37,6 +37,8 @@ properties:
|
||||
- ampire,am-480272h3tmqw-t01h
|
||||
# Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel
|
||||
- ampire,am-800480l1tmqw-t00h
|
||||
# Ampire AM-800480N3TZQW-00H 5" WVGA TFT LCD panel
|
||||
- ampire,am-800480n3tzqw-00h
|
||||
# Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
|
||||
- ampire,am800480r3tmqwa1h
|
||||
# Ampire AM-800600P5TMQW-TB8H 8.0" SVGA TFT LCD panel
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/renesas,r63419.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas R63419 based dual-DSI video mode Display Panel
|
||||
|
||||
maintainers:
|
||||
- Neil Armstrong <neil.armstrong@linaro.org>
|
||||
|
||||
description:
|
||||
The Renesas R63419 is a generic DDIC used to control dual-DSI LCD panels.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common-dual.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- ayaneo,wt0600-2k
|
||||
- ayaneo,wt0630-2k
|
||||
- const: renesas,r63419
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
vsp-supply: true
|
||||
vsn-supply: true
|
||||
vci-supply: true
|
||||
|
||||
backlight: true
|
||||
reset-gpios: true
|
||||
rotation: true
|
||||
ports: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
- vsp-supply
|
||||
- vsn-supply
|
||||
- vci-supply
|
||||
- backlight
|
||||
- reset-gpios
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "ayaneo,wt0600-2k", "renesas,r63419";
|
||||
reg = <0>;
|
||||
|
||||
reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>;
|
||||
|
||||
vdd-supply = <&vdd_3v0_lcd>;
|
||||
vddio-supply = <&vdd_1v8_io>;
|
||||
vsn-supply = <&vdd_5v0_neg>;
|
||||
vsp-supply = <&vdd_5v0_pos>;
|
||||
vci-supply = <&vdd_3v0_vci>;
|
||||
|
||||
backlight = <&backlight>;
|
||||
|
||||
rotation = <90>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
panel_in0: endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
panel_in1: endpoint {
|
||||
remote-endpoint = <&dsi1_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -22,6 +22,8 @@ properties:
|
||||
enum:
|
||||
# Anberic RG353V-V2 5.0" 640x480 TFT LCD panel
|
||||
- anbernic,rg353v-panel-v2
|
||||
# AYANEO Pocket DS lower 5.0" 1024x768 IPS LCD panel
|
||||
- ayaneo,pocket-ds-lower-panel
|
||||
# GameForce Chi 3.5" 640x480 TFT LCD panel
|
||||
- gameforce,chi-panel
|
||||
# Powkiddy RGB10MAX3 5.0" 720x1280 TFT LCD panel
|
||||
|
||||
@@ -25,6 +25,8 @@ properties:
|
||||
- samsung,atna40ct06
|
||||
# Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel
|
||||
- samsung,atna40cu11
|
||||
# Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel
|
||||
- samsung,atna40hq08
|
||||
# Samsung 14" WQXGA+ (2880×1800 pixels) eDP AMOLED panel
|
||||
- samsung,atna40yk20
|
||||
# Samsung 14.5" WQXGA+ (2880x1800 pixels) eDP AMOLED panel
|
||||
|
||||
@@ -20,6 +20,7 @@ properties:
|
||||
- enum:
|
||||
- renesas,r9a07g043u-du # RZ/G2UL
|
||||
- renesas,r9a07g044-du # RZ/G2{L,LC}
|
||||
- renesas,r9a08g046-du # RZ/G3L
|
||||
- renesas,r9a09g057-du # RZ/V2H(P)
|
||||
- renesas,r9a09g077-du # RZ/T2H
|
||||
- items:
|
||||
@@ -65,7 +66,7 @@ properties:
|
||||
model-dependent. Each port shall have a single endpoint.
|
||||
|
||||
patternProperties:
|
||||
"^port@[0-1]$":
|
||||
"^port@[0-2]$":
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
unevaluatedProperties: false
|
||||
|
||||
@@ -108,6 +109,7 @@ allOf:
|
||||
port@0:
|
||||
description: DPI
|
||||
port@1: false
|
||||
port@2: false
|
||||
|
||||
required:
|
||||
- port@0
|
||||
@@ -124,10 +126,31 @@ allOf:
|
||||
description: DSI
|
||||
port@1:
|
||||
description: DPI
|
||||
port@2: false
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a08g046-du
|
||||
then:
|
||||
properties:
|
||||
ports:
|
||||
properties:
|
||||
port@0:
|
||||
description: DSI
|
||||
port@1:
|
||||
description: DPI
|
||||
port@2:
|
||||
description: LVDS
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- port@2
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -140,6 +163,7 @@ allOf:
|
||||
port@0:
|
||||
description: DSI
|
||||
port@1: false
|
||||
port@2: false
|
||||
|
||||
required:
|
||||
- port@0
|
||||
|
||||
@@ -15,18 +15,24 @@ properties:
|
||||
enum:
|
||||
- rockchip,rk3288-dp
|
||||
- rockchip,rk3399-edp
|
||||
- rockchip,rk3576-edp
|
||||
- rockchip,rk3588-edp
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
items:
|
||||
- description: Reference clock
|
||||
- description: APB bus clock
|
||||
- description: GRF or AHB bus clock
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: dp
|
||||
- const: pclk
|
||||
- const: grf
|
||||
- enum:
|
||||
- grf
|
||||
- hclk
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
@@ -65,9 +71,46 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3288-dp
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
maxItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3399-edp
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: dp
|
||||
- const: pclk
|
||||
- const: grf
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3576-edp
|
||||
- rockchip,rk3588-edp
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: dp
|
||||
- const: pclk
|
||||
- const: hclk
|
||||
resets:
|
||||
minItems: 2
|
||||
reset-names:
|
||||
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- nvidia,tegra186-vic
|
||||
- nvidia,tegra194-vic
|
||||
- nvidia,tegra234-vic
|
||||
- nvidia,tegra264-vic
|
||||
|
||||
- items:
|
||||
- const: nvidia,tegra132-vic
|
||||
|
||||
@@ -25,6 +25,7 @@ properties:
|
||||
- nvidia,tegra186-host1x
|
||||
- nvidia,tegra194-host1x
|
||||
- nvidia,tegra234-host1x
|
||||
- nvidia,tegra264-host1x
|
||||
|
||||
- items:
|
||||
- const: nvidia,tegra132-host1x
|
||||
@@ -57,7 +58,8 @@ properties:
|
||||
enum: [1, 2]
|
||||
|
||||
ranges:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
description: Must contain one entry, for the module clock. See
|
||||
@@ -192,6 +194,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- nvidia,tegra234-host1x
|
||||
- nvidia,tegra264-host1x
|
||||
then:
|
||||
properties:
|
||||
reg-names:
|
||||
@@ -239,6 +242,21 @@ allOf:
|
||||
|
||||
required:
|
||||
- reg-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- nvidia,tegra264-host1x
|
||||
then:
|
||||
properties:
|
||||
ranges:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
else:
|
||||
properties:
|
||||
ranges:
|
||||
maxItems: 1
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
@@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: Imagination Technologies PowerVR and IMG Rogue GPUs
|
||||
|
||||
maintainers:
|
||||
- Frank Binns <frank.binns@imgtec.com>
|
||||
- Alessio Belle <alessio.belle@imgtec.com>
|
||||
- Luigi Santivetti <luigi.santivetti@imgtec.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
||||
@@ -9,7 +9,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: Imagination Technologies PowerVR SGX GPUs
|
||||
|
||||
maintainers:
|
||||
- Frank Binns <frank.binns@imgtec.com>
|
||||
- Alessio Belle <alessio.belle@imgtec.com>
|
||||
- Luigi Santivetti <luigi.santivetti@imgtec.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
||||
@@ -227,6 +227,8 @@ patternProperties:
|
||||
description: Axis Communications AB
|
||||
"^ayaneo,.*":
|
||||
description: Anyun Intelligent Technology (Hong Kong) Co., Ltd
|
||||
"^ayntec,.*":
|
||||
description: AYN Technologies Co., Ltd.
|
||||
"^azoteq,.*":
|
||||
description: Azoteq (Pty) Ltd
|
||||
"^azw,.*":
|
||||
@@ -411,6 +413,8 @@ patternProperties:
|
||||
description: DataImage, Inc.
|
||||
"^davicom,.*":
|
||||
description: DAVICOM Semiconductor, Inc.
|
||||
"^dcltek,.*":
|
||||
description: DCL Technologies Inc.
|
||||
"^deepcomputing,.*":
|
||||
description: DeepComputing (HK) Limited
|
||||
"^dell,.*":
|
||||
@@ -610,6 +614,8 @@ patternProperties:
|
||||
description: Flipkart Inc.
|
||||
"^focaltech,.*":
|
||||
description: FocalTech Systems Co.,Ltd
|
||||
"^focuslcds,.*":
|
||||
description: Focus Display Solutions, Inc.
|
||||
"^forlinx,.*":
|
||||
description: Baoding Forlinx Embedded Technology Co., Ltd.
|
||||
"^foursemi,.*":
|
||||
@@ -1265,6 +1271,8 @@ patternProperties:
|
||||
description: OpenWrt
|
||||
"^option,.*":
|
||||
description: Option NV
|
||||
"^optologic,.*":
|
||||
description: Opto Logic S.A.
|
||||
"^oranth,.*":
|
||||
description: Shenzhen Oranth Technology Co., Ltd.
|
||||
"^ORCL,.*":
|
||||
@@ -1427,6 +1435,8 @@ patternProperties:
|
||||
description: Embest RIoT
|
||||
"^riscv,.*":
|
||||
description: RISC-V Foundation
|
||||
"^riverdi,.*":
|
||||
description: Riverdi Sp. z o.o
|
||||
"^rockchip,.*":
|
||||
description: Rockchip Electronics Co., Ltd.
|
||||
"^rocktech,.*":
|
||||
|
||||
@@ -32,6 +32,9 @@ Interrupts
|
||||
.. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||||
:functions: register_hpd_handlers dm_crtc_high_irq dm_pflip_high_irq
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
|
||||
:functions: amdgpu_display_hotplug_work_func
|
||||
|
||||
Atomic Implementation
|
||||
=====================
|
||||
|
||||
@@ -178,3 +181,9 @@ following path:
|
||||
2. On DC interface, :c:type:`struct mpcc_blnd_cfg <mpcc_blnd_cfg>` programs the
|
||||
MPCC blend configuration considering the :c:type:`dc_plane_info
|
||||
<dc_plane_info>` input from DPP.
|
||||
|
||||
Display Properties
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
|
||||
:doc: property for adaptive backlight modulation
|
||||
|
||||
@@ -99,7 +99,8 @@ How to enable automated testing on your tree
|
||||
============================================
|
||||
|
||||
1. Create a Linux tree in https://gitlab.freedesktop.org/ if you don't have one
|
||||
yet
|
||||
yet, by forking https://gitlab.freedesktop.org/drm/kernel (this allows GitLab
|
||||
to internally track that these are the same git objects).
|
||||
|
||||
2. In your kernel repo's configuration (eg.
|
||||
https://gitlab.freedesktop.org/janedoe/linux/-/settings/ci_cd), change the
|
||||
|
||||
@@ -171,6 +171,12 @@ Bridge Operations
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
|
||||
:doc: bridge operations
|
||||
|
||||
Bridge Chain Format Selection
|
||||
-----------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
|
||||
:doc: bridge chain format selection
|
||||
|
||||
Bridge Connector Helper
|
||||
-----------------------
|
||||
|
||||
|
||||
@@ -287,6 +287,12 @@ structure, ordering of committing state changes to hardware is sequenced using
|
||||
Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed
|
||||
coverage of specific topics.
|
||||
|
||||
Atomic State Lifetime
|
||||
---------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
|
||||
:doc: state lifetime
|
||||
|
||||
Handling Driver Private State
|
||||
-----------------------------
|
||||
|
||||
@@ -604,6 +610,12 @@ Color Management Properties
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
|
||||
:doc: overview
|
||||
|
||||
Color Format Property
|
||||
---------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_connector.c
|
||||
:doc: Color format
|
||||
|
||||
Tile Group Property
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.. SPDX-License-Identifier: MIT
|
||||
.. Copyright © 2026 Intel Corporation
|
||||
|
||||
DisplayPort Link Capabilities
|
||||
=============================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_caps.c
|
||||
:doc: DisplayPort link capabilities
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_caps.h
|
||||
:internal:
|
||||
@@ -0,0 +1,8 @@
|
||||
.. SPDX-License-Identifier: MIT
|
||||
.. Copyright © 2026 Intel Corporation
|
||||
|
||||
DisplayPort Link Training
|
||||
=========================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_training.c
|
||||
:doc: DisplayPort link training
|
||||
@@ -38,6 +38,8 @@ driver. The display driver isn't an independent driver in that sense.
|
||||
fifo-underrun
|
||||
frontbuffer
|
||||
hotplug
|
||||
dp-link-training
|
||||
dp-link-capabilities
|
||||
plane
|
||||
psr
|
||||
snps-phy
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===================================================
|
||||
FSP (Foundation Security Processor) and Secure Boot
|
||||
===================================================
|
||||
This document describes the role of the FSP in the GPU boot sequence on
|
||||
Hopper and Blackwell GPUs, and how it differs from the earlier Ampere boot
|
||||
flow. It also provides a brief overview of the PRC (Product Reconfiguration
|
||||
Control) protocol used to query device configuration through FSP. As with
|
||||
other documents in this directory, the information is subject to change and
|
||||
is intended to help developers understand the corresponding kernel code.
|
||||
|
||||
What is FSP?
|
||||
============
|
||||
The Foundation Security Processor (FSP) is the GPU's Internal Root of Trust
|
||||
(IROT). It is a dedicated security processor that boots from immutable ROM
|
||||
(Boot ROM) inside the GPU and is responsible for establishing the Chain of
|
||||
Trust before any other firmware is allowed to run.
|
||||
|
||||
FSP runs independently of the host CPU and starts executing as soon as the
|
||||
GPU is powered on. By the time the nova-core driver is loaded, FSP has
|
||||
already completed its own secure boot and is ready to accept commands from
|
||||
the driver.
|
||||
|
||||
Simplified boot flow (Hopper/Blackwell)
|
||||
=======================================
|
||||
Starting with Hopper, the boot flow is significantly simplified compared to
|
||||
earlier GPU generations like Ampere.
|
||||
|
||||
On an **Ampere** GPU, the boot verification chain involves multiple Falcon
|
||||
engines and multiple ucode stages (see falcon.rst for details)::
|
||||
|
||||
Hardware BROM (SEC2)
|
||||
-> HS Booter (SEC2)
|
||||
-> LS GSP-RM (GSP)
|
||||
|
||||
The driver must extract ucode from VBIOS, manage SEC2 and GSP, and
|
||||
orchestrate the Booter to load GSP-RM. This involves FWSEC-FRTS, devinit,
|
||||
and the Booter stages.
|
||||
|
||||
On **Hopper/Blackwell** GPUs, FSP replaces this multi-stage process with a
|
||||
single message-driven interface::
|
||||
|
||||
FSP (hardware root of trust, boots from ROM)
|
||||
-> FMC (Falcon Microcontroller, verified by FSP)
|
||||
-> GSP-RM (verified and loaded by FMC)
|
||||
|
||||
The driver only needs to:
|
||||
|
||||
1. Wait for FSP to complete its own secure boot (polling a scratch register).
|
||||
2. Send a Chain of Trust (COT) message to FSP with the FMC firmware location,
|
||||
cryptographic signatures, and GSP boot parameters.
|
||||
3. FSP authenticates the FMC firmware and boots it, FMC in turn loads GSP-RM.
|
||||
|
||||
There is no SEC2 involvement, no Booter ucode, and no FWSEC-FRTS stage. The
|
||||
entire secure boot is driven by a single FSP message exchange.
|
||||
|
||||
Chain of Trust (COT) protocol
|
||||
=============================
|
||||
The Chain of Trust establishes a cryptographically enforced boot sequence,
|
||||
ensuring the GPU reaches a known, trusted state.
|
||||
|
||||
The driver communicates with FSP using a message queue (Falcon MSGQ
|
||||
interface). Each message consists of an MCTP (Management Component Transport
|
||||
Protocol) transport header and an NVDM (NVIDIA Vendor Defined Message) header,
|
||||
followed by a protocol-specific payload.
|
||||
|
||||
For Chain of Trust, the payload includes:
|
||||
|
||||
- The system memory address of the FMC firmware image.
|
||||
- Cryptographic material: a SHA-384 hash, RSA-3K public key, and RSA-3K
|
||||
signature extracted from the FMC ELF firmware.
|
||||
- FRTS (Firmware Runtime Services) region information (vidmem offset and size).
|
||||
- The system memory address of the GSP boot arguments structure.
|
||||
|
||||
FSP verifies the signature against the provided public key and hash, and if
|
||||
verification succeeds, boots the FMC. The FMC then authenticates and launches
|
||||
GSP-RM.
|
||||
|
||||
The message flow is::
|
||||
|
||||
nova-core FSP
|
||||
| |
|
||||
| 1. Poll scratch register |
|
||||
| (wait for FSP boot complete) |
|
||||
| |
|
||||
| 2. COT message ------------> |
|
||||
| (FMC addr, signatures, |
|
||||
| boot params) |
|
||||
| |
|
||||
| |--- Verify FMC signature
|
||||
| |--- Boot FMC
|
||||
| |--- FMC loads GSP-RM
|
||||
| |
|
||||
| 3. COT response <------------ |
|
||||
| (success/error) |
|
||||
| |
|
||||
|
||||
FSP message format
|
||||
==================
|
||||
All FSP messages share a common header format consisting of two 32-bit words:
|
||||
|
||||
**MCTP header** (Management Component Transport Protocol):
|
||||
|
||||
- Bit 31: SOM (Start of Message)
|
||||
- Bit 30: EOM (End of Message)
|
||||
- Bits 29:28: Packet sequence number
|
||||
- Bits 23:16: Source Endpoint ID
|
||||
|
||||
**NVDM header** (NVIDIA Vendor Defined Message):
|
||||
|
||||
- Bits 6:0: MCTP message type (0x7e = vendor-defined PCI)
|
||||
- Bits 23:8: PCI vendor ID (0x10de = NVIDIA)
|
||||
- Bits 31:24: NVDM type (0x14 = COT, 0x13 = PRC, 0x15 = FSP response)
|
||||
|
||||
PRC (Product Reconfiguration Control) protocol
|
||||
===============================================
|
||||
PRC is an API system exposed through FSP's Management Partition that allows
|
||||
querying and modifying device configuration without firmware updates.
|
||||
|
||||
Configuration parameters are called "knobs". Each knob has a unique object
|
||||
ID and controls a specific device behavior. Examples include vGPU mode, ECC
|
||||
enable, confidential computing mode, and NVLINK configuration.
|
||||
|
||||
Each knob has two values:
|
||||
|
||||
- **Active**: the currently effective value for this boot cycle.
|
||||
- **Persistent**: the value stored in InfoROM, applied on subsequent boots.
|
||||
|
||||
The nova-core driver uses PRC to read the vGPU mode knob (object ID 0x29)
|
||||
during early boot, before firmware loading, to determine whether the GPU
|
||||
should operate in vGPU mode.
|
||||
|
||||
The PRC message format follows the same MCTP/NVDM header structure as COT,
|
||||
with NVDM type 0x13. The payload contains:
|
||||
|
||||
- A sub-command (e.g., 0x0c for read).
|
||||
- Flags indicating which value to read (bit 0 = persistent, bit 1 = active).
|
||||
- The knob object ID.
|
||||
|
||||
The response includes the common FSP response header (with error status)
|
||||
followed by the knob's 16-bit state value.
|
||||
@@ -0,0 +1,184 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
|
||||
==================================
|
||||
TLV Tags in Nova Firmware Images
|
||||
==================================
|
||||
|
||||
Nova firmware images use a Type-Length-Value (TLV) format to encapsulate
|
||||
firmware components and metadata. The TLV file begins with a 4-byte "magic"
|
||||
header that contains the string "NVFW". Following the header is a sequence of
|
||||
TLV blocks.
|
||||
|
||||
Each block consists of a 4-byte tag of ASCII characters, a 4-byte length
|
||||
encoded as a little-endian unsigned integer, and a sequence of bytes, the size
|
||||
of which is equal to the length rounded up to the next multiple of 4.
|
||||
|
||||
The driver code that reads the TLV and uses its contents is called the parser.
|
||||
It is the responsibility of the parser to handle missing or malformed tags,
|
||||
lengths, and values in the TLV.
|
||||
|
||||
::
|
||||
|
||||
+------+------+------+------+
|
||||
| 'N' | 'V' | 'F' | 'W' | Magic header
|
||||
+------+------+------+------+
|
||||
| Tag (4 bytes, ASCII) | TLV block 0
|
||||
+---------------------------+
|
||||
| Length (4 bytes, LE) |
|
||||
+---------------------------+
|
||||
| |
|
||||
| Value (length bytes, |
|
||||
| padded to 4-byte align) |
|
||||
| |
|
||||
+---------------------------+
|
||||
| Tag (4 bytes, ASCII) | TLV block 1
|
||||
+---------------------------+
|
||||
| Length (4 bytes, LE) |
|
||||
+---------------------------+
|
||||
| |
|
||||
| Value (length bytes, |
|
||||
| padded to 4-byte align) |
|
||||
| |
|
||||
+---------------------------+
|
||||
| ... | More TLV blocks
|
||||
+---------------------------+
|
||||
|
||||
Tags and Length
|
||||
===============
|
||||
TLV tags are always four-character words, with all letters being upper case.
|
||||
Duplicate tags are not allowed.
|
||||
|
||||
A TLV file may contain additional tags not described in this document.
|
||||
|
||||
Values
|
||||
======
|
||||
Values are one of four types. The type is not encoded in the format; rather,
|
||||
the parser expects a given tag to have a value of a given type.
|
||||
|
||||
1) Integers, encoded in 32-bit or 64-bit little-endian format.
|
||||
2) Strings, encoded as-is and required to be only printable ASCII characters
|
||||
and without a null terminator.
|
||||
3) An array of bytes, for binary data.
|
||||
4) Boolean, encoded as single byte, with a value of 0 for False or 1 for True.
|
||||
|
||||
Common Tags
|
||||
===========
|
||||
These tags are shared across firmware types and carry the same meaning
|
||||
wherever they appear. Unlike the firmware-specific tags below, a common tag
|
||||
is reserved: its meaning is fixed and may never be redefined for a particular
|
||||
firmware type.
|
||||
|
||||
``VERS`` (string)
|
||||
Human-readable firmware version string. Present in all TLV files.
|
||||
|
||||
A TLV image must contain either a single ``BLOB`` tag (firmware embedded
|
||||
inline) or a ``SIZE``/``FILE`` pair (firmware stored in a separate file).
|
||||
|
||||
``BLOB`` (bytes)
|
||||
If the firmware microcode binary is stored in the TLV, this tag contains
|
||||
the actual firmware image bytes.
|
||||
|
||||
``FILE`` (string)
|
||||
If the firmware binary is stored as a separate file, this tag contains the
|
||||
name of that file, which is required to be in the same directory as the TLV,
|
||||
so no paths are allowed in the filename. This tag is always paired with
|
||||
``SIZE``, so as to allow the driver to pre-allocate the buffer before
|
||||
loading the file.
|
||||
|
||||
``SIZE`` (u32)
|
||||
Total size in bytes of the firmware image to be loaded from the companion
|
||||
file named by ``FILE``. This tag is mandatory if ``FILE`` exists, so the
|
||||
size of the firmware image must be known when the TLV is created. If the
|
||||
firmware image is updated and its size changes, then the TLV must be
|
||||
updated with it.
|
||||
|
||||
GSP Firmware Tags
|
||||
=================
|
||||
``SIGN`` (bytes)
|
||||
Cryptographic signature for the GSP firmware.
|
||||
|
||||
``BLID`` (string)
|
||||
The build ID, extracted from the ".note.gnu.build-id" section.
|
||||
|
||||
Booter Firmware Tags
|
||||
====================
|
||||
``DAOF`` (u32) - ``os_data_offset``
|
||||
OS data section offset within the firmware image (absolute byte offset).
|
||||
Maps to the DMEM load source.
|
||||
|
||||
``DASZ`` (u32) - ``os_data_size``
|
||||
OS data section size in bytes.
|
||||
|
||||
``CDOF`` (u32) - ``os_code_offset``
|
||||
OS code section offset within the firmware image (absolute byte offset).
|
||||
Maps to the non-secure IMEM load source.
|
||||
|
||||
``CDSZ`` (u32) - ``os_code_size``
|
||||
OS code section size in bytes.
|
||||
|
||||
``PLOC`` (u32) - ``patch_loc``
|
||||
Signature patch location -- byte offset within the firmware image where the
|
||||
selected signature should be written.
|
||||
|
||||
``FUSE`` (u32) - ``fuse_version``
|
||||
Fuse version of the firmware, used with the hardware fuse register to
|
||||
select the correct signature index.
|
||||
|
||||
``ENID`` (u32) - ``engine_id``
|
||||
Engine ID mask identifying the falcon engine this firmware targets.
|
||||
|
||||
``UCID`` (u32) - ``ucode_id``
|
||||
Microcode ID used together with the engine ID to query hardware signature
|
||||
fuse registers.
|
||||
|
||||
``A0CO`` (u32) - ``app0_code_offset``
|
||||
App0 code offset -- start of the secure code region within the firmware
|
||||
image. Used as the IMEM secure section source.
|
||||
|
||||
``A0CS`` (u32) - ``app0_code_size``
|
||||
App0 code size in bytes.
|
||||
|
||||
``NSIG`` (u32) - ``num_sigs``
|
||||
Number of signatures included in the ``SIGN`` tag.
|
||||
|
||||
``SIGN`` (bytes)
|
||||
Concatenated array of firmware signatures. The size of each signature is
|
||||
the total length of the ``SIGN`` value divided by ``NSIG``. The correct
|
||||
signature is selected using the fuse-version-derived index.
|
||||
|
||||
Generic Bootloader Tags
|
||||
=======================
|
||||
``CDSZ`` (u32) - ``code_size``
|
||||
Size in bytes of the bootloader code to copy from the ``BLOB`` tag and
|
||||
PIO-load into falcon IMEM.
|
||||
|
||||
``STRT`` (u32) - ``start_tag``
|
||||
Start tag identifying the IMEM block where execution begins. The falcon
|
||||
boot address is derived as ``start_tag << 8``.
|
||||
|
||||
GSP Bootloader Tags
|
||||
===================
|
||||
``CDOF`` (u32) - ``code_offset``
|
||||
Offset within the firmware image at which the code section starts.
|
||||
|
||||
``DAOF`` (u32) - ``data_offset``
|
||||
Offset within the firmware image at which the data section starts.
|
||||
|
||||
``MFOF`` (u32) - ``manifest_offset``
|
||||
Offset within the firmware image at which the manifest starts.
|
||||
|
||||
``APPV`` (u32) - ``app_version``
|
||||
Application version of the firmware.
|
||||
|
||||
FMC Firmware Tags
|
||||
=================
|
||||
``HASH`` (bytes)
|
||||
SHA-384 hash of the FMC firmware, exactly 48 bytes long.
|
||||
|
||||
``PKEY`` (bytes)
|
||||
Public key used to verify the FMC firmware. At most 384 bytes (RSA-3072),
|
||||
but may be shorter.
|
||||
|
||||
``SIGN`` (bytes)
|
||||
Signature of the FMC firmware. At most 384 bytes (RSA-3072), but may
|
||||
be shorter.
|
||||
@@ -30,5 +30,7 @@ vGPU manager VFIO driver and the nova-drm driver.
|
||||
core/todo
|
||||
core/vbios
|
||||
core/devinit
|
||||
core/fsp
|
||||
core/fwsec
|
||||
core/falcon
|
||||
core/tlv
|
||||
|
||||
@@ -55,7 +55,7 @@ There are still drivers that use drm_simple_display_pipe. The task here is to
|
||||
convert them to use regular atomic helpers. Search for a driver that calls
|
||||
drm_simple_display_pipe_init() and inline all helpers from drm_simple_kms_helper.c
|
||||
into the driver, such that no simple-KMS interfaces are required. Please also
|
||||
rename all inlined fucntions according to driver conventions.
|
||||
rename all inlined functions according to driver conventions.
|
||||
|
||||
Contact: Thomas Zimmermann, respective driver maintainer
|
||||
|
||||
@@ -896,10 +896,11 @@ complection of submission.
|
||||
One minor feature still missing is a generic DRM IOCTL to query the error
|
||||
status of binary and timeline drm_syncobj.
|
||||
|
||||
This should probably be improved by implementing the necessary kernel interface
|
||||
and adding support for that in the userspace stack.
|
||||
This was already improved by implementing the necessary kernel interface
|
||||
(patches are on the dri-devel mailing list) but adding support for that in the
|
||||
userspace stack is still missing.
|
||||
|
||||
Contact: Christian König
|
||||
Contact: Christian König, Michel Dänzer
|
||||
|
||||
Level: Starter
|
||||
|
||||
@@ -948,6 +949,47 @@ Contact: Philipp Stanner <phasta@kernel.org>
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Replace the lockless queue with a locked list
|
||||
---------------------------------------------
|
||||
|
||||
drm_sched is the only user in the entire kernel of a special lockless queue, the
|
||||
spsc_queue. This queue utilizes:
|
||||
|
||||
- preempt_disable()
|
||||
- atomic instructions
|
||||
- memory barriers
|
||||
- ACCESS_ONCE()
|
||||
|
||||
whereas a conventional spinlock utilizes:
|
||||
|
||||
- preempt_disable()
|
||||
- 1 atomic instruction for taking / releasing the lock
|
||||
- memory barriers
|
||||
|
||||
Moreover, drm_sched_entity_push_job(), the only user of spsc_queue_push(), has
|
||||
to take a lock in some situations anyways and calls to it are often serialized
|
||||
with a driver lock.
|
||||
|
||||
It is, thus, highly questionable whether the lockless queue grants any advantage
|
||||
at all. Considering that its internals are not well documented and its correctness
|
||||
is not formally proven, it seems desirable to replace the queue with a mere list
|
||||
or hlist that is protected by a spinlock.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Replace the spsc_queue in drm/sched (and those who might access the scheduler's
|
||||
internal queue) with a spinlock + (h)list.
|
||||
- Ideally, check with some micro benchmarks and real world tests (preferably
|
||||
with amdgpu) for relevant performance regressions.
|
||||
- Remove the spsc_queue from the kernel altogether.
|
||||
|
||||
Contact:
|
||||
|
||||
- Philipp Stanner <phasta@kernel.org>
|
||||
- Christian König <christian.koenig@amd.com>
|
||||
|
||||
Level: Beginner
|
||||
|
||||
Outside DRM
|
||||
===========
|
||||
|
||||
|
||||
@@ -8,3 +8,10 @@ Xe Device Wedging
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_device.c
|
||||
:doc: Xe Device Wedging
|
||||
|
||||
====================
|
||||
GPU Health Indicator
|
||||
====================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_ras.c
|
||||
:doc: GPU Health Indicator
|
||||
|
||||
+17
-5
@@ -6641,7 +6641,7 @@ F: tools/testing/selftests/cgroup/test_cpuset_v1_base.sh
|
||||
CONTROL GROUP - DEVICE MEMORY CONTROLLER (DMEM)
|
||||
M: Maarten Lankhorst <dev@lankhorst.se>
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Natalie Vock <natalie.vock@gmx.de>
|
||||
M: Natalie Vock <nat@pixelcluster.dev>
|
||||
L: cgroups@vger.kernel.org
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
@@ -8138,6 +8138,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: Documentation/devicetree/bindings/display/ilitek,ili9486.yaml
|
||||
F: drivers/gpu/drm/tiny/ili9486.c
|
||||
|
||||
DRM DRIVER FOR ILITEK ILI9488 PANELS
|
||||
M: Igor Reznichenko <igor@reznichenko.net>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml
|
||||
F: drivers/gpu/drm/panel/panel-ilitek-ili9488.c
|
||||
|
||||
DRM DRIVER FOR ILITEK ILI9805 PANELS
|
||||
M: Michael Trimarchi <michael@amarulasolutions.com>
|
||||
S: Maintained
|
||||
@@ -8870,11 +8876,14 @@ M: Sandy Huang <hjc@rock-chips.com>
|
||||
M: Heiko Stübner <heiko@sntech.de>
|
||||
M: Andy Yan <andy.yan@rock-chips.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: linux-rockchip@lists.infradead.org
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: Documentation/devicetree/bindings/display/rockchip/
|
||||
F: drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.*
|
||||
F: drivers/gpu/drm/ci/xfails/rockchip*
|
||||
F: drivers/gpu/drm/rockchip/
|
||||
F: include/drm/bridge/dw_hdmi_qp.h
|
||||
|
||||
DRM DRIVERS FOR STI
|
||||
M: Alain Volmat <alain.volmat@foss.st.com>
|
||||
@@ -12687,12 +12696,15 @@ S: Orphan
|
||||
F: drivers/media/rc/img-ir/
|
||||
|
||||
IMGTEC POWERVR DRM DRIVER
|
||||
M: Frank Binns <frank.binns@imgtec.com>
|
||||
M: Matt Coster <matt.coster@imgtec.com>
|
||||
M: Alessio Belle <alessio.belle@imgtec.com>
|
||||
M: Luigi Santivetti <luigi.santivetti@imgtec.com>
|
||||
L: imagination@lists.freedesktop.org
|
||||
S: Supported
|
||||
Q: https://patchwork.freedesktop.org/project/imagination/list/
|
||||
B: https://gitlab.freedesktop.org/imagination/linux/-/issues
|
||||
C: irc://irc.oftc.net/powervr
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml
|
||||
F: Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml
|
||||
F: Documentation/devicetree/bindings/gpu/img,powervr-*.yaml
|
||||
F: Documentation/gpu/imagination/
|
||||
F: drivers/gpu/drm/ci/xfails/powervr*
|
||||
F: drivers/gpu/drm/imagination/
|
||||
|
||||
@@ -758,9 +758,15 @@ static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dma_buf *amdxdna_gem_dev_obj_export(struct drm_gem_object *gobj, int flags)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = {
|
||||
.free = amdxdna_gem_dev_obj_free,
|
||||
.vmap = amdxdna_gem_dev_obj_vmap,
|
||||
.export = amdxdna_gem_dev_obj_export,
|
||||
};
|
||||
|
||||
static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o
|
||||
ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o
|
||||
ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o ethosu_perfmon.o
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
@@ -43,6 +44,15 @@ struct gen_pool;
|
||||
#define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8)
|
||||
#define NPU_BASEP_REGION_MAX 8
|
||||
|
||||
#define NPU_REG_PMCR 0x0180
|
||||
#define NPU_REG_PMCNTENSET 0x0184
|
||||
#define NPU_REG_PMCNTENCLR 0x0188
|
||||
#define NPU_REG_PMCCNTR_LO 0x01A0
|
||||
#define NPU_REG_PMCCNTR_HI 0x01A4
|
||||
#define NPU_REG_PMCCNTR_CFG 0x01A8
|
||||
#define NPU_REG_PMU_EVCNTR(x) (0x0300 + (x) * 4)
|
||||
#define NPU_REG_PMU_EVTYPER(x) (0x0380 + (x) * 4)
|
||||
|
||||
#define ID_ARCH_MAJOR_MASK GENMASK(31, 28)
|
||||
#define ID_ARCH_MINOR_MASK GENMASK(27, 20)
|
||||
#define ID_ARCH_PATCH_MASK GENMASK(19, 16)
|
||||
@@ -67,6 +77,15 @@ struct gen_pool;
|
||||
|
||||
#define PROT_ACTIVE_CSL BIT(1)
|
||||
|
||||
#define PMCR_NUM_EVENT_CNT_MASK GENMASK(15, 11)
|
||||
#define PMCR_CYCLE_CNT_RST BIT(2)
|
||||
#define PMCR_EVENT_CNT_RST BIT(1)
|
||||
#define PMCR_CNT_EN BIT(0)
|
||||
|
||||
#define PMU_EV_TYPE_NONE 0
|
||||
#define PMU_EV_TYPE_CYCLES 0x11
|
||||
#define PMU_EV_TYPE_IDLE 0x20
|
||||
|
||||
enum ethosu_cmds {
|
||||
NPU_OP_CONV = 0x2,
|
||||
NPU_OP_DEPTHWISE = 0x3,
|
||||
@@ -152,6 +171,8 @@ enum ethosu_cmds {
|
||||
|
||||
#define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */
|
||||
|
||||
struct ethosu_perfmon;
|
||||
|
||||
/**
|
||||
* struct ethosu_device - Ethosu device
|
||||
*/
|
||||
@@ -161,6 +182,7 @@ struct ethosu_device {
|
||||
|
||||
/** @iomem: CPU mapping of the registers. */
|
||||
void __iomem *regs;
|
||||
void __iomem *pmu_regs;
|
||||
|
||||
void __iomem *sram;
|
||||
struct gen_pool *srampool;
|
||||
@@ -173,8 +195,6 @@ struct ethosu_device {
|
||||
struct drm_ethosu_npu_info npu_info;
|
||||
|
||||
struct ethosu_job *in_flight_job;
|
||||
/* For in_flight_job and ethosu_job_hw_submit() */
|
||||
struct mutex job_lock;
|
||||
|
||||
/* For dma_fence */
|
||||
spinlock_t fence_lock;
|
||||
@@ -184,6 +204,17 @@ struct ethosu_device {
|
||||
struct mutex sched_lock;
|
||||
u64 fence_context;
|
||||
u64 emit_seqno;
|
||||
|
||||
/* Tracks the performance monitor state. */
|
||||
struct {
|
||||
/* Protects @active. */
|
||||
struct mutex lock;
|
||||
|
||||
/* Perfmon currently programmed in HW (or NULL if none). */
|
||||
struct ethosu_perfmon *active;
|
||||
} perfmon_state;
|
||||
|
||||
struct ethosu_perfmon *global_perfmon;
|
||||
};
|
||||
|
||||
#define to_ethosu_device(drm_dev) \
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <drm/drm_utils.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_accel.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/ethosu_accel.h>
|
||||
|
||||
#include "ethosu_drv.h"
|
||||
@@ -154,6 +155,7 @@ static int ethosu_open(struct drm_device *ddev, struct drm_file *file)
|
||||
if (ret)
|
||||
goto err_put_mod;
|
||||
|
||||
ethosu_perfmon_open_file(priv);
|
||||
file->driver_priv = no_free_ptr(priv);
|
||||
return 0;
|
||||
|
||||
@@ -165,6 +167,7 @@ err_put_mod:
|
||||
static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file)
|
||||
{
|
||||
ethosu_job_close(file->driver_priv);
|
||||
ethosu_perfmon_close_file(file->driver_priv);
|
||||
kfree(file->driver_priv);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
@@ -179,6 +182,10 @@ static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = {
|
||||
ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0),
|
||||
ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0),
|
||||
ETHOSU_IOCTL(SUBMIT, submit, 0),
|
||||
ETHOSU_IOCTL(PERFMON_CREATE, perfmon_create, 0),
|
||||
ETHOSU_IOCTL(PERFMON_DESTROY, perfmon_destroy, 0),
|
||||
ETHOSU_IOCTL(PERFMON_GET_VALUES, perfmon_get_values, 0),
|
||||
ETHOSU_IOCTL(PERFMON_SET_GLOBAL, perfmon_set_global, 0),
|
||||
};
|
||||
|
||||
DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops);
|
||||
@@ -314,8 +321,14 @@ static int ethosu_init(struct ethosu_device *ethosudev)
|
||||
|
||||
ethosu_sram_init(ethosudev);
|
||||
|
||||
if (!ethosu_is_u65(ethosudev))
|
||||
ethosudev->pmu_regs += 0x1000;
|
||||
|
||||
ethosudev->npu_info.pmu_counters = FIELD_GET(PMCR_NUM_EVENT_CNT_MASK,
|
||||
readl_relaxed(ethosudev->pmu_regs + NPU_REG_PMCR));
|
||||
|
||||
dev_info(ethosudev->base.dev,
|
||||
"Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n",
|
||||
"Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM, %d PMU cntrs\n",
|
||||
FIELD_GET(ID_ARCH_MAJOR_MASK, id),
|
||||
FIELD_GET(ID_ARCH_MINOR_MASK, id),
|
||||
FIELD_GET(ID_ARCH_PATCH_MASK, id),
|
||||
@@ -323,7 +336,8 @@ static int ethosu_init(struct ethosu_device *ethosudev)
|
||||
FIELD_GET(ID_VER_MINOR_MASK, id),
|
||||
FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config),
|
||||
1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config),
|
||||
ethosudev->npu_info.sram_size / 1024);
|
||||
ethosudev->npu_info.sram_size / 1024,
|
||||
ethosudev->npu_info.pmu_counters);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -342,11 +356,16 @@ static int ethosu_probe(struct platform_device *pdev)
|
||||
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
|
||||
|
||||
ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
ethosudev->pmu_regs = ethosudev->regs;
|
||||
|
||||
ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks);
|
||||
if (ethosudev->num_clks < 0)
|
||||
return ethosudev->num_clks;
|
||||
|
||||
ret = drmm_mutex_init(ðosudev->base, ðosudev->perfmon_state.lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ethosu_job_init(ethosudev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1,15 +1,74 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
/* Copyright 2025-2026 Arm, Ltd. */
|
||||
#ifndef __ETHOSU_DRV_H__
|
||||
#define __ETHOSU_DRV_H__
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/xarray.h>
|
||||
#include <drm/gpu_scheduler.h>
|
||||
|
||||
struct ethosu_device;
|
||||
struct drm_device;
|
||||
struct drm_file;
|
||||
|
||||
struct ethosu_file_priv {
|
||||
struct ethosu_device *edev;
|
||||
struct drm_sched_entity sched_entity;
|
||||
struct xarray perfmons;
|
||||
};
|
||||
|
||||
/* Performance monitor object. The perfmon lifetime is controlled by userspace
|
||||
* using perfmon related ioctls. A perfmon can be attached to a DRM_ETHOSU_SUBMIT
|
||||
* request, and when this is the case, HW perf counters will be activated just
|
||||
* before the job is submitted to the NPU and disabled when the job is
|
||||
* done. This way, only events related to a specific job will be counted.
|
||||
*/
|
||||
struct ethosu_perfmon {
|
||||
/* Tracks the number of users of the perfmon, when this counter reaches
|
||||
* zero the perfmon is destroyed.
|
||||
*/
|
||||
refcount_t refcnt;
|
||||
|
||||
/* Number of counters activated in this perfmon instance
|
||||
* (should be less than or equal to DRM_ETHOSU_MAX_PERF_COUNTERS).
|
||||
*/
|
||||
u8 ncounters;
|
||||
|
||||
/* Events counted by the HW perf counters. */
|
||||
u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS];
|
||||
|
||||
/*
|
||||
* Storage for counter values. Counters are incremented by the HW
|
||||
* perf counter values every time the perfmon is attached to an
|
||||
* NPU job. This way, perfmon users don't have to retrieve the
|
||||
* results after each job if they want to track events covering
|
||||
* several submissions. Note that counter values can't be reset,
|
||||
* but you can fake a reset by destroying the perfmon and
|
||||
* creating a new one.
|
||||
*/
|
||||
u64 values[] __counted_by(ncounters);
|
||||
};
|
||||
|
||||
/* ethosu_perfmon.c */
|
||||
void ethosu_perfmon_get(struct ethosu_perfmon *perfmon);
|
||||
void ethosu_perfmon_put(struct ethosu_perfmon *perfmon);
|
||||
void ethosu_perfmon_start(struct ethosu_device *ethosu,
|
||||
struct ethosu_perfmon *perfmon);
|
||||
void ethosu_perfmon_stop(struct ethosu_device *ethosu,
|
||||
struct ethosu_perfmon *perfmon, bool capture);
|
||||
void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon,
|
||||
bool capture);
|
||||
struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv,
|
||||
int id);
|
||||
void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv);
|
||||
void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv);
|
||||
int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
/* Copyright 2025-2026 Arm, Ltd. */
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/genalloc.h>
|
||||
@@ -147,6 +147,8 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
ethosu_perfmon_put(job->perfmon);
|
||||
|
||||
for (i = 0; i < job->region_cnt; i++)
|
||||
drm_gem_object_put(job->region_bo[i]);
|
||||
|
||||
@@ -181,6 +183,26 @@ static void ethosu_job_free(struct drm_sched_job *sched_job)
|
||||
ethosu_job_put(job);
|
||||
}
|
||||
|
||||
static void
|
||||
ethosu_switch_perfmon(struct ethosu_device *ethosu, struct ethosu_job *job)
|
||||
{
|
||||
struct ethosu_perfmon *perfmon;
|
||||
|
||||
guard(mutex)(ðosu->perfmon_state.lock);
|
||||
|
||||
perfmon = ethosu->global_perfmon;
|
||||
if (!perfmon)
|
||||
perfmon = job->perfmon;
|
||||
|
||||
if (perfmon == ethosu->perfmon_state.active)
|
||||
return;
|
||||
|
||||
ethosu_perfmon_stop_locked(ethosu, ethosu->perfmon_state.active, true);
|
||||
|
||||
if (perfmon)
|
||||
ethosu_perfmon_start(ethosu, perfmon);
|
||||
}
|
||||
|
||||
static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct ethosu_job *job = to_ethosu_job(sched_job);
|
||||
@@ -194,10 +216,10 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
|
||||
dev->fence_context, ++dev->emit_seqno);
|
||||
dma_fence_get(fence);
|
||||
|
||||
scoped_guard(mutex, &dev->job_lock) {
|
||||
dev->in_flight_job = job;
|
||||
ethosu_job_hw_submit(dev, job);
|
||||
}
|
||||
ethosu_switch_perfmon(dev, job);
|
||||
|
||||
WRITE_ONCE(dev->in_flight_job, job);
|
||||
ethosu_job_hw_submit(dev, job);
|
||||
|
||||
return fence;
|
||||
}
|
||||
@@ -205,6 +227,7 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
|
||||
static void ethosu_job_handle_irq(struct ethosu_device *dev)
|
||||
{
|
||||
u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
|
||||
struct ethosu_job *job;
|
||||
|
||||
if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) {
|
||||
dev_err(dev->base.dev, "Error IRQ - %x\n", status);
|
||||
@@ -212,11 +235,10 @@ static void ethosu_job_handle_irq(struct ethosu_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_guard(mutex, &dev->job_lock) {
|
||||
if (dev->in_flight_job) {
|
||||
dma_fence_signal(dev->in_flight_job->done_fence);
|
||||
dev->in_flight_job = NULL;
|
||||
}
|
||||
job = READ_ONCE(dev->in_flight_job);
|
||||
if (job) {
|
||||
WRITE_ONCE(dev->in_flight_job, NULL);
|
||||
dma_fence_signal(job->done_fence);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,8 +294,7 @@ static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad)
|
||||
|
||||
drm_sched_stop(&dev->sched, bad);
|
||||
|
||||
scoped_guard(mutex, &dev->job_lock)
|
||||
dev->in_flight_job = NULL;
|
||||
WRITE_ONCE(dev->in_flight_job, NULL);
|
||||
|
||||
/* Proceed with reset now. */
|
||||
pm_runtime_force_suspend(dev->base.dev);
|
||||
@@ -305,9 +326,6 @@ int ethosu_job_init(struct ethosu_device *edev)
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&edev->fence_lock);
|
||||
ret = devm_mutex_init(dev, &edev->job_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_mutex_init(dev, &edev->sched_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -366,7 +384,8 @@ void ethosu_job_close(struct ethosu_file_priv *ethosu_priv)
|
||||
}
|
||||
|
||||
static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
|
||||
struct drm_ethosu_job *job)
|
||||
struct drm_ethosu_job *job,
|
||||
int perfmon_id)
|
||||
{
|
||||
struct ethosu_device *edev = to_ethosu_device(dev);
|
||||
struct ethosu_file_priv *file_priv = file->driver_priv;
|
||||
@@ -390,6 +409,9 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
|
||||
ejob->dev = edev;
|
||||
ejob->sram_size = job->sram_size;
|
||||
|
||||
if (perfmon_id)
|
||||
ejob->perfmon = ethosu_perfmon_find(file_priv, perfmon_id);
|
||||
|
||||
ejob->done_fence = kzalloc_obj(*ejob->done_fence);
|
||||
if (!ejob->done_fence) {
|
||||
ret = -ENOMEM;
|
||||
@@ -492,11 +514,6 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil
|
||||
int ret = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (args->pad) {
|
||||
drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct drm_ethosu_job __free(kvfree) *jobs =
|
||||
kvmalloc_objs(*jobs, args->job_count);
|
||||
if (!jobs)
|
||||
@@ -510,7 +527,7 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil
|
||||
}
|
||||
|
||||
for (i = 0; i < args->job_count; i++) {
|
||||
ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]);
|
||||
ret = ethosu_ioctl_submit_job(dev, file, &jobs[i], args->perfmon_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ struct ethosu_job {
|
||||
u8 region_cnt;
|
||||
u32 sram_size;
|
||||
|
||||
struct ethosu_perfmon *perfmon;
|
||||
|
||||
/* Fence to be signaled by drm-sched once its done with the job */
|
||||
struct dma_fence *inference_done_fence;
|
||||
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2026 Arm, Ltd. */
|
||||
/* Based on v3d_perfmon.c, Copyright (C) 2021 Raspberry Pi */
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
|
||||
#include <uapi/drm/ethosu_accel.h>
|
||||
|
||||
#include "ethosu_drv.h"
|
||||
#include "ethosu_device.h"
|
||||
|
||||
void ethosu_perfmon_get(struct ethosu_perfmon *perfmon)
|
||||
{
|
||||
if (perfmon)
|
||||
refcount_inc(&perfmon->refcnt);
|
||||
}
|
||||
|
||||
void ethosu_perfmon_put(struct ethosu_perfmon *perfmon)
|
||||
{
|
||||
if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
|
||||
kfree(perfmon);
|
||||
}
|
||||
|
||||
void ethosu_perfmon_start(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 ncounters;
|
||||
u32 mask;
|
||||
|
||||
lockdep_assert_held(ðosu->perfmon_state.lock);
|
||||
|
||||
if (WARN_ON_ONCE(!perfmon || ethosu->perfmon_state.active))
|
||||
return;
|
||||
|
||||
writel_relaxed(PMCR_CNT_EN, ethosu->pmu_regs + NPU_REG_PMCR);
|
||||
writel_relaxed(PMU_EV_TYPE_CYCLES, ethosu->pmu_regs + NPU_REG_PMCCNTR_CFG);
|
||||
|
||||
mask = 0x80000000;
|
||||
ncounters = perfmon->ncounters - 1;
|
||||
if (ncounters)
|
||||
mask |= GENMASK(ncounters - 1, 0);
|
||||
|
||||
for (i = 0; i < ncounters; i++)
|
||||
writel_relaxed(perfmon->counters[i], ethosu->pmu_regs + NPU_REG_PMU_EVTYPER(i));
|
||||
|
||||
writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENSET);
|
||||
writel_relaxed(PMCR_CNT_EN | PMCR_EVENT_CNT_RST | PMCR_CYCLE_CNT_RST,
|
||||
ethosu->pmu_regs + NPU_REG_PMCR);
|
||||
ethosu->perfmon_state.active = perfmon;
|
||||
}
|
||||
|
||||
void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon,
|
||||
bool capture)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 ncounters;
|
||||
u32 mask;
|
||||
|
||||
lockdep_assert_held(ðosu->perfmon_state.lock);
|
||||
|
||||
if (!perfmon || perfmon != ethosu->perfmon_state.active)
|
||||
return;
|
||||
|
||||
ncounters = perfmon->ncounters - 1;
|
||||
|
||||
if (!pm_runtime_get_if_active(ethosu->base.dev)) {
|
||||
ethosu->perfmon_state.active = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (capture) {
|
||||
for (i = 0; i < ncounters; i++)
|
||||
perfmon->values[i] += readl_relaxed(ethosu->pmu_regs + NPU_REG_PMU_EVCNTR(i));
|
||||
|
||||
perfmon->values[ncounters] +=
|
||||
readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_LO) |
|
||||
(u64)readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_HI) << 32;
|
||||
}
|
||||
|
||||
mask = 0x80000000;
|
||||
if (ncounters)
|
||||
mask |= GENMASK(ncounters - 1, 0);
|
||||
writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENCLR);
|
||||
|
||||
writel_relaxed(0, ethosu->pmu_regs + NPU_REG_PMCR);
|
||||
ethosu->perfmon_state.active = NULL;
|
||||
|
||||
pm_runtime_put(ethosu->base.dev);
|
||||
}
|
||||
|
||||
void ethosu_perfmon_stop(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon,
|
||||
bool capture)
|
||||
{
|
||||
if (!perfmon)
|
||||
return;
|
||||
|
||||
guard(mutex)(ðosu->perfmon_state.lock);
|
||||
ethosu_perfmon_stop_locked(ethosu, perfmon, capture);
|
||||
}
|
||||
|
||||
struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, int id)
|
||||
{
|
||||
struct ethosu_perfmon *perfmon;
|
||||
|
||||
xa_lock(ðosu_priv->perfmons);
|
||||
perfmon = xa_load(ðosu_priv->perfmons, id);
|
||||
ethosu_perfmon_get(perfmon);
|
||||
xa_unlock(ðosu_priv->perfmons);
|
||||
|
||||
return perfmon;
|
||||
}
|
||||
|
||||
void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv)
|
||||
{
|
||||
xa_init_flags(ðosu_priv->perfmons, XA_FLAGS_ALLOC1);
|
||||
}
|
||||
|
||||
static void ethosu_perfmon_delete(struct ethosu_file_priv *ethosu_priv,
|
||||
struct ethosu_perfmon *perfmon)
|
||||
{
|
||||
struct ethosu_device *ethosu = ethosu_priv->edev;
|
||||
|
||||
/* If the active perfmon is being destroyed, stop it first */
|
||||
scoped_guard(mutex, ðosu->perfmon_state.lock) {
|
||||
/* If the global perfmon is being destroyed, set it to NULL */
|
||||
if (ethosu->global_perfmon == perfmon) {
|
||||
ethosu->global_perfmon = NULL;
|
||||
ethosu_perfmon_put(perfmon);
|
||||
}
|
||||
|
||||
ethosu_perfmon_stop_locked(ethosu, perfmon, false);
|
||||
}
|
||||
|
||||
ethosu_perfmon_put(perfmon);
|
||||
}
|
||||
|
||||
void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv)
|
||||
{
|
||||
struct ethosu_perfmon *perfmon;
|
||||
unsigned long id;
|
||||
|
||||
xa_for_each(ðosu_priv->perfmons, id, perfmon)
|
||||
ethosu_perfmon_delete(ethosu_priv, perfmon);
|
||||
|
||||
xa_destroy(ðosu_priv->perfmons);
|
||||
}
|
||||
|
||||
int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
|
||||
struct drm_ethosu_perfmon_create *req = data;
|
||||
struct ethosu_device *ethosu = to_ethosu_device(dev);
|
||||
struct ethosu_perfmon *perfmon;
|
||||
unsigned int i, event_max;
|
||||
int ret;
|
||||
u32 id;
|
||||
|
||||
/* Number of monitored counters cannot exceed HW limits. */
|
||||
if (req->ncounters > ethosu->npu_info.pmu_counters)
|
||||
return -EINVAL;
|
||||
|
||||
/* Make sure all counters are valid. */
|
||||
event_max = ethosu_is_u65(ethosu) ? 433 : 671;
|
||||
for (i = 0; i < req->ncounters; i++) {
|
||||
if (req->counters[i] > event_max)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Add 1 more counter for cycle counter */
|
||||
req->ncounters++;
|
||||
|
||||
perfmon = kzalloc_flex(*perfmon, values, req->ncounters);
|
||||
if (!perfmon)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < req->ncounters - 1; i++)
|
||||
perfmon->counters[i] = req->counters[i];
|
||||
|
||||
perfmon->ncounters = req->ncounters;
|
||||
|
||||
refcount_set(&perfmon->refcnt, 1);
|
||||
|
||||
ret = xa_alloc(ðosu_priv->perfmons, &id, perfmon, xa_limit_32b,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (ret < 0) {
|
||||
kfree(perfmon);
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->id = id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
|
||||
struct drm_ethosu_perfmon_destroy *req = data;
|
||||
struct ethosu_perfmon *perfmon;
|
||||
|
||||
perfmon = xa_erase(ðosu_priv->perfmons, req->id);
|
||||
if (!perfmon)
|
||||
return -EINVAL;
|
||||
|
||||
ethosu_perfmon_delete(ethosu_priv, perfmon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct ethosu_device *ethosu = to_ethosu_device(dev);
|
||||
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
|
||||
struct drm_ethosu_perfmon_get_values *req = data;
|
||||
struct ethosu_perfmon *perfmon;
|
||||
int ret = 0;
|
||||
|
||||
if (req->pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
perfmon = ethosu_perfmon_find(ethosu_priv, req->id);
|
||||
if (!perfmon)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev->dev);
|
||||
if (ret) {
|
||||
ethosu_perfmon_put(perfmon);
|
||||
return ret;
|
||||
}
|
||||
ethosu_perfmon_stop(ethosu, perfmon, true);
|
||||
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
|
||||
perfmon->ncounters * sizeof(u64)))
|
||||
ret = -EFAULT;
|
||||
|
||||
ethosu_perfmon_put(perfmon);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
|
||||
struct drm_ethosu_perfmon_set_global *req = data;
|
||||
struct ethosu_device *ethosu = to_ethosu_device(dev);
|
||||
struct ethosu_perfmon *perfmon;
|
||||
|
||||
if (req->flags & ~DRM_ETHOSU_PERFMON_CLEAR_GLOBAL)
|
||||
return -EINVAL;
|
||||
|
||||
perfmon = ethosu_perfmon_find(ethosu_priv, req->id);
|
||||
if (!perfmon)
|
||||
return -EINVAL;
|
||||
|
||||
/* If the request is to clear the global performance monitor */
|
||||
if (req->flags & DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) {
|
||||
struct ethosu_perfmon *old;
|
||||
|
||||
scoped_guard(mutex, ðosu->perfmon_state.lock) {
|
||||
old = ethosu->global_perfmon;
|
||||
if (!old) {
|
||||
ethosu_perfmon_put(perfmon);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ethosu->global_perfmon = NULL;
|
||||
ethosu_perfmon_stop_locked(ethosu, old, true);
|
||||
}
|
||||
|
||||
ethosu_perfmon_put(old);
|
||||
ethosu_perfmon_put(perfmon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
scoped_guard(mutex, ðosu->perfmon_state.lock) {
|
||||
if (ethosu->perfmon_state.active || ethosu->global_perfmon) {
|
||||
ethosu_perfmon_put(perfmon);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ethosu->global_perfmon = perfmon;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -307,6 +307,11 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file)
|
||||
return -ENODEV;
|
||||
|
||||
limits = ivpu_user_limits_get(vdev);
|
||||
if (IS_ERR(limits) && PTR_ERR(limits) == -EMFILE) {
|
||||
/* Context limit may be held by jobs pending deferred cleanup */
|
||||
flush_work(&vdev->job_destroy_work);
|
||||
limits = ivpu_user_limits_get(vdev);
|
||||
}
|
||||
if (IS_ERR(limits)) {
|
||||
ret = PTR_ERR(limits);
|
||||
goto err_dev_exit;
|
||||
@@ -510,9 +515,9 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
|
||||
{
|
||||
ivpu_hw_irq_disable(vdev);
|
||||
disable_irq(vdev->irq);
|
||||
flush_work(&vdev->irq_ipc_work);
|
||||
flush_work(&vdev->irq_dct_work);
|
||||
flush_work(&vdev->context_abort_work);
|
||||
flush_work(&vdev->job_destroy_work);
|
||||
ivpu_ipc_disable(vdev);
|
||||
ivpu_mmu_disable(vdev);
|
||||
}
|
||||
@@ -584,6 +589,11 @@ static const struct drm_driver driver = {
|
||||
.major = 1,
|
||||
};
|
||||
|
||||
static void ivpu_destroy_workqueue(void *wq)
|
||||
{
|
||||
destroy_workqueue(wq);
|
||||
}
|
||||
|
||||
static int ivpu_irq_init(struct ivpu_device *vdev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
|
||||
@@ -595,16 +605,26 @@ static int ivpu_irq_init(struct ivpu_device *vdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn);
|
||||
INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn);
|
||||
INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn);
|
||||
init_llist_head(&vdev->job_destroy_list);
|
||||
INIT_WORK(&vdev->job_destroy_work, ivpu_job_destroy_work_fn);
|
||||
|
||||
vdev->job_destroy_wq = alloc_workqueue("ivpu_job_destroy", WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
|
||||
if (!vdev->job_destroy_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_add_action_or_reset(vdev->drm.dev, ivpu_destroy_workqueue, vdev->job_destroy_wq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ivpu_irq_handlers_init(vdev);
|
||||
|
||||
vdev->irq = pci_irq_vector(pdev, 0);
|
||||
|
||||
ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
|
||||
IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
|
||||
ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
|
||||
ivpu_ipc_irq_thread_handler, IRQF_NO_AUTOEN,
|
||||
DRIVER_NAME, vdev);
|
||||
if (ret)
|
||||
ivpu_err(vdev, "Failed to request an IRQ %d\n", ret);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/llist.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/xarray.h>
|
||||
#include <uapi/drm/ivpu_accel.h>
|
||||
@@ -157,9 +158,11 @@ struct ivpu_device {
|
||||
struct xa_limit db_limit;
|
||||
u32 db_next;
|
||||
|
||||
struct work_struct irq_ipc_work;
|
||||
struct work_struct irq_dct_work;
|
||||
struct work_struct context_abort_work;
|
||||
struct llist_head job_destroy_list;
|
||||
struct work_struct job_destroy_work;
|
||||
struct workqueue_struct *job_destroy_wq;
|
||||
|
||||
struct mutex bo_list_lock; /* Protects bo_list */
|
||||
struct list_head bo_list;
|
||||
|
||||
@@ -318,7 +318,7 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
|
||||
fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size);
|
||||
fw->cold_boot_entry_point = fw_hdr->entry_point;
|
||||
|
||||
fw->trace_level = min_t(u32, ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
|
||||
fw->trace_level = min(ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
|
||||
fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING;
|
||||
fw->trace_hw_component_mask = -1;
|
||||
|
||||
|
||||
@@ -399,6 +399,10 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
|
||||
return IRQ_NONE;
|
||||
|
||||
pm_runtime_mark_last_busy(vdev->drm.dev);
|
||||
|
||||
if (ip_handled)
|
||||
return IRQ_WAKE_THREAD;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
|
||||
|
||||
lockdep_assert_held(&ipc->cons_lock);
|
||||
|
||||
rx_msg = kzalloc_obj(*rx_msg, GFP_ATOMIC);
|
||||
rx_msg = kmem_cache_zalloc(ipc->rx_msg_cache, GFP_ATOMIC);
|
||||
if (!rx_msg) {
|
||||
ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
|
||||
return;
|
||||
@@ -174,7 +174,7 @@ ivpu_ipc_rx_msg_del(struct ivpu_device *vdev, struct ivpu_ipc_rx_msg *rx_msg)
|
||||
list_del(&rx_msg->link);
|
||||
ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg);
|
||||
atomic_dec(&vdev->ipc->rx_msg_count);
|
||||
kfree(rx_msg);
|
||||
kmem_cache_free(vdev->ipc->rx_msg_cache, rx_msg);
|
||||
}
|
||||
|
||||
void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
|
||||
@@ -462,13 +462,11 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev)
|
||||
ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
|
||||
}
|
||||
}
|
||||
|
||||
queue_work(system_percpu_wq, &vdev->irq_ipc_work);
|
||||
}
|
||||
|
||||
void ivpu_ipc_irq_work_fn(struct work_struct *work)
|
||||
irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr)
|
||||
{
|
||||
struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work);
|
||||
struct ivpu_device *vdev = ptr;
|
||||
struct ivpu_ipc_info *ipc = vdev->ipc;
|
||||
struct ivpu_ipc_rx_msg *rx_msg, *r;
|
||||
struct list_head cb_msg_list;
|
||||
@@ -483,6 +481,8 @@ void ivpu_ipc_irq_work_fn(struct work_struct *work)
|
||||
rx_msg->callback(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg);
|
||||
ivpu_ipc_rx_msg_del(vdev, rx_msg);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int ivpu_ipc_init(struct ivpu_device *vdev)
|
||||
@@ -490,10 +490,18 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
|
||||
struct ivpu_ipc_info *ipc = vdev->ipc;
|
||||
int ret;
|
||||
|
||||
ipc->rx_msg_cache = kmem_cache_create("ivpu_ipc_rx_msg", sizeof(struct ivpu_ipc_rx_msg), 0,
|
||||
SLAB_HWCACHE_ALIGN, NULL);
|
||||
if (!ipc->rx_msg_cache) {
|
||||
ivpu_err(vdev, "Failed to create rx_msg_cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ipc->mem_tx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
|
||||
if (!ipc->mem_tx) {
|
||||
ivpu_err(vdev, "Failed to allocate mem_tx\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_destroy_cache;
|
||||
}
|
||||
|
||||
ipc->mem_rx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
|
||||
@@ -532,6 +540,8 @@ err_free_rx:
|
||||
ivpu_bo_free(ipc->mem_rx);
|
||||
err_free_tx:
|
||||
ivpu_bo_free(ipc->mem_tx);
|
||||
err_destroy_cache:
|
||||
kmem_cache_destroy(ipc->rx_msg_cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -544,6 +554,7 @@ void ivpu_ipc_fini(struct ivpu_device *vdev)
|
||||
drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0);
|
||||
|
||||
ivpu_ipc_mem_fini(vdev);
|
||||
kmem_cache_destroy(ipc->rx_msg_cache);
|
||||
}
|
||||
|
||||
void ivpu_ipc_enable(struct ivpu_device *vdev)
|
||||
|
||||
@@ -70,6 +70,7 @@ struct ivpu_ipc_info {
|
||||
struct gen_pool *mm_tx;
|
||||
struct ivpu_bo *mem_tx;
|
||||
struct ivpu_bo *mem_rx;
|
||||
struct kmem_cache *rx_msg_cache;
|
||||
|
||||
atomic_t rx_msg_count;
|
||||
|
||||
@@ -90,7 +91,7 @@ void ivpu_ipc_disable(struct ivpu_device *vdev);
|
||||
void ivpu_ipc_reset(struct ivpu_device *vdev);
|
||||
|
||||
void ivpu_ipc_irq_handler(struct ivpu_device *vdev);
|
||||
void ivpu_ipc_irq_work_fn(struct work_struct *work);
|
||||
irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr);
|
||||
|
||||
void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
|
||||
u32 channel, ivpu_ipc_rx_callback_t callback);
|
||||
|
||||
@@ -535,6 +535,20 @@ static void ivpu_job_destroy(struct ivpu_job *job)
|
||||
kfree(job);
|
||||
}
|
||||
|
||||
void ivpu_job_destroy_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct ivpu_device *vdev = container_of(work, struct ivpu_device, job_destroy_work);
|
||||
struct ivpu_job *job, *tmp;
|
||||
struct llist_node *list;
|
||||
|
||||
list = llist_del_all(&vdev->job_destroy_list);
|
||||
|
||||
llist_for_each_entry_safe(job, tmp, list, destroy_node) {
|
||||
ivpu_job_destroy(job);
|
||||
ivpu_rpm_put(vdev);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ivpu_job *
|
||||
ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
|
||||
{
|
||||
@@ -619,7 +633,7 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
|
||||
static struct ivpu_job *ivpu_job_signal(struct ivpu_device *vdev, u32 job_id, u32 job_status)
|
||||
{
|
||||
struct ivpu_job *job;
|
||||
|
||||
@@ -627,7 +641,7 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
|
||||
|
||||
job = xa_load(&vdev->submitted_jobs_xa, job_id);
|
||||
if (!job)
|
||||
return -ENOENT;
|
||||
return NULL;
|
||||
|
||||
ivpu_job_remove_from_submitted_jobs(vdev, job_id);
|
||||
|
||||
@@ -646,14 +660,37 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
|
||||
job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx,
|
||||
job->job_status);
|
||||
|
||||
ivpu_job_destroy(job);
|
||||
ivpu_stop_job_timeout_detection(vdev);
|
||||
|
||||
ivpu_rpm_put(vdev);
|
||||
|
||||
if (!xa_empty(&vdev->submitted_jobs_xa))
|
||||
ivpu_start_job_timeout_detection(vdev);
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
|
||||
{
|
||||
struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status);
|
||||
|
||||
if (!job)
|
||||
return -ENOENT;
|
||||
|
||||
ivpu_job_destroy(job);
|
||||
ivpu_rpm_put(vdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ivpu_job_signal_and_defer_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
|
||||
{
|
||||
struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status);
|
||||
|
||||
if (!job)
|
||||
return -ENOENT;
|
||||
|
||||
llist_add(&job->destroy_node, &vdev->job_destroy_list);
|
||||
queue_work(vdev->job_destroy_wq, &vdev->job_destroy_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -689,6 +726,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
|
||||
struct ivpu_file_priv *file_priv = job->file_priv;
|
||||
struct ivpu_device *vdev = job->vdev;
|
||||
struct ivpu_cmdq *cmdq;
|
||||
bool flushed = false;
|
||||
bool is_first_job;
|
||||
int ret;
|
||||
|
||||
@@ -696,6 +734,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
retry:
|
||||
mutex_lock(&vdev->submitted_jobs_lock);
|
||||
mutex_lock(&file_priv->lock);
|
||||
|
||||
@@ -709,6 +748,14 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
|
||||
}
|
||||
|
||||
ret = ivpu_cmdq_register(file_priv, cmdq);
|
||||
if (ret == -EBUSY && !flushed) {
|
||||
/* Doorbell may be held by jobs pending deferred cleanup */
|
||||
mutex_unlock(&file_priv->lock);
|
||||
mutex_unlock(&vdev->submitted_jobs_lock);
|
||||
flush_work(&vdev->job_destroy_work);
|
||||
flushed = true;
|
||||
goto retry;
|
||||
}
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Failed to register command queue: %d\n", ret);
|
||||
goto err_unlock;
|
||||
@@ -1101,7 +1148,7 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
|
||||
mutex_lock(&vdev->submitted_jobs_lock);
|
||||
if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status))
|
||||
/* No engine error, complete the job normally */
|
||||
ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
|
||||
ivpu_job_signal_and_defer_destroy(vdev, payload->job_id, payload->job_status);
|
||||
mutex_unlock(&vdev->submitted_jobs_lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
#ifndef __IVPU_JOB_H__
|
||||
#define __IVPU_JOB_H__
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/llist.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "ivpu_gem.h"
|
||||
|
||||
@@ -47,6 +49,7 @@ struct ivpu_cmdq {
|
||||
* @vdev: Pointer to the VPU device
|
||||
* @file_priv: The client context that submitted this job
|
||||
* @done_fence: Fence signaled when job completes
|
||||
* @destroy_node: List node for deferred resource cleanup after job completion
|
||||
* @cmd_buf_vpu_addr: VPU address of the command buffer for this job
|
||||
* @cmdq_id: Command queue ID used for submission
|
||||
* @job_id: Unique job ID for tracking and status reporting
|
||||
@@ -61,6 +64,7 @@ struct ivpu_job {
|
||||
struct ivpu_device *vdev;
|
||||
struct ivpu_file_priv *file_priv;
|
||||
struct dma_fence *done_fence;
|
||||
struct llist_node destroy_node;
|
||||
u64 cmd_buf_vpu_addr;
|
||||
u32 cmdq_id;
|
||||
u32 job_id;
|
||||
@@ -87,6 +91,7 @@ void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
|
||||
void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
|
||||
bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status);
|
||||
void ivpu_context_abort_work_fn(struct work_struct *work);
|
||||
void ivpu_job_destroy_work_fn(struct work_struct *work);
|
||||
|
||||
void ivpu_jobs_abort_all(struct ivpu_device *vdev);
|
||||
|
||||
|
||||
@@ -588,8 +588,8 @@ void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ct
|
||||
start = vdev->hw->ranges.runtime.start;
|
||||
end = vdev->hw->ranges.shave.end;
|
||||
} else {
|
||||
start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
|
||||
end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
|
||||
start = min(vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
|
||||
end = max(vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
|
||||
}
|
||||
|
||||
drm_mm_init(&ctx->mm, start, end - start);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/rocket_accel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@@ -102,6 +103,7 @@ rocket_copy_tasks(struct drm_device *dev,
|
||||
|
||||
fail:
|
||||
kvfree(rjob->tasks);
|
||||
rjob->tasks = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -188,14 +190,19 @@ static int rocket_job_push(struct rocket_job *job)
|
||||
struct rocket_device *rdev = job->rdev;
|
||||
struct drm_gem_object **bos;
|
||||
struct ww_acquire_ctx acquire_ctx;
|
||||
u32 bo_count;
|
||||
int ret = 0;
|
||||
|
||||
bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count))
|
||||
return -EINVAL;
|
||||
|
||||
bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL);
|
||||
if (!bos)
|
||||
return -ENOMEM;
|
||||
memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
|
||||
memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
|
||||
|
||||
ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
|
||||
ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@@ -220,7 +227,7 @@ static int rocket_job_push(struct rocket_job *job)
|
||||
rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
|
||||
|
||||
err_unlock:
|
||||
drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
|
||||
drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx);
|
||||
err:
|
||||
kvfree(bos);
|
||||
|
||||
@@ -310,13 +317,13 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
|
||||
dma_fence_put(job->done_fence);
|
||||
job->done_fence = dma_fence_get(fence);
|
||||
|
||||
ret = pm_runtime_get_sync(core->dev);
|
||||
ret = pm_runtime_resume_and_get(core->dev);
|
||||
if (ret < 0)
|
||||
return fence;
|
||||
goto err_put_fences;
|
||||
|
||||
ret = iommu_attach_group(job->domain->domain, core->iommu_group);
|
||||
if (ret < 0)
|
||||
return fence;
|
||||
goto err_put_pm;
|
||||
|
||||
scoped_guard(mutex, &core->job_lock) {
|
||||
core->in_flight_job = job;
|
||||
@@ -324,6 +331,14 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
|
||||
}
|
||||
|
||||
return fence;
|
||||
|
||||
err_put_pm:
|
||||
pm_runtime_put(core->dev);
|
||||
err_put_fences:
|
||||
dma_fence_put(job->done_fence);
|
||||
job->done_fence = NULL;
|
||||
dma_fence_put(fence);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void rocket_job_handle_irq(struct rocket_core *core)
|
||||
@@ -549,6 +564,7 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
|
||||
kref_init(&rjob->refcount);
|
||||
|
||||
rjob->rdev = rdev;
|
||||
rjob->domain = rocket_iommu_domain_get(file_priv);
|
||||
|
||||
ret = drm_sched_job_init(&rjob->base,
|
||||
&file_priv->sched_entity,
|
||||
@@ -574,8 +590,6 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
|
||||
|
||||
rjob->out_bo_count = job->out_bo_handle_count;
|
||||
|
||||
rjob->domain = rocket_iommu_domain_get(file_priv);
|
||||
|
||||
ret = rocket_job_push(rjob);
|
||||
if (ret)
|
||||
goto out_cleanup_job;
|
||||
|
||||
@@ -93,9 +93,9 @@ static int fence_cmp(const void *_a, const void *_b)
|
||||
*
|
||||
* Return: Number of unique fences remaining in the array.
|
||||
*/
|
||||
int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
|
||||
size_t dma_fence_dedup_array(struct dma_fence **fences, size_t num_fences)
|
||||
{
|
||||
int i, j;
|
||||
size_t i, j;
|
||||
|
||||
if (!num_fences)
|
||||
return 0;
|
||||
@@ -118,14 +118,14 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
|
||||
EXPORT_SYMBOL_GPL(dma_fence_dedup_array);
|
||||
|
||||
/* Implementation for the dma_fence_merge() marco, don't use directly */
|
||||
struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
|
||||
struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences,
|
||||
struct dma_fence **fences,
|
||||
struct dma_fence_unwrap *iter)
|
||||
{
|
||||
struct dma_fence *tmp, *unsignaled = NULL, **array;
|
||||
struct dma_fence_array *result;
|
||||
ktime_t timestamp;
|
||||
int i, count;
|
||||
size_t i, count;
|
||||
|
||||
count = 0;
|
||||
timestamp = ns_to_ktime(0);
|
||||
|
||||
+12
-10
@@ -534,7 +534,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
|
||||
|
||||
__dma_fence_might_wait();
|
||||
|
||||
dma_fence_enable_sw_signaling(fence);
|
||||
dma_fence_enable_signaling(fence);
|
||||
|
||||
rcu_read_lock();
|
||||
ops = rcu_dereference(fence->ops);
|
||||
@@ -656,14 +656,14 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence)
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_fence_enable_sw_signaling - enable signaling on fence
|
||||
* dma_fence_enable_signaling - enable signaling on fence
|
||||
* @fence: the fence to enable
|
||||
*
|
||||
* This will request for sw signaling to be enabled, to make the fence
|
||||
* complete as soon as possible. This calls &dma_fence_ops.enable_signaling
|
||||
* internally.
|
||||
*/
|
||||
void dma_fence_enable_sw_signaling(struct dma_fence *fence)
|
||||
void dma_fence_enable_signaling(struct dma_fence *fence)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@@ -671,7 +671,7 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence)
|
||||
__dma_fence_enable_signaling(fence);
|
||||
dma_fence_unlock_irqrestore(fence, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
|
||||
EXPORT_SYMBOL(dma_fence_enable_signaling);
|
||||
|
||||
/**
|
||||
* dma_fence_add_callback - add a callback to be called when the fence
|
||||
@@ -1102,9 +1102,12 @@ __dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
|
||||
* context and seqno are used for easy comparison between fences, allowing
|
||||
* to check which fence is later by simply using dma_fence_later().
|
||||
*
|
||||
* It is strongly discouraged to provide an external lock because this couples
|
||||
* lock and fence life time. This is only allowed for legacy use cases when
|
||||
* multiple fences need to be prevented from signaling out of order.
|
||||
* External locks are a relic of legacy use cases that needed a shared lock
|
||||
* to serialize signaling when no out-of-order signaling was possible through
|
||||
* &dma_fence_ops.signaled. Drivers have abandoned this concept since the
|
||||
* introduction of the callback, but the external lock is still around. New
|
||||
* users MUST NOT use external locks, as they force the issuer to outlive all
|
||||
* fences that reference the lock.
|
||||
*/
|
||||
void
|
||||
dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
|
||||
@@ -1129,9 +1132,8 @@ EXPORT_SYMBOL(dma_fence_init);
|
||||
* Context and seqno are used for easy comparison between fences, allowing
|
||||
* to check which fence is later by simply using dma_fence_later().
|
||||
*
|
||||
* It is strongly discouraged to provide an external lock because this couples
|
||||
* lock and fence life time. This is only allowed for legacy use cases when
|
||||
* multiple fences need to be prevented from signaling out of order.
|
||||
* New users MUST NOT use external locks. Check the documentation in
|
||||
* dma_fence_init() to understand the motives behind the legacy use cases.
|
||||
*/
|
||||
void
|
||||
dma_fence_init64(struct dma_fence *fence, const struct dma_fence_ops *ops,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
@@ -154,7 +155,7 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
|
||||
in_size = 0;
|
||||
if ((ucmd & kcmd & IOC_OUT) == 0)
|
||||
out_size = 0;
|
||||
ksize = max(max(in_size, out_size), drv_size);
|
||||
ksize = max3(in_size, out_size, drv_size);
|
||||
|
||||
/* If necessary, allocate buffer for ioctl argument */
|
||||
if (ksize > sizeof(stack_kdata)) {
|
||||
|
||||
@@ -82,7 +82,7 @@ static void test_sanitycheck(struct kunit *test)
|
||||
|
||||
chain = mock_chain(NULL, f, 1);
|
||||
if (chain)
|
||||
dma_fence_enable_sw_signaling(chain);
|
||||
dma_fence_enable_signaling(chain);
|
||||
else
|
||||
KUNIT_FAIL(test, "Failed to create chain");
|
||||
|
||||
@@ -139,7 +139,7 @@ static int fence_chains_init(struct fence_chains *fc, unsigned int count,
|
||||
|
||||
fc->tail = fc->chains[i];
|
||||
|
||||
dma_fence_enable_sw_signaling(fc->chains[i]);
|
||||
dma_fence_enable_signaling(fc->chains[i]);
|
||||
}
|
||||
|
||||
fc->chain_length = i;
|
||||
|
||||
@@ -103,7 +103,7 @@ static void test_sanitycheck(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
array = mock_array(1, f);
|
||||
KUNIT_ASSERT_NOT_NULL(test, array);
|
||||
@@ -122,7 +122,7 @@ static void test_unwrap_array(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = mock_fence();
|
||||
if (!f2) {
|
||||
@@ -131,7 +131,7 @@ static void test_unwrap_array(struct kunit *test)
|
||||
return;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
array = mock_array(2, f1, f2);
|
||||
KUNIT_ASSERT_NOT_NULL(test, array);
|
||||
@@ -160,7 +160,7 @@ static void test_unwrap_chain(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = mock_fence();
|
||||
if (!f2) {
|
||||
@@ -169,7 +169,7 @@ static void test_unwrap_chain(struct kunit *test)
|
||||
return;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
chain = mock_chain(f1, f2);
|
||||
KUNIT_ASSERT_NOT_NULL(test, chain);
|
||||
@@ -198,7 +198,7 @@ static void test_unwrap_chain_array(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = mock_fence();
|
||||
if (!f2) {
|
||||
@@ -207,7 +207,7 @@ static void test_unwrap_chain_array(struct kunit *test)
|
||||
return;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
array = mock_array(2, f1, f2);
|
||||
KUNIT_ASSERT_NOT_NULL(test, array);
|
||||
@@ -239,7 +239,7 @@ static void test_unwrap_merge(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = mock_fence();
|
||||
if (!f2) {
|
||||
@@ -247,7 +247,7 @@ static void test_unwrap_merge(struct kunit *test)
|
||||
goto error_put_f1;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
f3 = dma_fence_unwrap_merge(f1, f2);
|
||||
if (!f3) {
|
||||
@@ -285,7 +285,7 @@ static void test_unwrap_merge_duplicate(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = dma_fence_unwrap_merge(f1, f1);
|
||||
if (!f2) {
|
||||
@@ -322,7 +322,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
|
||||
f1 = __mock_fence(ctx[1], 1);
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = __mock_fence(ctx[1], 2);
|
||||
if (!f2) {
|
||||
@@ -330,7 +330,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
|
||||
goto error_put_f1;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
f3 = __mock_fence(ctx[0], 1);
|
||||
if (!f3) {
|
||||
@@ -338,7 +338,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
|
||||
goto error_put_f2;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f3);
|
||||
dma_fence_enable_signaling(f3);
|
||||
|
||||
f4 = dma_fence_unwrap_merge(f1, f2, f3);
|
||||
if (!f4) {
|
||||
@@ -378,7 +378,7 @@ static void test_unwrap_merge_order(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = mock_fence();
|
||||
if (!f2) {
|
||||
@@ -387,7 +387,7 @@ static void test_unwrap_merge_order(struct kunit *test)
|
||||
return;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
a1 = mock_array(2, f1, f2);
|
||||
KUNIT_ASSERT_NOT_NULL(test, a1);
|
||||
@@ -442,7 +442,7 @@ static void test_unwrap_merge_complex(struct kunit *test)
|
||||
f1 = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = mock_fence();
|
||||
if (!f2) {
|
||||
@@ -450,7 +450,7 @@ static void test_unwrap_merge_complex(struct kunit *test)
|
||||
goto error_put_f1;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
f3 = dma_fence_unwrap_merge(f1, f2);
|
||||
if (!f3) {
|
||||
@@ -510,7 +510,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
|
||||
f1 = __mock_fence(ctx[0], 2);
|
||||
KUNIT_ASSERT_NOT_NULL(test, f1);
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
f2 = __mock_fence(ctx[1], 1);
|
||||
if (!f2) {
|
||||
@@ -518,7 +518,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
|
||||
goto error_put_f1;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f2);
|
||||
dma_fence_enable_signaling(f2);
|
||||
|
||||
f3 = __mock_fence(ctx[0], 1);
|
||||
if (!f3) {
|
||||
@@ -526,7 +526,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
|
||||
goto error_put_f2;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f3);
|
||||
dma_fence_enable_signaling(f3);
|
||||
|
||||
f4 = __mock_fence(ctx[1], 2);
|
||||
if (!f4) {
|
||||
@@ -534,7 +534,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
|
||||
goto error_put_f3;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f4);
|
||||
dma_fence_enable_signaling(f4);
|
||||
|
||||
f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2));
|
||||
if (!f5) {
|
||||
|
||||
@@ -42,7 +42,7 @@ static void test_sanitycheck(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_fence_signal(f);
|
||||
dma_fence_put(f);
|
||||
@@ -55,7 +55,7 @@ static void test_signaling(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
if (dma_fence_is_signaled(f)) {
|
||||
KUNIT_FAIL(test, "Fence unexpectedly signaled on creation");
|
||||
@@ -127,7 +127,7 @@ static void test_late_add_callback(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_fence_signal(f);
|
||||
|
||||
@@ -209,7 +209,7 @@ static void test_status(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
if (dma_fence_get_status(f)) {
|
||||
KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation");
|
||||
@@ -233,7 +233,7 @@ static void test_error(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_fence_set_error(f, -EIO);
|
||||
|
||||
@@ -260,7 +260,7 @@ static void test_wait(struct kunit *test)
|
||||
f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
if (dma_fence_wait_timeout(f, false, 0) != 0) {
|
||||
KUNIT_FAIL(test, "Wait reported complete before being signaled");
|
||||
@@ -300,7 +300,7 @@ static void test_wait_timeout(struct kunit *test)
|
||||
wt.f = mock_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, wt.f);
|
||||
|
||||
dma_fence_enable_sw_signaling(wt.f);
|
||||
dma_fence_enable_signaling(wt.f);
|
||||
|
||||
if (dma_fence_wait_timeout(wt.f, false, 1) != 0) {
|
||||
KUNIT_FAIL(test, "Wait reported complete before being signaled");
|
||||
@@ -379,7 +379,7 @@ static int thread_signal_callback(void *arg)
|
||||
break;
|
||||
}
|
||||
|
||||
dma_fence_enable_sw_signaling(f1);
|
||||
dma_fence_enable_signaling(f1);
|
||||
|
||||
rcu_assign_pointer(t->fences[t->id], f1);
|
||||
smp_wmb();
|
||||
|
||||
@@ -48,7 +48,7 @@ static void test_sanitycheck(struct kunit *test)
|
||||
f = alloc_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_fence_signal(f);
|
||||
dma_fence_put(f);
|
||||
@@ -73,7 +73,7 @@ static void test_signaling(struct kunit *test)
|
||||
f = alloc_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_resv_init(&resv);
|
||||
r = dma_resv_lock(&resv, NULL);
|
||||
@@ -117,7 +117,7 @@ static void test_for_each(struct kunit *test)
|
||||
f = alloc_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_resv_init(&resv);
|
||||
r = dma_resv_lock(&resv, NULL);
|
||||
@@ -176,7 +176,7 @@ static void test_for_each_unlocked(struct kunit *test)
|
||||
f = alloc_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_resv_init(&resv);
|
||||
r = dma_resv_lock(&resv, NULL);
|
||||
@@ -246,7 +246,7 @@ static void test_get_fences(struct kunit *test)
|
||||
f = alloc_fence();
|
||||
KUNIT_ASSERT_NOT_NULL(test, f);
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
dma_fence_enable_signaling(f);
|
||||
|
||||
dma_resv_init(&resv);
|
||||
r = dma_resv_lock(&resv, NULL);
|
||||
|
||||
@@ -20,9 +20,9 @@ static int list_limit = 1024;
|
||||
module_param(list_limit, int, 0644);
|
||||
MODULE_PARM_DESC(list_limit, "udmabuf_create_list->count limit. Default is 1024.");
|
||||
|
||||
static int size_limit_mb = 64;
|
||||
static int size_limit_mb = INT_MAX;
|
||||
module_param(size_limit_mb, int, 0644);
|
||||
MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is 64.");
|
||||
MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is INT_MAX.");
|
||||
|
||||
struct udmabuf {
|
||||
pgoff_t pagecount;
|
||||
|
||||
@@ -208,32 +208,20 @@ config SYSFB
|
||||
select SCREEN_INFO
|
||||
|
||||
config SYSFB_SIMPLEFB
|
||||
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
|
||||
bool "Mark VGA/VBE/EFI FB as generic system framebuffer (deprecated)"
|
||||
depends on X86 || EFI
|
||||
select SYSFB
|
||||
help
|
||||
Firmwares often provide initial graphics framebuffers so the BIOS,
|
||||
Firmware often provides initial graphics framebuffers so the BIOS,
|
||||
bootloader or kernel can show basic video-output during boot for
|
||||
user-guidance and debugging. Historically, x86 used the VESA BIOS
|
||||
Extensions and EFI-framebuffers for this, which are mostly limited
|
||||
to x86 BIOS or EFI systems.
|
||||
This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
|
||||
framebuffers so the new generic system-framebuffer drivers can be
|
||||
used instead. If the framebuffer is not compatible with the generic
|
||||
modes, it is advertised as fallback platform framebuffer so legacy
|
||||
drivers like efifb, vesafb and uvesafb can pick it up.
|
||||
If this option is not selected, all system framebuffers are always
|
||||
marked as fallback platform framebuffers as usual.
|
||||
user-guidance and debugging.
|
||||
|
||||
Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will
|
||||
not be able to pick up generic system framebuffers if this option
|
||||
is selected. You are highly encouraged to enable simplefb as
|
||||
replacement if you select this option. simplefb can correctly deal
|
||||
with generic system framebuffers. But you should still keep vesafb
|
||||
and others enabled as fallback if a system framebuffer is
|
||||
incompatible with simplefb.
|
||||
This option, if enabled, marks VBE/EFI framebuffers as system
|
||||
framebuffers so the generic simpledrm driver can be used.
|
||||
|
||||
If unsure, say Y.
|
||||
This option is deprecated and will be removed in the near future. If
|
||||
unsure, say N and select efidrm, vesadrm instead. The dedicated DRM
|
||||
drivers provide the same functionality plus additional features.
|
||||
|
||||
config TH1520_AON_PROTOCOL
|
||||
tristate "Always-On firmware protocol"
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@ config GPU_BUDDY
|
||||
|
||||
config GPU_BUDDY_KUNIT_TEST
|
||||
tristate "KUnit tests for GPU buddy allocator" if !KUNIT_ALL_TESTS
|
||||
depends on GPU_BUDDY && KUNIT
|
||||
depends on KUNIT
|
||||
select GPU_BUDDY
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
KUnit tests for the GPU buddy allocator.
|
||||
|
||||
+57
-1
@@ -7,4 +7,60 @@ obj-$(CONFIG_GPU_BUDDY) += buddy.o
|
||||
obj-y += host1x/ drm/ vga/ tests/
|
||||
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
|
||||
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
|
||||
obj-$(CONFIG_NOVA_CORE) += nova-core/
|
||||
|
||||
# nova-core and nova-drm are built from this Makefile so nova-drm's dependency
|
||||
# on nova-core can be expressed as a plain Make prerequisite rather than a
|
||||
# recursive sub-make. This is a temporary workaround until the Rust build
|
||||
# system supports cross-crate dependencies natively.
|
||||
|
||||
obj-$(CONFIG_NOVA_CORE) += nova-core.o
|
||||
nova-core-y := nova-core/nova_core.o nova-core/nova_core_exports.o
|
||||
|
||||
obj-$(CONFIG_DRM_NOVA) += nova-drm.o
|
||||
nova-drm-y := drm/nova/nova.o
|
||||
|
||||
# Export Rust symbols from nova-core only if nova-drm actually references them.
|
||||
nova-core-export-deps := $(if $(CONFIG_DRM_NOVA),$(obj)/drm/nova/nova.o)
|
||||
|
||||
rust_needed_exports = \
|
||||
{ $(if $(strip $(2)),$(NM) -u $(2);,) echo "__DEFINED_RUST_SYMBOLS__"; \
|
||||
$(NM) -p --defined-only $(1); } | \
|
||||
awk -v fmt='$(3)' ' \
|
||||
/^__DEFINED_RUST_SYMBOLS__$$/ { defs = 1; next } \
|
||||
!defs { if ($$NF ~ /^_R/) needed[$$NF] = 1; next } \
|
||||
defs && $$2 ~ /(T|R|D|B)/ && $$3 ~ /^_R/ && \
|
||||
$$3 !~ /_(init|cleanup)_module$$/ && \
|
||||
$$3 !~ /__(pfx|cfi|odr_asan)/ && \
|
||||
$$3 in needed { printf fmt, $$3 } \
|
||||
'
|
||||
|
||||
quiet_cmd_exports = EXPORTS $@
|
||||
cmd_exports = \
|
||||
$(call rust_needed_exports,$<,$(nova-core-export-deps),EXPORT_SYMBOL_RUST_GPL(%s);\n) > $@
|
||||
|
||||
$(obj)/nova-core/exports_nova_core_generated.h: $(obj)/nova-core/nova_core.o $(nova-core-export-deps) FORCE
|
||||
$(call if_changed,exports)
|
||||
|
||||
targets += nova-core/exports_nova_core_generated.h
|
||||
|
||||
$(obj)/nova-core/nova_core_exports.o: $(obj)/nova-core/exports_nova_core_generated.h
|
||||
CFLAGS_nova-core/nova_core_exports.o := -I $(objtree)/$(obj)/nova-core
|
||||
|
||||
ifdef CONFIG_MODVERSIONS
|
||||
# The C export shim declares Rust symbols as `extern int`, so reuse its export
|
||||
# list but generate symbol CRCs from the Rust object instead of the shim's DWARF.
|
||||
$(obj)/nova-core/nova_core_exports.o: private cmd_gensymtypes_c = \
|
||||
$(call getexportsymbols,\1) | \
|
||||
$(objtree)/scripts/gendwarfksyms/gendwarfksyms \
|
||||
$(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable) \
|
||||
$(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes),) \
|
||||
$(obj)/nova-core/nova_core.o
|
||||
endif
|
||||
|
||||
# Output nova-core's crate metadata for use by nova-drm at compile time.
|
||||
RUSTFLAGS_nova-core/nova_core.o += \
|
||||
--emit=metadata=$(objtree)/$(obj)/nova-core/libnova_core.rmeta
|
||||
|
||||
# Allow nova-drm to import nova-core's types.
|
||||
$(obj)/drm/nova/nova.o: $(obj)/nova-core/nova_core.o
|
||||
RUSTFLAGS_drm/nova/nova.o := -L $(objtree)/$(obj)/nova-core --extern nova_core
|
||||
|
||||
+118
-40
@@ -193,6 +193,9 @@ static void mark_allocated(struct gpu_buddy *mm,
|
||||
block->header &= ~GPU_BUDDY_HEADER_STATE;
|
||||
block->header |= GPU_BUDDY_ALLOCATED;
|
||||
|
||||
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
|
||||
mm->used_scoreboard[gpu_buddy_block_order(block)]++;
|
||||
|
||||
rbtree_remove(mm, block);
|
||||
}
|
||||
|
||||
@@ -201,9 +204,14 @@ static void mark_free(struct gpu_buddy *mm,
|
||||
{
|
||||
enum gpu_buddy_free_tree tree;
|
||||
|
||||
if (gpu_buddy_block_is_allocated(block))
|
||||
mm->used_scoreboard[gpu_buddy_block_order(block)]--;
|
||||
|
||||
block->header &= ~GPU_BUDDY_HEADER_STATE;
|
||||
block->header |= GPU_BUDDY_FREE;
|
||||
|
||||
mm->free_scoreboard[gpu_buddy_block_order(block)]++;
|
||||
|
||||
tree = get_block_tree(block);
|
||||
rbtree_insert(mm, block, tree);
|
||||
}
|
||||
@@ -214,6 +222,8 @@ static void mark_split(struct gpu_buddy *mm,
|
||||
block->header &= ~GPU_BUDDY_HEADER_STATE;
|
||||
block->header |= GPU_BUDDY_SPLIT;
|
||||
|
||||
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
|
||||
|
||||
rbtree_remove(mm, block);
|
||||
}
|
||||
|
||||
@@ -271,9 +281,13 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm,
|
||||
}
|
||||
|
||||
rbtree_remove(mm, buddy);
|
||||
mm->free_scoreboard[gpu_buddy_block_order(buddy)]--;
|
||||
if (force_merge && gpu_buddy_block_is_clear(buddy))
|
||||
mm->clear_avail -= gpu_buddy_block_size(mm, buddy);
|
||||
|
||||
if (gpu_buddy_block_is_allocated(block))
|
||||
mm->used_scoreboard[gpu_buddy_block_order(block)]--;
|
||||
|
||||
gpu_block_free(mm, block);
|
||||
gpu_block_free(mm, buddy);
|
||||
|
||||
@@ -335,6 +349,7 @@ static int __force_merge(struct gpu_buddy *mm,
|
||||
iter = rb_prev(iter);
|
||||
|
||||
rbtree_remove(mm, block);
|
||||
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
|
||||
if (gpu_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= gpu_buddy_block_size(mm, block);
|
||||
|
||||
@@ -384,11 +399,23 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
|
||||
|
||||
BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER);
|
||||
|
||||
mm->free_scoreboard = kcalloc(mm->max_order + 1,
|
||||
sizeof(*mm->free_scoreboard),
|
||||
GFP_KERNEL);
|
||||
if (!mm->free_scoreboard)
|
||||
return -ENOMEM;
|
||||
|
||||
mm->used_scoreboard = kcalloc(mm->max_order + 1,
|
||||
sizeof(*mm->used_scoreboard),
|
||||
GFP_KERNEL);
|
||||
if (!mm->used_scoreboard)
|
||||
goto out_free_free_scoreboard;
|
||||
|
||||
mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES,
|
||||
sizeof(*mm->free_trees),
|
||||
GFP_KERNEL);
|
||||
if (!mm->free_trees)
|
||||
return -ENOMEM;
|
||||
goto out_free_used_scoreboard;
|
||||
|
||||
for_each_free_tree(i) {
|
||||
mm->free_trees[i] = kmalloc_array(mm->max_order + 1,
|
||||
@@ -450,6 +477,10 @@ out_free_tree:
|
||||
while (i--)
|
||||
kfree(mm->free_trees[i]);
|
||||
kfree(mm->free_trees);
|
||||
out_free_used_scoreboard:
|
||||
kfree(mm->used_scoreboard);
|
||||
out_free_free_scoreboard:
|
||||
kfree(mm->free_scoreboard);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_init);
|
||||
@@ -484,10 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm)
|
||||
|
||||
gpu_buddy_assert(mm->avail == mm->size);
|
||||
|
||||
for (i = 0; i <= mm->max_order; ++i)
|
||||
gpu_buddy_assert(!mm->used_scoreboard[i]);
|
||||
|
||||
for_each_free_tree(i)
|
||||
kfree(mm->free_trees[i]);
|
||||
kfree(mm->free_trees);
|
||||
kfree(mm->roots);
|
||||
kfree(mm->free_scoreboard);
|
||||
kfree(mm->used_scoreboard);
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_fini);
|
||||
|
||||
@@ -594,6 +630,59 @@ void gpu_buddy_free_block(struct gpu_buddy *mm,
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_free_block);
|
||||
|
||||
/**
|
||||
* gpu_buddy_allocated_addr_to_block - given relative address find the allocated block
|
||||
*
|
||||
* @mm: GPU buddy manager
|
||||
* @addr: Relative address
|
||||
*
|
||||
* Returns:
|
||||
* gpu_buddy_block on success, NULL or error code on failure
|
||||
*/
|
||||
struct gpu_buddy_block *gpu_buddy_allocated_addr_to_block(struct gpu_buddy *mm, u64 addr)
|
||||
{
|
||||
struct gpu_buddy_block *block;
|
||||
LIST_HEAD(dfs);
|
||||
u64 end;
|
||||
int i;
|
||||
|
||||
gpu_buddy_driver_lock_held(mm);
|
||||
|
||||
end = addr + mm->chunk_size - 1;
|
||||
for (i = 0; i < mm->n_roots; ++i)
|
||||
list_add_tail(&mm->roots[i]->tmp_link, &dfs);
|
||||
|
||||
do {
|
||||
u64 block_start;
|
||||
u64 block_end;
|
||||
|
||||
block = list_first_entry_or_null(&dfs,
|
||||
struct gpu_buddy_block,
|
||||
tmp_link);
|
||||
if (!block)
|
||||
break;
|
||||
|
||||
list_del(&block->tmp_link);
|
||||
|
||||
block_start = gpu_buddy_block_offset(block);
|
||||
block_end = block_start + gpu_buddy_block_size(mm, block) - 1;
|
||||
|
||||
if (!overlaps(addr, end, block_start, block_end))
|
||||
continue;
|
||||
|
||||
if (gpu_buddy_block_is_allocated(block))
|
||||
return block;
|
||||
else if (gpu_buddy_block_is_free(block))
|
||||
return NULL;
|
||||
|
||||
list_add(&block->right->tmp_link, &dfs);
|
||||
list_add(&block->left->tmp_link, &dfs);
|
||||
} while (1);
|
||||
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_allocated_addr_to_block);
|
||||
|
||||
static void __gpu_buddy_free_list(struct gpu_buddy *mm,
|
||||
struct list_head *objects,
|
||||
bool mark_clear,
|
||||
@@ -650,6 +739,20 @@ static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags
|
||||
return needs_clear != gpu_buddy_block_is_clear(block);
|
||||
}
|
||||
|
||||
static void __gpu_buddy_undo_splits(struct gpu_buddy *mm,
|
||||
struct gpu_buddy_block *block)
|
||||
{
|
||||
struct gpu_buddy_block *buddy = __get_buddy(block);
|
||||
|
||||
if (buddy &&
|
||||
(gpu_buddy_block_is_free(block) &&
|
||||
gpu_buddy_block_is_free(buddy))) {
|
||||
rbtree_remove(mm, block);
|
||||
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
|
||||
__gpu_buddy_free(mm, block, false);
|
||||
}
|
||||
}
|
||||
|
||||
static struct gpu_buddy_block *
|
||||
__alloc_range_bias(struct gpu_buddy *mm,
|
||||
u64 start, u64 end,
|
||||
@@ -659,7 +762,6 @@ __alloc_range_bias(struct gpu_buddy *mm,
|
||||
{
|
||||
u64 req_size = mm->chunk_size << order;
|
||||
struct gpu_buddy_block *block;
|
||||
struct gpu_buddy_block *buddy;
|
||||
LIST_HEAD(dfs);
|
||||
int err;
|
||||
int i;
|
||||
@@ -734,11 +836,7 @@ err_undo:
|
||||
* bigger is better, so make sure we merge everything back before we
|
||||
* free the allocated blocks.
|
||||
*/
|
||||
buddy = __get_buddy(block);
|
||||
if (buddy &&
|
||||
(gpu_buddy_block_is_free(block) &&
|
||||
gpu_buddy_block_is_free(buddy)))
|
||||
__gpu_buddy_free(mm, block, false);
|
||||
__gpu_buddy_undo_splits(mm, block);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@@ -847,8 +945,7 @@ alloc_from_freetree(struct gpu_buddy *mm,
|
||||
return block;
|
||||
|
||||
err_undo:
|
||||
if (tmp != order)
|
||||
__gpu_buddy_free(mm, block, false);
|
||||
__gpu_buddy_undo_splits(mm, block);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@@ -912,7 +1009,6 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
|
||||
{
|
||||
struct gpu_buddy_block *block = NULL;
|
||||
unsigned int order, tmp, alignment;
|
||||
struct gpu_buddy_block *buddy;
|
||||
enum gpu_buddy_free_tree tree;
|
||||
unsigned long pages;
|
||||
int err;
|
||||
@@ -965,11 +1061,7 @@ err_undo:
|
||||
* bigger is better, so make sure we merge everything back before we
|
||||
* free the allocated blocks.
|
||||
*/
|
||||
buddy = __get_buddy(block);
|
||||
if (buddy &&
|
||||
(gpu_buddy_block_is_free(block) &&
|
||||
gpu_buddy_block_is_free(buddy)))
|
||||
__gpu_buddy_free(mm, block, false);
|
||||
__gpu_buddy_undo_splits(mm, block);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@@ -980,7 +1072,6 @@ static int __alloc_range(struct gpu_buddy *mm,
|
||||
u64 *total_allocated_on_err)
|
||||
{
|
||||
struct gpu_buddy_block *block;
|
||||
struct gpu_buddy_block *buddy;
|
||||
u64 total_allocated = 0;
|
||||
LIST_HEAD(allocated);
|
||||
u64 end;
|
||||
@@ -1051,11 +1142,7 @@ err_undo:
|
||||
* bigger is better, so make sure we merge everything back before we
|
||||
* free the allocated blocks.
|
||||
*/
|
||||
buddy = __get_buddy(block);
|
||||
if (buddy &&
|
||||
(gpu_buddy_block_is_free(block) &&
|
||||
gpu_buddy_block_is_free(buddy)))
|
||||
__gpu_buddy_free(mm, block, false);
|
||||
__gpu_buddy_undo_splits(mm, block);
|
||||
|
||||
err_free:
|
||||
if (err == -ENOSPC && total_allocated_on_err) {
|
||||
@@ -1515,27 +1602,18 @@ void gpu_buddy_print(struct gpu_buddy *mm)
|
||||
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
|
||||
|
||||
for (order = mm->max_order; order >= 0; order--) {
|
||||
struct gpu_buddy_block *block, *tmp;
|
||||
struct rb_root *root;
|
||||
u64 count = 0, free;
|
||||
unsigned int tree;
|
||||
u64 free_count = mm->free_scoreboard[order];
|
||||
u64 used_count = mm->used_scoreboard[order];
|
||||
u64 block_size = mm->chunk_size << order;
|
||||
u64 free = free_count * block_size;
|
||||
u64 used = used_count * block_size;
|
||||
|
||||
for_each_free_tree(tree) {
|
||||
root = &mm->free_trees[tree][order];
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
|
||||
BUG_ON(!gpu_buddy_block_is_free(block));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
free = count * (mm->chunk_size << order);
|
||||
if (free < SZ_1M)
|
||||
pr_info("order-%2d free: %8llu KiB, blocks: %llu\n",
|
||||
order, free >> 10, count);
|
||||
if (block_size < SZ_1M)
|
||||
pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n",
|
||||
order, free >> 10, used >> 10, free_count, used_count);
|
||||
else
|
||||
pr_info("order-%2d free: %8llu MiB, blocks: %llu\n",
|
||||
order, free >> 20, count);
|
||||
pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n",
|
||||
order, free >> 20, used >> 20, free_count, used_count);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_print);
|
||||
|
||||
@@ -186,7 +186,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
||||
obj-$(CONFIG_DRM_VGEM) += vgem/
|
||||
obj-$(CONFIG_DRM_VKMS) += vkms/
|
||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||
obj-$(CONFIG_DRM_NOVA) += nova/
|
||||
# nova-drm is built from drivers/gpu/Makefile together with nova-core.
|
||||
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
|
||||
obj-$(CONFIG_DRM_GMA500) += gma500/
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
|
||||
@@ -222,6 +223,9 @@ static int adp_dsi_bridge_attach(struct drm_bridge *bridge,
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
|
||||
.atomic_create_state = drm_atomic_helper_bridge_create_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.attach = adp_dsi_bridge_attach,
|
||||
};
|
||||
|
||||
|
||||
@@ -70,8 +70,9 @@ amdgpu-y += amdgpu_device.o amdgpu_reg_access.o amdgpu_doorbell_mgr.o amdgpu_kms
|
||||
amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o amdgpu_rap.o \
|
||||
amdgpu_fw_attestation.o amdgpu_securedisplay.o \
|
||||
amdgpu_eeprom.o amdgpu_mca.o amdgpu_psp_ta.o amdgpu_lsdma.o amdgpu_lockdep.o \
|
||||
amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_aca.o amdgpu_dev_coredump.o \
|
||||
amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o
|
||||
amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_dev_coredump.o \
|
||||
amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o \
|
||||
amdgpu_wb.o
|
||||
|
||||
amdgpu-$(CONFIG_PROC_FS) += amdgpu_fdinfo.o
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/dma-fence.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
#include <drm/ttm/ttm_placement.h>
|
||||
@@ -95,7 +96,6 @@
|
||||
#include "amdgpu_doorbell.h"
|
||||
#include "amdgpu_amdkfd.h"
|
||||
#include "amdgpu_discovery.h"
|
||||
#include "amdgpu_mes.h"
|
||||
#include "amdgpu_umc.h"
|
||||
#include "amdgpu_mmhub.h"
|
||||
#include "amdgpu_gfxhub.h"
|
||||
@@ -103,7 +103,6 @@
|
||||
#include "amdgpu_smuio.h"
|
||||
#include "amdgpu_fdinfo.h"
|
||||
#include "amdgpu_mca.h"
|
||||
#include "amdgpu_aca.h"
|
||||
#include "amdgpu_ras.h"
|
||||
#include "amdgpu_lockdep.h"
|
||||
#include "amdgpu_cper.h"
|
||||
@@ -112,7 +111,11 @@
|
||||
#include "amdgpu_reg_state.h"
|
||||
#include "amdgpu_userq.h"
|
||||
#include "amdgpu_eviction_fence.h"
|
||||
#include "amdgpu_wb.h"
|
||||
#include "amdgpu_ip.h"
|
||||
#include "amdgpu_mes.h"
|
||||
#include "amdgpu_sa.h"
|
||||
#include "amdgpu_acpi.h"
|
||||
#if defined(CONFIG_DRM_AMD_ISP)
|
||||
#include "amdgpu_isp.h"
|
||||
#endif
|
||||
@@ -134,13 +137,6 @@ struct amdgpu_mgpu_info {
|
||||
uint32_t num_apu;
|
||||
};
|
||||
|
||||
enum amdgpu_ss {
|
||||
AMDGPU_SS_DRV_LOAD,
|
||||
AMDGPU_SS_DEV_D0,
|
||||
AMDGPU_SS_DEV_D3,
|
||||
AMDGPU_SS_DRV_UNLOAD
|
||||
};
|
||||
|
||||
struct amdgpu_hwip_reg_entry {
|
||||
u32 hwip;
|
||||
u32 inst;
|
||||
@@ -227,6 +223,7 @@ extern int amdgpu_smartshift_bias;
|
||||
extern int amdgpu_use_xgmi_p2p;
|
||||
extern int amdgpu_mtype_local;
|
||||
extern int amdgpu_enforce_isolation;
|
||||
extern uint amdgpu_debug_mask;
|
||||
#ifdef CONFIG_HSA_AMD
|
||||
extern int sched_policy;
|
||||
extern bool debug_evictions;
|
||||
@@ -272,7 +269,6 @@ extern int amdgpu_ptl;
|
||||
|
||||
extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
|
||||
|
||||
#define AMDGPU_VM_MAX_NUM_CTX 4096
|
||||
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
|
||||
#define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
|
||||
#define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */
|
||||
@@ -305,9 +301,10 @@ extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
|
||||
|
||||
/* reset mask */
|
||||
#define AMDGPU_RESET_TYPE_FULL (1 << 0) /* full adapter reset, mode1/mode2/BACO/etc. */
|
||||
#define AMDGPU_RESET_TYPE_SOFT_RESET (1 << 1) /* IP level soft reset */
|
||||
#define AMDGPU_RESET_TYPE_SOFT_RECOVERY (1 << 1) /* soft recovery, eg. kill shaders */
|
||||
#define AMDGPU_RESET_TYPE_PER_QUEUE (1 << 2) /* per queue */
|
||||
#define AMDGPU_RESET_TYPE_PER_PIPE (1 << 3) /* per pipe */
|
||||
#define AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET (1 << 4) /* soft-resets an IP block */
|
||||
|
||||
/* max cursor sizes (in pixels) */
|
||||
#define CIK_CURSOR_WIDTH 128
|
||||
@@ -330,6 +327,7 @@ struct amdgpu_hive_info;
|
||||
struct amdgpu_reset_context;
|
||||
struct amdgpu_reset_control;
|
||||
struct amdgpu_coredump_info;
|
||||
struct amdgpu_video_codecs;
|
||||
|
||||
enum amdgpu_cp_irq {
|
||||
AMDGPU_CP_IRQ_GFX_ME0_PIPE0_EOP = 0,
|
||||
@@ -387,37 +385,6 @@ struct amdgpu_clock {
|
||||
uint32_t max_pixel_clock;
|
||||
};
|
||||
|
||||
/* sub-allocation manager, it has to be protected by another lock.
|
||||
* By conception this is an helper for other part of the driver
|
||||
* like the indirect buffer or semaphore, which both have their
|
||||
* locking.
|
||||
*
|
||||
* Principe is simple, we keep a list of sub allocation in offset
|
||||
* order (first entry has offset == 0, last entry has the highest
|
||||
* offset).
|
||||
*
|
||||
* When allocating new object we first check if there is room at
|
||||
* the end total_size - (last_object_offset + last_object_size) >=
|
||||
* alloc_size. If so we allocate new object there.
|
||||
*
|
||||
* When there is not enough room at the end, we start waiting for
|
||||
* each sub object until we reach object_offset+object_size >=
|
||||
* alloc_size, this object then become the sub object we return.
|
||||
*
|
||||
* Alignment can't be bigger than page size.
|
||||
*
|
||||
* Hole are not considered for allocation to keep things simple.
|
||||
* Assumption is that there won't be hole (all object on same
|
||||
* alignment).
|
||||
*/
|
||||
|
||||
struct amdgpu_sa_manager {
|
||||
struct drm_suballoc_manager base;
|
||||
struct amdgpu_bo *bo;
|
||||
uint64_t gpu_addr;
|
||||
void *cpu_ptr;
|
||||
};
|
||||
|
||||
/*
|
||||
* IRQS.
|
||||
*/
|
||||
@@ -446,8 +413,7 @@ struct amdgpu_fpriv {
|
||||
struct amdgpu_bo_va *prt_va;
|
||||
struct amdgpu_bo_va *csa_va;
|
||||
struct amdgpu_bo_va *seq64_va;
|
||||
struct mutex bo_list_lock;
|
||||
struct idr bo_list_handles;
|
||||
struct xarray bo_list_handles;
|
||||
struct amdgpu_ctx_mgr ctx_mgr;
|
||||
struct amdgpu_userq_mgr userq_mgr;
|
||||
|
||||
@@ -460,106 +426,11 @@ struct amdgpu_fpriv {
|
||||
|
||||
int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv);
|
||||
|
||||
/*
|
||||
* Writeback
|
||||
*/
|
||||
#define AMDGPU_MAX_WB 1024 /* Reserve at most 1024 WB slots for amdgpu-owned rings. */
|
||||
|
||||
/**
|
||||
* struct amdgpu_wb - This struct is used for small GPU memory allocation.
|
||||
*
|
||||
* This struct is used to allocate a small amount of GPU memory that can be
|
||||
* used to shadow certain states into the memory. This is especially useful for
|
||||
* providing easy CPU access to some states without requiring register access
|
||||
* (e.g., if some block is power gated, reading register may be problematic).
|
||||
*
|
||||
* Note: the term writeback was initially used because many of the amdgpu
|
||||
* components had some level of writeback memory, and this struct initially
|
||||
* described those components.
|
||||
*/
|
||||
struct amdgpu_wb {
|
||||
|
||||
/**
|
||||
* @wb_obj:
|
||||
*
|
||||
* Buffer Object used for the writeback memory.
|
||||
*/
|
||||
struct amdgpu_bo *wb_obj;
|
||||
|
||||
/**
|
||||
* @wb:
|
||||
*
|
||||
* Pointer to the first writeback slot. In terms of CPU address
|
||||
* this value can be accessed directly by using the offset as an index.
|
||||
* For the GPU address, it is necessary to use gpu_addr and the offset.
|
||||
*/
|
||||
uint32_t *wb;
|
||||
|
||||
/**
|
||||
* @gpu_addr:
|
||||
*
|
||||
* Writeback base address in the GPU.
|
||||
*/
|
||||
uint64_t gpu_addr;
|
||||
|
||||
/**
|
||||
* @num_wb:
|
||||
*
|
||||
* Number of writeback slots reserved for amdgpu.
|
||||
*/
|
||||
u32 num_wb;
|
||||
|
||||
/**
|
||||
* @used:
|
||||
*
|
||||
* Track the writeback slot already used.
|
||||
*/
|
||||
unsigned long used[DIV_ROUND_UP(AMDGPU_MAX_WB, BITS_PER_LONG)];
|
||||
|
||||
/**
|
||||
* @lock:
|
||||
*
|
||||
* Protects read and write of the used field array.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
int amdgpu_device_wb_get(struct amdgpu_device *adev, u32 *wb);
|
||||
void amdgpu_device_wb_free(struct amdgpu_device *adev, u32 wb);
|
||||
|
||||
/*
|
||||
* Benchmarking
|
||||
*/
|
||||
int amdgpu_benchmark(struct amdgpu_device *adev, int test_number);
|
||||
|
||||
/*
|
||||
* ASIC specific register table accessible by UMD
|
||||
*/
|
||||
struct amdgpu_allowed_register_entry {
|
||||
uint32_t reg_offset;
|
||||
bool grbm_indexed;
|
||||
};
|
||||
|
||||
struct amdgpu_video_codec_info {
|
||||
u32 codec_type;
|
||||
u32 max_width;
|
||||
u32 max_height;
|
||||
u32 max_pixels_per_frame;
|
||||
u32 max_level;
|
||||
};
|
||||
|
||||
#define codec_info_build(type, width, height, level) \
|
||||
.codec_type = type,\
|
||||
.max_width = width,\
|
||||
.max_height = height,\
|
||||
.max_pixels_per_frame = height * width,\
|
||||
.max_level = level,
|
||||
|
||||
struct amdgpu_video_codecs {
|
||||
const u32 codec_count;
|
||||
const struct amdgpu_video_codec_info *codec_array;
|
||||
};
|
||||
|
||||
/*
|
||||
* ASIC specific functions.
|
||||
*/
|
||||
@@ -587,8 +458,6 @@ struct amdgpu_asic_funcs {
|
||||
/* invalidate hdp read cache */
|
||||
void (*invalidate_hdp)(struct amdgpu_device *adev,
|
||||
struct amdgpu_ring *ring);
|
||||
/* check if the asic needs a full reset of if soft reset will work */
|
||||
bool (*need_full_reset)(struct amdgpu_device *adev);
|
||||
/* initialize doorbell layout for specific asic*/
|
||||
void (*init_doorbell_index)(struct amdgpu_device *adev);
|
||||
/* PCIe bandwidth usage */
|
||||
@@ -665,38 +534,6 @@ struct amdgpu_uid {
|
||||
struct amdgpu_device *adev;
|
||||
};
|
||||
|
||||
#define MAX_UMA_OPTION_NAME 28
|
||||
#define MAX_UMA_OPTION_ENTRIES 19
|
||||
|
||||
#define AMDGPU_UMA_FLAG_AUTO BIT(1)
|
||||
#define AMDGPU_UMA_FLAG_CUSTOM BIT(0)
|
||||
|
||||
/**
|
||||
* struct amdgpu_uma_carveout_option - single UMA carveout option
|
||||
* @name: Name of the carveout option
|
||||
* @memory_carved_mb: Amount of memory carved in MB
|
||||
* @flags: ATCS flags supported by this option
|
||||
*/
|
||||
struct amdgpu_uma_carveout_option {
|
||||
char name[MAX_UMA_OPTION_NAME];
|
||||
uint32_t memory_carved_mb;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdgpu_uma_carveout_info - table of available UMA carveout options
|
||||
* @num_entries: Number of available options
|
||||
* @uma_option_index: The index of the option currently applied
|
||||
* @update_lock: Lock to serialize changes to the option
|
||||
* @entries: The array of carveout options
|
||||
*/
|
||||
struct amdgpu_uma_carveout_info {
|
||||
uint8_t num_entries;
|
||||
uint8_t uma_option_index;
|
||||
struct mutex update_lock;
|
||||
struct amdgpu_uma_carveout_option entries[MAX_UMA_OPTION_ENTRIES];
|
||||
};
|
||||
|
||||
struct amd_powerplay {
|
||||
void *pp_handle;
|
||||
const struct amd_pm_funcs *pp_funcs;
|
||||
@@ -741,44 +578,6 @@ struct amd_powerplay {
|
||||
(rid == 0x01) || \
|
||||
(rid == 0x10))))
|
||||
|
||||
enum amdgpu_mqd_update_flag {
|
||||
AMDGPU_UPDATE_FLAG_DBG_WA_ENABLE = 1,
|
||||
AMDGPU_UPDATE_FLAG_DBG_WA_DISABLE = 2,
|
||||
AMDGPU_UPDATE_FLAG_IS_GWS = 4, /* quirk for gfx9 IP */
|
||||
};
|
||||
|
||||
struct amdgpu_mqd_prop {
|
||||
uint64_t mqd_gpu_addr;
|
||||
uint64_t hqd_base_gpu_addr;
|
||||
uint64_t rptr_gpu_addr;
|
||||
uint64_t wptr_gpu_addr;
|
||||
uint32_t queue_size;
|
||||
bool use_doorbell;
|
||||
uint32_t doorbell_index;
|
||||
uint64_t eop_gpu_addr;
|
||||
uint32_t hqd_pipe_priority;
|
||||
uint32_t hqd_queue_priority;
|
||||
uint32_t mqd_stride_size;
|
||||
bool allow_tunneling;
|
||||
bool hqd_active;
|
||||
uint64_t shadow_addr;
|
||||
uint64_t gds_bkup_addr;
|
||||
uint64_t csa_addr;
|
||||
uint64_t fence_address;
|
||||
bool tmz_queue;
|
||||
bool kernel_queue;
|
||||
uint32_t *cu_mask;
|
||||
uint32_t cu_mask_count;
|
||||
uint32_t cu_flags;
|
||||
bool is_user_cu_masked;
|
||||
};
|
||||
|
||||
struct amdgpu_mqd {
|
||||
unsigned mqd_size;
|
||||
int (*init_mqd)(struct amdgpu_device *adev, void *mqd,
|
||||
struct amdgpu_mqd_prop *p);
|
||||
};
|
||||
|
||||
struct amdgpu_pcie_reset_ctx {
|
||||
bool in_link_reset;
|
||||
bool occurs_dpc;
|
||||
@@ -851,6 +650,7 @@ struct amdgpu_device {
|
||||
struct dev_pm_domain vga_pm_domain;
|
||||
bool have_disp_power_ref;
|
||||
bool have_atomics_support;
|
||||
bool is_sw_smu;
|
||||
|
||||
/* BIOS */
|
||||
bool is_atom_fw;
|
||||
@@ -1022,9 +822,6 @@ struct amdgpu_device {
|
||||
/* MCA */
|
||||
struct amdgpu_mca mca;
|
||||
|
||||
/* ACA */
|
||||
struct amdgpu_aca aca;
|
||||
|
||||
/* CPER */
|
||||
struct amdgpu_cper cper;
|
||||
|
||||
@@ -1130,12 +927,13 @@ struct amdgpu_device {
|
||||
bool debug_largebar;
|
||||
bool debug_disable_soft_recovery;
|
||||
bool debug_use_vram_fw_buf;
|
||||
bool debug_enable_ras_aca;
|
||||
bool debug_exp_resets;
|
||||
bool debug_disable_gpu_ring_reset;
|
||||
bool debug_vm_userptr;
|
||||
bool debug_disable_ce_logs;
|
||||
bool debug_enable_ce_cs;
|
||||
bool debug_hibernation_thaw_resume_gpu;
|
||||
bool debug_disable_ip_block_soft_reset;
|
||||
|
||||
/* Protection for the following isolation structure */
|
||||
struct mutex enforce_isolation_mutex;
|
||||
@@ -1165,14 +963,6 @@ struct amdgpu_device {
|
||||
struct amdgpu_kfd_dev kfd;
|
||||
};
|
||||
|
||||
/*
|
||||
* MES FW uses address(mqd_addr + sizeof(struct mqd) + 3*sizeof(uint32_t))
|
||||
* as fence address and writes a 32 bit fence value to this address.
|
||||
* Driver needs to allocate at least 4 DWs extra memory in addition to
|
||||
* sizeof(struct mqd). Add 8 DWs and align to AMDGPU_GPU_PAGE_SIZE for safety.
|
||||
*/
|
||||
#define AMDGPU_MQD_SIZE_ALIGN(mqd_size) AMDGPU_GPU_PAGE_ALIGN(((mqd_size) + 32))
|
||||
|
||||
static inline uint32_t amdgpu_ip_version(const struct amdgpu_device *adev,
|
||||
uint8_t ip, uint8_t inst)
|
||||
{
|
||||
@@ -1356,7 +1146,6 @@ int emu_soc_asic_init(struct amdgpu_device *adev);
|
||||
#define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l))
|
||||
#define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v)))
|
||||
#define amdgpu_asic_get_config_memsize(adev) (adev)->asic_funcs->get_config_memsize((adev))
|
||||
#define amdgpu_asic_need_full_reset(adev) (adev)->asic_funcs->need_full_reset((adev))
|
||||
#define amdgpu_asic_init_doorbell_index(adev) (adev)->asic_funcs->init_doorbell_index((adev))
|
||||
#define amdgpu_asic_get_pcie_usage(adev, cnt0, cnt1) ((adev)->asic_funcs->get_pcie_usage((adev), (cnt0), (cnt1)))
|
||||
#define amdgpu_asic_need_reset_on_init(adev) (adev)->asic_funcs->need_reset_on_init((adev))
|
||||
@@ -1468,6 +1257,8 @@ int amdgpu_enable_vblank_kms(struct drm_crtc *crtc);
|
||||
void amdgpu_disable_vblank_kms(struct drm_crtc *crtc);
|
||||
int amdgpu_info_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filp);
|
||||
int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filp);
|
||||
|
||||
/*
|
||||
* functions used by amdgpu_encoder.c
|
||||
@@ -1488,88 +1279,6 @@ struct amdgpu_afmt_acr {
|
||||
|
||||
struct amdgpu_afmt_acr amdgpu_afmt_acr(uint32_t clock);
|
||||
|
||||
/* amdgpu_acpi.c */
|
||||
|
||||
struct amdgpu_numa_info {
|
||||
uint64_t size;
|
||||
int pxm;
|
||||
int nid;
|
||||
};
|
||||
|
||||
/* ATCS Device/Driver State */
|
||||
#define AMDGPU_ATCS_PSC_DEV_STATE_D0 0
|
||||
#define AMDGPU_ATCS_PSC_DEV_STATE_D3_HOT 3
|
||||
#define AMDGPU_ATCS_PSC_DRV_STATE_OPR 0
|
||||
#define AMDGPU_ATCS_PSC_DRV_STATE_NOT_OPR 1
|
||||
|
||||
#if defined(CONFIG_ACPI)
|
||||
int amdgpu_acpi_init(struct amdgpu_device *adev);
|
||||
void amdgpu_acpi_fini(struct amdgpu_device *adev);
|
||||
bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev);
|
||||
bool amdgpu_acpi_is_power_shift_control_supported(void);
|
||||
bool amdgpu_acpi_is_set_uma_allocation_size_supported(void);
|
||||
int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev,
|
||||
u8 perf_req, bool advertise);
|
||||
int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
|
||||
u8 dev_state, bool drv_state);
|
||||
int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
|
||||
enum amdgpu_ss ss_state);
|
||||
int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type);
|
||||
int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev);
|
||||
int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev, u64 *tmr_offset,
|
||||
u64 *tmr_size);
|
||||
int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev, int xcc_id,
|
||||
struct amdgpu_numa_info *numa_info);
|
||||
|
||||
void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps);
|
||||
bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev);
|
||||
void amdgpu_acpi_detect(void);
|
||||
void amdgpu_acpi_release(void);
|
||||
#else
|
||||
static inline int amdgpu_acpi_init(struct amdgpu_device *adev) { return 0; }
|
||||
static inline int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev,
|
||||
u64 *tmr_offset, u64 *tmr_size)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev,
|
||||
int xcc_id,
|
||||
struct amdgpu_numa_info *numa_info)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { }
|
||||
static inline bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev) { return false; }
|
||||
static inline void amdgpu_acpi_detect(void) { }
|
||||
static inline void amdgpu_acpi_release(void) { }
|
||||
static inline bool amdgpu_acpi_is_power_shift_control_supported(void) { return false; }
|
||||
static inline bool amdgpu_acpi_is_set_uma_allocation_size_supported(void) { return false; }
|
||||
static inline int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
|
||||
u8 dev_state, bool drv_state) { return 0; }
|
||||
static inline int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
|
||||
enum amdgpu_ss ss_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
|
||||
bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev);
|
||||
bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev);
|
||||
#else
|
||||
static inline bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev) { return false; }
|
||||
static inline bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev) { return false; }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_DRM_AMD_ISP)
|
||||
int amdgpu_acpi_get_isp4_dev(struct acpi_device **dev);
|
||||
#endif
|
||||
|
||||
void amdgpu_register_gpu_instance(struct amdgpu_device *adev);
|
||||
void amdgpu_unregister_gpu_instance(struct amdgpu_device *adev);
|
||||
|
||||
|
||||
@@ -1,985 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_aca.h"
|
||||
#include "amdgpu_ras.h"
|
||||
|
||||
#define ACA_BANK_HWID(type, hwid, mcatype) [ACA_HWIP_TYPE_##type] = {hwid, mcatype}
|
||||
|
||||
typedef int bank_handler_t(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data);
|
||||
|
||||
static struct aca_hwip aca_hwid_mcatypes[ACA_HWIP_TYPE_COUNT] = {
|
||||
ACA_BANK_HWID(SMU, 0x01, 0x01),
|
||||
ACA_BANK_HWID(PCS_XGMI, 0x50, 0x00),
|
||||
ACA_BANK_HWID(UMC, 0x96, 0x00),
|
||||
};
|
||||
|
||||
static void aca_banks_init(struct aca_banks *banks)
|
||||
{
|
||||
if (!banks)
|
||||
return;
|
||||
|
||||
memset(banks, 0, sizeof(*banks));
|
||||
INIT_LIST_HEAD(&banks->list);
|
||||
}
|
||||
|
||||
static int aca_banks_add_bank(struct aca_banks *banks, struct aca_bank *bank)
|
||||
{
|
||||
struct aca_bank_node *node;
|
||||
|
||||
if (!bank)
|
||||
return -EINVAL;
|
||||
|
||||
node = kvzalloc_obj(*node);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&node->bank, bank, sizeof(*bank));
|
||||
|
||||
INIT_LIST_HEAD(&node->node);
|
||||
list_add_tail(&node->node, &banks->list);
|
||||
|
||||
banks->nr_banks++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aca_banks_release(struct aca_banks *banks)
|
||||
{
|
||||
struct aca_bank_node *node, *tmp;
|
||||
|
||||
if (list_empty(&banks->list))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(node, tmp, &banks->list, node) {
|
||||
list_del(&node->node);
|
||||
kvfree(node);
|
||||
banks->nr_banks--;
|
||||
}
|
||||
}
|
||||
|
||||
static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
|
||||
if (!smu_funcs || !smu_funcs->get_valid_aca_count)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return smu_funcs->get_valid_aca_count(adev, type, count);
|
||||
}
|
||||
|
||||
static struct aca_regs_dump {
|
||||
const char *name;
|
||||
int reg_idx;
|
||||
} aca_regs[] = {
|
||||
{"CONTROL", ACA_REG_IDX_CTL},
|
||||
{"STATUS", ACA_REG_IDX_STATUS},
|
||||
{"ADDR", ACA_REG_IDX_ADDR},
|
||||
{"MISC", ACA_REG_IDX_MISC0},
|
||||
{"CONFIG", ACA_REG_IDX_CONFIG},
|
||||
{"IPID", ACA_REG_IDX_IPID},
|
||||
{"SYND", ACA_REG_IDX_SYND},
|
||||
{"DESTAT", ACA_REG_IDX_DESTAT},
|
||||
{"DEADDR", ACA_REG_IDX_DEADDR},
|
||||
{"CONTROL_MASK", ACA_REG_IDX_CTL_MASK},
|
||||
};
|
||||
|
||||
static void aca_smu_bank_dump(struct amdgpu_device *adev, int idx, int total, struct aca_bank *bank,
|
||||
struct ras_query_context *qctx)
|
||||
{
|
||||
u64 event_id = qctx ? qctx->evid.event_id : RAS_EVENT_INVALID_ID;
|
||||
int i;
|
||||
|
||||
if (adev->debug_disable_ce_logs &&
|
||||
bank->smu_err_type == ACA_SMU_TYPE_CE &&
|
||||
!ACA_BANK_ERR_IS_DEFFERED(bank))
|
||||
return;
|
||||
|
||||
RAS_EVENT_LOG(adev, event_id, HW_ERR "Accelerator Check Architecture events logged\n");
|
||||
/* plus 1 for output format, e.g: ACA[08/08]: xxxx */
|
||||
for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
|
||||
RAS_EVENT_LOG(adev, event_id, HW_ERR "ACA[%02d/%02d].%s=0x%016llx\n",
|
||||
idx + 1, total, aca_regs[i].name, bank->regs[aca_regs[i].reg_idx]);
|
||||
|
||||
if (ACA_REG__STATUS__SCRUB(bank->regs[ACA_REG_IDX_STATUS]))
|
||||
RAS_EVENT_LOG(adev, event_id, HW_ERR "hardware error logged by the scrubber\n");
|
||||
}
|
||||
|
||||
static bool aca_bank_hwip_is_matched(struct aca_bank *bank, enum aca_hwip_type type)
|
||||
{
|
||||
|
||||
struct aca_hwip *hwip;
|
||||
int hwid, mcatype;
|
||||
u64 ipid;
|
||||
|
||||
if (!bank || type == ACA_HWIP_TYPE_UNKNOW)
|
||||
return false;
|
||||
|
||||
hwip = &aca_hwid_mcatypes[type];
|
||||
if (!hwip->hwid)
|
||||
return false;
|
||||
|
||||
ipid = bank->regs[ACA_REG_IDX_IPID];
|
||||
hwid = ACA_REG__IPID__HARDWAREID(ipid);
|
||||
mcatype = ACA_REG__IPID__MCATYPE(ipid);
|
||||
|
||||
return hwip->hwid == hwid && hwip->mcatype == mcatype;
|
||||
}
|
||||
|
||||
static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_type type,
|
||||
int start, int count,
|
||||
struct aca_banks *banks, struct ras_query_context *qctx)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
|
||||
struct aca_bank bank;
|
||||
int i, max_count, ret;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (!smu_funcs || !smu_funcs->get_valid_aca_bank)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case ACA_SMU_TYPE_UE:
|
||||
max_count = smu_funcs->max_ue_bank_count;
|
||||
break;
|
||||
case ACA_SMU_TYPE_CE:
|
||||
max_count = smu_funcs->max_ce_bank_count;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (start + count > max_count)
|
||||
return -EINVAL;
|
||||
|
||||
count = min_t(int, count, max_count);
|
||||
for (i = 0; i < count; i++) {
|
||||
memset(&bank, 0, sizeof(bank));
|
||||
ret = smu_funcs->get_valid_aca_bank(adev, type, start + i, &bank);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bank.smu_err_type = type;
|
||||
|
||||
/*
|
||||
* Poison being consumed when injecting a UE while running background workloads,
|
||||
* which are unexpected.
|
||||
*/
|
||||
if (type == ACA_SMU_TYPE_UE &&
|
||||
ACA_REG__STATUS__POISON(bank.regs[ACA_REG_IDX_STATUS]) &&
|
||||
!aca_bank_hwip_is_matched(&bank, ACA_HWIP_TYPE_UMC))
|
||||
continue;
|
||||
|
||||
aca_smu_bank_dump(adev, i, count, &bank, qctx);
|
||||
|
||||
ret = aca_banks_add_bank(banks, &bank);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type)
|
||||
{
|
||||
const struct aca_bank_ops *bank_ops = handle->bank_ops;
|
||||
|
||||
/* Parse all deferred errors with UMC aca handle */
|
||||
if (ACA_BANK_ERR_IS_DEFFERED(bank))
|
||||
return handle->hwip == ACA_HWIP_TYPE_UMC;
|
||||
|
||||
if (!aca_bank_hwip_is_matched(bank, handle->hwip))
|
||||
return false;
|
||||
|
||||
if (!bank_ops->aca_bank_is_valid)
|
||||
return true;
|
||||
|
||||
return bank_ops->aca_bank_is_valid(handle, bank, type, handle->data);
|
||||
}
|
||||
|
||||
static struct aca_bank_error *new_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
|
||||
{
|
||||
struct aca_bank_error *bank_error;
|
||||
|
||||
bank_error = kvzalloc_obj(*bank_error);
|
||||
if (!bank_error)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&bank_error->node);
|
||||
memcpy(&bank_error->info, info, sizeof(*info));
|
||||
|
||||
mutex_lock(&aerr->lock);
|
||||
list_add_tail(&bank_error->node, &aerr->list);
|
||||
aerr->nr_errors++;
|
||||
mutex_unlock(&aerr->lock);
|
||||
|
||||
return bank_error;
|
||||
}
|
||||
|
||||
static struct aca_bank_error *find_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
|
||||
{
|
||||
struct aca_bank_error *bank_error = NULL;
|
||||
struct aca_bank_info *tmp_info;
|
||||
bool found = false;
|
||||
|
||||
mutex_lock(&aerr->lock);
|
||||
list_for_each_entry(bank_error, &aerr->list, node) {
|
||||
tmp_info = &bank_error->info;
|
||||
if (tmp_info->socket_id == info->socket_id &&
|
||||
tmp_info->die_id == info->die_id) {
|
||||
found = true;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&aerr->lock);
|
||||
|
||||
return found ? bank_error : NULL;
|
||||
}
|
||||
|
||||
static void aca_bank_error_remove(struct aca_error *aerr, struct aca_bank_error *bank_error)
|
||||
{
|
||||
if (!aerr || !bank_error)
|
||||
return;
|
||||
|
||||
list_del(&bank_error->node);
|
||||
aerr->nr_errors--;
|
||||
|
||||
kvfree(bank_error);
|
||||
}
|
||||
|
||||
static struct aca_bank_error *get_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
|
||||
{
|
||||
struct aca_bank_error *bank_error;
|
||||
|
||||
if (!aerr || !info)
|
||||
return NULL;
|
||||
|
||||
bank_error = find_bank_error(aerr, info);
|
||||
if (bank_error)
|
||||
return bank_error;
|
||||
|
||||
return new_bank_error(aerr, info);
|
||||
}
|
||||
|
||||
int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info,
|
||||
enum aca_error_type type, u64 count)
|
||||
{
|
||||
struct aca_error_cache *error_cache = &handle->error_cache;
|
||||
struct aca_bank_error *bank_error;
|
||||
struct aca_error *aerr;
|
||||
|
||||
if (!handle || !info || type >= ACA_ERROR_TYPE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
aerr = &error_cache->errors[type];
|
||||
bank_error = get_bank_error(aerr, info);
|
||||
if (!bank_error)
|
||||
return -ENOMEM;
|
||||
|
||||
bank_error->count += count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type)
|
||||
{
|
||||
const struct aca_bank_ops *bank_ops = handle->bank_ops;
|
||||
|
||||
if (!bank)
|
||||
return -EINVAL;
|
||||
|
||||
if (!bank_ops->aca_bank_parser)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return bank_ops->aca_bank_parser(handle, bank, type,
|
||||
handle->data);
|
||||
}
|
||||
|
||||
static int handler_aca_log_bank_error(struct aca_handle *handle, struct aca_bank *bank,
|
||||
enum aca_smu_type type, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = aca_bank_parser(handle, bank, type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aca_dispatch_bank(struct aca_handle_manager *mgr, struct aca_bank *bank,
|
||||
enum aca_smu_type type, bank_handler_t handler, void *data)
|
||||
{
|
||||
struct aca_handle *handle;
|
||||
int ret;
|
||||
|
||||
if (list_empty(&mgr->list))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(handle, &mgr->list, node) {
|
||||
if (!aca_bank_is_valid(handle, bank, type))
|
||||
continue;
|
||||
|
||||
ret = handler(handle, bank, type, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aca_dispatch_banks(struct aca_handle_manager *mgr, struct aca_banks *banks,
|
||||
enum aca_smu_type type, bank_handler_t handler, void *data)
|
||||
{
|
||||
struct aca_bank_node *node;
|
||||
struct aca_bank *bank;
|
||||
int ret;
|
||||
|
||||
if (!mgr || !banks)
|
||||
return -EINVAL;
|
||||
|
||||
/* pre check to avoid unnecessary operations */
|
||||
if (list_empty(&mgr->list) || list_empty(&banks->list))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(node, &banks->list, node) {
|
||||
bank = &node->bank;
|
||||
|
||||
ret = aca_dispatch_bank(mgr, bank, type, handler, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool aca_bank_should_update(struct amdgpu_device *adev, enum aca_smu_type type)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
bool ret = true;
|
||||
|
||||
/*
|
||||
* Because the UE Valid MCA count will only be cleared after reset,
|
||||
* in order to avoid repeated counting of the error count,
|
||||
* the aca bank is only updated once during the gpu recovery stage.
|
||||
*/
|
||||
if (type == ACA_SMU_TYPE_UE) {
|
||||
if (amdgpu_ras_intr_triggered())
|
||||
ret = atomic_cmpxchg(&aca->ue_update_flag, 0, 1) == 0;
|
||||
else
|
||||
atomic_set(&aca->ue_update_flag, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aca_banks_generate_cper(struct amdgpu_device *adev,
|
||||
enum aca_smu_type type,
|
||||
struct aca_banks *banks,
|
||||
int count)
|
||||
{
|
||||
struct aca_bank_node *node;
|
||||
struct aca_bank *bank;
|
||||
int r;
|
||||
|
||||
if (!adev->cper.enabled)
|
||||
return;
|
||||
|
||||
if (!banks || !count) {
|
||||
dev_warn(adev->dev, "fail to generate cper records\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* UEs must be encoded into separate CPER entries */
|
||||
if (type == ACA_SMU_TYPE_UE) {
|
||||
struct aca_banks de_banks;
|
||||
|
||||
aca_banks_init(&de_banks);
|
||||
list_for_each_entry(node, &banks->list, node) {
|
||||
bank = &node->bank;
|
||||
if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) {
|
||||
r = aca_banks_add_bank(&de_banks, bank);
|
||||
if (r)
|
||||
dev_warn(adev->dev, "fail to add de banks, ret = %d\n", r);
|
||||
} else {
|
||||
if (amdgpu_cper_generate_ue_record(adev, bank))
|
||||
dev_warn(adev->dev, "fail to generate ue cper records\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!list_empty(&de_banks.list)) {
|
||||
if (amdgpu_cper_generate_ce_records(adev, &de_banks, de_banks.nr_banks))
|
||||
dev_warn(adev->dev, "fail to generate de cper records\n");
|
||||
}
|
||||
|
||||
aca_banks_release(&de_banks);
|
||||
} else {
|
||||
/*
|
||||
* SMU_TYPE_CE banks are combined into 1 CPER entries,
|
||||
* they could be CEs or DEs or both
|
||||
*/
|
||||
if (amdgpu_cper_generate_ce_records(adev, banks, count))
|
||||
dev_warn(adev->dev, "fail to generate ce cper records\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int aca_banks_update(struct amdgpu_device *adev, enum aca_smu_type type,
|
||||
bank_handler_t handler, struct ras_query_context *qctx, void *data)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
struct aca_banks banks;
|
||||
u32 count = 0;
|
||||
int ret;
|
||||
|
||||
if (list_empty(&aca->mgr.list))
|
||||
return 0;
|
||||
|
||||
if (!aca_bank_should_update(adev, type))
|
||||
return 0;
|
||||
|
||||
ret = aca_smu_get_valid_aca_count(adev, type, &count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
aca_banks_init(&banks);
|
||||
|
||||
ret = aca_smu_get_valid_aca_banks(adev, type, 0, count, &banks, qctx);
|
||||
if (ret)
|
||||
goto err_release_banks;
|
||||
|
||||
if (list_empty(&banks.list)) {
|
||||
ret = 0;
|
||||
goto err_release_banks;
|
||||
}
|
||||
|
||||
ret = aca_dispatch_banks(&aca->mgr, &banks, type,
|
||||
handler, data);
|
||||
if (ret)
|
||||
goto err_release_banks;
|
||||
|
||||
aca_banks_generate_cper(adev, type, &banks, count);
|
||||
|
||||
err_release_banks:
|
||||
aca_banks_release(&banks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aca_log_aca_error_data(struct aca_bank_error *bank_error, enum aca_error_type type, struct ras_err_data *err_data)
|
||||
{
|
||||
struct aca_bank_info *info;
|
||||
struct amdgpu_smuio_mcm_config_info mcm_info;
|
||||
u64 count;
|
||||
|
||||
if (type >= ACA_ERROR_TYPE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
count = bank_error->count;
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
info = &bank_error->info;
|
||||
mcm_info.die_id = info->die_id;
|
||||
mcm_info.socket_id = info->socket_id;
|
||||
|
||||
switch (type) {
|
||||
case ACA_ERROR_TYPE_UE:
|
||||
amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, count);
|
||||
break;
|
||||
case ACA_ERROR_TYPE_CE:
|
||||
amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, count);
|
||||
break;
|
||||
case ACA_ERROR_TYPE_DEFERRED:
|
||||
amdgpu_ras_error_statistic_de_count(err_data, &mcm_info, count);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aca_log_aca_error(struct aca_handle *handle, enum aca_error_type type, struct ras_err_data *err_data)
|
||||
{
|
||||
struct aca_error_cache *error_cache = &handle->error_cache;
|
||||
struct aca_error *aerr = &error_cache->errors[type];
|
||||
struct aca_bank_error *bank_error, *tmp;
|
||||
|
||||
mutex_lock(&aerr->lock);
|
||||
|
||||
if (list_empty(&aerr->list))
|
||||
goto out_unlock;
|
||||
|
||||
list_for_each_entry_safe(bank_error, tmp, &aerr->list, node) {
|
||||
aca_log_aca_error_data(bank_error, type, err_data);
|
||||
aca_bank_error_remove(aerr, bank_error);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&aerr->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle, enum aca_error_type type,
|
||||
struct ras_err_data *err_data, struct ras_query_context *qctx)
|
||||
{
|
||||
enum aca_smu_type smu_type;
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case ACA_ERROR_TYPE_UE:
|
||||
smu_type = ACA_SMU_TYPE_UE;
|
||||
break;
|
||||
case ACA_ERROR_TYPE_CE:
|
||||
case ACA_ERROR_TYPE_DEFERRED:
|
||||
smu_type = ACA_SMU_TYPE_CE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* update aca bank to aca source error_cache first */
|
||||
ret = aca_banks_update(adev, smu_type, handler_aca_log_bank_error, qctx, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* DEs may contain in CEs or UEs */
|
||||
if (type != ACA_ERROR_TYPE_DEFERRED)
|
||||
aca_log_aca_error(handle, ACA_ERROR_TYPE_DEFERRED, err_data);
|
||||
|
||||
return aca_log_aca_error(handle, type, err_data);
|
||||
}
|
||||
|
||||
static bool aca_handle_is_valid(struct aca_handle *handle)
|
||||
{
|
||||
if (!handle->mask || !list_empty(&handle->node))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle,
|
||||
enum aca_error_type type, struct ras_err_data *err_data,
|
||||
struct ras_query_context *qctx)
|
||||
{
|
||||
if (!handle || !err_data)
|
||||
return -EINVAL;
|
||||
|
||||
if (aca_handle_is_valid(handle))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((type < 0) || (!(BIT(type) & handle->mask)))
|
||||
return 0;
|
||||
|
||||
return __aca_get_error_data(adev, handle, type, err_data, qctx);
|
||||
}
|
||||
|
||||
static void aca_error_init(struct aca_error *aerr, enum aca_error_type type)
|
||||
{
|
||||
mutex_init(&aerr->lock);
|
||||
INIT_LIST_HEAD(&aerr->list);
|
||||
aerr->type = type;
|
||||
aerr->nr_errors = 0;
|
||||
}
|
||||
|
||||
static void aca_init_error_cache(struct aca_handle *handle)
|
||||
{
|
||||
struct aca_error_cache *error_cache = &handle->error_cache;
|
||||
int type;
|
||||
|
||||
for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++)
|
||||
aca_error_init(&error_cache->errors[type], type);
|
||||
}
|
||||
|
||||
static void aca_error_fini(struct aca_error *aerr)
|
||||
{
|
||||
struct aca_bank_error *bank_error, *tmp;
|
||||
|
||||
mutex_lock(&aerr->lock);
|
||||
if (list_empty(&aerr->list))
|
||||
goto out_unlock;
|
||||
|
||||
list_for_each_entry_safe(bank_error, tmp, &aerr->list, node)
|
||||
aca_bank_error_remove(aerr, bank_error);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&aerr->lock);
|
||||
mutex_destroy(&aerr->lock);
|
||||
}
|
||||
|
||||
static void aca_fini_error_cache(struct aca_handle *handle)
|
||||
{
|
||||
struct aca_error_cache *error_cache = &handle->error_cache;
|
||||
int type;
|
||||
|
||||
for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++)
|
||||
aca_error_fini(&error_cache->errors[type]);
|
||||
}
|
||||
|
||||
static int add_aca_handle(struct amdgpu_device *adev, struct aca_handle_manager *mgr, struct aca_handle *handle,
|
||||
const char *name, const struct aca_info *ras_info, void *data)
|
||||
{
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
|
||||
handle->adev = adev;
|
||||
handle->mgr = mgr;
|
||||
handle->name = name;
|
||||
handle->hwip = ras_info->hwip;
|
||||
handle->mask = ras_info->mask;
|
||||
handle->bank_ops = ras_info->bank_ops;
|
||||
handle->data = data;
|
||||
aca_init_error_cache(handle);
|
||||
|
||||
INIT_LIST_HEAD(&handle->node);
|
||||
list_add_tail(&handle->node, &mgr->list);
|
||||
mgr->nr_handles++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t aca_sysfs_read(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct aca_handle *handle = container_of(attr, struct aca_handle, aca_attr);
|
||||
|
||||
/* NOTE: the aca cache will be auto cleared once read,
|
||||
* So the driver should unify the query entry point, forward request to ras query interface directly */
|
||||
return amdgpu_ras_aca_sysfs_read(dev, attr, handle, buf, handle->data);
|
||||
}
|
||||
|
||||
static int add_aca_sysfs(struct amdgpu_device *adev, struct aca_handle *handle)
|
||||
{
|
||||
struct device_attribute *aca_attr = &handle->aca_attr;
|
||||
|
||||
snprintf(handle->attr_name, sizeof(handle->attr_name) - 1, "aca_%s", handle->name);
|
||||
aca_attr->show = aca_sysfs_read;
|
||||
aca_attr->attr.name = handle->attr_name;
|
||||
aca_attr->attr.mode = S_IRUGO;
|
||||
sysfs_attr_init(&aca_attr->attr);
|
||||
|
||||
return sysfs_add_file_to_group(&adev->dev->kobj,
|
||||
&aca_attr->attr,
|
||||
"ras");
|
||||
}
|
||||
|
||||
int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle,
|
||||
const char *name, const struct aca_info *ras_info, void *data)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
int ret;
|
||||
|
||||
if (!amdgpu_aca_is_enabled(adev))
|
||||
return 0;
|
||||
|
||||
ret = add_aca_handle(adev, &aca->mgr, handle, name, ras_info, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return add_aca_sysfs(adev, handle);
|
||||
}
|
||||
|
||||
static void remove_aca_handle(struct aca_handle *handle)
|
||||
{
|
||||
struct aca_handle_manager *mgr = handle->mgr;
|
||||
|
||||
aca_fini_error_cache(handle);
|
||||
list_del(&handle->node);
|
||||
mgr->nr_handles--;
|
||||
}
|
||||
|
||||
static void remove_aca_sysfs(struct aca_handle *handle)
|
||||
{
|
||||
struct amdgpu_device *adev = handle->adev;
|
||||
struct device_attribute *aca_attr = &handle->aca_attr;
|
||||
|
||||
if (adev->dev->kobj.sd)
|
||||
sysfs_remove_file_from_group(&adev->dev->kobj,
|
||||
&aca_attr->attr,
|
||||
"ras");
|
||||
}
|
||||
|
||||
void amdgpu_aca_remove_handle(struct aca_handle *handle)
|
||||
{
|
||||
if (!handle || list_empty(&handle->node))
|
||||
return;
|
||||
|
||||
remove_aca_sysfs(handle);
|
||||
remove_aca_handle(handle);
|
||||
}
|
||||
|
||||
static int aca_manager_init(struct aca_handle_manager *mgr)
|
||||
{
|
||||
INIT_LIST_HEAD(&mgr->list);
|
||||
mgr->nr_handles = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aca_manager_fini(struct aca_handle_manager *mgr)
|
||||
{
|
||||
struct aca_handle *handle, *tmp;
|
||||
|
||||
if (list_empty(&mgr->list))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(handle, tmp, &mgr->list, node)
|
||||
amdgpu_aca_remove_handle(handle);
|
||||
}
|
||||
|
||||
bool amdgpu_aca_is_enabled(struct amdgpu_device *adev)
|
||||
{
|
||||
return (adev->aca.is_enabled ||
|
||||
adev->debug_enable_ras_aca);
|
||||
}
|
||||
|
||||
int amdgpu_aca_init(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
int ret;
|
||||
|
||||
atomic_set(&aca->ue_update_flag, 0);
|
||||
|
||||
ret = aca_manager_init(&aca->mgr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amdgpu_aca_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
|
||||
aca_manager_fini(&aca->mgr);
|
||||
|
||||
atomic_set(&aca->ue_update_flag, 0);
|
||||
}
|
||||
|
||||
int amdgpu_aca_reset(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
|
||||
atomic_set(&aca->ue_update_flag, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
|
||||
WARN_ON(aca->smu_funcs);
|
||||
aca->smu_funcs = smu_funcs;
|
||||
}
|
||||
|
||||
int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info)
|
||||
{
|
||||
u64 ipid;
|
||||
u32 instidhi, instidlo;
|
||||
|
||||
if (!bank || !info)
|
||||
return -EINVAL;
|
||||
|
||||
ipid = bank->regs[ACA_REG_IDX_IPID];
|
||||
info->hwid = ACA_REG__IPID__HARDWAREID(ipid);
|
||||
info->mcatype = ACA_REG__IPID__MCATYPE(ipid);
|
||||
/*
|
||||
* Unfied DieID Format: SAASS. A:AID, S:Socket.
|
||||
* Unfied DieID[4:4] = InstanceId[0:0]
|
||||
* Unfied DieID[0:3] = InstanceIdHi[0:3]
|
||||
*/
|
||||
instidhi = ACA_REG__IPID__INSTANCEIDHI(ipid);
|
||||
instidlo = ACA_REG__IPID__INSTANCEIDLO(ipid);
|
||||
info->die_id = ((instidhi >> 2) & 0x03);
|
||||
info->socket_id = ((instidlo & 0x1) << 2) | (instidhi & 0x03);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aca_bank_get_error_code(struct amdgpu_device *adev, struct aca_bank *bank)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
|
||||
|
||||
if (!smu_funcs || !smu_funcs->parse_error_code)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return smu_funcs->parse_error_code(adev, bank);
|
||||
}
|
||||
|
||||
int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size)
|
||||
{
|
||||
int i, error_code;
|
||||
|
||||
if (!bank || !err_codes)
|
||||
return -EINVAL;
|
||||
|
||||
error_code = aca_bank_get_error_code(adev, bank);
|
||||
if (error_code < 0)
|
||||
return error_code;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (err_codes[i] == error_code)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en)
|
||||
{
|
||||
struct amdgpu_aca *aca = &adev->aca;
|
||||
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
|
||||
|
||||
if (!smu_funcs || !smu_funcs->set_debug_mode)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return smu_funcs->set_debug_mode(adev, en);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
static int amdgpu_aca_smu_debug_mode_set(void *data, u64 val)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)data;
|
||||
int ret;
|
||||
|
||||
ret = amdgpu_ras_set_aca_debug_mode(adev, val ? true : false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(adev->dev, "amdgpu set smu aca debug mode %s success\n", val ? "on" : "off");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aca_dump_entry(struct seq_file *m, struct aca_bank *bank, enum aca_smu_type type, int idx)
|
||||
{
|
||||
struct aca_bank_info info;
|
||||
int i, ret;
|
||||
|
||||
ret = aca_bank_info_decode(bank, &info);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
seq_printf(m, "aca entry[%d].type: %s\n", idx, type == ACA_SMU_TYPE_UE ? "UE" : "CE");
|
||||
seq_printf(m, "aca entry[%d].info: socketid:%d aid:%d hwid:0x%03x mcatype:0x%04x\n",
|
||||
idx, info.socket_id, info.die_id, info.hwid, info.mcatype);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
|
||||
seq_printf(m, "aca entry[%d].regs[%d]: 0x%016llx\n", idx, aca_regs[i].reg_idx, bank->regs[aca_regs[i].reg_idx]);
|
||||
}
|
||||
|
||||
struct aca_dump_context {
|
||||
struct seq_file *m;
|
||||
int idx;
|
||||
};
|
||||
|
||||
static int handler_aca_bank_dump(struct aca_handle *handle, struct aca_bank *bank,
|
||||
enum aca_smu_type type, void *data)
|
||||
{
|
||||
struct aca_dump_context *ctx = (struct aca_dump_context *)data;
|
||||
|
||||
aca_dump_entry(ctx->m, bank, type, ctx->idx++);
|
||||
|
||||
return handler_aca_log_bank_error(handle, bank, type, NULL);
|
||||
}
|
||||
|
||||
static int aca_dump_show(struct seq_file *m, enum aca_smu_type type)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
|
||||
struct aca_dump_context context = {
|
||||
.m = m,
|
||||
.idx = 0,
|
||||
};
|
||||
|
||||
return aca_banks_update(adev, type, handler_aca_bank_dump, NULL, (void *)&context);
|
||||
}
|
||||
|
||||
static int aca_dump_ce_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
return aca_dump_show(m, ACA_SMU_TYPE_CE);
|
||||
}
|
||||
|
||||
static int aca_dump_ce_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, aca_dump_ce_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations aca_ce_dump_debug_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = aca_dump_ce_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int aca_dump_ue_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
return aca_dump_show(m, ACA_SMU_TYPE_UE);
|
||||
}
|
||||
|
||||
static int aca_dump_ue_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, aca_dump_ue_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations aca_ue_dump_debug_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = aca_dump_ue_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(aca_debug_mode_fops, NULL, amdgpu_aca_smu_debug_mode_set, "%llu\n");
|
||||
#endif
|
||||
|
||||
void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root)
|
||||
{
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
debugfs_create_file("aca_debug_mode", 0200, root, adev, &aca_debug_mode_fops);
|
||||
debugfs_create_file("aca_ue_dump", 0400, root, adev, &aca_ue_dump_debug_fops);
|
||||
debugfs_create_file("aca_ce_dump", 0400, root, adev, &aca_ce_dump_debug_fops);
|
||||
#endif
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AMDGPU_ACA_H__
|
||||
#define __AMDGPU_ACA_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
struct ras_err_data;
|
||||
struct ras_query_context;
|
||||
|
||||
#define ACA_MAX_REGS_COUNT (16)
|
||||
|
||||
#define ACA_REG_FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l)
|
||||
#define ACA_REG__STATUS__VAL(x) ACA_REG_FIELD(x, 63, 63)
|
||||
#define ACA_REG__STATUS__OVERFLOW(x) ACA_REG_FIELD(x, 62, 62)
|
||||
#define ACA_REG__STATUS__UC(x) ACA_REG_FIELD(x, 61, 61)
|
||||
#define ACA_REG__STATUS__EN(x) ACA_REG_FIELD(x, 60, 60)
|
||||
#define ACA_REG__STATUS__MISCV(x) ACA_REG_FIELD(x, 59, 59)
|
||||
#define ACA_REG__STATUS__ADDRV(x) ACA_REG_FIELD(x, 58, 58)
|
||||
#define ACA_REG__STATUS__PCC(x) ACA_REG_FIELD(x, 57, 57)
|
||||
#define ACA_REG__STATUS__ERRCOREIDVAL(x) ACA_REG_FIELD(x, 56, 56)
|
||||
#define ACA_REG__STATUS__TCC(x) ACA_REG_FIELD(x, 55, 55)
|
||||
#define ACA_REG__STATUS__SYNDV(x) ACA_REG_FIELD(x, 53, 53)
|
||||
#define ACA_REG__STATUS__CECC(x) ACA_REG_FIELD(x, 46, 46)
|
||||
#define ACA_REG__STATUS__UECC(x) ACA_REG_FIELD(x, 45, 45)
|
||||
#define ACA_REG__STATUS__DEFERRED(x) ACA_REG_FIELD(x, 44, 44)
|
||||
#define ACA_REG__STATUS__POISON(x) ACA_REG_FIELD(x, 43, 43)
|
||||
#define ACA_REG__STATUS__SCRUB(x) ACA_REG_FIELD(x, 40, 40)
|
||||
#define ACA_REG__STATUS__ERRCOREID(x) ACA_REG_FIELD(x, 37, 32)
|
||||
#define ACA_REG__STATUS__ADDRLSB(x) ACA_REG_FIELD(x, 29, 24)
|
||||
#define ACA_REG__STATUS__ERRORCODEEXT(x) ACA_REG_FIELD(x, 21, 16)
|
||||
#define ACA_REG__STATUS__ERRORCODE(x) ACA_REG_FIELD(x, 15, 0)
|
||||
|
||||
#define ACA_REG__IPID__MCATYPE(x) ACA_REG_FIELD(x, 63, 48)
|
||||
#define ACA_REG__IPID__INSTANCEIDHI(x) ACA_REG_FIELD(x, 47, 44)
|
||||
#define ACA_REG__IPID__HARDWAREID(x) ACA_REG_FIELD(x, 43, 32)
|
||||
#define ACA_REG__IPID__INSTANCEIDLO(x) ACA_REG_FIELD(x, 31, 0)
|
||||
|
||||
#define ACA_REG__MISC0__VALID(x) ACA_REG_FIELD(x, 63, 63)
|
||||
#define ACA_REG__MISC0__OVRFLW(x) ACA_REG_FIELD(x, 48, 48)
|
||||
#define ACA_REG__MISC0__ERRCNT(x) ACA_REG_FIELD(x, 43, 32)
|
||||
|
||||
#define ACA_REG__SYND__ERRORINFORMATION(x) ACA_REG_FIELD(x, 17, 0)
|
||||
|
||||
/* NOTE: The following codes refers to the smu header file */
|
||||
#define ACA_EXTERROR_CODE_CE 0x3a
|
||||
#define ACA_EXTERROR_CODE_FAULT 0x3b
|
||||
|
||||
#define ACA_ERROR_UE_MASK BIT_MASK(ACA_ERROR_TYPE_UE)
|
||||
#define ACA_ERROR_CE_MASK BIT_MASK(ACA_ERROR_TYPE_CE)
|
||||
#define ACA_ERROR_DEFERRED_MASK BIT_MASK(ACA_ERROR_TYPE_DEFERRED)
|
||||
|
||||
#define mmSMNAID_AID0_MCA_SMU 0x03b30400 /* SMN AID AID0 */
|
||||
#define mmSMNAID_XCD0_MCA_SMU 0x36430400 /* SMN AID XCD0 */
|
||||
#define mmSMNAID_XCD1_MCA_SMU 0x38430400 /* SMN AID XCD1 */
|
||||
#define mmSMNXCD_XCD0_MCA_SMU 0x40430400 /* SMN XCD XCD0 */
|
||||
|
||||
#define ACA_BANK_ERR_IS_DEFFERED(bank) \
|
||||
(ACA_REG__STATUS__POISON((bank)->regs[ACA_REG_IDX_STATUS]) || \
|
||||
ACA_REG__STATUS__DEFERRED((bank)->regs[ACA_REG_IDX_STATUS]))
|
||||
|
||||
enum aca_reg_idx {
|
||||
ACA_REG_IDX_CTL = 0,
|
||||
ACA_REG_IDX_STATUS = 1,
|
||||
ACA_REG_IDX_ADDR = 2,
|
||||
ACA_REG_IDX_MISC0 = 3,
|
||||
ACA_REG_IDX_CONFIG = 4,
|
||||
ACA_REG_IDX_IPID = 5,
|
||||
ACA_REG_IDX_SYND = 6,
|
||||
ACA_REG_IDX_DESTAT = 8,
|
||||
ACA_REG_IDX_DEADDR = 9,
|
||||
ACA_REG_IDX_CTL_MASK = 10,
|
||||
ACA_REG_IDX_COUNT = 16,
|
||||
};
|
||||
|
||||
enum aca_hwip_type {
|
||||
ACA_HWIP_TYPE_UNKNOW = -1,
|
||||
ACA_HWIP_TYPE_PSP = 0,
|
||||
ACA_HWIP_TYPE_UMC,
|
||||
ACA_HWIP_TYPE_SMU,
|
||||
ACA_HWIP_TYPE_PCS_XGMI,
|
||||
ACA_HWIP_TYPE_COUNT,
|
||||
};
|
||||
|
||||
enum aca_error_type {
|
||||
ACA_ERROR_TYPE_INVALID = -1,
|
||||
ACA_ERROR_TYPE_UE = 0,
|
||||
ACA_ERROR_TYPE_CE,
|
||||
ACA_ERROR_TYPE_DEFERRED,
|
||||
ACA_ERROR_TYPE_COUNT
|
||||
};
|
||||
|
||||
enum aca_smu_type {
|
||||
ACA_SMU_TYPE_INVALID = -1,
|
||||
ACA_SMU_TYPE_UE = 0,
|
||||
ACA_SMU_TYPE_CE,
|
||||
ACA_SMU_TYPE_COUNT,
|
||||
};
|
||||
|
||||
struct aca_hwip {
|
||||
int hwid;
|
||||
int mcatype;
|
||||
};
|
||||
|
||||
struct aca_bank {
|
||||
enum aca_error_type aca_err_type;
|
||||
enum aca_smu_type smu_err_type;
|
||||
u64 regs[ACA_MAX_REGS_COUNT];
|
||||
};
|
||||
|
||||
struct aca_bank_node {
|
||||
struct aca_bank bank;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct aca_banks {
|
||||
int nr_banks;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct aca_bank_info {
|
||||
int die_id;
|
||||
int socket_id;
|
||||
int hwid;
|
||||
int mcatype;
|
||||
};
|
||||
|
||||
struct aca_bank_error {
|
||||
struct list_head node;
|
||||
struct aca_bank_info info;
|
||||
u64 count;
|
||||
};
|
||||
|
||||
struct aca_error {
|
||||
struct list_head list;
|
||||
struct mutex lock;
|
||||
enum aca_error_type type;
|
||||
int nr_errors;
|
||||
};
|
||||
|
||||
struct aca_handle_manager {
|
||||
struct list_head list;
|
||||
int nr_handles;
|
||||
};
|
||||
|
||||
struct aca_error_cache {
|
||||
struct aca_error errors[ACA_ERROR_TYPE_COUNT];
|
||||
};
|
||||
|
||||
struct aca_handle {
|
||||
struct list_head node;
|
||||
enum aca_hwip_type hwip;
|
||||
struct amdgpu_device *adev;
|
||||
struct aca_handle_manager *mgr;
|
||||
struct aca_error_cache error_cache;
|
||||
const struct aca_bank_ops *bank_ops;
|
||||
struct device_attribute aca_attr;
|
||||
char attr_name[64];
|
||||
const char *name;
|
||||
u32 mask;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct aca_bank_ops {
|
||||
int (*aca_bank_parser)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data);
|
||||
bool (*aca_bank_is_valid)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type,
|
||||
void *data);
|
||||
};
|
||||
|
||||
struct aca_smu_funcs {
|
||||
int max_ue_bank_count;
|
||||
int max_ce_bank_count;
|
||||
int (*set_debug_mode)(struct amdgpu_device *adev, bool enable);
|
||||
int (*get_valid_aca_count)(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count);
|
||||
int (*get_valid_aca_bank)(struct amdgpu_device *adev, enum aca_smu_type type, int idx, struct aca_bank *bank);
|
||||
int (*parse_error_code)(struct amdgpu_device *adev, struct aca_bank *bank);
|
||||
};
|
||||
|
||||
struct amdgpu_aca {
|
||||
struct aca_handle_manager mgr;
|
||||
const struct aca_smu_funcs *smu_funcs;
|
||||
atomic_t ue_update_flag;
|
||||
bool is_enabled;
|
||||
};
|
||||
|
||||
struct aca_info {
|
||||
enum aca_hwip_type hwip;
|
||||
const struct aca_bank_ops *bank_ops;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
int amdgpu_aca_init(struct amdgpu_device *adev);
|
||||
void amdgpu_aca_fini(struct amdgpu_device *adev);
|
||||
int amdgpu_aca_reset(struct amdgpu_device *adev);
|
||||
void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs);
|
||||
bool amdgpu_aca_is_enabled(struct amdgpu_device *adev);
|
||||
|
||||
int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info);
|
||||
int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size);
|
||||
|
||||
int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle,
|
||||
const char *name, const struct aca_info *aca_info, void *data);
|
||||
void amdgpu_aca_remove_handle(struct aca_handle *handle);
|
||||
int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle,
|
||||
enum aca_error_type type, struct ras_err_data *err_data,
|
||||
struct ras_query_context *qctx);
|
||||
int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en);
|
||||
void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root);
|
||||
int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info,
|
||||
enum aca_error_type type, u64 count);
|
||||
#endif
|
||||
@@ -140,13 +140,15 @@ static struct amdgpu_acpi_priv {
|
||||
* @atif: atif structure
|
||||
* @function: the ATIF function to execute
|
||||
* @params: ATIF function params
|
||||
* @min_size: minimum size of the expected output buffer in bytes
|
||||
*
|
||||
* Executes the requested ATIF function (all asics).
|
||||
* Returns a pointer to the acpi output buffer.
|
||||
*/
|
||||
static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif,
|
||||
int function,
|
||||
struct acpi_buffer *params)
|
||||
struct acpi_buffer *params,
|
||||
size_t min_size)
|
||||
{
|
||||
acpi_status status;
|
||||
union acpi_object *obj;
|
||||
@@ -189,6 +191,28 @@ static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (obj->buffer.length < sizeof(u16)) {
|
||||
DRM_DEBUG_DRIVER("ATIF buffer too small to hold size field: %u\n",
|
||||
obj->buffer.length);
|
||||
kfree(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (obj->buffer.length < *(u16 *)obj->buffer.pointer) {
|
||||
DRM_DEBUG_DRIVER("ATIF buffer length mismatch: reported %u, actual %u\n",
|
||||
*(u16 *)obj->buffer.pointer,
|
||||
obj->buffer.length);
|
||||
kfree(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*(u16 *)obj->buffer.pointer < min_size) {
|
||||
DRM_DEBUG_DRIVER("ATIF buffer too small: expected %zu, got %u\n",
|
||||
min_size, *(u16 *)obj->buffer.pointer);
|
||||
kfree(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -251,19 +275,14 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif)
|
||||
size_t size;
|
||||
int err = 0;
|
||||
|
||||
info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL);
|
||||
info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL,
|
||||
sizeof(output));
|
||||
if (!info)
|
||||
return -EIO;
|
||||
|
||||
memset(&output, 0, sizeof(output));
|
||||
|
||||
size = *(u16 *) info->buffer.pointer;
|
||||
if (size < 12) {
|
||||
DRM_INFO("ATIF buffer is too small: %zu\n", size);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
size = min(sizeof(output), size);
|
||||
size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
|
||||
|
||||
memcpy(&output, info->buffer.pointer, size);
|
||||
|
||||
@@ -273,7 +292,6 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif)
|
||||
amdgpu_atif_parse_notification(&atif->notifications, output.notification_mask);
|
||||
amdgpu_atif_parse_functions(&atif->functions, output.function_bits);
|
||||
|
||||
out:
|
||||
kfree(info);
|
||||
return err;
|
||||
}
|
||||
@@ -299,20 +317,14 @@ int amdgpu_atif_get_notification_params(struct amdgpu_atif *atif)
|
||||
int err = 0;
|
||||
|
||||
info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS,
|
||||
NULL);
|
||||
NULL, offsetof(struct atif_system_params, command_code));
|
||||
if (!info) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = *(u16 *) info->buffer.pointer;
|
||||
if (size < 10) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
size = min(sizeof(params), size);
|
||||
size = min(sizeof(params), (size_t)*(u16 *)info->buffer.pointer);
|
||||
memcpy(¶ms, info->buffer.pointer, size);
|
||||
|
||||
DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n",
|
||||
@@ -376,20 +388,14 @@ int amdgpu_atif_query_backlight_caps(struct amdgpu_atif *atif)
|
||||
|
||||
info = amdgpu_atif_call(atif,
|
||||
ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS,
|
||||
¶ms);
|
||||
¶ms, offsetof(struct atif_qbtc_output, data_points));
|
||||
if (!info) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = *(u16 *) info->buffer.pointer;
|
||||
if (size < 10) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&characteristics, 0, sizeof(characteristics));
|
||||
size = min(sizeof(characteristics), size);
|
||||
size = min(sizeof(characteristics), (size_t)*(u16 *)info->buffer.pointer);
|
||||
memcpy(&characteristics, info->buffer.pointer, size);
|
||||
|
||||
atif->backlight_caps.caps_valid = true;
|
||||
@@ -427,24 +433,18 @@ static int amdgpu_atif_get_sbios_requests(struct amdgpu_atif *atif,
|
||||
int count = 0;
|
||||
|
||||
info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS,
|
||||
NULL);
|
||||
NULL, sizeof(*req));
|
||||
if (!info)
|
||||
return -EIO;
|
||||
|
||||
size = *(u16 *)info->buffer.pointer;
|
||||
if (size < 0xd) {
|
||||
count = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memset(req, 0, sizeof(*req));
|
||||
|
||||
size = min(sizeof(*req), size);
|
||||
size = min(sizeof(*req), (size_t)*(u16 *)info->buffer.pointer);
|
||||
memcpy(req, info->buffer.pointer, size);
|
||||
DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending);
|
||||
|
||||
count = hweight32(req->pending);
|
||||
|
||||
out:
|
||||
kfree(info);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
*
|
||||
* Copyright 2026 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef __AMDGPU_ACPI_H__
|
||||
#define __AMDGPU_ACPI_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex_types.h>
|
||||
|
||||
struct amdgpu_device;
|
||||
struct acpi_device;
|
||||
struct amdgpu_dm_backlight_caps;
|
||||
|
||||
#define MAX_UMA_OPTION_NAME 28
|
||||
#define MAX_UMA_OPTION_ENTRIES 19
|
||||
|
||||
#define AMDGPU_UMA_FLAG_AUTO BIT(1)
|
||||
#define AMDGPU_UMA_FLAG_CUSTOM BIT(0)
|
||||
|
||||
/* ATCS Device/Driver State */
|
||||
#define AMDGPU_ATCS_PSC_DEV_STATE_D0 0
|
||||
#define AMDGPU_ATCS_PSC_DEV_STATE_D3_HOT 3
|
||||
#define AMDGPU_ATCS_PSC_DRV_STATE_OPR 0
|
||||
#define AMDGPU_ATCS_PSC_DRV_STATE_NOT_OPR 1
|
||||
|
||||
enum amdgpu_ss {
|
||||
AMDGPU_SS_DRV_LOAD,
|
||||
AMDGPU_SS_DEV_D0,
|
||||
AMDGPU_SS_DEV_D3,
|
||||
AMDGPU_SS_DRV_UNLOAD
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdgpu_uma_carveout_option - single UMA carveout option
|
||||
* @name: Name of the carveout option
|
||||
* @memory_carved_mb: Amount of memory carved in MB
|
||||
* @flags: ATCS flags supported by this option
|
||||
*/
|
||||
struct amdgpu_uma_carveout_option {
|
||||
char name[MAX_UMA_OPTION_NAME];
|
||||
uint32_t memory_carved_mb;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdgpu_uma_carveout_info - table of available UMA carveout options
|
||||
* @num_entries: Number of available options
|
||||
* @uma_option_index: The index of the option currently applied
|
||||
* @update_lock: Lock to serialize changes to the option
|
||||
* @entries: The array of carveout options
|
||||
*/
|
||||
struct amdgpu_uma_carveout_info {
|
||||
uint8_t num_entries;
|
||||
uint8_t uma_option_index;
|
||||
struct mutex update_lock;
|
||||
struct amdgpu_uma_carveout_option entries[MAX_UMA_OPTION_ENTRIES];
|
||||
};
|
||||
|
||||
struct amdgpu_numa_info {
|
||||
uint64_t size;
|
||||
int pxm;
|
||||
int nid;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ACPI)
|
||||
int amdgpu_acpi_init(struct amdgpu_device *adev);
|
||||
void amdgpu_acpi_fini(struct amdgpu_device *adev);
|
||||
bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev);
|
||||
bool amdgpu_acpi_is_power_shift_control_supported(void);
|
||||
bool amdgpu_acpi_is_set_uma_allocation_size_supported(void);
|
||||
int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev,
|
||||
u8 perf_req, bool advertise);
|
||||
int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
|
||||
u8 dev_state, bool drv_state);
|
||||
int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
|
||||
enum amdgpu_ss ss_state);
|
||||
int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type);
|
||||
int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev);
|
||||
int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev, u64 *tmr_offset,
|
||||
u64 *tmr_size);
|
||||
int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev, int xcc_id,
|
||||
struct amdgpu_numa_info *numa_info);
|
||||
|
||||
void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps);
|
||||
bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev);
|
||||
void amdgpu_acpi_detect(void);
|
||||
void amdgpu_acpi_release(void);
|
||||
#else
|
||||
static inline int amdgpu_acpi_init(struct amdgpu_device *adev) { return 0; }
|
||||
static inline int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev,
|
||||
u64 *tmr_offset, u64 *tmr_size)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev,
|
||||
int xcc_id,
|
||||
struct amdgpu_numa_info *numa_info)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { }
|
||||
static inline bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev) { return false; }
|
||||
static inline void amdgpu_acpi_detect(void) { }
|
||||
static inline void amdgpu_acpi_release(void) { }
|
||||
static inline bool amdgpu_acpi_is_power_shift_control_supported(void) { return false; }
|
||||
static inline bool amdgpu_acpi_is_set_uma_allocation_size_supported(void) { return false; }
|
||||
static inline int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
|
||||
u8 dev_state, bool drv_state) { return 0; }
|
||||
static inline int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
|
||||
enum amdgpu_ss ss_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
|
||||
bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev);
|
||||
bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev);
|
||||
#else
|
||||
static inline bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev) { return false; }
|
||||
static inline bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev) { return false; }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_DRM_AMD_ISP)
|
||||
int amdgpu_acpi_get_isp4_dev(struct acpi_device **dev);
|
||||
#endif
|
||||
#endif /* __AMDGPU_ACPI_H__ */
|
||||
@@ -39,6 +39,7 @@
|
||||
#if IS_ENABLED(CONFIG_HSA_AMD)
|
||||
#include "kfd_priv.h"
|
||||
#endif
|
||||
#include "kfd_svm.h"
|
||||
|
||||
/* Total memory size in system memory and all GPU VRAM. Used to
|
||||
* estimate worst case amount of memory to reserve for page tables
|
||||
@@ -711,7 +712,8 @@ int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev,
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, &job, 0);
|
||||
ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, 0, GFP_KERNEL,
|
||||
&job);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@@ -957,3 +959,17 @@ int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id,
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Reset an MES queue */
|
||||
int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev,
|
||||
uint32_t node_id,
|
||||
int queue_type,
|
||||
int pipe, int queue,
|
||||
unsigned int db)
|
||||
{
|
||||
if (!adev->kfd.init_complete)
|
||||
return 0;
|
||||
|
||||
return kgd2kfd_reset_mes_queue(adev->kfd.dev, node_id, queue_type,
|
||||
pipe, queue, db);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,6 @@ struct amdgpu_amdkfd_fence {
|
||||
struct mm_struct *mm;
|
||||
spinlock_t lock;
|
||||
char timeline_name[TASK_COMM_LEN];
|
||||
struct svm_range_bo *svm_bo;
|
||||
uint16_t context_id;
|
||||
};
|
||||
|
||||
@@ -194,7 +193,6 @@ int amdgpu_queue_mask_bit_to_set_resource_bit(struct amdgpu_device *adev,
|
||||
|
||||
struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
|
||||
struct mm_struct *mm,
|
||||
struct svm_range_bo *svm_bo,
|
||||
u16 context_id);
|
||||
|
||||
int amdgpu_amdkfd_drm_client_create(struct amdgpu_device *adev);
|
||||
@@ -210,6 +208,7 @@ int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni,
|
||||
int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo,
|
||||
uint32_t domain,
|
||||
struct dma_fence *fence);
|
||||
int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms);
|
||||
#else
|
||||
static inline
|
||||
bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
|
||||
@@ -241,6 +240,11 @@ int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline
|
||||
int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
/* Shared API */
|
||||
int amdgpu_amdkfd_alloc_kernel_mem(struct amdgpu_device *adev, size_t size,
|
||||
@@ -275,7 +279,11 @@ int amdgpu_amdkfd_stop_sched(struct amdgpu_device *adev, uint32_t node_id);
|
||||
int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id,
|
||||
bool core_override_enable, bool reg_override_enable, bool perfmon_override_enable);
|
||||
bool amdgpu_amdkfd_compute_active(struct amdgpu_device *adev, uint32_t node_id);
|
||||
|
||||
int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev,
|
||||
uint32_t node_id,
|
||||
int queue_type,
|
||||
int pipe, int queue,
|
||||
unsigned int db);
|
||||
|
||||
/* Read user wptr from a specified user address space with page fault
|
||||
* disabled. The memory must be pinned and mapped to the hardware when
|
||||
@@ -326,9 +334,9 @@ int amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu(
|
||||
int amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv);
|
||||
int amdgpu_amdkfd_gpuvm_sync_memory(
|
||||
struct amdgpu_device *adev, struct kgd_mem *mem, bool intr);
|
||||
int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
|
||||
void **kptr, uint64_t *size);
|
||||
void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem);
|
||||
int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr,
|
||||
u64 *size, u32 domain);
|
||||
void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem);
|
||||
|
||||
int amdgpu_amdkfd_map_gtt_bo_to_gart(struct amdgpu_bo *bo, struct amdgpu_bo **bo_gart);
|
||||
|
||||
@@ -446,6 +454,9 @@ bool kgd2kfd_vmfault_fast_path(struct amdgpu_device *adev, struct amdgpu_iv_entr
|
||||
bool retry_fault);
|
||||
void kgd2kfd_lock_kfd(void);
|
||||
void kgd2kfd_teardown_processes(struct amdgpu_device *adev);
|
||||
int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
|
||||
int queue_type, int pipe, int queue,
|
||||
unsigned int db);
|
||||
|
||||
#else
|
||||
static inline int kgd2kfd_init(void)
|
||||
@@ -576,5 +587,12 @@ static inline void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
|
||||
int queue_type, int pipe, int queue,
|
||||
unsigned int db)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* AMDGPU_AMDKFD_H_INCLUDED */
|
||||
|
||||
@@ -62,7 +62,6 @@ static atomic_t fence_seq = ATOMIC_INIT(0);
|
||||
|
||||
struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
|
||||
struct mm_struct *mm,
|
||||
struct svm_range_bo *svm_bo,
|
||||
u16 context_id)
|
||||
{
|
||||
struct amdgpu_amdkfd_fence *fence;
|
||||
@@ -76,7 +75,6 @@ struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
|
||||
fence->mm = mm;
|
||||
get_task_comm(fence->timeline_name, current);
|
||||
spin_lock_init(&fence->lock);
|
||||
fence->svm_bo = svm_bo;
|
||||
fence->context_id = context_id;
|
||||
dma_fence_init(&fence->base, &amdkfd_fence_ops, &fence->lock,
|
||||
context, atomic_inc_return(&fence_seq));
|
||||
@@ -128,17 +126,8 @@ static bool amdkfd_fence_enable_signaling(struct dma_fence *f)
|
||||
if (dma_fence_is_signaled(f))
|
||||
return true;
|
||||
|
||||
/* if fence->svm_bo is NULL, means this fence is created through
|
||||
* init_kfd_vm() or amdgpu_amdkfd_gpuvm_restore_process_bos().
|
||||
* Therefore, this fence is amdgpu_amdkfd_fence->eviction_fence.
|
||||
*/
|
||||
if (!fence->svm_bo) {
|
||||
if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f))
|
||||
return true;
|
||||
} else {
|
||||
if (!svm_range_schedule_evict_svm_bo(fence))
|
||||
return true;
|
||||
}
|
||||
if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -172,7 +161,6 @@ static void amdkfd_fence_release(struct dma_fence *f)
|
||||
*
|
||||
* Check if @mm is same as that of the fence @f, if same return TRUE else
|
||||
* return FALSE.
|
||||
* For svm bo, which support vram overcommitment, always return FALSE.
|
||||
*/
|
||||
bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
|
||||
{
|
||||
@@ -180,7 +168,7 @@ bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
|
||||
|
||||
if (!fence)
|
||||
return false;
|
||||
else if (fence->mm == mm && !fence->svm_bo)
|
||||
else if (fence->mm == mm)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@@ -530,6 +530,66 @@ static uint32_t kgd_v9_4_3_ptl_ctrl(struct amdgpu_device *adev,
|
||||
ptl_state, fmt1, fmt2);
|
||||
}
|
||||
|
||||
static int kgd_gfx_v9_4_3_hqd_sdma_get_counter(struct amdgpu_device *adev,
|
||||
void *mqd, uint32_t num_sdma_queues_per_eng,
|
||||
uint64_t *val)
|
||||
{
|
||||
struct v9_sdma_mqd *m = get_sdma_mqd(mqd);
|
||||
uint32_t sdma_rlc_reg_offset = 0;
|
||||
uint32_t sdma_rlc_rb_cntl;
|
||||
uint32_t engine_id, queue_id;
|
||||
uint32_t engines = adev->sdma.num_instances;
|
||||
uint32_t sdma_rlcx_rb_base, sdma_rlcx_rb_base_hi;
|
||||
bool found = false;
|
||||
|
||||
if (!m)
|
||||
return -EINVAL;
|
||||
|
||||
if (((amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) ||
|
||||
amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4)) &&
|
||||
adev->gfx.mec_fw_version < 194) ||
|
||||
(amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0) &&
|
||||
adev->gfx.mec_fw_version < 44)) {
|
||||
pr_warn_once("MEC FW doesn't support SDMA counter!\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* SDMA doesn't support over-subscription, there must be
|
||||
* a HQD associated with a MQD, so found must be true in
|
||||
* the finding loop.
|
||||
*/
|
||||
for (engine_id = 0; engine_id < engines && !found; engine_id++) {
|
||||
for (queue_id = 0; queue_id < num_sdma_queues_per_eng; queue_id++) {
|
||||
sdma_rlc_reg_offset = get_sdma_rlc_reg_offset(adev,
|
||||
engine_id, queue_id);
|
||||
sdma_rlcx_rb_base = RREG32(sdma_rlc_reg_offset +
|
||||
regSDMA_RLC0_RB_BASE);
|
||||
sdma_rlcx_rb_base_hi = RREG32(sdma_rlc_reg_offset +
|
||||
regSDMA_RLC0_RB_BASE_HI);
|
||||
|
||||
if (m->sdmax_rlcx_rb_base == sdma_rlcx_rb_base &&
|
||||
m->sdmax_rlcx_rb_base_hi == sdma_rlcx_rb_base_hi) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sdma_rlc_rb_cntl = RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_RB_CNTL);
|
||||
|
||||
/* Read sdma activity counter from utilization register
|
||||
* if hw queue is enabled, otherwise read from MQD.
|
||||
*/
|
||||
if (sdma_rlc_rb_cntl & SDMA_RLC0_RB_CNTL__RB_ENABLE_MASK)
|
||||
*val = (uint64_t)RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_HI) << 32 |
|
||||
RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_LO);
|
||||
else
|
||||
*val = (uint64_t)m->sdmax_rlcx_utilization_hi << 32 |
|
||||
m->sdmax_rlcx_utilization_lo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = {
|
||||
.program_sh_mem_settings = kgd_gfx_v9_program_sh_mem_settings,
|
||||
.set_pasid_vmid_mapping = kgd_gfx_v9_4_3_set_pasid_vmid_mapping,
|
||||
@@ -566,5 +626,6 @@ const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = {
|
||||
.hqd_get_pq_addr = kgd_gfx_v9_hqd_get_pq_addr,
|
||||
.hqd_reset = kgd_gfx_v9_hqd_reset,
|
||||
.hqd_sdma_get_doorbell = kgd_gfx_v9_4_3_hqd_sdma_get_doorbell,
|
||||
.ptl_ctrl = kgd_v9_4_3_ptl_ctrl
|
||||
.ptl_ctrl = kgd_v9_4_3_ptl_ctrl,
|
||||
.hqd_sdma_get_counter = kgd_gfx_v9_4_3_hqd_sdma_get_counter
|
||||
};
|
||||
|
||||
@@ -139,7 +139,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
|
||||
regSDMA1_QUEUE0_RB_CNTL) - regSDMA0_QUEUE0_RB_CNTL;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
WARN(1, "Invalid SDMA engine id %d\n", engine_id);
|
||||
break;
|
||||
}
|
||||
|
||||
sdma_rlc_reg_offset = sdma_engine_reg_base
|
||||
|
||||
@@ -90,7 +90,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
|
||||
regSDMA1_QUEUE0_RB_CNTL) - regSDMA0_QUEUE0_RB_CNTL;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
WARN(1, "Invalid SDMA engine id %d\n", engine_id);
|
||||
break;
|
||||
}
|
||||
|
||||
sdma_rlc_reg_offset = sdma_engine_reg_base
|
||||
|
||||
@@ -93,7 +93,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
|
||||
regSDMA1_SDMA_QUEUE0_RB_CNTL) - regSDMA0_SDMA_QUEUE0_RB_CNTL;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
WARN(1, "Invalid SDMA engine id %d\n", engine_id);
|
||||
break;
|
||||
}
|
||||
|
||||
sdma_rlc_reg_offset = sdma_engine_reg_base
|
||||
|
||||
@@ -1423,7 +1423,7 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info,
|
||||
info->eviction_fence =
|
||||
amdgpu_amdkfd_fence_create(dma_fence_context_alloc(1),
|
||||
current->mm,
|
||||
NULL, process->context_id);
|
||||
process->context_id);
|
||||
if (!info->eviction_fence) {
|
||||
pr_err("Failed to create eviction fence\n");
|
||||
ret = -ENOMEM;
|
||||
@@ -2271,11 +2271,14 @@ err_reserve_bo_failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Map a GTT BO for kernel CPU access
|
||||
/** amdgpu_amdkfd_gpuvm_map_bo_to_kernel() - Map GTT or VRAM BO for kernel CPU access
|
||||
*
|
||||
* @mem: Buffer object to be mapped for CPU access
|
||||
* @kptr[out]: pointer in kernel CPU address space
|
||||
* @size[out]: size of the buffer
|
||||
* @domain[IN]: domain for pinning (AMDGPU_GEM_DOMAIN_GTT, AMDGPU_GEM_DOMAIN_VRAM,
|
||||
* or their combination to let the driver choose). CPU visibility is
|
||||
* automatically enforced by amdgpu_bo_pin()
|
||||
*
|
||||
* Pins the BO and maps it for kernel CPU access. The eviction fence is removed
|
||||
* from the BO, since pinned BOs cannot be evicted. The bo must remain on the
|
||||
@@ -2284,8 +2287,8 @@ err_reserve_bo_failed:
|
||||
*
|
||||
* Return: 0 on success, error code on failure
|
||||
*/
|
||||
int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
|
||||
void **kptr, uint64_t *size)
|
||||
int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr,
|
||||
u64 *size, u32 domain)
|
||||
{
|
||||
int ret;
|
||||
struct amdgpu_bo *bo = mem->bo;
|
||||
@@ -2295,6 +2298,11 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(domain & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM))) {
|
||||
pr_debug("Invalid domain 0x%x for kernel mapping\n", domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&mem->process_info->lock);
|
||||
|
||||
ret = amdgpu_bo_reserve(bo, true);
|
||||
@@ -2303,7 +2311,7 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
|
||||
goto bo_reserve_failed;
|
||||
}
|
||||
|
||||
ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
|
||||
ret = amdgpu_bo_pin(bo, domain);
|
||||
if (ret) {
|
||||
pr_err("Failed to pin bo. ret %d\n", ret);
|
||||
goto pin_failed;
|
||||
@@ -2336,7 +2344,7 @@ bo_reserve_failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Unmap a GTT BO for kernel CPU access
|
||||
/** amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel() - Unmap GTT or VRAM BO for kernel CPU access
|
||||
*
|
||||
* @mem: Buffer object to be unmapped for CPU access
|
||||
*
|
||||
@@ -2344,7 +2352,7 @@ bo_reserve_failed:
|
||||
* eviction fence, so this function should only be used for cleanup before the
|
||||
* BO is destroyed.
|
||||
*/
|
||||
void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem)
|
||||
void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem)
|
||||
{
|
||||
struct amdgpu_bo *bo = mem->bo;
|
||||
|
||||
@@ -3085,7 +3093,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu *
|
||||
amdgpu_amdkfd_fence_create(
|
||||
process_info->eviction_fence->base.context,
|
||||
process_info->eviction_fence->mm,
|
||||
NULL, process_info->context_id);
|
||||
process_info->context_id);
|
||||
|
||||
if (!new_fence) {
|
||||
pr_err("Failed to create eviction fence\n");
|
||||
|
||||
@@ -1923,7 +1923,7 @@ int amdgpu_atombios_init(struct amdgpu_device *adev)
|
||||
atom_card_info->pll_read = cail_pll_read;
|
||||
atom_card_info->pll_write = cail_pll_write;
|
||||
|
||||
adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios);
|
||||
adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios, adev->bios_size);
|
||||
if (!adev->mode_info.atom_context) {
|
||||
amdgpu_atombios_fini(adev);
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -917,8 +917,6 @@ int amdgpu_atomfirmware_get_clock_info(struct amdgpu_device *adev)
|
||||
(crev != 6)) {
|
||||
spll->reference_freq = le32_to_cpu(gfx_info->v22.rlc_gpu_timer_refclk);
|
||||
ret = 0;
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,15 @@ bool amdgpu_is_atpx_hybrid(void)
|
||||
return amdgpu_atpx_priv.atpx.is_hybrid;
|
||||
}
|
||||
|
||||
static bool amdgpu_atpx_buffer_validate(const union acpi_object *obj,
|
||||
size_t min_size)
|
||||
{
|
||||
return obj && obj->type == ACPI_TYPE_BUFFER &&
|
||||
obj->buffer.length >= sizeof(u16) &&
|
||||
obj->buffer.length >= *(u16 *)obj->buffer.pointer &&
|
||||
*(u16 *)obj->buffer.pointer >= min_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_atpx_call - call an ATPX method
|
||||
*
|
||||
@@ -179,15 +188,15 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
|
||||
if (!info)
|
||||
return -EIO;
|
||||
|
||||
memset(&output, 0, sizeof(output));
|
||||
|
||||
size = *(u16 *) info->buffer.pointer;
|
||||
if (size < 10) {
|
||||
pr_err("ATPX buffer is too small: %zu\n", size);
|
||||
if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) {
|
||||
pr_err("Invalid ATPX GET_PX_PARAMETERS response\n");
|
||||
kfree(info);
|
||||
return -EINVAL;
|
||||
}
|
||||
size = min(sizeof(output), size);
|
||||
|
||||
memset(&output, 0, sizeof(output));
|
||||
|
||||
size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
|
||||
|
||||
memcpy(&output, info->buffer.pointer, size);
|
||||
|
||||
@@ -258,15 +267,15 @@ static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
|
||||
if (!info)
|
||||
return -EIO;
|
||||
|
||||
memset(&output, 0, sizeof(output));
|
||||
|
||||
size = *(u16 *) info->buffer.pointer;
|
||||
if (size < 8) {
|
||||
pr_err("ATPX buffer is too small: %zu\n", size);
|
||||
if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) {
|
||||
pr_err("Invalid ATPX VERIFY_INTERFACE response\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
size = min(sizeof(output), size);
|
||||
|
||||
memset(&output, 0, sizeof(output));
|
||||
|
||||
size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
|
||||
|
||||
memcpy(&output, info->buffer.pointer, size);
|
||||
|
||||
|
||||
@@ -296,8 +296,14 @@ static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)buffer.pointer;
|
||||
memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
|
||||
len = obj->buffer.length;
|
||||
if (!obj || obj->type != ACPI_TYPE_BUFFER) {
|
||||
DRM_ERROR("ATRM returned an invalid object\n");
|
||||
kfree(buffer.pointer);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = min_t(size_t, obj->buffer.length, len);
|
||||
memcpy(bios+offset, obj->buffer.pointer, len);
|
||||
kfree(buffer.pointer);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -38,14 +38,6 @@
|
||||
#define AMDGPU_BO_LIST_NUM_BUCKETS (AMDGPU_BO_LIST_MAX_PRIORITY + 1)
|
||||
#define AMDGPU_BO_LIST_MAX_ENTRIES (128 * 1024)
|
||||
|
||||
static void amdgpu_bo_list_free_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct amdgpu_bo_list *list = container_of(rcu, struct amdgpu_bo_list,
|
||||
rhead);
|
||||
mutex_destroy(&list->bo_list_mutex);
|
||||
kvfree(list);
|
||||
}
|
||||
|
||||
static void amdgpu_bo_list_free(struct kref *ref)
|
||||
{
|
||||
struct amdgpu_bo_list *list = container_of(ref, struct amdgpu_bo_list,
|
||||
@@ -54,7 +46,8 @@ static void amdgpu_bo_list_free(struct kref *ref)
|
||||
|
||||
amdgpu_bo_list_for_each_entry(e, list)
|
||||
amdgpu_bo_unref(&e->bo);
|
||||
call_rcu(&list->rhead, amdgpu_bo_list_free_rcu);
|
||||
|
||||
kvfree(list);
|
||||
}
|
||||
|
||||
static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b)
|
||||
@@ -66,9 +59,9 @@ static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b)
|
||||
return (int)a->priority - (int)b->priority;
|
||||
}
|
||||
|
||||
int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
|
||||
struct drm_amdgpu_bo_list_entry *info,
|
||||
size_t num_entries, struct amdgpu_bo_list **result)
|
||||
struct amdgpu_bo_list *
|
||||
amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
|
||||
struct drm_amdgpu_bo_list_entry *info, size_t num_entries)
|
||||
{
|
||||
unsigned last_entry = 0, first_userptr = num_entries;
|
||||
struct amdgpu_bo_list_entry *array;
|
||||
@@ -79,7 +72,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
|
||||
|
||||
list = kvzalloc_flex(*list, entries, num_entries);
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
kref_init(&list->refcount);
|
||||
|
||||
@@ -134,9 +127,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
|
||||
|
||||
trace_amdgpu_cs_bo_status(list->num_entries, total_size);
|
||||
|
||||
mutex_init(&list->bo_list_mutex);
|
||||
*result = list;
|
||||
return 0;
|
||||
return list;
|
||||
|
||||
error_free:
|
||||
for (i = 0; i < last_entry; ++i)
|
||||
@@ -144,150 +135,125 @@ error_free:
|
||||
for (i = first_userptr; i < num_entries; ++i)
|
||||
amdgpu_bo_unref(&array[i].bo);
|
||||
kvfree(list);
|
||||
return r;
|
||||
return ERR_PTR(r);
|
||||
|
||||
}
|
||||
|
||||
static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id)
|
||||
struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id)
|
||||
{
|
||||
struct amdgpu_bo_list *list;
|
||||
|
||||
mutex_lock(&fpriv->bo_list_lock);
|
||||
list = idr_remove(&fpriv->bo_list_handles, id);
|
||||
mutex_unlock(&fpriv->bo_list_lock);
|
||||
xa_lock(&fpriv->bo_list_handles);
|
||||
list = xa_load(&fpriv->bo_list_handles, id);
|
||||
if (list)
|
||||
kref_put(&list->refcount, amdgpu_bo_list_free);
|
||||
}
|
||||
kref_get(&list->refcount);
|
||||
else
|
||||
list = ERR_PTR(-ENOENT);
|
||||
xa_unlock(&fpriv->bo_list_handles);
|
||||
|
||||
int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id,
|
||||
struct amdgpu_bo_list **result)
|
||||
{
|
||||
rcu_read_lock();
|
||||
*result = idr_find(&fpriv->bo_list_handles, id);
|
||||
|
||||
if (*result && kref_get_unless_zero(&(*result)->refcount)) {
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
*result = NULL;
|
||||
return -ENOENT;
|
||||
return list;
|
||||
}
|
||||
|
||||
void amdgpu_bo_list_put(struct amdgpu_bo_list *list)
|
||||
{
|
||||
kref_put(&list->refcount, amdgpu_bo_list_free);
|
||||
if (list)
|
||||
kref_put(&list->refcount, amdgpu_bo_list_free);
|
||||
}
|
||||
|
||||
int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in,
|
||||
struct drm_amdgpu_bo_list_entry **info_param)
|
||||
struct drm_amdgpu_bo_list_entry *
|
||||
amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in)
|
||||
{
|
||||
const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry);
|
||||
const void __user *uptr = u64_to_user_ptr(in->bo_info_ptr);
|
||||
const uint32_t bo_info_size = in->bo_info_size;
|
||||
const uint32_t bo_number = in->bo_number;
|
||||
struct drm_amdgpu_bo_list_entry *info;
|
||||
|
||||
if (bo_number > AMDGPU_BO_LIST_MAX_ENTRIES)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* copy the handle array from userspace to a kernel buffer */
|
||||
if (likely(info_size == bo_info_size)) {
|
||||
info = vmemdup_array_user(uptr, bo_number, info_size);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
} else {
|
||||
const uint32_t bytes = min(bo_info_size, info_size);
|
||||
unsigned i;
|
||||
if (in->bo_info_size != sizeof(struct drm_amdgpu_bo_list_entry))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
info = kvmalloc_array(bo_number, info_size, GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(info, 0, bo_number * info_size);
|
||||
for (i = 0; i < bo_number; ++i, uptr += bo_info_size) {
|
||||
if (copy_from_user(&info[i], uptr, bytes)) {
|
||||
kvfree(info);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*info_param = info;
|
||||
return 0;
|
||||
return vmemdup_array_user(uptr, bo_number,
|
||||
sizeof(struct drm_amdgpu_bo_list_entry));
|
||||
}
|
||||
|
||||
int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filp)
|
||||
{
|
||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||
struct amdgpu_fpriv *fpriv = filp->driver_priv;
|
||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||
struct amdgpu_bo_list *list, *prev, *curr;
|
||||
union drm_amdgpu_bo_list *args = data;
|
||||
uint32_t handle = args->in.list_handle;
|
||||
struct drm_amdgpu_bo_list_entry *info = NULL;
|
||||
struct amdgpu_bo_list *list, *old;
|
||||
struct drm_amdgpu_bo_list_entry *info;
|
||||
int r;
|
||||
|
||||
r = amdgpu_bo_create_list_entry_array(&args->in, &info);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
switch (args->in.operation) {
|
||||
case AMDGPU_BO_LIST_OP_CREATE:
|
||||
r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number,
|
||||
&list);
|
||||
if (r)
|
||||
goto error_free;
|
||||
case AMDGPU_BO_LIST_OP_UPDATE:
|
||||
info = amdgpu_bo_create_list_entry_array(&args->in);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
mutex_lock(&fpriv->bo_list_lock);
|
||||
r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL);
|
||||
mutex_unlock(&fpriv->bo_list_lock);
|
||||
if (r < 0) {
|
||||
goto error_put_list;
|
||||
}
|
||||
list = amdgpu_bo_list_create(adev, filp, info,
|
||||
args->in.bo_number);
|
||||
kvfree(info);
|
||||
if (IS_ERR(list))
|
||||
return PTR_ERR(list);
|
||||
|
||||
handle = r;
|
||||
break;
|
||||
|
||||
case AMDGPU_BO_LIST_OP_DESTROY:
|
||||
amdgpu_bo_list_destroy(fpriv, handle);
|
||||
list = xa_erase(&fpriv->bo_list_handles, handle);
|
||||
amdgpu_bo_list_put(list);
|
||||
handle = 0;
|
||||
break;
|
||||
|
||||
case AMDGPU_BO_LIST_OP_UPDATE:
|
||||
r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number,
|
||||
&list);
|
||||
if (r)
|
||||
goto error_free;
|
||||
|
||||
mutex_lock(&fpriv->bo_list_lock);
|
||||
old = idr_replace(&fpriv->bo_list_handles, list, handle);
|
||||
mutex_unlock(&fpriv->bo_list_lock);
|
||||
|
||||
if (IS_ERR(old)) {
|
||||
r = PTR_ERR(old);
|
||||
goto error_put_list;
|
||||
}
|
||||
|
||||
amdgpu_bo_list_put(old);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = -EINVAL;
|
||||
goto error_free;
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
switch (args->in.operation) {
|
||||
case AMDGPU_BO_LIST_OP_CREATE:
|
||||
r = xa_alloc(&fpriv->bo_list_handles, &handle, list,
|
||||
xa_limit_32b, GFP_KERNEL);
|
||||
if (r)
|
||||
goto error_put_list;
|
||||
|
||||
break;
|
||||
|
||||
case AMDGPU_BO_LIST_OP_UPDATE:
|
||||
curr = xa_load(&fpriv->bo_list_handles, handle);
|
||||
if (!curr) {
|
||||
r = -ENOENT;
|
||||
goto error_put_list;
|
||||
}
|
||||
|
||||
prev = xa_cmpxchg(&fpriv->bo_list_handles, handle, curr, list,
|
||||
GFP_KERNEL);
|
||||
if (xa_is_err(prev)) {
|
||||
r = xa_err(prev);
|
||||
goto error_put_list;
|
||||
} else if (prev != curr) {
|
||||
r = -ENOENT;
|
||||
goto error_put_list;
|
||||
}
|
||||
|
||||
amdgpu_bo_list_put(curr);
|
||||
break;
|
||||
|
||||
case AMDGPU_BO_LIST_OP_DESTROY:
|
||||
default:
|
||||
/* Handled above. */
|
||||
break;
|
||||
}
|
||||
|
||||
memset(args, 0, sizeof(*args));
|
||||
args->out.list_handle = handle;
|
||||
kvfree(info);
|
||||
|
||||
return 0;
|
||||
|
||||
error_put_list:
|
||||
amdgpu_bo_list_put(list);
|
||||
|
||||
error_free:
|
||||
kvfree(info);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ struct amdgpu_bo_list_entry {
|
||||
};
|
||||
|
||||
struct amdgpu_bo_list {
|
||||
struct rcu_head rhead;
|
||||
struct kref refcount;
|
||||
struct amdgpu_bo *gds_obj;
|
||||
struct amdgpu_bo *gws_obj;
|
||||
@@ -51,24 +50,19 @@ struct amdgpu_bo_list {
|
||||
unsigned first_userptr;
|
||||
unsigned num_entries;
|
||||
|
||||
/* Protect access during command submission.
|
||||
*/
|
||||
struct mutex bo_list_mutex;
|
||||
|
||||
struct amdgpu_bo_list_entry entries[] __counted_by(num_entries);
|
||||
};
|
||||
|
||||
int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id,
|
||||
struct amdgpu_bo_list **result);
|
||||
struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id);
|
||||
void amdgpu_bo_list_put(struct amdgpu_bo_list *list);
|
||||
int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in,
|
||||
struct drm_amdgpu_bo_list_entry **info_param);
|
||||
struct drm_amdgpu_bo_list_entry *
|
||||
amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in);
|
||||
|
||||
int amdgpu_bo_list_create(struct amdgpu_device *adev,
|
||||
struct drm_file *filp,
|
||||
struct drm_amdgpu_bo_list_entry *info,
|
||||
size_t num_entries,
|
||||
struct amdgpu_bo_list **list);
|
||||
struct amdgpu_bo_list *
|
||||
amdgpu_bo_list_create(struct amdgpu_device *adev,
|
||||
struct drm_file *filp,
|
||||
struct drm_amdgpu_bo_list_entry *info,
|
||||
size_t num_entries);
|
||||
|
||||
#define amdgpu_bo_list_for_each_entry(e, list) \
|
||||
for (e = list->entries; \
|
||||
|
||||
@@ -76,10 +76,9 @@ static uint32_t amdgpu_cgs_read_ind_register(struct cgs_device *cgs_device,
|
||||
DRM_ERROR("audio endpt register access not implemented.\n");
|
||||
return 0;
|
||||
default:
|
||||
BUG();
|
||||
WARN(1, "Invalid indirect register space");
|
||||
return 0;
|
||||
}
|
||||
WARN(1, "Invalid indirect register space");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdgpu_cgs_write_ind_register(struct cgs_device *cgs_device,
|
||||
@@ -104,9 +103,8 @@ static void amdgpu_cgs_write_ind_register(struct cgs_device *cgs_device,
|
||||
DRM_ERROR("audio endpt register access not implemented.\n");
|
||||
return;
|
||||
default:
|
||||
BUG();
|
||||
WARN(1, "Invalid indirect register space");
|
||||
}
|
||||
WARN(1, "Invalid indirect register space");
|
||||
}
|
||||
|
||||
static uint32_t fw_type_convert(struct cgs_device *cgs_device, uint32_t fw_type)
|
||||
@@ -241,7 +239,7 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
info->fw_version = amdgpu_get_firmware_version(cgs_device, type);
|
||||
info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
|
||||
} else {
|
||||
char fw_name[30] = {0};
|
||||
const char *fw_name = NULL;
|
||||
int err = 0;
|
||||
uint32_t ucode_size;
|
||||
uint32_t ucode_start_address;
|
||||
@@ -257,17 +255,17 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
(adev->pdev->revision == 0x81) ||
|
||||
(adev->pdev->device == 0x665f)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/bonaire_k_smc.bin");
|
||||
fw_name = "bonaire_k_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/bonaire_smc.bin");
|
||||
fw_name = "bonaire_smc.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_HAWAII:
|
||||
if (adev->pdev->revision == 0x80) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/hawaii_k_smc.bin");
|
||||
fw_name = "hawaii_k_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/hawaii_smc.bin");
|
||||
fw_name = "hawaii_smc.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_TOPAZ:
|
||||
@@ -277,76 +275,76 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD1)) ||
|
||||
((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD3))) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/topaz_k_smc.bin");
|
||||
fw_name = "topaz_k_smc.bin";
|
||||
} else
|
||||
strscpy(fw_name, "amdgpu/topaz_smc.bin");
|
||||
fw_name = "topaz_smc.bin";
|
||||
break;
|
||||
case CHIP_TONGA:
|
||||
if (((adev->pdev->device == 0x6939) && (adev->pdev->revision == 0xf1)) ||
|
||||
((adev->pdev->device == 0x6938) && (adev->pdev->revision == 0xf1))) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/tonga_k_smc.bin");
|
||||
fw_name = "tonga_k_smc.bin";
|
||||
} else
|
||||
strscpy(fw_name, "amdgpu/tonga_smc.bin");
|
||||
fw_name = "tonga_smc.bin";
|
||||
break;
|
||||
case CHIP_FIJI:
|
||||
strscpy(fw_name, "amdgpu/fiji_smc.bin");
|
||||
fw_name = "fiji_smc.bin";
|
||||
break;
|
||||
case CHIP_POLARIS11:
|
||||
if (type == CGS_UCODE_ID_SMU) {
|
||||
if (ASICID_IS_P21(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris11_k_smc.bin");
|
||||
fw_name = "polaris11_k_smc.bin";
|
||||
} else if (ASICID_IS_P31(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris11_k2_smc.bin");
|
||||
fw_name = "polaris11_k2_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/polaris11_smc.bin");
|
||||
fw_name = "polaris11_smc.bin";
|
||||
}
|
||||
} else if (type == CGS_UCODE_ID_SMU_SK) {
|
||||
strscpy(fw_name, "amdgpu/polaris11_smc_sk.bin");
|
||||
fw_name = "polaris11_smc_sk.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_POLARIS10:
|
||||
if (type == CGS_UCODE_ID_SMU) {
|
||||
if (ASICID_IS_P20(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris10_k_smc.bin");
|
||||
fw_name = "polaris10_k_smc.bin";
|
||||
} else if (ASICID_IS_P30(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris10_k2_smc.bin");
|
||||
fw_name = "polaris10_k2_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/polaris10_smc.bin");
|
||||
fw_name = "polaris10_smc.bin";
|
||||
}
|
||||
} else if (type == CGS_UCODE_ID_SMU_SK) {
|
||||
strscpy(fw_name, "amdgpu/polaris10_smc_sk.bin");
|
||||
fw_name = "polaris10_smc_sk.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_POLARIS12:
|
||||
if (ASICID_IS_P23(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris12_k_smc.bin");
|
||||
fw_name = "polaris12_k_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/polaris12_smc.bin");
|
||||
fw_name = "polaris12_smc.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_VEGAM:
|
||||
strscpy(fw_name, "amdgpu/vegam_smc.bin");
|
||||
fw_name = "vegam_smc.bin";
|
||||
break;
|
||||
case CHIP_VEGA10:
|
||||
if ((adev->pdev->device == 0x687f) &&
|
||||
((adev->pdev->revision == 0xc0) ||
|
||||
(adev->pdev->revision == 0xc1) ||
|
||||
(adev->pdev->revision == 0xc3)))
|
||||
strscpy(fw_name, "amdgpu/vega10_acg_smc.bin");
|
||||
fw_name = "vega10_acg_smc.bin";
|
||||
else
|
||||
strscpy(fw_name, "amdgpu/vega10_smc.bin");
|
||||
fw_name = "vega10_smc.bin";
|
||||
break;
|
||||
case CHIP_VEGA12:
|
||||
strscpy(fw_name, "amdgpu/vega12_smc.bin");
|
||||
fw_name = "vega12_smc.bin";
|
||||
break;
|
||||
case CHIP_VEGA20:
|
||||
strscpy(fw_name, "amdgpu/vega20_smc.bin");
|
||||
fw_name = "vega20_smc.bin";
|
||||
break;
|
||||
default:
|
||||
drm_err(adev_to_drm(adev), "SMC firmware not supported\n");
|
||||
@@ -355,10 +353,8 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
|
||||
err = amdgpu_ucode_request(adev, &adev->pm.fw,
|
||||
AMDGPU_UCODE_REQUIRED,
|
||||
"%s", fw_name);
|
||||
"amdgpu/%s", fw_name);
|
||||
if (err) {
|
||||
drm_err(adev_to_drm(adev),
|
||||
"Failed to load firmware \"%s\"\n", fw_name);
|
||||
amdgpu_ucode_release(&adev->pm.fw);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -289,40 +289,6 @@ struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev,
|
||||
return hdr;
|
||||
}
|
||||
|
||||
int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev,
|
||||
struct aca_bank *bank)
|
||||
{
|
||||
struct cper_hdr *fatal = NULL;
|
||||
struct cper_sec_crashdump_reg_data reg_data = { 0 };
|
||||
struct amdgpu_ring *ring = &adev->cper.ring_buf;
|
||||
int ret;
|
||||
|
||||
fatal = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_FATAL, 1);
|
||||
if (!fatal) {
|
||||
dev_err(adev->dev, "fail to alloc cper entry for ue record\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
reg_data.status_lo = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
|
||||
reg_data.status_hi = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
|
||||
reg_data.addr_lo = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
|
||||
reg_data.addr_hi = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
|
||||
reg_data.ipid_lo = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]);
|
||||
reg_data.ipid_hi = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]);
|
||||
reg_data.synd_lo = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]);
|
||||
reg_data.synd_hi = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]);
|
||||
|
||||
amdgpu_cper_entry_fill_hdr(adev, fatal, AMDGPU_CPER_TYPE_FATAL, CPER_SEV_FATAL_UNCORRECTED);
|
||||
ret = amdgpu_cper_entry_fill_fatal_section(adev, fatal, 0, reg_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
amdgpu_cper_ring_write(ring, fatal, fatal->record_length);
|
||||
kfree(fatal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev)
|
||||
{
|
||||
struct cper_hdr *bp_threshold = NULL;
|
||||
@@ -348,83 +314,6 @@ int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum cper_error_severity amdgpu_aca_err_type_to_cper_sev(struct amdgpu_device *adev,
|
||||
enum aca_error_type aca_err_type)
|
||||
{
|
||||
switch (aca_err_type) {
|
||||
case ACA_ERROR_TYPE_UE:
|
||||
return CPER_SEV_FATAL_UNCORRECTED;
|
||||
case ACA_ERROR_TYPE_CE:
|
||||
return CPER_SEV_NON_FATAL_CORRECTED;
|
||||
case ACA_ERROR_TYPE_DEFERRED:
|
||||
return CPER_SEV_NON_FATAL_UNCORRECTED;
|
||||
default:
|
||||
dev_err(adev->dev, "Unknown ACA error type!\n");
|
||||
return CPER_SEV_FATAL_UNCORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev,
|
||||
struct aca_banks *banks,
|
||||
uint16_t bank_count)
|
||||
{
|
||||
struct cper_hdr *corrected = NULL;
|
||||
enum cper_error_severity sev = CPER_SEV_NON_FATAL_CORRECTED;
|
||||
struct amdgpu_ring *ring = &adev->cper.ring_buf;
|
||||
uint32_t reg_data[CPER_ACA_REG_COUNT] = { 0 };
|
||||
struct aca_bank_node *node;
|
||||
struct aca_bank *bank;
|
||||
uint32_t i = 0;
|
||||
int ret;
|
||||
|
||||
corrected = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_RUNTIME, bank_count);
|
||||
if (!corrected) {
|
||||
dev_err(adev->dev, "fail to allocate cper entry for ce records\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Raise severity if any DE is detected in the ACA bank list */
|
||||
list_for_each_entry(node, &banks->list, node) {
|
||||
bank = &node->bank;
|
||||
if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) {
|
||||
sev = CPER_SEV_NON_FATAL_UNCORRECTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
amdgpu_cper_entry_fill_hdr(adev, corrected, AMDGPU_CPER_TYPE_RUNTIME, sev);
|
||||
|
||||
/* Combine CE and DE in cper record */
|
||||
list_for_each_entry(node, &banks->list, node) {
|
||||
bank = &node->bank;
|
||||
reg_data[CPER_ACA_REG_CTL_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CTL]);
|
||||
reg_data[CPER_ACA_REG_CTL_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CTL]);
|
||||
reg_data[CPER_ACA_REG_STATUS_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
|
||||
reg_data[CPER_ACA_REG_STATUS_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
|
||||
reg_data[CPER_ACA_REG_ADDR_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
|
||||
reg_data[CPER_ACA_REG_ADDR_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
|
||||
reg_data[CPER_ACA_REG_MISC0_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_MISC0]);
|
||||
reg_data[CPER_ACA_REG_MISC0_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_MISC0]);
|
||||
reg_data[CPER_ACA_REG_CONFIG_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CONFIG]);
|
||||
reg_data[CPER_ACA_REG_CONFIG_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CONFIG]);
|
||||
reg_data[CPER_ACA_REG_IPID_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]);
|
||||
reg_data[CPER_ACA_REG_IPID_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]);
|
||||
reg_data[CPER_ACA_REG_SYND_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]);
|
||||
reg_data[CPER_ACA_REG_SYND_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]);
|
||||
|
||||
ret = amdgpu_cper_entry_fill_runtime_section(adev, corrected, i++,
|
||||
amdgpu_aca_err_type_to_cper_sev(adev, bank->aca_err_type),
|
||||
reg_data, CPER_ACA_REG_COUNT);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
amdgpu_cper_ring_write(ring, corrected, corrected->record_length);
|
||||
kfree(corrected);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos)
|
||||
{
|
||||
char signature[CPER_SIGNATURE_SZ];
|
||||
@@ -592,8 +481,7 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
|
||||
|
||||
if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev))
|
||||
return 0;
|
||||
else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev) &&
|
||||
!amdgpu_aca_is_enabled(adev))
|
||||
else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev))
|
||||
return 0;
|
||||
|
||||
r = amdgpu_cper_ring_init(adev);
|
||||
@@ -610,9 +498,28 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_cper_deferred_init(struct amdgpu_device *adev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (adev->cper.enabled)
|
||||
return 0;
|
||||
|
||||
r = amdgpu_cper_init(adev);
|
||||
if (r || !adev->cper.enabled)
|
||||
return r;
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
if (adev_to_drm(adev)->primary->debugfs_root)
|
||||
amdgpu_debugfs_ring_init(adev, &adev->cper.ring_buf);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_cper_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
if (!amdgpu_aca_is_enabled(adev) && !amdgpu_sriov_ras_cper_en(adev))
|
||||
if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev))
|
||||
return 0;
|
||||
|
||||
adev->cper.enabled = false;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#define __AMDGPU_CPER_H__
|
||||
|
||||
#include "amd_cper.h"
|
||||
#include "amdgpu_aca.h"
|
||||
|
||||
#define CPER_MAX_ALLOWED_COUNT 0x1000
|
||||
#define CPER_MAX_RING_SIZE 0X100000
|
||||
@@ -88,18 +87,12 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev
|
||||
struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev,
|
||||
enum amdgpu_cper_type type,
|
||||
uint16_t section_count);
|
||||
/* UE must be encoded into separated cper entries, 1 UE 1 cper */
|
||||
int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev,
|
||||
struct aca_bank *bank);
|
||||
/* CEs and DEs are combined into 1 cper entry */
|
||||
int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev,
|
||||
struct aca_banks *banks,
|
||||
uint16_t bank_count);
|
||||
/* Bad page threshold is encoded into separated cper entry */
|
||||
int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev);
|
||||
void amdgpu_cper_ring_write(struct amdgpu_ring *ring,
|
||||
void *src, int count);
|
||||
int amdgpu_cper_init(struct amdgpu_device *adev);
|
||||
int amdgpu_cper_deferred_init(struct amdgpu_device *adev);
|
||||
int amdgpu_cper_fini(struct amdgpu_device *adev);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -160,24 +160,19 @@ static int amdgpu_cs_p1_bo_handles(struct amdgpu_cs_parser *p,
|
||||
struct drm_amdgpu_bo_list_in *data)
|
||||
{
|
||||
struct drm_amdgpu_bo_list_entry *info;
|
||||
int r;
|
||||
struct amdgpu_bo_list *list;
|
||||
|
||||
r = amdgpu_bo_create_list_entry_array(data, &info);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number,
|
||||
&p->bo_list);
|
||||
if (r)
|
||||
goto error_free;
|
||||
info = amdgpu_bo_create_list_entry_array(data);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
list = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number);
|
||||
kvfree(info);
|
||||
if (IS_ERR(list))
|
||||
return PTR_ERR(list);
|
||||
|
||||
p->bo_list = list;
|
||||
return 0;
|
||||
|
||||
error_free:
|
||||
kvfree(info);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Copy the data from userspace and go over it the first time */
|
||||
@@ -296,8 +291,8 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,
|
||||
|
||||
for (i = 0; i < p->gang_size; ++i) {
|
||||
ret = amdgpu_job_alloc(p->adev, vm, p->entities[i], vm,
|
||||
num_ibs[i], &p->jobs[i],
|
||||
p->filp->client_id);
|
||||
num_ibs[i], p->filp->client_id,
|
||||
GFP_KERNEL, &p->jobs[i]);
|
||||
if (ret)
|
||||
goto free_all_kdata;
|
||||
switch (p->adev->enforce_isolation[fpriv->xcp_id]) {
|
||||
@@ -875,6 +870,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
|
||||
{
|
||||
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
|
||||
struct ttm_operation_ctx ctx = { true, false };
|
||||
struct amdgpu_bo_list *list = NULL;
|
||||
struct amdgpu_vm *vm = &fpriv->vm;
|
||||
struct amdgpu_bo_list_entry *e;
|
||||
struct drm_gem_object *obj;
|
||||
@@ -886,25 +882,24 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
|
||||
if (p->bo_list)
|
||||
return -EINVAL;
|
||||
|
||||
r = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle,
|
||||
&p->bo_list);
|
||||
if (r)
|
||||
return r;
|
||||
list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle);
|
||||
} else if (!p->bo_list) {
|
||||
/* Create a empty bo_list when no handle is provided */
|
||||
r = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0,
|
||||
&p->bo_list);
|
||||
if (r)
|
||||
return r;
|
||||
list = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0);
|
||||
}
|
||||
|
||||
mutex_lock(&p->bo_list->bo_list_mutex);
|
||||
if (IS_ERR(list))
|
||||
return PTR_ERR(list);
|
||||
else if (list)
|
||||
p->bo_list = list;
|
||||
else
|
||||
list = p->bo_list;
|
||||
|
||||
/* Get userptr backing pages. If pages are updated after registered
|
||||
* in amdgpu_gem_userptr_ioctl(), amdgpu_cs_list_validate() will do
|
||||
* amdgpu_ttm_backend_bind() to flush and invalidate new pages
|
||||
*/
|
||||
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
|
||||
amdgpu_bo_list_for_each_userptr_entry(e, list) {
|
||||
bool userpage_invalidated = false;
|
||||
struct amdgpu_bo *bo = e->bo;
|
||||
|
||||
@@ -934,7 +929,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
|
||||
if (unlikely(r))
|
||||
goto out_free_user_pages;
|
||||
|
||||
amdgpu_bo_list_for_each_entry(e, p->bo_list) {
|
||||
amdgpu_bo_list_for_each_entry(e, list) {
|
||||
r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base,
|
||||
TTM_NUM_MOVE_FENCES + p->gang_size);
|
||||
drm_exec_retry_on_contention(&p->exec);
|
||||
@@ -953,7 +948,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
|
||||
}
|
||||
}
|
||||
|
||||
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
|
||||
amdgpu_bo_list_for_each_userptr_entry(e, list) {
|
||||
struct mm_struct *usermm;
|
||||
|
||||
usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm);
|
||||
@@ -1006,17 +1001,15 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
|
||||
p->bytes_moved_vis);
|
||||
|
||||
for (i = 0; i < p->gang_size; ++i)
|
||||
amdgpu_job_set_resources(p->jobs[i], p->bo_list->gds_obj,
|
||||
p->bo_list->gws_obj,
|
||||
p->bo_list->oa_obj);
|
||||
amdgpu_job_set_resources(p->jobs[i], list->gds_obj,
|
||||
list->gws_obj, list->oa_obj);
|
||||
return 0;
|
||||
|
||||
out_free_user_pages:
|
||||
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
|
||||
amdgpu_bo_list_for_each_userptr_entry(e, list) {
|
||||
amdgpu_hmm_range_free(e->range);
|
||||
e->range = NULL;
|
||||
}
|
||||
mutex_unlock(&p->bo_list->bo_list_mutex);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1153,7 +1146,8 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
|
||||
|
||||
if (fpriv->csa_va) {
|
||||
bo_va = fpriv->csa_va;
|
||||
BUG_ON(!bo_va);
|
||||
if (!bo_va)
|
||||
return -ENOMEM;
|
||||
r = amdgpu_vm_bo_update(adev, bo_va, false);
|
||||
if (r)
|
||||
return r;
|
||||
@@ -1385,7 +1379,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
|
||||
amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm);
|
||||
|
||||
mutex_unlock(&p->adev->notifier_lock);
|
||||
mutex_unlock(&p->bo_list->bo_list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1472,28 +1465,25 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
|
||||
|
||||
r = amdgpu_cs_patch_jobs(&parser);
|
||||
if (r)
|
||||
goto error_backoff;
|
||||
goto error_fini;
|
||||
|
||||
r = amdgpu_cs_vm_handling(&parser);
|
||||
if (r)
|
||||
goto error_backoff;
|
||||
goto error_fini;
|
||||
|
||||
r = amdgpu_cs_sync_rings(&parser);
|
||||
if (r)
|
||||
goto error_backoff;
|
||||
goto error_fini;
|
||||
|
||||
trace_amdgpu_cs_ibs(&parser);
|
||||
|
||||
r = amdgpu_cs_submit(&parser, data);
|
||||
if (r)
|
||||
goto error_backoff;
|
||||
goto error_fini;
|
||||
|
||||
amdgpu_cs_parser_fini(&parser);
|
||||
return 0;
|
||||
|
||||
error_backoff:
|
||||
mutex_unlock(&parser.bo_list->bo_list_mutex);
|
||||
|
||||
error_fini:
|
||||
amdgpu_cs_parser_fini(&parser);
|
||||
return r;
|
||||
|
||||
@@ -283,6 +283,8 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev,
|
||||
if (!entity)
|
||||
return res;
|
||||
|
||||
drm_sched_entity_destroy(&entity->entity);
|
||||
|
||||
for (i = 0; i < amdgpu_sched_jobs; ++i) {
|
||||
res = ktime_add(res, amdgpu_ctx_fence_time(entity->fences[i]));
|
||||
dma_fence_put(entity->fences[i]);
|
||||
@@ -294,32 +296,20 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev,
|
||||
return res;
|
||||
}
|
||||
|
||||
static int amdgpu_ctx_get_stable_pstate(struct amdgpu_ctx *ctx,
|
||||
u32 *stable_pstate)
|
||||
static u32 amdgpu_get_stable_pstate(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_device *adev = ctx->mgr->adev;
|
||||
enum amd_dpm_forced_level current_level;
|
||||
|
||||
current_level = amdgpu_dpm_get_performance_level(adev);
|
||||
|
||||
switch (current_level) {
|
||||
switch (amdgpu_dpm_get_performance_level(adev)) {
|
||||
case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
|
||||
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_STANDARD;
|
||||
break;
|
||||
return AMDGPU_CTX_STABLE_PSTATE_STANDARD;
|
||||
case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
|
||||
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK;
|
||||
break;
|
||||
return AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK;
|
||||
case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
|
||||
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK;
|
||||
break;
|
||||
return AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK;
|
||||
case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
|
||||
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_PEAK;
|
||||
break;
|
||||
return AMDGPU_CTX_STABLE_PSTATE_PEAK;
|
||||
default:
|
||||
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE;
|
||||
break;
|
||||
return AMDGPU_CTX_STABLE_PSTATE_NONE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority,
|
||||
@@ -383,9 +373,9 @@ static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
|
||||
if (current_ctx && current_ctx != ctx)
|
||||
return -EBUSY;
|
||||
|
||||
r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate);
|
||||
if (r || current_stable_pstate == stable_pstate)
|
||||
return r;
|
||||
current_stable_pstate = amdgpu_get_stable_pstate(adev);
|
||||
if (current_stable_pstate == stable_pstate)
|
||||
return 0;
|
||||
|
||||
r = amdgpu_dpm_force_performance_level(adev, level);
|
||||
if (r)
|
||||
@@ -416,7 +406,7 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
|
||||
return r;
|
||||
}
|
||||
|
||||
static void amdgpu_ctx_fini(struct kref *ref)
|
||||
void amdgpu_ctx_fini(struct kref *ref)
|
||||
{
|
||||
struct amdgpu_ctx *ctx = container_of(ref, struct amdgpu_ctx, refcount);
|
||||
struct amdgpu_ctx_mgr *mgr = ctx->mgr;
|
||||
@@ -504,53 +494,26 @@ static int amdgpu_ctx_alloc(struct amdgpu_device *adev,
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&mgr->lock);
|
||||
r = idr_alloc(&mgr->ctx_handles, ctx, 1, AMDGPU_VM_MAX_NUM_CTX, GFP_KERNEL);
|
||||
if (r < 0) {
|
||||
mutex_unlock(&mgr->lock);
|
||||
r = amdgpu_ctx_init(mgr, priority, filp, ctx);
|
||||
if (r) {
|
||||
kfree(ctx);
|
||||
return r;
|
||||
}
|
||||
|
||||
*id = (uint32_t)r;
|
||||
r = amdgpu_ctx_init(mgr, priority, filp, ctx);
|
||||
if (r) {
|
||||
idr_remove(&mgr->ctx_handles, *id);
|
||||
*id = 0;
|
||||
kfree(ctx);
|
||||
}
|
||||
mutex_unlock(&mgr->lock);
|
||||
r = xa_alloc(&mgr->ctx_handles, id, ctx, xa_limit_32b, GFP_KERNEL);
|
||||
if (r)
|
||||
amdgpu_ctx_put(ctx);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void amdgpu_ctx_do_release(struct kref *ref)
|
||||
{
|
||||
struct amdgpu_ctx *ctx;
|
||||
u32 i, j;
|
||||
|
||||
ctx = container_of(ref, struct amdgpu_ctx, refcount);
|
||||
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
|
||||
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
|
||||
if (!ctx->entities[i][j])
|
||||
continue;
|
||||
|
||||
drm_sched_entity_destroy(&ctx->entities[i][j]->entity);
|
||||
}
|
||||
}
|
||||
|
||||
amdgpu_ctx_fini(ref);
|
||||
}
|
||||
|
||||
static int amdgpu_ctx_free(struct amdgpu_fpriv *fpriv, uint32_t id)
|
||||
{
|
||||
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
|
||||
struct amdgpu_ctx *ctx;
|
||||
|
||||
mutex_lock(&mgr->lock);
|
||||
ctx = idr_remove(&mgr->ctx_handles, id);
|
||||
if (ctx)
|
||||
kref_put(&ctx->refcount, amdgpu_ctx_do_release);
|
||||
mutex_unlock(&mgr->lock);
|
||||
ctx = xa_erase(&fpriv->ctx_mgr.ctx_handles, id);
|
||||
amdgpu_ctx_put(ctx);
|
||||
|
||||
return ctx ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
@@ -559,20 +522,12 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
|
||||
union drm_amdgpu_ctx_out *out)
|
||||
{
|
||||
struct amdgpu_ctx *ctx;
|
||||
struct amdgpu_ctx_mgr *mgr;
|
||||
unsigned reset_counter;
|
||||
|
||||
if (!fpriv)
|
||||
ctx = amdgpu_ctx_get(fpriv, id);
|
||||
if (!ctx)
|
||||
return -EINVAL;
|
||||
|
||||
mgr = &fpriv->ctx_mgr;
|
||||
mutex_lock(&mgr->lock);
|
||||
ctx = idr_find(&mgr->ctx_handles, id);
|
||||
if (!ctx) {
|
||||
mutex_unlock(&mgr->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* TODO: these two are always zero */
|
||||
out->state.flags = 0x0;
|
||||
out->state.hangs = 0x0;
|
||||
@@ -586,7 +541,8 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
|
||||
out->state.reset_status = AMDGPU_CTX_UNKNOWN_RESET;
|
||||
ctx->reset_counter_query = reset_counter;
|
||||
|
||||
mutex_unlock(&mgr->lock);
|
||||
amdgpu_ctx_put(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -619,19 +575,11 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev,
|
||||
{
|
||||
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
|
||||
struct amdgpu_ctx *ctx;
|
||||
struct amdgpu_ctx_mgr *mgr;
|
||||
|
||||
if (!fpriv)
|
||||
ctx = amdgpu_ctx_get(fpriv, id);
|
||||
if (!ctx)
|
||||
return -EINVAL;
|
||||
|
||||
mgr = &fpriv->ctx_mgr;
|
||||
mutex_lock(&mgr->lock);
|
||||
ctx = idr_find(&mgr->ctx_handles, id);
|
||||
if (!ctx) {
|
||||
mutex_unlock(&mgr->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out->state.flags = 0x0;
|
||||
out->state.hangs = 0x0;
|
||||
|
||||
@@ -671,7 +619,8 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev,
|
||||
msecs_to_jiffies(AMDGPU_RAS_COUNTE_DELAY_MS));
|
||||
}
|
||||
|
||||
mutex_unlock(&mgr->lock);
|
||||
amdgpu_ctx_put(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -680,26 +629,26 @@ static int amdgpu_ctx_stable_pstate(struct amdgpu_device *adev,
|
||||
bool set, u32 *stable_pstate)
|
||||
{
|
||||
struct amdgpu_ctx *ctx;
|
||||
struct amdgpu_ctx_mgr *mgr;
|
||||
int r;
|
||||
int r = 0;
|
||||
|
||||
if (!fpriv)
|
||||
ctx = amdgpu_ctx_get(fpriv, id);
|
||||
if (!ctx)
|
||||
return -EINVAL;
|
||||
|
||||
mgr = &fpriv->ctx_mgr;
|
||||
mutex_lock(&mgr->lock);
|
||||
ctx = idr_find(&mgr->ctx_handles, id);
|
||||
if (!ctx) {
|
||||
mutex_unlock(&mgr->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* The get path is odd in this uapi - it will check whether the context
|
||||
* id exist, but otherwise does nothing with it. In other words, the
|
||||
* uapi has historically been implemented as being able to query the
|
||||
* global device state, as long as the caller supplies a random valid
|
||||
* context id.
|
||||
*/
|
||||
|
||||
if (set)
|
||||
r = amdgpu_ctx_set_stable_pstate(ctx, *stable_pstate);
|
||||
else
|
||||
r = amdgpu_ctx_get_stable_pstate(ctx, stable_pstate);
|
||||
*stable_pstate = amdgpu_get_stable_pstate(adev);
|
||||
|
||||
mutex_unlock(&mgr->lock);
|
||||
amdgpu_ctx_put(ctx);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -778,23 +727,14 @@ struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id)
|
||||
|
||||
mgr = &fpriv->ctx_mgr;
|
||||
|
||||
mutex_lock(&mgr->lock);
|
||||
ctx = idr_find(&mgr->ctx_handles, id);
|
||||
xa_lock(&mgr->ctx_handles);
|
||||
ctx = xa_load(&mgr->ctx_handles, id);
|
||||
if (ctx)
|
||||
kref_get(&ctx->refcount);
|
||||
mutex_unlock(&mgr->lock);
|
||||
xa_unlock(&mgr->ctx_handles);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int amdgpu_ctx_put(struct amdgpu_ctx *ctx)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
kref_put(&ctx->refcount, amdgpu_ctx_do_release);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx,
|
||||
struct drm_sched_entity *entity,
|
||||
struct dma_fence *fence)
|
||||
@@ -928,8 +868,7 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr,
|
||||
unsigned int i;
|
||||
|
||||
mgr->adev = adev;
|
||||
mutex_init(&mgr->lock);
|
||||
idr_init_base(&mgr->ctx_handles, 1);
|
||||
xa_init_flags(&mgr->ctx_handles, XA_FLAGS_ALLOC1);
|
||||
|
||||
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i)
|
||||
atomic64_set(&mgr->time_spend[i], 0);
|
||||
@@ -938,13 +877,13 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr,
|
||||
long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout)
|
||||
{
|
||||
struct amdgpu_ctx *ctx;
|
||||
struct idr *idp;
|
||||
uint32_t id, i, j;
|
||||
unsigned long id;
|
||||
int i, j;
|
||||
|
||||
idp = &mgr->ctx_handles;
|
||||
|
||||
mutex_lock(&mgr->lock);
|
||||
idr_for_each_entry(idp, ctx, id) {
|
||||
xa_lock(&mgr->ctx_handles);
|
||||
xa_for_each(&mgr->ctx_handles, id, ctx) {
|
||||
kref_get(&ctx->refcount);
|
||||
xa_unlock(&mgr->ctx_handles);
|
||||
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
|
||||
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
|
||||
struct drm_sched_entity *entity;
|
||||
@@ -956,45 +895,21 @@ long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout)
|
||||
timeout = drm_sched_entity_flush(entity, timeout);
|
||||
}
|
||||
}
|
||||
amdgpu_ctx_put(ctx);
|
||||
xa_lock(&mgr->ctx_handles);
|
||||
}
|
||||
mutex_unlock(&mgr->lock);
|
||||
xa_unlock(&mgr->ctx_handles);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static void amdgpu_ctx_mgr_entity_fini(struct amdgpu_ctx_mgr *mgr)
|
||||
{
|
||||
struct amdgpu_ctx *ctx;
|
||||
struct idr *idp;
|
||||
uint32_t id, i, j;
|
||||
|
||||
idp = &mgr->ctx_handles;
|
||||
|
||||
idr_for_each_entry(idp, ctx, id) {
|
||||
if (kref_read(&ctx->refcount) != 1) {
|
||||
drm_err(adev_to_drm(mgr->adev), "ctx %p is still alive\n", ctx);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
|
||||
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
|
||||
struct drm_sched_entity *entity;
|
||||
|
||||
if (!ctx->entities[i][j])
|
||||
continue;
|
||||
|
||||
entity = &ctx->entities[i][j]->entity;
|
||||
drm_sched_entity_fini(entity);
|
||||
}
|
||||
}
|
||||
kref_put(&ctx->refcount, amdgpu_ctx_fini);
|
||||
}
|
||||
}
|
||||
|
||||
void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr)
|
||||
{
|
||||
amdgpu_ctx_mgr_entity_fini(mgr);
|
||||
idr_destroy(&mgr->ctx_handles);
|
||||
mutex_destroy(&mgr->lock);
|
||||
struct amdgpu_ctx *ctx;
|
||||
unsigned long id;
|
||||
|
||||
xa_for_each(&mgr->ctx_handles, id, ctx)
|
||||
amdgpu_ctx_put(ctx);
|
||||
xa_destroy(&mgr->ctx_handles);
|
||||
}
|
||||
|
||||
void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
|
||||
@@ -1002,21 +917,21 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
|
||||
{
|
||||
struct amdgpu_ctx *ctx;
|
||||
unsigned int hw_ip, i;
|
||||
uint32_t id;
|
||||
unsigned long id;
|
||||
|
||||
/*
|
||||
* This is a little bit racy because it can be that a ctx or a fence are
|
||||
* destroyed just in the moment we try to account them. But that is ok
|
||||
* since exactly that case is explicitely allowed by the interface.
|
||||
*/
|
||||
mutex_lock(&mgr->lock);
|
||||
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
|
||||
uint64_t ns = atomic64_read(&mgr->time_spend[hw_ip]);
|
||||
|
||||
usage[hw_ip] = ns_to_ktime(ns);
|
||||
}
|
||||
|
||||
idr_for_each_entry(&mgr->ctx_handles, ctx, id) {
|
||||
xa_lock(&mgr->ctx_handles);
|
||||
xa_for_each(&mgr->ctx_handles, id, ctx) {
|
||||
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
|
||||
for (i = 0; i < amdgpu_ctx_num_entities[hw_ip]; ++i) {
|
||||
struct amdgpu_ctx_entity *centity;
|
||||
@@ -1030,5 +945,5 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&mgr->lock);
|
||||
xa_unlock(&mgr->ctx_handles);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include "amdgpu_ring.h"
|
||||
|
||||
@@ -60,16 +61,21 @@ struct amdgpu_ctx {
|
||||
|
||||
struct amdgpu_ctx_mgr {
|
||||
struct amdgpu_device *adev;
|
||||
struct mutex lock;
|
||||
/* protected by lock */
|
||||
struct idr ctx_handles;
|
||||
struct xarray ctx_handles;
|
||||
atomic64_t time_spend[AMDGPU_HW_IP_NUM];
|
||||
};
|
||||
|
||||
extern const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM];
|
||||
|
||||
struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id);
|
||||
int amdgpu_ctx_put(struct amdgpu_ctx *ctx);
|
||||
|
||||
void amdgpu_ctx_fini(struct kref *kref);
|
||||
|
||||
static inline void amdgpu_ctx_put(struct amdgpu_ctx *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
kref_put(&ctx->refcount, amdgpu_ctx_fini);
|
||||
}
|
||||
|
||||
int amdgpu_ctx_get_entity(struct amdgpu_ctx *ctx, u32 hw_ip, u32 instance,
|
||||
u32 ring, struct drm_sched_entity **entity);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "amdgpu.h"
|
||||
@@ -1748,6 +1749,12 @@ int amdgpu_debugfs_regs_init(struct amdgpu_device *adev)
|
||||
struct dentry *ent, *root = minor->debugfs_root;
|
||||
unsigned int i;
|
||||
|
||||
if (security_locked_down(LOCKDOWN_PCI_ACCESS)) {
|
||||
drm_info(adev_to_drm(adev),
|
||||
"amdgpu: HW debugfs nodes disabled (kernel lockdown)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
|
||||
ent = debugfs_create_file(debugfs_regs_names[i],
|
||||
S_IFREG | 0400, root,
|
||||
@@ -2180,6 +2187,8 @@ int amdgpu_debugfs_init(struct amdgpu_device *adev)
|
||||
|
||||
if (!ring)
|
||||
continue;
|
||||
if (ring == &adev->cper.ring_buf && !adev->cper.enabled)
|
||||
continue;
|
||||
|
||||
amdgpu_debugfs_ring_init(adev, ring);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ const char *hw_ip_names[MAX_HWIP] = {
|
||||
[VCN1_HWIP] = "VCN1",
|
||||
[VCE_HWIP] = "VCE",
|
||||
[VPE_HWIP] = "VPE",
|
||||
[UMSCH_HWIP] = "UMSCH",
|
||||
[DF_HWIP] = "DF",
|
||||
[DCE_HWIP] = "DCE",
|
||||
[OSSSYS_HWIP] = "OSSSYS",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user